Eloquent提供了一個易於閱讀且很形象化的表和表之間的關係對應和調用,好比說,一條評論是屬於一個帖子的,一個帖子擁有不少的評論,一篇帖子和一個視頻頁同時擁有不少標籤,下面咱們來看看如何建立這些關係。html
咱們就以一篇帖子有不少的評論來舉列,帖子和評論是一對多的關係,咱們上一節已經創建了帖子的表posts
, 下面咱們來創建評論表comments
和評論的ModelComment
, 在上一節咱們是經過下面兩條命令來創建migration文件和Model的laravel
// 創建帖子的migration文件 php artisan make:migration create_posts_table --create=posts // 創建帖子Model php artisan make:model Post
咱們在創建評論表和模型的時候,用另外一種方法,咱們在創建Model的時候,只要加上-m
參數,就能在創建Model的時候,同時生成migration文件了。(執行php artisan命令都是須要進入到項目的根目錄下執行的,之後我就不說這點了)sql
➜ php artisan make:model Comment -m Model created successfully. Created Migration: 2016_11_14_125930_create_comments_table
從上面咱們能夠看出,咱們建立模型的時候,laravel也幫咱們生成了表名爲模型名複數的migration文件,咱們打開這個migration文件,並更改up()函數以下:數據庫
public function up() { Schema::create('comments', function (Blueprint $table) { $table->increments('id'); $table->integer('post_id')->unsigned()->index(); $table->text('content'); $table->timestamps(); }); }
上面的post_id
是posts表的外鍵,在正式開發的時候,咱們須要作到外鍵約束,同時作到刪除的及聯操做,這裏咱們先不添加了。咱們將這個表執行到數據庫中數組
➜ php artisan migrate Migrated: 2016_11_14_125930_create_comments_table
如今在咱們的app
目錄下,咱們已經有了Post.php和Comment.php兩個模型,下面咱們打開tinker
緩存
➜ php artisan tinker Psy Shell v0.7.2 (PHP 7.0.12 — cli) by Justin Hileman >>>
如下的代碼都在tinker
中執行生成,咱們先來獲取第一條帖子數據:bash
>>> $post = App\Post::first(); => App\Post {#636 id: "1", title: "My New Post Title", content: "new post content", created_at: "2016-11-14 07:22:32", updated_at: "2016-11-14 07:22:32", }
咱們再來建立屬於帖子1的一條評論,咱們此次建立先手動的來維護外鍵(post_id):app
>>> $comment = new App\Comment; => App\Comment {#625} >>> $comment->content = 'Some comment for the post'; => "Some comment for the post" >>> $comment->post_id = 1; => 1 >>> $comment->save(); => true >>> App\Comment::all(); => Illuminate\Database\Eloquent\Collection {#640 all: [ App\Comment {#641 id: "1", post_id: "1", content: "Some comment for the post", created_at: "2016-11-15 01:07:53", updated_at: "2016-11-15 01:07:53", }, ], } >>>
下面咱們來經過Eloquent在各模型間創建表與表之間的對應關係,首先咱們先理一下,一個帖子會有不少評論,A post has many comments
, 一個評論屬於一個帖子:a comment that belongs to a post
,咱們打開Post.php,編寫一個comments()
函數,意思是一個帖子有不少評論,因此注意這個comments()
必定要寫成複數形式,寫代碼單詞的單複數對於易讀性來講很是的重要。編輯器
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Post extends Model { public function comments() { return $this->hasMany('App\Comment'); } }
好的,咱們再進入到tinker
中來測試下:
咱們先拿到第一個帖子:
>>> $post = App\Post::first(); => App\Post {#636 id: "1", title: "My New Post Title", content: "new post content", created_at: "2016-11-14 07:22:32", updated_at: "2016-11-14 07:22:32", }
咱們拿取這個帖子的全部評論,咱們能夠這麼寫$post->comments()->get()
,也能夠這麼寫$post->comments;
, 後面這種寫法,laravel文檔叫它動態屬性。
>>> $post->comments; => Illuminate\Database\Eloquent\Collection {#633 all: [ App\Comment {#637 id: "1", post_id: "1", content: "Some comment for the post", created_at: "2016-11-15 01:07:53", updated_at: "2016-11-15 01:07:53", }, ], }
用上面的方法拿出的數據實際上是Comment對象的一個集合(Collection),咱們能夠像操做數組同樣的操做這個集合,如:
>>> $post->comments[0]; => App\Comment {#637 id: "1", post_id: "1", content: "Some comment for the post", created_at: "2016-11-15 01:07:53", updated_at: "2016-11-15 01:07:53", }
固然,開發的時候不多像上面這麼作,由於Laravel給咱們提供了不少關於操做這個集合的方法,好比說,取集合中的第一個對象:
>>> $post->comments->first(); => App\Comment {#637 id: "1", post_id: "1", content: "Some comment for the post", created_at: "2016-11-15 01:07:53", updated_at: "2016-11-15 01:07:53", }
這裏有一個很是重要的地方,咱們來嘗試下面這條語句:
>>> $post->comments()->first(); => App\Comment {#651 id: "1", post_id: "1", content: "Some comment for the post", created_at: "2016-11-15 01:07:53", updated_at: "2016-11-15 01:07:53", }
咱們看到$post->comments->first();
和$post->comments()->first();
這兩條語句輸出的結果是同樣的,可是具體的操做卻不一樣,咱們假設帖子1有500條評論,那麼$post->comments->first();
會先經過$post->comments
從數據庫拿到這500條評論的數據放進集合,而後再從集合中獲取第一條數據。而$post->comments()->first();
呢,當執行到$post->comments()
時,它並無拿出這500條數據,這裏還處於一個查詢的階段,等到執行first()
時,從數據庫只拿出一條數據,咱們應該使用哪一種寫法,你們應該就很明白了。曾有人說不要用ORM,太慢,可是不少慢的緣由不在於ORM, 而是不瞭解它,沒用好而已。
咱們再看看這兩條語句執行的原生SQL語句,咱們在tinker
中讓每次執行語句的時候都打印出原生的SQL,能夠這麼作:
➜ php artisan tinker Psy Shell v0.7.2 (PHP 7.0.12 — cli) by Justin Hileman >>> DB::listen(function ($query) { var_dump($query->sql); }); => null
咱們再來拿第一個帖子:
>>> $post = App\Post::first(); string(29) "select * from "posts" limit 1" => App\Post {#637 id: "1", title: "My New Post Title", content: "new post content", created_at: "2016-11-14 07:22:32", updated_at: "2016-11-14 07:22:32", } >>>
拿帖子的全部評論,本身看下sql語句:
>>> $post->comments; string(92) "select * from "comments" where "comments"."post_id" = ? and "comments"."post_id" is not null" => Illuminate\Database\Eloquent\Collection {#623 all: [ App\Comment {#638 id: "1", post_id: "1", content: "Some comment for the post", created_at: "2016-11-15 01:07:53", updated_at: "2016-11-15 01:07:53", },
咱們再來執行一次$post->comments;
>>> $post->comments; => Illuminate\Database\Eloquent\Collection {#623 all: [ App\Comment {#638 id: "1", post_id: "1", content: "Some comment for the post", created_at: "2016-11-15 01:07:53", updated_at: "2016-11-15 01:07:53", }, ], }
咱們發現此次沒有出現SQL語句,那是由於laravel已經緩存了此次查詢的結果,咱們再來看下$post
的結果,它也被緩存了,而且咱們查詢的$post->comments
的內容也被插入到這個對象中。
>>> $post => App\Post {#637 id: "1", title: "My New Post Title", content: "new post content", created_at: "2016-11-14 07:22:32", updated_at: "2016-11-14 07:22:32", comments: Illuminate\Database\Eloquent\Collection {#623 all: [ App\Comment {#638 id: "1", post_id: "1", content: "Some comment for the post", created_at: "2016-11-15 01:07:53", updated_at: "2016-11-15 01:07:53", }, ], }, }
若是咱們刷新獲取下$post
,在打印$post
, 你們在看下結果:
>>> $post = $post->fresh(); string(44) "select * from "posts" where "id" = ? limit 1" => App\Post {#643 id: "1", title: "My New Post Title", content: "new post content", created_at: "2016-11-14 07:22:32", updated_at: "2016-11-14 07:22:32", } >>> $post => App\Post {#643 id: "1", title: "My New Post Title", content: "new post content", created_at: "2016-11-14 07:22:32", updated_at: "2016-11-14 07:22:32", }
由於laravel會緩存查詢,因此你們在測試的時候必定要加上fresh()
才能準確,咱們來看$post->comments->first()
,執行的時候要加上fresh()
,這很是的重要,千萬不要作了錯誤的測試誤導了你。
>>> $post->fresh()->comments->first(); string(44) "select * from "posts" where "id" = ? limit 1" string(92) "select * from "comments" where "comments"."post_id" = ? and "comments"."post_id" is not null" => App\Comment {#630 id: "1", post_id: "1", content: "Some comment for the post", created_at: "2016-11-15 01:07:53", updated_at: "2016-11-15 01:07:53", }
看上面的第二條SQL語句,它是查詢出數據庫的全部的評論。
咱們在來看$post->comments()->first()
這條語句:
>>> $post->fresh()->comments()->first(); string(44) "select * from "posts" where "id" = ? limit 1" string(100) "select * from "comments" where "comments"."post_id" = ? and "comments"."post_id" is not null limit 1" => App\Comment {#644 id: "1", post_id: "1", content: "Some comment for the post", created_at: "2016-11-15 01:07:53", updated_at: "2016-11-15 01:07:53", }
咱們看第2條SQL語句,用這種寫法,只會從數據庫拿出1條記錄,這裏我說這麼多,是由於我看見不少人在濫用動態屬性,因此咱們必定要注意這點。
好了,咱們如今看看在Comment模型中如何寫對應的關係呢?拿出以前咱們寫的英文句子:a comment that belongs to a post
, 咱們打開Comment.php,
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Comment extends Model { // 注意這裏post應該是單數形式 public function post() { return $this->belongsTo('App\Post'); // 若是你使用的是PhpStrom編輯器,你也能夠按下面這麼寫,這樣點擊能夠跳轉到對應的類文件中 // return $this->belongsTo(Post::class); } }
咱們從新打開tinker
, 當你修改了代碼後,要從新打開tinker
再測試,不然tinker
執行的仍是修改前的代碼:
>>> $comment = App\Comment::first(); => App\Comment {#636 id: "1", post_id: "1", content: "Some comment for the post", created_at: "2016-11-15 01:07:53", updated_at: "2016-11-15 01:07:53", } >>> $comment->post; => App\Post {#637 id: "1", title: "My New Post Title", content: "new post content", created_at: "2016-11-14 07:22:32", updated_at: "2016-11-14 07:22:32", } >>>
下面咱們來看下,咱們建立一條評論的時候,如何讓Eloquent的關聯關係給咱們自動維護外鍵:
咱們先建立一個$comment對象,設置它的內容:
>>> $comment = new App\Comment; => App\Comment {#622} >>> $comment->content = 'Here is another comment.'; => "Here is another comment." >>>
如今咱們不用手動去設置post_id
,咱們直接找到評論須要屬於的post,好比,仍是打算讓這條評論屬於第一個帖子:
>>> $post = App\Post::first(); => App\Post {#639 id: "1", title: "My New Post Title", content: "new post content", created_at: "2016-11-14 07:22:32", updated_at: "2016-11-14 07:22:32", }
下面咱們只須要經過Post的Comments()關聯去存儲屬於它的評論便可,會自動設置$post對象的ID到對應的評論的post_id
>>> $post->comments()->save($comment); => App\Comment {#622 content: "Here is another comment.", post_id: 1, updated_at: "2016-11-15 02:38:01", created_at: "2016-11-15 02:38:01", id: 2, }
如今經過$post->comments;
查看下,發現已經存在兩條評論了。
好了,上面的代碼都是在tinker
中測試的,咱們如今進入了PostsController中,修改下show()
函數:
public function show(Post $post) { return view('posts.show', compact('post')); }
而後創建show.blade.php視圖層,輸入如下代碼:
@extends('layout') @section('content') <h1>{{ $post->title }}</h1> <ul> @foreach ($post->comments as $comment) <li>{{ $comment->content }}</li> @endforeach </ul> @stop
好,咱們訪問下: http://localhost:8000/posts/1
咱們剛纔是用save()
方法來存儲一條評論,如今咱們來試試使用create()
方法來建立呢! 仍是打開tinker
嗯, 出現了匹配異常錯位,這是Laravel對使用create()
和update()
這兩個函數作的保護機制,咱們知道create()
和update()
能夠批量的設置表字段,若是不作一些保護錯位的話,可能會被人經過設置某些字段的值來串改你的數據,因此在Laravel中,你容許批量建立和修改的字段,你都要本身在模型中明確指定,咱們打開Comment.php
, 爲Comment模型添加$fillbale屬性:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Comment extends Model { // 容許使用create()和update()批量建立和更新的字段 protected $fillable = ['content']; public function post() { return $this->belongsTo(Post::class); } }
下面從新啓動tinker
,在執行一次,就能成功建立了
Psy Shell v0.7.2 (PHP 7.0.12 — cli) by Justin Hileman >>> $post = App\Post::first(); => App\Post {#636 id: "1", title: "My New Post Title", content: "new post content", created_at: "2016-11-14 07:22:32", updated_at: "2016-11-14 07:22:32", } >>> $post->comments()->create(['content' => 'Yet another comment about this post']); => App\Comment {#640 content: "Yet another comment about this post", post_id: 1, updated_at: "2016-11-15 03:08:25", created_at: "2016-11-15 03:08:25", id: 3, } >>>
咱們在進入到posts/index.balde.php
中,咱們給帖子都加上連接:
@extends('layout') @section('content') <h1>全部的帖子</h1> @foreach ($posts as $post) <h2><a href="posts/{{ $post->id }}">{{ $post->title }}</a></h2> <p>{{ $post->content }}</p> @endforeach @stop
加連接有不少方法,也有人會寫成一個函數,如<a href="{{ $post->path() }}">
, 而後在Post模型層寫一個path()函數
public function path() { return '/posts/' . $this->id; }
不過把這個寫成函數我認爲也沒多大必要。好了,本節到這裏結束。
Eloquent關聯模型的用法你們應該知道了,可是除了一對多關係,還有一對一,多對多,多態一對多等,要用的時候,能夠去查這篇文檔