原文來自 https://laravist.com/article/18 javascript
Laravist 是我剛剛上線的Laravel社區,有任何與Laravel相關的問題能夠到這裏來問我,我會盡力去幫你們解決問題,後期會嘗試錄製一些視頻教程,形式大概是這樣的php
https://laravist.com/lesson/1 css
在開始正文以前,咱們首先來講說在實際的開發中,常常會接觸到幾種常見的對應關係模式:html
One-To-One //一對一One-To-Many //一對多Many-To-Many //多對多
不知道你對這些概念是一種什麼樣的感覺,若是是不太理解的。你能夠將這些概念應用到生活中,理解起來就很簡單了,就舉一個與咱們在網上常常見到的例子:java
User-To-Profile // One-To-OneUser-To-Articles // One-To-ManyArticle-To-Comments // One-To-ManyArticles-To-Tags // Many-To-Many
翻譯過來就是:jquery
一個用戶對應一個用戶檔案laravel
一個用戶能夠發表多篇文章git
一篇文章能夠有多個評論github
而文章和標籤確實多對多的關係,一篇文章能夠有多個標籤;一個標籤能夠屬於多篇文章sql
在這些關係模型中,最難實現的就是Many-To-Many這種多對多的關係,可是咱們這個簡單地博客並無用戶管理,也就是並無開放讓用戶注 冊,因此咱們在這裏仍是要挑戰一下難度,實現Articles-To-Tags這種Many-To-Many關係,藉助Laravel的強大的 Eloquent,實現這個功能仍是比較順心的。至於一對一和一對多這兩種關係,能夠舉一反三。
要實現Articles-To-Tags這種Many-To-Many關係,咱們須要 tags
表和 Tag
模型,因此咱們分別來建立之。
php artisan make:migration create_tags_table --create=tags
打開生成的migration文件,爲up()方法增長一行代碼:
public function up(){ Schema::create('tags', function (Blueprint $table) { $table->increments('id'); $table->string('name'); $table->timestamps(); }); }
這裏咱們增長了 $table->string('name');
這一行,這個字段表示爲 tags table
添加一個 name
字段,表明標籤的名字。
接下來,咱們爲 tags
表建立一個 Tag
模型:
php artisan make:model Tag
生成了 Tag
模型以後,咱們先不用去管 Tag.php
文件,由於咱們還須要一張關係表 article_tag
,這個表只存 tag_id
和 article_id
,因此咱們來建立之:
php artisan make:migration create_article_tag_table --create=article_tag
打開migration文件來爲之加上 tag_id
和 article_id
這兩個字段:
public function up() { Schema::create('article_tag', function (Blueprint $table) { $table->increments('id'); $table->integer('article_id')->unsigned()->index(); $table->foreign('article_id')->references('id')->on('articles')->onDelete('cascade'); $table->integer('tag_id')->unsigned()->index(); $table->foreign('tag_id')->references('id')->on('tags')->onDelete('cascade'); $table->timestamps(); }); }
這裏貌似就添加tag_id和article_id這兩個字段,可是用了不少行代碼,咱們只要是理解下面這個:
$table->foreign('article_id')->references('id')->on('articles')->onDelete('cascade');
foreign():外鍵
references():參照字段
on():參照表
onDelete():刪除時的執行動做
這裏是跟着刪除,好比刪除了某篇文章,咱們將article_tag中包含article_id同樣的記錄也刪除
最後,執行migration生成 article_tag
表:
php artisan migrate
OK,生成這兩個表以後,咱們就能夠正式開始咱們的工做了。
Articles和Tags是多對多的關係,因此咱們須要在 Article.php
中聲明下面的關係:
public function tags() { return $this->belongsToMany('App\Tag'); }
在Tag.php,也一樣:
public function articles() { return $this->belongsToMany('App\Article'); }
咱們使用 $this->belongsToMany()
來代表Eloquent的關係,這裏須要注意的是若是你的外鍵並非 article_id
和 tag_id
,你須要在第三個參數進行設置,寫成相似下面這樣:
public function articles() { return $this->belongsToMany('App\Article','conversation_id'); }
OK,這樣,咱們的多對多關係就聲明完畢了。
在開始以前,咱們使用tinker生成幾個tag,過程就不演示了,最後是這樣的:
而後,爲了更好地用戶體驗,咱們引入 Select2 ,這個對選擇多個選項的時候表現得異常完美。
Select2 用法: https://select2.github.io/examples.html
咱們在app.blade.php引入Select2的css文件和js文件:
<link rel='stylesheet' href="/css/select2.css" type='text/css' media='all'/><script src="/js/jquery-2.1.0.min.js"></script><script src="/js/select2.full.min.js"></script>
在<head></head>標籤內,咱們還引入了jquery,由於select2依賴於jquery,因此。注意文件的下載或來源,請自行獲取。
引入以後,咱們就能夠在文件建立的頁面依舊使用咱們的 Form
來生成咱們的選擇框了,來到 articles/create.blade.php
文件,在 published_at
下面添加一個輸入表單:
<div class="form-group"> {!! Form::label('tag_list','選擇標籤') !!} {!! Form::select('tag_list[]',$tags,null,['class'=>'form-control js-example-basic-multiple','multiple'=>'multiple']) !!}</div>
這裏須要注意的是 tag_list[]
,若是咱們只是使用 tag_list
,就只能選到一個標籤,若是咱們須要選擇多個,咱們須要已數組的形式來儲存咱們的標籤,還有一個就是指定一下 'multiple'=>'multiple'
,就是開啓支持多選模式。而後 $tags
就是咱們須要從數據庫獲tags表取到得數據,因此天然而然,咱們到 ArticleController
中的 create()
方法中,稍微修改一下代碼:
public function create() { $tags = Tag::lists('name', 'id'); //爲了在界面中顯示標籤name,id爲了在保存文章的時候使用。 return view('articles.create',compact('tags')); }
這裏咱們使用lists()方法將Tag中(對於tags數據表)name和id以一個Eluqoent的方式返回,你可使用dd($tags),來看看。恩,這個時候來看看咱們的create頁面:
這時候咱們發現,樣式並無Select2那麼好看,那是由於咱們尚未初始化Select2,因此咱們在create.blade.php寫幾行簡單地js代碼:
<script type="text/javascript"> $(function() { $(".js-example-basic-multiple").select2({ placeholder: "添加標籤" }); }); </script>
在 @endsection
緊接着的上一行加上上面的代碼,這裏咱們使用jquery的選擇器,而後調用 select2();
來初始化咱們的選擇框,再來看看效果:
很完美,咱們將整個UI完善得還不錯,咱們用 dd()
;來看看咱們表單提交過來的是什麼,在 ArticleController
中的 store()
方法中添加一行代碼:
dd($request->all());
咱們來看看效果:
咱們看到得 tag_list
是一個數組,裏面的值並非咱們選擇的標籤的 name
,而是標籤的 id
,這樣咱們就可使用laravel提供的 attach()
來添加咱們的標籤了,這個 attach()
接受一個id的數組,這裏正好!,因此咱們來稍微來修改一下 store()
方法:
public function store(Requests\StoreArticleRequest $request) { $input = $request->all(); $input['intro'] = mb_substr($request->get('content'),0,64); $article = Article::create($input); $article->tags()->attach($request->input('tag_list')); return redirect('/'); }
咱們這裏首先將 Article::create($input)
賦予 $article
變量(Eloquent對象),而後使用 $article->tags()->attach()
來添加標籤,並將咱們的標籤數組傳給 attach()
方法,咱們來看看有沒有成功:
這裏的文章是發表成功了,咱們再來看看咱們的標籤是否添加成功,來看看咱們的 article_tag
表:
是添加了三個標籤,可是咱們發現這個 created_at
和 updated_at
貌似有點問題,咱們來修復一下,在 Article.php
中的 tags()
方法中:
public function tags() { return $this->belongsToMany('App\Tag')->withTimestamps(); }
咱們在後面直接使用 withTimestamps()
來同步咱們的時間,咱們再來試一試:
再來看看咱們的數據庫:
看到最後的兩個記錄,很完美。
咱們既然有了標籤,咱們爲何不來將它展現出來呢?在 articles/index.blade.php
中,咱們來將文件的標籤輸出一下:
<h2 class="post-title pad"> <a href="/articles/{{ $article->id }}"> {{ $article->title }}</a></h2><ul class="post-meta pad group"> <li><i class="fa fa-clock-o"></i>{{ $article->published_at->diffForHumans() }}</li> @if($article->tags) @foreach($article->tags as $tag) <li><i class="fa fa-tag"></i>{{ $tag->name }}</li> @endforeach @endif</ul>
咱們在 <h2>
標籤下面增長一個 <ul>
列表,而後是首先將發表日期 published_at
輸出了,這裏咱們使用了Carbon的 diffForHumans()
方法,這個方法就會產生幾分鐘以前,幾個小時以前的效果,這裏也能夠體會咱們以前須要將 published_at
這個對象做爲Carbon對象來對待了,若是是簡單地字符串,是不能調用Carbon的 diffForHumans()
方法的。
接下來,咱們使用 $article->tags
取得文章的標籤,這個 tags
就是咱們聲明多對多關係的 tags()
方法。咱們來看看效果:
咱們發現 咱們的多少分鐘以前
都是英文,那是由於咱們沒有設置Carbon,咱們來修復一下,在 app/Providers/AppServiceProvider.php
中的 boot()
方法添加下面這一行:
\Carbon\Carbon::setLocale('zh');
而後刷新,見證一下奇蹟吧:
到這裏咱們利用laravel提供的 attach()
方法將基本的多對多關係實現了,而且還稍微美化了一下輸出,將 published_at
字段完美呈現。