這一章我們將基于博客文章模型進(jìn)行延伸,建立評(píng)論模型坏瘩,處理文章的評(píng)論功能盅抚。我們使用 ORM 進(jìn)行數(shù)據(jù)檢索,并介紹兩個(gè)模型之間的關(guān)聯(lián)倔矾。還將探索如何通過(guò)數(shù)據(jù)庫(kù)遷移對(duì)數(shù)據(jù)庫(kù)進(jìn)行更改操作妄均,創(chuàng)建首頁(yè)和提供用戶(hù)為博客文章發(fā)表評(píng)論的能力。
關(guān)于首頁(yè)
我們先開(kāi)始建立了首頁(yè)哪自,一般博客會(huì)在這里顯示每片文章的摘要丰包,從最新的向最舊的排序顯示,完整的博客文章會(huì)在點(diǎn)擊鏈接后進(jìn)入文章的展示頁(yè)。
由于我們已經(jīng)建立首頁(yè)的路由,控制器和視圖,我們只要更新它們就好檀训。
獲取文章數(shù)據(jù)
為了顯示博客文章,我們需要從數(shù)據(jù)庫(kù)讀取他們,Eloquent 提供了基于模型的查詢(xún)構(gòu)造器侦讨。我們可以利用基于模型的 QueryBuilder 來(lái)讀取數(shù)據(jù)需忿。
現(xiàn)在找到位于 app/Http/Controllers/PageController.php
的 index
方法澈魄,更新為如下內(nèi)容:
public function index()
{
$posts = Post::orderBy('id', 'DESC')->get();
return view('pages.index', ['posts' => $posts]);
}
這里按照 id
排序是因?yàn)樗跀?shù)據(jù)庫(kù)是自增長(zhǎng)字段和添加時(shí)間字段在排序上作用一樣。
顯示視圖
@extends('layouts.app')
@section('title', 'larablog')
@section('body')
@forelse($posts as $post)
<article class="blog">
<div class="date">{{ $post->created_at->toDateTimeString() }}</div>
<header>
<h2><a href="/posts/{{ $post->id }}">{{ $post->title }}</a></h2>
</header>
<img src="{{ asset('images/'.$post->image) }}" />
<div class="snippet">
<p>{!! str_limit(strip_tags($post->content), 500) !!}</p>
<p class="continue"><a href="/posts/{{ $post->id }}">Continue reading...</a></p>
</div>
<footer class="meta">
<p>Comments: -</p>
<p>Posted by <span class="highlight">{{ $post->author }}</span> at {{ $post->created_at->toDateTimeString() }}</p>
<p>Tags: <span class="highlight">{{ $post->tags }}</span></p>
</footer>
</article>
@empty
<p>There are no blog entries for larablog</p>
@endforelse
@endsection
我們?cè)谶@里使用了一個(gè)模板引擎的控制結(jié)構(gòu) forelse..empty..endforelse
勉痴,如果你過(guò)去沒(méi)有使用模板引擎斩祭,你也應(yīng)該熟悉這樣的代碼:
<?php if (count($posts)): ?>
<?php foreach ($posts as $post): ?>
<h1><?php echo $post->getTitle() ?><?h1>
<!-- rest of content -->
<?php endforeach ?>
<?php else: ?>
<p>There are no post entries</p>
<?php endif ?>
在 Laravel 中 Blade 模型引擎的 forelse..empty..endforelse
控制結(jié)構(gòu)可以簡(jiǎn)潔的完成上面的任務(wù)。
接著癞揉,我們用 <p>{{ str_limit(strip_tags($post->content), 500) }}</p>
輸出文章摘要烦味,傳入的參數(shù) 500
是摘要字符的限制長(zhǎng)度钥勋。
我們通過(guò) strip_tags
剝除字符串中的 HTML、XML 以及 PHP 的標(biāo)簽沐旨,這樣截取長(zhǎng)度時(shí)不會(huì)將 HTML 標(biāo)記計(jì)算在內(nèi)谊迄。
打開(kāi)瀏覽器重新訪(fǎng)問(wèn) http://localhost:8000/
你將會(huì)看到顯示最新文章的首頁(yè),也能夠點(diǎn)擊文章標(biāo)題或 Continue reading...
鏈接來(lái)查看整篇文章的內(nèi)容。
評(píng)論模型
除了文章剪个,我們還希望讀者能夠?qū)ξ恼略u(píng)論扣囊。這些評(píng)論需要和關(guān)聯(lián)在一起骂澄,因?yàn)橐黄恼驴梢园鄠€(gè)評(píng)論內(nèi)容。
在命令行中進(jìn)入項(xiàng)目所在目錄紊遵,執(zhí)行命令:
php artisan make:model Comment -m
該命令將會(huì)生成模型 app/Comment.php
和數(shù)據(jù)遷移文件 database/migrations/xxxx_xx_xx_xxxxxx_create_comments_table.php
御雕。
編輯 app/Comment.php
,修改為如下內(nèi)容:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model
{
/**
* 獲取擁有此評(píng)論的文章闽坡。
*/
public function post()
{
return $this->belongsTo(App\Post::class);
}
}
這里是用了 Eloquent 關(guān)聯(lián)的關(guān)系來(lái)定義兩個(gè)模型之間的聯(lián)系栽惶。
一旦關(guān)聯(lián)被定義之后,則可以通過(guò) post「動(dòng)態(tài)屬性」來(lái)獲取 Comment
的 Post
模型疾嗅,如下:
$comment = App\Comment::find(1);
echo $comment->post->title;
詳細(xì)的關(guān)聯(lián)關(guān)系內(nèi)容可以查閱文檔 Eloquent 定義關(guān)聯(lián)
接下來(lái)外厂,我們?cè)?app/Post.php
中也定義相對(duì)對(duì)應(yīng)的關(guān)系。
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
/**
* 獲取博客文章的評(píng)論代承。
*/
public function comments()
{
return $this->hasMany(App\Comment::class);
}
}
用簡(jiǎn)單的話(huà)來(lái)描述即是:一篇文章有多條評(píng)論汁蝶,一條評(píng)論屬于某篇文章。
數(shù)據(jù)遷移
在我們定義模型以及關(guān)聯(lián)關(guān)系后论悴,接下來(lái)我們要?jiǎng)?chuàng)建評(píng)論表掖棉,打開(kāi)我們生成的遷移文件 database/migrations/xxxx_xx_xx_xxxxxx_create_comments_table.php
。編輯內(nèi)容如下:
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateCommentsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('comments', function (Blueprint $table) {
$table->increments('id');
$table->integer('post_id')->unsigned();
$table->string('user');
$table->text('comment');
$table->boolean('approved');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('comments');
}
}
這里的 post_id
將于 posts
表中的 id
關(guān)聯(lián)膀估,approved
表示評(píng)論是否通過(guò)審核幔亥,user
為評(píng)論發(fā)布人的名稱(chēng)。
接下來(lái)玖像,執(zhí)行命令 php artisan migrate
讓這個(gè)新的數(shù)據(jù)庫(kù)遷移腳本來(lái)創(chuàng)建我們需要的 comments
表。
填充數(shù)據(jù)
和之前一樣,我們對(duì)文章進(jìn)行過(guò)數(shù)據(jù)填充一樣捐寥,我們需要填充一些評(píng)論數(shù)據(jù)來(lái)進(jìn)行模擬和測(cè)試笤昨。
評(píng)論數(shù)據(jù)模板
我們打開(kāi) database/factories/ModelFactory.php
,新增評(píng)論模型數(shù)據(jù)模板:
$factory->define(App\Comment::class, function (Faker\Generator $faker) {
return [
'user' => $faker->name,
'comment' => $faker->paragraphs(rand(1, 3), true),
'approved' => rand(0, 1),
];
});
然后我們建立一個(gè)評(píng)論填充類(lèi)握恳,項(xiàng)目所在目錄下執(zhí)行命令 php artisan make:seed CommentsTableSeeder
瞒窒,打開(kāi)文件 database/seeds/CommentsTableSeeder.php
,編輯內(nèi)容如以下所示:
<?php
use Illuminate\Database\Seeder;
class CommentsTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$posts = App\Post::where('id', '<=', 10)->get();
foreach ($posts as $post) {
factory(App\Comment::class, rand(10, 20))->create()->each(function($c) use ($post) {
$c->post()->associate($post);
$c->save();
});
}
}
}
在這個(gè)數(shù)據(jù)填充類(lèi)中乡洼,我們的任務(wù)是崇裁,獲取 id
小于等于前 10
的文章,為它們產(chǎn)生評(píng)論數(shù)據(jù)束昵,每篇產(chǎn)生不等于 10
到 20
條評(píng)論記錄拔稳。完成了數(shù)據(jù)填充類(lèi),接下來(lái)執(zhí)行數(shù)據(jù)填充命令將測(cè)試數(shù)據(jù)添加到數(shù)據(jù)庫(kù)中:
php artisan db:seed --class=CommentsTableSeeder
這個(gè)命令指定了僅執(zhí)行 CommentsTableSeeder
類(lèi)指定的數(shù)據(jù)填充任務(wù)锹雏。執(zhí)行完畢后巴比,我們可以訪(fǎng)問(wèn) http://localhost/phpmyadmin
來(lái)查看我們最終添加的實(shí)際測(cè)試數(shù)據(jù)了。
顯示評(píng)論
有了之前的工作礁遵,接下來(lái)我們要為每一篇文章顯示其評(píng)論轻绞。
控制器
接著我們需要更新 PostsController
的 show
方法來(lái)獲取文章的評(píng)論。打開(kāi) app/Http/Controllers/PostsController.php
更改為如下內(nèi)容:
public function show($id)
{
$post = Post::findOrFail($id);
$comments = $post->comments;
return view('posts.show', [
'post' => $post,
'comments' => $comments
]);
}
這里的 $comments = $post->comments;
獲取某篇文章的評(píng)論數(shù)據(jù)是依靠 Post.php
中定義的 comments()
模型關(guān)系實(shí)現(xiàn)的佣耐。
更多內(nèi)容可以查閱 模型的查找關(guān)聯(lián)
文章顯示視圖
我們現(xiàn)在獲取到了評(píng)論的數(shù)據(jù)政勃,可以更新博客的視圖來(lái)顯示評(píng)論信息。我們可以在博客模板頁(yè)面直接放入評(píng)論內(nèi)容兼砖,不過(guò)由于評(píng)論相對(duì)于文章有一定的獨(dú)立性奸远,比較建議將評(píng)論部分的內(nèi)容單獨(dú)作為模板,然后再引入到博客模板頁(yè)面掖鱼,這樣我們可以在應(yīng)用中重復(fù)使用評(píng)論的顯示模板然走。打開(kāi) resources/views/posts/show.blade.php
,更新 body
塊中的內(nèi)容戏挡,如下所示:
@section('body')
<article class="blog">
<header>
<div class="date">{{ $post->created_at->format('l, F j, Y') }}</div>
<h2>{{ $post->title }}</h2>
</header>
<img src="{{ asset('images/'.$post->image) }}" alt="{{ $post->title }} image not found" class="large" />
<div>
<p>{{ $post->content }}</p>
</div>
</article>
<section class="comments" id="comments">
<section class="previous-comments">
<h3>Comments</h3>
@include('comments.index', ['comments' => $comments])
</section>
</section>
@endsection
你可以看到我們通過(guò)模板標(biāo)簽 @include
引入 comments/index.blade.php
文件中的內(nèi)容芍瑞。同時(shí)我們向引入的模板傳入了參數(shù) comments
,它是評(píng)論的數(shù)據(jù)褐墅。
評(píng)論顯示視圖
新建 resources/views/comments/index.blade.php
拆檬,內(nèi)容如下:
@forelse($comments as $i => $comment)
<article class="comment {{ $i % 2 == 0 ? 'odd' : 'even' }}" id="comment-{{ $comment->id }}">
<header>
<p><span class="highlight">{{ $comment->user }}</span> commented {{ $comment->created_at->format('l, F j, Y') }}</p>
</header>
<p>{{ $comment->comment }}</p>
</article>
@empty
<p>There are no comments for this post. Be the first to comment...</p>
@endforelse()
如你所見(jiàn),我們通過(guò)迭代循環(huán)輸出了每一條評(píng)論的信息妥凳。
評(píng)論顯示 CSS
最后我們?cè)黾右恍?CSS 來(lái)讓評(píng)論看起來(lái)會(huì)更漂亮竟贯,打開(kāi) public/css/blog.css
, 增加如下內(nèi)容:
.comments { clear: both; }
.comments .odd { background: #eee; }
.comments .comment { padding: 20px; }
.comments .comment p { margin-bottom: 0; }
.comments h3 { background: #eee; padding: 10px; font-size: 20px; margin-bottom: 20px; clear: both; }
.comments .previous-comments { margin-bottom: 20px; }
如果你通過(guò)瀏覽器打開(kāi) http://localhost:8000/posts/1
頁(yè)面,文章的評(píng)論將會(huì)呈現(xiàn)在你的面前逝钥。
新增評(píng)論
這個(gè)章節(jié)的最后一個(gè)部分會(huì)加入一個(gè)功能讓用戶(hù)能對(duì)文章進(jìn)行評(píng)論屑那,我們通過(guò)博客文章的詳情頁(yè)表單來(lái)進(jìn)行處理。我們構(gòu)建聯(lián)系頁(yè)面時(shí)已介紹過(guò)如何提交表單和處理表單,接下來(lái)也會(huì)按照之前的思路來(lái)建立這個(gè)表單功能持际。
路由
我們需要建立一個(gè)新的路由來(lái)處理表單請(qǐng)求的提交沃琅。打開(kāi) app/Http/routes.php
,添加如下內(nèi)容:
Route::post('posts/{post_id}/comments', 'CommentsController@store');
我們建立一了一個(gè)接受 POST
請(qǐng)求的路由配置信息蜘欲,它通過(guò)的 CommentsController
的 store
進(jìn)行處理益眉。
表單請(qǐng)求處理
接下來(lái),我們建立表單請(qǐng)求姥份,在命令行中進(jìn)入項(xiàng)目目錄執(zhí)行如下命令 php artisan make:request CommentRequest
郭脂。
打開(kāi)表單請(qǐng)求文件 app/Http/Requests/CommentRequest.php
,編輯內(nèi)容如下所示:
<?php
namespace App\Http\Requests;
use App\Http\Requests\Request;
class CommentRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'user' => 'required',
'comment' => 'required'
];
}
}
由于我們不要需要用戶(hù)授權(quán)認(rèn)證澈歉,所以 authorize
方法直接返回 true
展鸡,我們?cè)?rules
中定義了表單字段的要求,姓名 user
和評(píng)論內(nèi)容 comment
為必填內(nèi)容闷祥。
控制器處理
定義了路由和表單請(qǐng)求娱颊,我們需要建立控制器來(lái)處理請(qǐng)求,在命令行中國(guó)年進(jìn)入項(xiàng)目目錄凯砍,執(zhí)行 php artisan make:controller CommentsController
箱硕,修改內(nèi)容如下所示:
<?php
namespace App\Http\Controllers;
use App\Post;
use App\Comment;
use App\Http\Requests\CommentRequest;
class CommentsController extends Controller
{
public function store(CommentRequest $request, $postId)
{
$post = Post::findOrFail($postId);
$comment = new Comment;
$comment->user = $request->get('user');
$comment->comment = $request->get('comment');
$comment->post()->associate($post);
$comment->save();
return redirect()->back()->with('message', 'Success!');
}
}
請(qǐng)求的 URL(posts/{post_id}/comments
) 中包含的 post_id
可以通過(guò)控制器方法的參數(shù) $postId
獲取到相應(yīng)的值。
其次悟衩,我們之前提到建立的 CommentRequest
會(huì)先處理表單提交過(guò)來(lái)的請(qǐng)求剧罩,表單合法后才會(huì)執(zhí)行控制器方法中的內(nèi)容。
我們?cè)?store
中方法實(shí)例化了 Comment
座泳,獲取請(qǐng)求的數(shù)據(jù)設(shè)置給評(píng)論模型的實(shí)例惠昔,還為評(píng)論實(shí)例設(shè)置了關(guān)聯(lián)對(duì)象實(shí)例 $post
,最后通過(guò) save
方法保存實(shí)例到數(shù)據(jù)庫(kù)挑势,整個(gè)處理過(guò)程就是這樣镇防。
視圖顯示
接著我們來(lái)建立評(píng)論的表單模板,位于 resources/views/comments/new.blade.php
潮饱,內(nèi)容如下:
<form action="/posts/{{ $post->id }}/comments" method="post" class="blogger">
{!! csrf_field() !!}
<div>
<label for="user" class="required">User</label>
<input type="text" id="user" name="user" required="required" />
</div>
<div>
<label for="comment" class="required">Comment</label>
<textarea id="comment" name="comment" required="required"></textarea>
</div>
<p><input type="submit" value="Submit"></p>
</form>
同時(shí)来氧,我還要修改博客文章的顯示模板 resources/views/posts/show.blade.php
, 修改為以下內(nèi)容:
@extends('layouts.app')
@section('title', $post->title)
@section('body')
<article class="blog">
<header>
<div class="date">{{ $post->created_at->format('l, F j, Y') }}</div>
<h2>{{ $post->title }}</h2>
</header>
<img src="{{ asset('images/'.$post->image) }}" alt="{{ $post->title }} image not found" class="large" />
<div>
<p>{{ $post->content }}</p>
</div>
</article>
<section class="comments" id="comments">
<section class="previous-comments">
<h3>Comments</h3>
@include('comments.index', ['comments' => $comments])
</section>
<h3>Add Comment</h3>
@include('comments.new', ['post' => $post])
</section>
@endsection
我們?cè)谖恼略斍榈哪0屙?yè)面引入了添加評(píng)論的表單視圖模板 comments.new
,同時(shí)傳入當(dāng)前文章對(duì)象香拉。通過(guò)片段組合的方式我們將文章模板和評(píng)論模板聯(lián)系在一了一起啦扬。
打開(kāi)瀏覽器午阵,訪(fǎng)問(wèn)網(wǎng)站中其中一篇文章的詳情頁(yè)面圈匆,你現(xiàn)在應(yīng)該可以對(duì)文章進(jìn)行評(píng)論了,試試看淌喻。
總結(jié)
我們?cè)谶@個(gè)章節(jié)有不錯(cuò)的進(jìn)展盛险,我們的博客開(kāi)始像預(yù)期一樣的運(yùn)作瞄摊。我們現(xiàn)在建立了基本的首頁(yè)與評(píng)論模型勋又,用戶(hù)可以給某篇文章發(fā)表評(píng)論以及閱讀其它用戶(hù)留下的評(píng)論,我們看到如何建立多個(gè)數(shù)據(jù)模型之間的關(guān)系换帜,并使用模型來(lái)建立查詢(xún)和存儲(chǔ)操作赐写。
接下來(lái)我們會(huì)建立一些頁(yè)面片段,包含標(biāo)簽云和最新評(píng)論膜赃,我們也會(huì)來(lái)了解如何擴(kuò)展模版引擎,最后我們會(huì)看看如何管理靜態(tài)資源庫(kù)揉忘。