我无法在symfony 3中显示嵌入的嵌套表单

时间:2017-10-27 11:46:23

标签: php symfony formbuilder symfony3.x

我有一个很大的问题,因为我无法在互联网上找到解决问题的方法。我从一个Post Entity工作,我想添加Images Entities。这个想法是当我登录我的应用程序时,我可以写一个帖子并上传一个或者n个图像。之后,用户将允许从他的帖子中查看图像。

我在第一步,似乎我无法在 PostType中显示我的 ImageType :: Class CollectionClass :: Class .PHP 即可。我没有错误,它根本没有显示在视图中。提前谢谢!

PostType.php

namespace UserBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use UserBundle\Entity\Post;


class PostType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('title')
            ->add('content')
            ->add('images', CollectionType::class, [
                'entry_type' => ImageType::class,
                'entry_options' => array('label' => false),
                'allow_add' => true,
            ]);
        ;
    }

    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => Post::class,
        ));
    }

    /**
     * {@inheritdoc}
     */
    public function getBlockPrefix()
    {
        return 'userbundle_post';
    }


}

ImageType.php

namespace UserBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use UserBundle\Entity\Image;

class ImageType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('file', FileType::class, ['required' => false, 'data_class' => null]);
    }

    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => Image::class,
        ));
    }

    /**
     * {@inheritdoc}
     */
    public function getBlockPrefix()
    {
        return 'userbundle_image';
    }


}

post.php中

namespace UserBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Common\Collections\ArrayCollection;

/**
 * Post
 *
 * @ORM\Table(name="post")
 * @ORM\Entity(repositoryClass="UserBundle\Repository\PostRepository")
 */
class Post
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

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

    /**
     * @var string
     *
     * @ORM\Column(name="Content", type="text")
     */
    private $content;


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

    /**
     * @ORM\OneToMany(targetEntity="Image", mappedBy="post", cascade={"persist"})
     */
    private $images;

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

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

        return $this;
    }

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

    /**
     * Set content
     *
     * @param string $content
     *
     * @return Post
     */
    public function setContent($content)
    {
        $this->content = $content;

        return $this;
    }

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

    /**
     * Set post
     *
     * @param \
     *
     * @return Post
     */
    public function setUser($user)
    {
        $this->user = $user;

        return $this;
    }

    /**
     * Get post
     *
     * @return \?
     */
    public function getUser()
    {
        return $this->user;
    }
    /**
     * Constructor
     */
    public function __construct()
    {
        $this->images = new \Doctrine\Common\Collections\ArrayCollection();
    }

    /**
     * Add image
     *
     * @param \UserBundle\Entity\Image $image
     *
     * @return Post
     */
    public function addImage(\UserBundle\Entity\Image $image)
    {
        $this->images[] = $image;

        return $this;
    }

    /**
     * Remove image
     *
     * @param \UserBundle\Entity\Image $image
     */
    public function removeImage(\UserBundle\Entity\Image $image)
    {
        $this->images->removeElement($image);
    }

    /**
     * Get images
     *
     * @return \Doctrine\Common\Collections\Collection
     */
    public function getImages()
    {
        return $this->images;
    }
}

Image.php

namespace UserBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass="UserBundle\Entity\ImageRepository")
 */
class Image
{
  /**
   * @ORM\Column(name="id", type="integer")
   * @ORM\Id
   * @ORM\GeneratedValue(strategy="AUTO")
   */
  private $id;

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

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

  private $file;

  public function getFile()
  {
    return $this->file;
  }

  public function setFile(UploadedFile $file = null)
  {
    $this->file = $file;
  }


  /**
   * @ORM\ManyToOne(targetEntity="Post", inversedBy="images")
   * @ORM\JoinColumn(name="post_id", referencedColumnName="id")
  */
  private $post;


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

  /**
   * Set url
   *
   * @param string $url
   *
   * @return Image
   */
  public function setUrl($url)
  {
      $this->url = $url;

      return $this;
  }

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

  /**
   * Set alt
   *
   * @param string $alt
   *
   * @return Image
   */
  public function setAlt($alt)
  {
      $this->alt = $alt;

      return $this;
  }

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

  /**
   * Set post
   *
   * @param \UserBundle\Entity\Post $post
   *
   * @return Image
   */
  public function setPost(\UserBundle\Entity\Post $post = null)
  {
      $this->post = $post;

      return $this;
  }

  /**
   * Get post
   *
   * @return \UserBundle\Entity\Post
   */
  public function getPost()
  {
      return $this->post;
  }
}

修改 我的帖子新动作视图: new.html.twig

{% extends 'base.html.twig' %}
    {% block javascripts %}
        <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
        {# Voici le script en question : #}
        <script type="text/javascript">
            $(document).ready(function() {
                // On récupère la balise <div> en question qui contient l'attribut « data-prototype » qui nous intéresse.
                var $container = $('div#userbundle_post_images');

                // On définit un compteur unique pour nommer les champs qu'on va ajouter dynamiquement
                var index = $container.find(':input').length;

                // On ajoute un nouveau champ à chaque clic sur le lien d'ajout.
                $('#add_category').click(function(e) {
                    addImage($container);
                    e.preventDefault(); // évite qu'un # apparaisse dans l'URL
                    return false;
                });
                // On ajoute un premier champ automatiquement s'il n'en existe pas déjà un (cas d'une nouvelle annonce par exemple).
                if (index == 0) {
                    addImage($container);
                } else {
                    // S'il existe déjà des catégories, on ajoute un lien de suppression pour chacune d'entre elles
                    $container.children('div').each(function() {
                        addDeleteLink($(this));
                    });
                }

                // La fonction qui ajoute un formulaire CategoryType
                function addImage($container) {
                    // Dans le contenu de l'attribut « data-prototype », on remplace :
                    // - le texte "__name__label__" qu'il contient par le label du champ
                    // - le texte "__name__" qu'il contient par le numéro du champ
                    var template = $container.attr('data-prototype')
                        .replace(/__name__label__/g, 'Catégorie n°' + (index+1))
                        .replace(/__name__/g,        index)
                    ;

                    // On crée un objet jquery qui contient ce template
                    var $prototype = $(template);

                    // On ajoute au prototype un lien pour pouvoir supprimer la catégorie
                    addDeleteLink($prototype);

                    // On ajoute le prototype modifié à la fin de la balise <div>
                    $container.append($prototype);

                    // Enfin, on incrémente le compteur pour que le prochain ajout se fasse avec un autre numéro
                    index++;
                }

                // La fonction qui ajoute un lien de suppression d'une catégorie
                function addDeleteLink($prototype) {
                    // Création du lien
                    var $deleteLink = $('<a href="#" class="btn btn-danger">Supprimer l\'image</a>');

                    // Ajout du lien
                    $prototype.append($deleteLink);

                    // Ajout du listener sur le clic du lien pour effectivement supprimer la catégorie
                    $deleteLink.click(function(e) {
                        $prototype.remove();

                        e.preventDefault(); // évite qu'un # apparaisse dans l'URL
                        return false;
                    });
                }
            });
        </script>
    {% endblock %}

    {% block body %}
        <h1>Post creation</h1>
        {{ form_start(form) }}
            {{ form_row(form.title) }}
            {{ form_row(form.content) }}
            <a href="#" id="add_category" class="btn btn-default">Ajouter une Image</a>
            {{ form_row(form.images) }}
            <br/>
            <input type="submit" value="Create" />
        {{ form_end(form) }}

        <ul>
            <li>
                <a href="{{ path('post_index') }}">Back to the list</a>
            </li>
        </ul>
    {% endblock %}

修改: PostController.php

/**
 * Creates a new post entity.
 *
 * @Route("/new", name="post_new")
 * @Method({"GET", "POST"})
 */
public function newAction(Request $request)
{
    $post = new Post();
    //dump($post);

    $user = $this->getUser();
    if (!is_object($user) || !$user instanceof UserInterface) {
        throw new AccessDeniedException('This user does not have access to this section.');
    }

    $form = $this->createForm('UserBundle\Form\PostType', $post);
    dump($form);
    //die();
    $form->handleRequest($request);

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

        // relate this post to the owner user
        $user->addPost($post);
        // relate the owner user to this post
        $post->setUser($user);

        $em = $this->getDoctrine()->getManager();
        $em->persist($post);
        $em->persist($user);
        $em->flush();

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

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

1 个答案:

答案 0 :(得分:1)

查看您的观点非常有用,但只是提醒:要能够添加新图片,您的表单需要一些javascript,请查看此处:https://symfony.com/doc/current/form/form_collections.html#allowing-new-tags-with-the-prototype

没有javascript,默认情况下它只会渲染已添加的集合对象。如果您的实体中没有,则默认情况下不会显示任何内容。