利用 n-n 表关联实现文章标签
创建标签Entity
首先我们要创建标签实体类,创建src/AppBundle/Entity/Tag.php文件,内容为:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Tag
*
* @ORM\Table()
* @ORM\Entity
*/
class Tag
{
/**
* @ORM\ManyToMany(targetEntity="BlogPost", mappedBy="tag")
*/
private $blogPosts;
public function __construct()
{
$this->blogPosts = new ArrayCollection();
}
public function getBlogPosts()
{
return $this->blogPosts;
}
/**
* @var integer
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* @param string $name
* @return Tag
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* @return string
*/
public function getName()
{
return $this->name;
}
}
创建关联关系
有了标签实体Tag,我们要把BlogPost和Tag关联起来,这是一个多对多的关系,因此在src/AppBundle/Entity/BlogPost.php中添加:
/**
* @ORM\ManyToMany(targetEntity="Tag")
* @ORM\JoinTable(name="blogpost_tag",
* joinColumns={@ORM\JoinColumn(name="blogpost_id", referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="tag_id", referencedColumnName="id")}
* )
*/
private $tags;
同时生成它的get和set方法
/**
* @return mixed
*/
public function getTags()
{
return $this->tags;
}
/**
* @param mixed $tags
*/
public function setTags($tags)
{
$this->tags = $tags;
}
创建mysql表
执行
php app/console doctrine:schema:update --force
会自动生成两个表:
Tag(id, name)
blogpost_tag(blogpost_id, tag_id)
增加Tag的Admin类
为了能在sonataadmin管理后台对Tag进行管理,我们创建src/AppBundle/Admin/TagAdmin.php内容如下:
<?php
namespace AppBundle\Admin;
use Sonata\AdminBundle\Admin\Admin;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Form\FormMapper;
class TagAdmin extends Admin
{
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper->add('name', 'text');
}
protected function configureDatagridFilters(DatagridMapper $datagridMapper)
{
$datagridMapper->add('name');
}
protected function configureListFields(ListMapper $listMapper)
{
$listMapper->addIdentifier('name');
}
}
增加tag的service配置
修改app/config/services.yml,增加:
admin.tag:
class: AppBundle\Admin\TagAdmin
arguments: [~, AppBundle\Entity\Tag, ~]
tags:
- { name: sonata.admin, manager_type: orm, label: Tag }
增加BlogPost的tag管理控件
修改src/AppBundle/Admin/BlogPostAdmin.php,在configureFormFields中增加
->with('Meta data', array('class' => 'col-md-3'))
->add('tags', null, array(
'class' => 'AppBundle\Entity\Tag',
'property' => 'name',
))
->end()
好,现在看一下效果,打开后台管理页面,我们看到多了Tag的一组管理:
点开List可以看到我刚刚添加的两个标签
新建一个BlogPost,也会发现在右侧多了Tag的填写框:
因为BlogPost和Tag是多对多的关系,所以这里是可以填写多个值的:
Tag的展示
Tag的管理功能已经完成了,那么怎么才能把tag展示出来呢?我们希望有两处展示,一处是单独一个页面,列出指定tag的所有文章列表,另一处是在文章展示页面展示出它的所有标签,同时链接到tag文章列表
为BlogPost创建listbytagAction,修改src/AppBundle/Controller/BlogController.php,在适当位置增加如下内容(如果你看了前面的文章,你就知道放到什么位置了):
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\QueryBuilder;
/**
- @var EntityManager
/
protected $em;
/*
- @var QueryBuilder
*/
protected $builder;
public function listbytagAction(Request $request)
{
$tagName = $request->get(‘tagname’);
$this->em = $this->get(‘doctrine.orm.entity_manager’);
$this->builder = $this->em->createQueryBuilder();
$query = $this->builder->select(‘b’)
->add(‘from’, ‘AppBundle:BlogPost b INNER JOIN b.tags t’)
->where(’t.name=:tag_name')
->setParameter(‘tag_name’, $tagName)
->getQuery();
$paginator = $this->get(‘knp_paginator’);
$pagination = $paginator->paginate(
$query,
$request->query->get(‘page’, 1)/page number/,
100/limit per page/
);
return $this->render(‘blog/listbytag.html.twig’, array(‘pagination’ => $pagination,
‘tagname’ => $tagName,
‘latestblogs’ => BlogController::getLatestBlogs($this),
‘tophotblogs’ => BlogController::getTopHotBlogs($this)));
}
创建app/Resources/views/blog/listbytag.html.twig,内容如下:
{% extends "base.html.twig" %}
{% block title %}{{ tagname }} - lcsays - 关注大数据技术{% endblock title %}
{% block body %}
<div class="row">
<div class="col-sm-3 col-xs-1"></div>
<div class="col-sm-6 col-xs-10">
<h1>{{ tagname }}</h1>
</div>
<div class="col-sm-3 col-xs-1"></div>
</div>
<div class="row">
<div class="col-sm-3 col-xs-1"></div>
<div class="col-sm-6 col-xs-10">
<br />
{% for article in pagination %}
<h4><a href="{{ path('blog_show', {'blogId':article.id}) }}">{{ article.title }}</a>({{ article.createDate }})</h4>
{% endfor %}
<div class="navigation">
{{ knp_pagination_render(pagination) }}
</div>
</div>
<div class="col-sm-3 col-xs-1"></div>
</div>
{% endblock body %}
为这个action创建路由,修改app/config/routing.yml,增加如下内容:
blog_listbytag:
path: /bloglistbytag/
defaults: { _controller: AppBundle:Blog:listbytag }
修改app/Resources/views/blog/show.html.twig,在展示subject和category两个标签的后面添加:
{% for tag in blogpost.tags %}
<a class="btn btn-warning btn-xs" href="{{ path('blog_listbytag', {'tagname':tag.name}) }}">
{{ tag.name }}
</a>
{% endfor %}
在app/Resources/views/blog/list.html.twig也同样加入如下内容:
{% for tag in article.tags %}
<a class="btn btn-warning btn-xs" href="{{ path('blog_listbytag', {'tagname':tag.name}) }}">
{{ tag.name }}
</a>
{% endfor %}
下面欣赏一下最终效果: