简体   繁体   中英

How do I perform validation on embedded form PHP/Symfony?

I have three entities: User, Comment, Article. A User is able to make Comments/Articles.

In my articles controller, I have a method called commentAction that is supposed to return the comment form. I'm calling this controller in the article show template. It works, but hte problem comes when I try to validate the form.

I'd like to return the user back to the article show page, with the comment form errors displayed.

Two problems: the form takes us to commentAction and NOT back to showAction. Also, I haven't been able to get validation errors to show for some reason.

I had incorporated a forwarder (which doesn't seem to work right anyways). Can somebody tell me the right way to get this working???

Thanks so much!

PS I'm new with Symfon so if you can alert me if I've violated any best practices I'd appreciate it if you let me know :).

Comment Entity:

<?php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Validator\Constraints as Assert;
/**
 * Comment
 *
 * @ORM\Table(name="comment")
 * @ORM\Entity(repositoryClass="AppBundle\Repository\CommentRepository")
 */
class Comment
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var int $rating
     *
     * @ORM\Column(name="rating", type="smallint")
     * @Assert\NotBlank()
     */
    private $rating;

    /**
     * @var string
     *
     * @ORM\Column(name="title", type="string", length=255)
     */
    private $title;

    /**
     * @ORM\ManyToOne(targetEntity="User", inversedBy="comments")
     * @ORM\JoinColumn(name="user_id", referencedColumnName="id")
     */
    private $user;

    /**
     * @ORM\ManyToOne(targetEntity="Article", inversedBy="comments")
     * @ORM\JoinColumn(name="article_id", referencedColumnName="id")
     */
    private $article;

    /**
     * @var string
     *
     * @ORM\Column(name="comment", type="text", nullable=true)
     */
    private $comment;


    /**
     * Get id
     *
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set rating
     *
     * @param integer $rating
     *
     * @return Comment
     */
    public function setRating($rating)
    {
        $this->rating = $rating;

        return $this;
    }

    /**
     * Get rating
     *
     * @return int
     */
    public function getRating()
    {
        return $this->rating;
    }

    /**
     * Set title
     *
     * @param string $title
     *
     * @return Comment
     */
    public function setTitle($title)
    {
        $this->title = $title;

        return $this;
    }

    /**
     * Get title
     *
     * @return string
     */
    public function getTitle()
    {
        return $this->title;
    }

    /**
     * Set userId
     *
     * @param integer $userId
     *
     * @return Comment
     */
    public function setUserId($userId)
    {
        $this->userId = $userId;

        return $this;
    }

    /**
     * Get userId
     *
     * @return int
     */
    public function getUserId()
    {
        return $this->userId;
    }

    /**
     * Set comment
     *
     * @param string $comment
     *
     * @return Comment
     */
    public function setComment($comment)
    {
        $this->comment = $comment;

        return $this;
    }

    /**
     * Get comment
     *
     * @return string
     */
    public function getComment()
    {
        return $this->comment;
    }

    /**
     * Set user
     *
     * @param \AppBundle\Entity\User $user
     *
     * @return Comment
     */
    public function setUser(\AppBundle\Entity\User $user = null)
    {
        $this->user = $user;

        return $this;
    }

    /**
     * Get user
     *
     * @return \AppBundle\Entity\User
     */
    public function getUser()
    {
        return $this->user;
    }

    /**
     * Set article
     *
     * @param \AppBundle\Entity\Article $article
     *
     * @return Comment
     */
    public function setArticle(\AppBundle\Entity\Article $article = null)
    {
        $this->article = $article;

        return $this;
    }

    /**
     * Get article
     *
     * @return \AppBundle\Entity\User
     */
    public function getArticle()
    {
        return $this->article;
    }
}

Article Controller

<?php

namespace AppBundle\Controller;

use AppBundle\Entity\Article;
use AppBundle\Entity\Comment;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\Request;
use AppBundle\General\HelperClass;

use AppBundle\Form\CommentType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;

/**
 * Article controller.
 *
 * @Route("article")
 */
class ArticleController extends Controller
{
    /**
     * Show all article entities.
     *
     * @Route("/", name="article_index")
     * @Method("GET")
     */
    public function indexAction(Request $request)
    {
        return $this->render('article/index.html.twig');
    }

    /**
     * Lists all article entities, optionally sorted.
     *
     * @Route("/list", name="article_list")
     * @Method("GET")
     */
    public function listAction(Request $request)
    {
        //if we're passing a sort_by it'll be an embed in twig template.
        $sortBy = $request->get('sort_by');

        $em = $this->getDoctrine()->getManager();

        if('average-rating' === $sortBy){
            $sortBy = "avg_rating";
            $articles = $em->getRepository('AppBundle:Article')->getArticlesByAverageRating($sortBy);
        } else {
            $sortBy = "name";
            $articles = $em->getRepository('AppBundle:Article')->getArticlesByAverageRating($sortBy);
        }

        return $this->render('article/list.html.twig', array(
            'articles' => $articles,
        ));
    }

   protected function getComment(\AppBundle\Entity\User $user, \AppBundle\Entity\Article $article, \AppBundle\Entity\Comment $comment){

        $em = $this->getDoctrine()->getManager();
        $userId = $user->getId();

        $existingComment = $em->getRepository('AppBundle:Comment')->getCommentByUserIdAndArticleId($user, $article);

        if($existingComment) {
            $existingComment->setRating($comment->getRating());
            $existingComment->setTitle($comment->getTitle());
            $existingComment->setComment($comment->getComment());
            $comment = $existingComment;
        }

        return $comment;

    }

    /**
     * Creates a new article entity.
     *
     * @Route("/new", name="article_new")
     * @Method({"GET", "POST"})
     */
    public function newAction(Request $request)
    {
        $article = new Article();
        $form = $this->createForm('AppBundle\Form\ArticleType', $article);



        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {



            $em = $this->getDoctrine()->getManager();
            $user = $this->getUser();
            //Set the user
            $article = $form->getData();
            $article->setUser($user);
            //Set Created Date
            $date = new \DateTime("now");
            $article->setCreatedDate($date);
            $em->persist($article);
            $em->flush($article);




            return $this->redirectToRoute('article_show', array('id' => $article->getId()));

        }

        return $this->render('article/new.html.twig', array(
            'article' => $article,
            'form' => $form->createView(),
        ));
    }

    /**
     * Finds and displays a article entity.
     *
     * @Route("/{id}", name="article_show")
     * @Method("GET")
     */
    public function showAction(Article $article)
    {

        $deleteForm = $this->createDeleteForm($article);

        return $this->render('article/show.html.twig', array(
            'article' => $article,
            'delete_form' => $deleteForm->createView(),
        ));
    }

    /**
     * The comment form for articles
     *
     * @Route("/{id}/comment", name="article_comment")
     * @Method({"GET", "POST"})
     */
    public function commentAction(Request $request, Article $article)
    {
        $comment = new Comment();
        //Build the comment form. 

        $commentForm = $this->createFormBuilder($comment)
            ->add('comment', CommentType::class, array("label" => FALSE))
            ->setAction($this->generateUrl('article_comment', array('id' =>$article->getId())))
            ->getForm();

        $commentForm->handleRequest($request);

//THIS IS MY FORWARDER BUT I SEEM TO LOSE ALL FORM DATA WHEN I DO THIS SO I DON"T THINK IT"S RIGHT!!
        if ($commentForm->isSubmitted() && !$commentForm->isValid()) {
            return $this->forward(
                'AppBundle:Article:show',
                array(
                    'form'  => $commentForm->createView(),
                    'article' => $article,
                )
            );
        }

        if ($commentForm->isSubmitted() && $commentForm->isValid()) {

            //Update existing user or create new
            $em = $this->getDoctrine()->getManager();
            $comment = $commentForm->getData()->getComment();
            $user = $this->getUser();

            $comment = $this->getComment($user, $article, $comment);

            //Set the user and article for the comment.
            $comment->setUser($user);
            $comment->setArticle($article);

            $em->persist($comment);
            $em->flush($comment);

            $this->getDoctrine()->getManager()->flush();
            return $this->redirectToRoute('article_show', array('id' => $article->getId()));
        }

        return $this->render('article/comment.html.twig', array(
            'form' => $commentForm->createView(),
        ));
    }

    /**
     * Displays a form to edit an existing article entity.
     *
     * @Route("/{id}/edit", name="article_edit")
     * @Method({"GET", "POST"})
     */
    public function editAction(Request $request, Article $article)
    {
        $deleteForm = $this->createDeleteForm($article);
        $editForm = $this->createForm('AppBundle\Form\ArticleType', $article);
        $editForm->handleRequest($request);

        if ($editForm->isSubmitted() && $editForm->isValid()) {
            $this->getDoctrine()->getManager()->flush();

            return $this->redirectToRoute('article_edit', array('id' => $article->getId()));
        }

        return $this->render('article/edit.html.twig', array(
            'article' => $article,
            'edit_form' => $editForm->createView(),
            'delete_form' => $deleteForm->createView(),
        ));
    }

    /**
     * Deletes a article entity.
     *
     * @Route("/{id}", name="article_delete")
     * @Method("DELETE")
     */
    public function deleteAction(Request $request, Article $article)
    {
        $form = $this->createDeleteForm($article);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $em = $this->getDoctrine()->getManager();
            $em->remove($article);
            $em->flush($article);
        }

        return $this->redirectToRoute('article_index');
    }

    /**
     * Creates a form to delete a article entity.
     *
     * @param Article $article The article entity
     *
     * @return \Symfony\Component\Form\Form The form
     */
    private function createDeleteForm(Article $article)
    {
        return $this->createFormBuilder()
            ->setAction($this->generateUrl('article_delete', array('id' => $article->getId())))
            ->setMethod('DELETE')
            ->getForm()
        ;
    }
}

Show Article Twig

{% extends 'base.html.twig' %}
{% block body_id 'articlepage' %}
{% block body %}

<h1>Article: {{ article.name }}</h1>

<div class="well"><div class="media">
    <div class="media-left media-top">
        <img class="media-object" src="{{ article.thumbnail }}">
    </div>
    <div class="media-body">

        <h4 class="media-heading">{{ article.name }}</h4>
        <p>{{ article.description }}</p>
    </div>
</div></div>

{{ render(controller('AppBundle:Comment:index', {'article_id': article.id})) }}

{% if is_granted('ROLE_USER') -%}
    <h2>Submit a new comment:</h2>
    {{ render(controller('AppBundle:Article:comment', {'id': article.id})) }}
{% else %}
    <h3>Log In To Leave A Comment!</h3>
    <p>Click <a href="{{ path('fos_user_security_login') }}">here</a> to log in or <a href="{{ path('fos_user_registration_register') }}">here</a> to register</p>
{% endif %}

{% endblock %}

Comment Article Twig

{% extends 'base.html.twig' %}
{% form_theme form 'form/fields.html.twig' %}

{% block body %}
    {{ form_start(form) }}{{ form_errors(form.comment.comment) }}{{form_errors(form.comment.title) }} 
    <div class="form-group">{{ form_row(form.comment.title) }}</div>
    <div class="form-group">{{ form_row(form.comment.rating) }}</div>
    <div class="form-group">{{ form_row(form.comment.comment) }}</div>
    <input class="btn btn-default" type="submit" value="Create" />
    {{ form_end(form) }}
{% endblock %}

Put your show action in the ->setAction() in your createFormBuilder instead of your comment action like you currently have it.

->setAction($this->generateUrl('article_show', array('id' =>$article->getId())))

You'll need to handle parsing the form in your show action instead. I always recommend using error bubbling to show all errors at the top of the page. In your createFormBuilder just modify your add:

>add('comment', CommentType::class, array("label" => false, 'error_bubbling' => true))

Then in your Twig place this above your form:

{% if form_errors(form)|length > 10 %}
    <div class="col-md-10 col-md-offset-1">
        <div class="alert alert-danger fade-in">
            {{ form_errors(form) }}
        </div>
    </div>
{% endif %}

I assume you can add many comments to an article. Then you have a one-to-many relationship between this entities. This must be defined in the declaration of the entities using annotations. See this article for details: http://symfony.com/doc/current/doctrine/associations.html

With this definition you can create a separate form type for entity Comment. In the form type for entity Article you define the field "comment" as a CollectionType . Symfony use the form type for entity Comment to render the field comment in entity Article. See this article for details: https://symfony.com/doc/current/form/form_collections.html

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM