實習生的門票 #2 來做個簡易 Blog 吧!

樣板、控制器、資源檔方法

上次完成了 Auth 認證登入,緊接著來做樣板。

建立樣板

前端主要的樣式套用 bootstrap
  1. 定義主樣板( Master )
<!DOCTYPE html>
<html lang="en">
<head>
   @include('partials.head')
   <title>Jerry's Blog - @yield('title')</title>
</head>
<body>
    @include('partials.nav')
    <div class="container">
        @section('sidebar')
           @include('partials.sidebar')
        @show
        @yield('content')
    </div>
    @include('partials.footer')
</body>
</html>

這裡主要想提一下 Blade 樣板的用法:

  • 引入樣板
@include('樣板名稱')
  • 定義一個區塊
@section('名稱')
  • 定義一個區塊,讓 section 名稱相對應的區塊內容填入
    簡單來說,就是先在 html 上挖一個空格預留給 section 區塊,可以稍後再定義 section 裡面的內容
@yield('名稱')
  • 繼承樣板
@extends('樣板名稱')

2. 定義 head 、nav、footer 樣板

  • head 樣板
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="{{ asset('css/bootstrap.min.css') }}"><script type="text/javascript" src="{{ asset('js/jquery.min.js') }}"></script>
<script type="text/javascript" src="{{ asset('js/bootstrap.min.js') }}"></script>

這裡有個重點,laravel 提供一個輔助方法 asset() 來幫助取得專案根目錄,也就是 public 資料夾所對應的位置

  • nav 樣板 (截取片段來說明)
@if(Auth::check())
  <li>
  <div class="btn-group">
  <button type="button" class="btn btn-warning dropdown-toggle">
    {{ Auth::()user->name }}
  </button>
@else
  <li>
    <a href="{{ route('login')}}">登入</a>
  </li>
@endif

這裡可以看到 blade 樣板提供了簡單的邏輯判斷,搭配 Auth::check() 方法。 Auth::check() 表示有認證登入狀態; Auth::user() 則是代表目前登入的 user。

  • footer 樣板
<footer class="footer">
<div class="container">
<p>
<a href="#">Copyright © Jerry's Blog</a>| 2018.8月 Pickone 作業
</p>
</div>
</footer>

簡單的做個頁尾

3. 建立首頁樣板

@extends('layout.master')
@section('title','所有文章')
@section('content')
<div class="content col-md-8">
<div class="row">
     @forelse($posts as $post)
        <div class="article col-sm-12">
<h3>{{ $post->title }}</h3>
<div class="detail">
<p>作者:<a href="">{{ $post->author }}</a> | 發文時間:{{ $post->timestamp }}</p>
</div>
<div class="cover">
<img src="{{ $post->cover_img }}" alt="{{ $post->title }}的封面圖片">
</div>
<div class="preview">
<p>{{ $post->content }}</p>
</div>
<a href="{{ route('post.show',['post'=>'post_id']) }}">閱讀全文</a>
</div>
    @empty
       <p class="text-center">沒有文章囉!趕快來投稿吧!</p>
    @endforelse
    </div>
</div>
@stop

下面這一組中間可以搭配資料庫沒有資料的狀況要呈現什麼

@forelse 
@empty
@endforelse

幾個樣板做完後,之後的 View 就可以用這些拼拼湊湊囉!


設定 Controller

  1. 先來改一下 login 與 register 後導向到首頁
     App\Http\Controllers\Auth 下的 LoginController.phpRegisterController.php
protected $redirectTo = '/';

2. 在寫 blade 的時候,發現登出的路由有點問題,我看 app.blade.php裡,確實登出只要下

{{ route('logout') }}

可是會噴錯,噴錯時候只出現

MethodNotAllowedHttpException

看了文件也沒發現有 Logout 專用的 Controller,後來找到這篇 stack overflow文章,非最佳解的第一個解答提供,加上以下路由即可

Route::get('logout','Auth\LoginController@logout');

嗯,還真的可以了!不過這裡我不太懂為什麼,因為 LoginController 沒有定義 logout( ) 方法,這裡算是還沒解決的問題之一,不過可以正常登出。

順便修改產生首頁的路由

Route::get('/', 'PostController@index');

3. 接下來進入重點 Controller: PostController

先用 artisan 建立 PostController,並帶入資源檔

php artisan make:controller PostController --resource

打開 PostController 就會發現 laravel 很貼心的幫你生成了一些會用到的方法

  • index( ) 方法
Display a listing of the resource.

註解明白的告訴我們,index( ) 方法是要把每筆資料都列印出來,這裡就可以運用到首頁列出所有文章的功能上,因此可以下列出所有文章的 query

$posts = PostEloquent::orderBy('created_at','DESC')->get();
return view('index',['posts'=>$posts]);

此外 get( ) 得到的結果是一串陣列,因此到要輸出的時候要用 foreach (我使用 forelse )才能將值取出。

寫完 index( ) 方法後,就順利的能在首頁看到資料了。(請忽略編排)

首頁有資料了!
  • show( $id ) 方法

show( $id ) 方法,取出特定 id 的資料,這裡運用到顯示單一篇文章及內容

$post = PostEloquent::findOrFail($id);
return view('post',['post'=>$post]);
在這裡我開始想,要做新增文章的功能,接下來要使用create( ) 方法,還是 store( ) 方法呢?

creat( ) 的 HTTP 動詞是 GET,store( ) 的 HTTP 動詞是 POST。因此如果要建立表單的話就要選擇 store( ) 方法。

那什麼時候使用 create( )呢?
就是秀出新增文章( 或者說用來新增一筆資料的表單 )的頁面。

  • create( ) 方法,直接回傳新增文章的頁面
return view('create');
  • store( ) 方法
public function store(PostRequest $request){

$post = new PostEloquent($request->all());
$post->authors = Auth::user()->users_id;
$post->save();
return Redirect::route('post.index');
}

新增一個 PostEloquent 的物件,傳入 $request 的陣列,進而新增一筆 post。然而資料庫還有一個欄位 authors 新增文章的表單無法直接存取,因此用 Auth 的 user( ) 方法存進欄位,最後將這筆資料用 save( ) 存進資料庫。

請求進來之前,勢必要檢查一下符不符合規定,因此要建立一個新的 PostRequest 來做檢查

php artisan make:request PostRequest

再來調整 PostRequest 檔案

public function authorize(){
return Auth::check(); //檢查登入
}
public function rules(){
return [
'title' => 'required|string',
'content' => 'required|string'
];
}
//撰寫規則

寫完這個方法就可以新增文章成功了,再來是刪除文章的功能

  • destroy ( ) 方法
$post = PostEloquent::findOrFail($id);
$post->delete();
return Redirect::route('post.index');

直觀的傳入 $id ,找到該筆資料並刪除
該注意的是,destroy( ) 也是使用 POST

遇到問題,刪除的動作一直無法成功,拋出錯誤

stack overflow 表示,在刪除的表單下必須增加

{{ method_field('DELETE') }}

文件 表示這是屬於「表單方法欺騙」

HTML forms do not support PUT, PATCH or DELETE actions. So, when defining PUT, PATCH or DELETE routes that are called from an HTML form, you will need to add a hidden _method field to the form.

Resource 資源檔方法剩下最後兩個,edit( ) 與 update( )。其實道理跟create( ) 及 store( ) 一樣,前者都是顯示要編輯/創建的頁面,後者都是做 POST 方法,把檔案傳進資料庫。

  • edit( ) 方法,把文章找出來,傳到編輯的 View
$post = PostEloquent::findOrFail($id);
return view('edit',['post'=>$post]);
  • update( ) 方法
$post = PostEloquent::findOrFail($id);
$post->title = $request->title;
$post->content = $request->content;
$post->save();
return Redirect::route('post.index');

把特定的文章找出來後,替換標題、內容之後,將資料再存回資料庫

記得要在修改的表單做表單方法欺騙,這次是 update 對應的 HTTP 動詞為 PUT。

{{ method_field('PUT') }}
Resource 資源檔方法默默被填寫完畢,而新增、修改、刪除文章的功能也大功告成囉!

套上一些樣式後,似乎有模有樣了!


此次多學到了

  • Controller 資源檔的 index、show、create、store、edit、update 方法的用法與意義。
  • 表單方法欺騙,因為 HTTP 表單只有預設 POST 及 GET 功能,如果做 DELETE、PUT 及 PATCH 就要使用表單方法欺騙

待解決的問題

  • Logout 與 LoginController 之間何處有連動

本次心得

想不到一天就能完成以前還沒用框架寫 PHP 時那些很基本的功能,開發速度真的快上很多。有越寫越得心應手,越來越想往後端發展了 XD。只是寫法還不太算熟練,有時還是得要去翻翻書、看看文件才會想起來。希望之後能多練幾個 Laravel 的專案,也想和他人共同維護專案,順便學習他人的寫法。

如果內容有什麼很大的錯誤,歡迎指正喔!還在學習寫文章中😂
Like what you read? Give Jerrry Weng a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.