Entity与Form

更新首页

在上一章中我们完成了文章展示页面。对于博客而言它的首页通常是显示最近的几篇文章的摘要信息。现在我们开始建立首页的内容。

更新控制器PageController

  1. public function indexAction()
  2. {
  3. $em = $this->getDoctrine()->getManager();
  4. $blogs = $em->createQueryBuilder()
  5. ->select('b')
  6. ->from('BloggerBlogBundle:Blog', 'b')
  7. ->addOrderBy('b.created', 'DESC')
  8. ->getQuery()
  9. ->getResult();
  10. return array('blogs' => $blogs);
  11. }

更新模板src/Blogger/BlogBundle/Resources/views/Page/index.html.twig

  1. {% extends 'BloggerBlogBundle::layout.html.twig' %}
  2. {% block body %}
  3. {% for blog in blogs %}
  4. <article class="blog">
  5. <div class="date"><time datetime="{{ blog.created|date('c') }}">{{ blog.created|date('l, F j, Y') }}</time></div>
  6. <header>
  7. <h2><a href="{{ path('blogger_blogBundle_blog_show', { 'id': blog.id }) }}">{{ blog.title }}</a></h2>
  8. </header>
  9. <img src="{{ asset(['images/', blog.image]|join) }}" />
  10. <div class="snippet">
  11. <p>{{ blog.blog(500) }}</p>
  12. <p class="continue"><a href="{{ path('blogger_blogBundle_blog_show', { 'id': blog.id }) }}">Continue reading...</a></p>
  13. </div>
  14. <footer class="meta">
  15. <p>Comments: -</p>
  16. <p>Posted by <span class="highlight">{{blog.author}}</span> at {{ blog.created|date('h:iA') }}</p>
  17. <p>Tags: <span class="highlight">{{ blog.tags }}</span></p>
  18. </footer>
  19. </article>
  20. {% else %}
  21. <p>There are no blog entries for symblog</p>
  22. {% endfor %}
  23. {% endblock %}

现在访问首页看下效果。

第6章 Entity与Form  - 图1

为了方便,我们为Blog实体创建一个订制一个查询方法getLatestBlogs。编辑文件src/Blogger/BlogBundle/Entity/BlogRepository.php,将上面PageController中的代码移动到这里来:

  1. class BlogRepository extends EntityRepository
  2. {
  3. /**
  4. * @param integer $limit
  5. *
  6. * @return array
  7. */
  8. public function getLatestBlogs($limit = null)
  9. {
  10. $qb = $this->createQueryBuilder('b')
  11. ->select('b')
  12. ->addOrderBy('b.created', 'DESC');
  13. if (!empty($limit)) {
  14. $qb->setMaxResults($limit);
  15. }
  16. return $qb->getQuery()
  17. ->getResult();
  18. }
  19. }

然后在控制器中只需要调用即可:

  1. $blogs = $em->getRepository('BloggerBlogBundle:Blog')->getLatestBlogs();

创建评论

在上一章中我们已经创建了评论实体Comment,它记录了用户对于文章的评论内容。接下来我们将创建发布评论的页面。

添加测试数据

同样的为了测试方便,我们再添加一些评论的测试数据。新建文件src/Blogger/BlogBundle/DataFixtures/ORM/CommentFixtures.php,然后再重新加载数据。

  1. $ php app/console doctrine:fixtures:load

显示评论

我们现在可以在第篇文章的展示页面显示它的评论内容。先更新CommentRepository,订制一个查询方法getCommentsForBlog

  1. public function getCommentsForBlog($blogId, $approved = true)
  2. {
  3. $qb = $this->createQueryBuilder('c')
  4. ->select('c')
  5. ->where('c.blog = :blog_id')
  6. ->addOrderBy('c.created')
  7. ->setParameter('blog_id', $blogId);
  8. if (false === is_null($approved)) {
  9. $qb->andWhere('c.approved = :approved')->setParameter('approved', $approved);
  10. }
  11. return $qb->getQuery()->getResult();
  12. }

接着更新BlogController,在展示页面查询文章对应的评论内容。

  1. public function showAction($id)
  2. {
  3. $em = $this->getDoctrine()->getEntityManager();
  4. $blog = $em->getRepository('BloggerBlogBundle:Blog')->find($id);
  5. if (!$blog) {
  6. throw $this->createNotFoundException('Unable to find Blog post.');
  7. }
  8. $comments = $em->getRepository('BloggerBlogBundle:Comment')
  9. ->getCommentsForBlog($blog->getId());
  10. return array('blog' => $blog, 'comments' => $comments);
  11. }

然后更新模板将评论数据进行渲染,src/Blogger/BlogBundle/Resources/views/Blog/show.html.twig

  1. {% block body %}
  2. {# .. #}
  3. <section class="comments" id="comments">
  4. <section class="previous-comments">
  5. <h3>Comments</h3>
  6. {% include 'BloggerBlogBundle:Comment:index.html.twig' with { 'comments': comments } %}
  7. </section>
  8. </section>
  9. {% endblock %}

这里我们通过include标签引用了另一个模板BloggerBlogBundle:Comment:index.html.twig,因此还需要再创建文件src/Blogger/BlogBundle/Resources/views/Comment/index.html.twig

  1. {% for comment in comments %}
  2. <article class="comment {{ cycle(['odd', 'even'], loop.index0) }}" id="comment-{{ comment.id }}">
  3. <header>
  4. <p><span class="highlight">{{ comment.user }}</span> commented <time datetime="{{ comment.created|date('c') }}">{{ comment.created|date('l, F j, Y') }}</time></p>
  5. </header>
  6. <p>{{ comment.comment }}</p>
  7. </article>
  8. {% else %}
  9. <p>There are no comments for this post. Be the first to comment...</p>
  10. {% endfor %}

现在打开一篇有评论的页面应该可以看到类似如下的效果:

第6章 Entity与Form  - 图3

创建评论表单

接下来将介绍本章的主要内容,表单(Form)与实体(Entity)关联。

现在我们来创建一个评论框表单,用户提交表单之后会创建一个实体对象,即就是往数据库中插入一条数据。

先为Comment实体创建一个表单类型CommentType

  1. $ php app/console generate:doctrine:form BloggerBlogBundle:Comment

上面这条命令将会新建一个文件: src/Blogger/BlogBundle/Form/CommentType.php,其内容与之前的EnquiryType类似。

显示评论表单

添加一条路由规则:

  1. blogger_blogBundle_comment_create:
  2. pattern: /comment/{blog_id}
  3. defaults: { _controller: BloggerBlogBundle:Comment:create }
  4. requirements:
  5. _method: POST
  6. blog_id: \d+

创建一个新的控制器:

  1. $ php app/console generate:controller --controller=BloggerBlogBundle:Comment --route-format=yml

完善控制器内容:

  1. class CommentController extends Controller
  2. {
  3. /**
  4. * @param integer $blogId
  5. *
  6. * @return Response
  7. */
  8. public function newAction($blogId)
  9. {
  10. $blog = $this->getBlog($blogId);
  11. $comment = new Comment();
  12. $comment->setBlog($blog);
  13. $form = $this->createForm(new CommentType(), $comment);
  14. return $this->render('BloggerBlogBundle:Comment:form.html.twig', array(
  15. 'comment' => $comment,
  16. 'form' => $form->createView()
  17. ));
  18. }
  19. /**
  20. * @param integer $blogId
  21. *
  22. * @return Response
  23. */
  24. public function createAction($blogId)
  25. {
  26. $blog = $this->getBlog($blogId);
  27. $comment = new Comment();
  28. $comment->setBlog($blog);
  29. $request = $this->getRequest();
  30. $form = $this->createForm(new CommentType(), $comment);
  31. $form->bindRequest($request);
  32. if ($form->isValid()) {
  33. // TODO: Persist the comment entity
  34. return $this->redirect($this->generateUrl('blogger_blogBundle_blog_show', array(
  35. 'id' => $comment->getBlog()->getId())) .
  36. '#comment-' . $comment->getId()
  37. );
  38. }
  39. return $this->render('BloggerBlogBundle:Comment:create.html.twig', array(
  40. 'comment' => $comment,
  41. 'form' => $form->createView()
  42. ));
  43. }
  44. protected function getBlog($blogId)
  45. {
  46. $em = $this->getDoctrine()
  47. ->getEntityManager();
  48. $blog = $em->getRepository('BloggerBlogBundle:Blog')->find($blogId);
  49. if (!$blog) {
  50. throw $this->createNotFoundException('Unable to find Blog post.');
  51. }
  52. return $blog;
  53. }
  54. }

其中newAction是用于显示评论框表单的;createAction是用来处理表单提交的内容。

更新模板。新建newAction使用的模板文件src/Blogger/BlogBundle/Resources/views/Comment/form.html.twig

  1. {# src/Blogger/BlogBundle/Resources/views/Comment/form.html.twig #}
  2. <form action="{{ path('BloggerBlogBundle_comment_create', { 'blog_id' : comment.blog.id } ) }}" method="post" {{ form_enctype(form) }} class="blogger">
  3. {{ form_widget(form) }}
  4. <p>
  5. <input type="submit" value="Submit">
  6. </p>
  7. </form>

新建createAction使用的模板文件src/Blogger/BlogBundle/Resources/views/Comment/create.html.twig

  1. {% extends 'BloggerBlogBundle::layout.html.twig' %}
  2. {% block title %}Add Comment{% endblock%}
  3. {% block body %}
  4. <h1>Add comment for blog post "{{ comment.blog.title }}"</h1>
  5. {% include 'BloggerBlogBundle:Comment:form.html.twig' with { 'form': form } %}
  6. {% endblock %}

接着修改文章展示页面的模板,加入评论框表单:

  1. {% block body %}
  2. {# .. #}
  3. <section class="comments" id="comments">
  4. {# .. #}
  5. <h3>Add Comment</h3>
  6. {% render controller('BloggerBlogBundle:Comment:new', { 'blogId': blog.id }) %}
  7. </section>
  8. {% endblock %}

这里再打开文章页面看下效果。如果页面报错并提示:Entities passed to the choice field must have a “__toString()” method defined

那么编辑Blog实体类,加入__toString方法:

  1. public function __toString()
  2. {
  3. return $this->getTitle();
  4. }

现在应该就能看到如下的效果:第6章 Entity与Form  - 图4

用户在提交评论时应该只需要输入User和Comment,因此我们再对CommentType.php稍作修改移除多余字段。

  1. public function buildForm(FormBuilderInterface $builder, array $options)
  2. {
  3. $builder
  4. ->add('user')
  5. ->add('comment');
  6. }

存入数据库

在上面创建表单对象时,$form已经与$comment进行关联了。因此在表单处理提交的数据时就已经将数据内容写入了$comment对象中。现在只需要将$comment的数据存入数据库就行了。

保存数据库的方法在之前的DataFixtures中已经提到了。现在更新CommentControllercreate 方法來儲存 Comment实体到数据库中。

  1. if ($form->isValid()) {
  2. $em = $this->getDoctrine()
  3. ->getEntityManager();
  4. $em->persist($comment);
  5. $em->flush();
  6. return $this->redirect($this->generateUrl('blogger_blogBundle_blog_show', array(
  7. 'id' => $comment->getBlog()->getId())) .
  8. '#comment-' . $comment->getId()
  9. );
  10. }

表单验证

我們不希望用户提交的评论中usercomment为空。要处理这个问题可以参考之前的EnquiryType表单。

编辑文件src/Blogger/BlogBundle/Entity/Comment.phpComment实体,添加如下内容:

  1. public static function loadValidatorMetadata(ClassMetadata $metadata)
  2. {
  3. $metadata->addPropertyConstraint('user', new NotBlank(array(
  4. 'message' => 'You must enter your name'
  5. )));
  6. $metadata->addPropertyConstraint('comment', new NotBlank(array(
  7. 'message' => 'You must enter a comment'
  8. )));
  9. }

这样就可以防止用户提交空数据了。