Laravel 5:多對多,實現文章和tags

原文來自 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

  1. 一個用戶對應一個用戶檔案laravel

  2. 一個用戶能夠發表多篇文章git

  3. 一篇文章能夠有多個評論github

  4. 而文章和標籤確實多對多的關係,一篇文章能夠有多個標籤;一個標籤能夠屬於多篇文章sql

在這些關係模型中,最難實現的就是Many-To-Many這種多對多的關係,可是咱們這個簡單地博客並無用戶管理,也就是並無開放讓用戶注 冊,因此咱們在這裏仍是要挑戰一下難度,實現Articles-To-Tags這種Many-To-Many關係,藉助Laravel的強大的 Eloquent,實現這個功能仍是比較順心的。至於一對一和一對多這兩種關係,能夠舉一反三。

建立tags表

要實現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_idarticle_id ,因此咱們來建立之:

php artisan make:migration create_article_tag_table --create=article_tag

打開migration文件來爲之加上 tag_idarticle_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,生成這兩個表以後,咱們就能夠正式開始咱們的工做了。

聲明Eloquent的關係

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_idtag_id ,你須要在第三個參數進行設置,寫成相似下面這樣:

 public function articles()
    {
        return $this->belongsToMany('App\Article','conversation_id');
    }

OK,這樣,咱們的多對多關係就聲明完畢了。

使用Select2

在開始以前,咱們使用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_atupdated_at 貌似有點問題,咱們來修復一下,在 Article.php 中的 tags() 方法中:

public function tags()
    {
        return $this->belongsToMany('App\Tag')->withTimestamps();
    }

咱們在後面直接使用 withTimestamps() 來同步咱們的時間,咱們再來試一試:

再來看看咱們的數據庫:

看到最後的兩個記錄,很完美。

在視圖中顯示咱們的tags

咱們既然有了標籤,咱們爲何不來將它展現出來呢?在 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 字段完美呈現。

相關文章
相關標籤/搜索