Laravel 5:多对多,实现文章和tags

2016-07-12 10:23:31来源:oschina作者:mdoo人点击

原文来自 https://laravist.com/article/18


Laravist 是我刚刚上线的Laravel社区,有任何与Laravel相关的问题可以到这里来问我,我会尽力去帮大家解决问题,后期会尝试录制一些视频教程,形式大概是这样的


https://laravist.com/lesson/1


前奏

在开始正文之前,我们首先来说说在实际的开发中,经常会接触到几种常见的对应关系模式:


One-To-One//一对一One-To-Many//一对多Many-To-Many//多对多

不知道你对这些概念是一种什么样的感受,如果是不太理解的。你可以将这些概念应用到生活中,理解起来就很简单了,就举一个与我们在网上经常见到的例子:


User-To-Profile//One-To-OneUser-To-Articles//One-To-ManyArticle-To-Comments//One-To-ManyArticles-To-Tags//Many-To-Many

翻译过来就是:

一个用户对应一个用户档案


一个用户可以发表多篇文章


一篇文章可以有多个评论


而文章和标签确实多对多的关系,一篇文章可以有多个标签;一个标签可以属于多篇文章

在这些关系模型中,最难实现的就是Many-To-Many这种多对多的关系,但是我们这个简单地博客并没有用户管理,也就是并没有开放让用户注 册,所以我们在这里还是要挑战一下难度,实现Articles-To-Tags这种Many-To-Many关系,借助Laravel的强大的 Eloquent,实现这个功能还是比较顺心的。至于一对一和一对多这两种关系,可以触类旁通。


创建tags表

要实现Articles-To-Tags这种Many-To-Many关系,我们需要 tags 表和 Tag 模型,所以我们分别来创建之。


phpartisanmake:migrationcreate_tags_table--create=tags

打开生成的migration文件,为up()方法增加一行代码:


publicfunctionup(){Schema::create('tags',function(Blueprint$table){$table->increments('id');$table->string('name');$table->timestamps();});
}

这里我们增加了 $table->string('name'); 这一行,这个字段表示为 tags table 添加一个 name 字段,代表标签的名字。


接下来,我们为 tags 表创建一个 Tag 模型:


phpartisanmake:modelTag

生成了 Tag 模型之后,我们先不用去管 Tag.php 文件,因为我们还需要一张关系表 article_tag ,这个表只存 tag_id 和 article_id ,所以我们来创建之:


phpartisanmake:migrationcreate_article_tag_table--create=article_tag

打开migration文件来为之加上 tag_id 和 article_id 这两个字段:


publicfunctionup(){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 表:


phpartisanmigrate

OK,生成这两个表之后,我们就可以正式开始我们的工作了。


声明Eloquent的关系

Articles和Tags是多对多的关系,所以我们需要在 Article.php 中声明下面的关系:


publicfunctiontags()
{
return$this->belongsToMany('App/Tag');
}

在Tag.php,也同样:


publicfunctionarticles()
{
return$this->belongsToMany('App/Article');
}

我们使用 $this->belongsToMany() 来表明Eloquent的关系,这里需要注意的是如果你的外键并不是 article_id 和 tag_id ,你需要在第三个参数进行设置,写成类似下面这样:


publicfunctionarticles()
{
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文件:


在标签内,我们还引入了jquery,因为select2依赖于jquery,所以。注意文件的下载或来源,请自行获取。


引入之后,我们就可以在文件创建的页面依旧使用我们的 Form 来生成我们的选择框了,来到 articles/create.blade.php 文件,在 published_at 下面添加一个输入表单:



{!!Form::label('tag_list','选择标签')!!}
{!!Form::select('tag_list[]',$tags,null,['class'=>'form-controljs-example-basic-multiple','multiple'=>'multiple'])!!}

这里需要注意的是 tag_list[] ,如果我们只是使用 tag_list ,就只能选到一个标签,如果我们需要选择多个,我们需要已数组的形式来储存我们的标签,还有一个就是指定一下 'multiple'=>'multiple' ,就是开启支持多选模式。然后 $tags 就是我们需要从数据库获tags表取到得数据,所以自然而然,我们到 ArticleController 中的 create() 方法中,稍微修改一下代码:


publicfunctioncreate()
{
$tags=Tag::lists('name','id');//为了在界面中显示标签name,id为了在保存文章的时候使用。
returnview('articles.create',compact('tags'));
}

这里我们使用lists()方法将Tag中(对于tags数据表)name和id以一个Eluqoent的方式返回,你可以使用dd($tags),来看看。恩,这个时候来看看我们的create页面:



这时候我们发现,样式并没有Select2那么好看,那是因为我们还没有初始化Select2,所以我们在create.blade.php写几行简单地js代码:



$(function(){
$(".js-example-basic-multiple").select2({
placeholder:"添加标签"
});
});

在 @endsection 紧接着的上一行加上上面的代码,这里我们使用jquery的选择器,然后调用 select2(); 来初始化我们的选择框,再来看看效果:



很完美,我们将整个UI完善得还不错,我们用 dd() ;来看看我们表单提交过来的是什么,在 ArticleController 中的 store() 方法中添加一行代码:


dd($request->all());

我们来看看效果:



我们看到得 tag_list 是一个数组,里面的值并不是我们选择的标签的 name ,而是标签的 id ,这样我们就可以使用laravel提供的 attach() 来添加我们的标签了,这个 attach() 接受一个id的数组,这里正好!,所以我们来稍微来修改一下 store() 方法:


publicfunctionstore(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'));returnredirect('/');}

我们这里首先将 Article::create($input) 赋予 $article 变量(Eloquent对象),然后使用 $article->tags()->attach() 来添加标签,并将我们的标签数组传给 attach() 方法,我们来看看有没有成功:



这里的文章是发表成功了,我们再来看看我们的标签是否添加成功,来看看我们的 article_tag 表:



是添加了三个标签,但是我们发现这个 created_at 和 updated_at 貌似有点问题,我们来修复一下,在 Article.php 中的 tags() 方法中:


publicfunctiontags()
{
return$this->belongsToMany('App/Tag')->withTimestamps();
}

我们在后面直接使用 withTimestamps() 来同步我们的时间,我们再来试一试:



再来看看我们的数据库:



看到最后的两个记录,很完美。


在视图中显示我们的tags

我们既然有了标签,我们为什么不来将它展示出来呢?在 articles/index.blade.php 中,我们来将文件的标签输出一下:


id}}">{{$article->title}}
  • {{$article->published_at->diffForHumans()}}
  • @if($article->tags)@foreach($article->tagsas$tag)
  • {{$tag->name}}
  • @endforeach@endif

    我们在

    标签下面增加一个
      列表,然后是首先将发表日期 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 字段完美呈现。

    最新文章

    123

    最新摄影

    微信扫一扫

    第七城市微信公众平台