简体   繁体   中英

Symfony2 Form: AUTOMATION on persisting with related objects

I have a form for inserting an entity Category . This entity has two other entities that are related to it. One related entity is an other separate Entity Group . The other entity is itself self-referenced Category that is an array collection that represents preconditions. So far so good, i can persist the main entity with the relations with the correct ORM annotations.

Rough scheme of Category

  • id : int
  • title: string
  • group : Group obj
  • preconditions : [Category obj, Category obj, ...]

I made an type class for creating the form as described as best-practice in the documentation.

$form = $this->createForm(new CategoryType($em));

Situation

Before i persist the entity, i must initialize it and set the posted datas to it. The posted related objects can't simply setted to the persisting entity, because they have the wrong datatype. (Eg the self-referencing collection is posted only as array with id's, and not an array collection of the choosed items.) So i catch this raw datas and get separatelly the related entities from the entity manager.

Goal

The inserting entity should be filled automatically with the related entities, whitout get those separately through the entity manager

Question

Is this the meaning of the form component that those related objects are not posted and made available fully? Or what im missing in my implementation? Is there a way to do this more automated?

On the form class for the 'preconditions' property i had to do mapped => false otherwise i recieve an exception that a wrong type was passed. But at the end i want that the form matches all automatically through the mapping, whitout skipping a mapping, and whitout getting the related entities separately from the entity manager.

class CategoryType extends AbstractType
{

    public function __construct($em)
    {
        $this->em = $em;
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $qb = $this->em->createQueryBuilder();

        $categories = $qb->select('e.id, e.title')
            ->from('MyvendorCoreBundle:Category', 'e')
            ->indexBy('e', 'e.id')
            ->orderBy('e.title')
            ->getQuery()
            ->getResult();

        $categories_choice = array_map(function ($value) {
            return $value['title'];
        }, $categories);

        $builder->add('title')
            ->add('group_Id', new GroupType($this->em))
            ->add('preconditions', 'choice', array(
            'choices' => $categories_choice,
            'multiple' => true,
            'mapped' => false
        ))
            ->add('save', 'submit');
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'Myvendor\CoreBundle\Entity\Category'
        ));
    }

    public function getName()
    {
        return 'category';
    }
}

Controller method

public function newAction(Request $request)
    {
        $em = $this->getDoctrine()->getManager();
        $form = $this->createForm(new CategoryType($em));

        // Repopulating the form after submission
        $form->handleRequest($request);

        // Prepare a new empty Category
        $category = new Category();

        if ($form->isValid()) {
            /* Catch some raw datas posted from the form */
            // Posted precondition category ids to get its entities more later
            $precondition_category_ids = $form->get('preconditions')->getData();
            // Posted group entity that have only filled the group id in the object          
            $group_raw = $form->get('group_Id')->getData();

            // Get the explicit filled group entity throuth the posted id.
            $group = $em->find('MyvendorCoreBundle:Group', $group_raw->getGroupid());

            // Fill the prepaired group with the posted datas
            $category->setTitle($form->get('title')->getData());
            $category->setGroupId($group);

            // Adding preconditions
            try {
                for ($i = 0; count($precondition_category_ids) > $i; $i ++) {
                    $precondition_category_id = $precondition_category_ids[$i];
                    if (0 >= $precondition_category_id) { // Retrieving id must be greater than 0
                        throw new \Exception('Error retrieving precondition id');
                    }
                    $precondition_category = $em->find('MyvendorCoreBundle:Category', $precondition_category_id);

                    if ($precondition_category instanceof Category) {
                        $category->addPrecondition($precondition_category);
                    } else {
                        throw new \Exception('Error retrieving precondition as Myvendor\CoreBundle\Entity\Category');
                    }
                }
                $em->persist($category); // Insert the group item with its relations
                $em->flush();
                echo '<h1 style="color:green">persisted</h1>';
            } catch (\Exception $e) {
                echo '<h1 style="color:red">' . $e->getMessage() . '</h1>';
            }
        }

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

GroupType

class GroupType extends AbstractType
{
    public function __construct($em){
        $this->em = $em;
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $groups = $this->em->createQuery("
            SELECT o.groupid, o.descr
            FROM MyvendorCoreBundle:Group o
            INDEX BY o.groupid
            ORDER BY o.descr
            ")->getResult();
        $groups_dropdown = array();
        $groups_dropdown = array_map(function($value) { return $value['descr']; }, $groups);
        $builder->add('groupid', 'choice', array(
            'label' => false,
            'choices' => $groups_dropdown,
            'attr' => array('style' => 'width: 300px')
        ));
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'Myvendor\CoreBundle\Entity\Group',
        ));
    }

    public function getName()
    {
        return 'group';
    }
}
/**
 * @ORM\Entity
 * @ORM\Table(name="category")
 */
class Category
{
    public function __construct()
    {
        $this->preconditions = new ArrayCollection();
    }

    /**
    * @ORM\Column(type="integer")
    * @ORM\Id
    * @ORM\GeneratedValue(strategy="AUTO")
    */
    protected $id;

    /**
     * @var \Myvendor\CoreBundle\Entity\Group
     *
     * @Assert\Type(type="Myvendor\CoreBundle\Entity\Group")
     * @Assert\Valid()
     * @ORM\ManyToOne(targetEntity="Myvendor\CoreBundle\Entity\Group", inversedBy="Category")
     * @ORM\JoinColumn(name="group_id", nullable=false, referencedColumnName="groupid")
     */
    private $group_Id;

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

       /**
     * Preconditions are Categorys referencing to an Category.
     * For a single Category its empty (which have no subelements).
     * A join table holds the references of a main Category to its sub-Categorys (preconditions)
     *
     * @ORM\ManyToMany(targetEntity="Category")
     * @ORM\JoinTable(name="category_precondition",
     *     joinColumns={@JoinColumn(name="category_id", referencedColumnName="id")},
     *     inverseJoinColumns={@JoinColumn(name="category_precondition_id", referencedColumnName="id")}
     * )
     */
    private $preconditions;


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

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

        return $this;
    }

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

    /**
     * Set groupId
     *
     * @param \Myvendor\CoreBundle\Entity\Group $groupId
     *
     * @return Category
     */
    public function setGroupId(\Myvendor\CoreBundle\Entity\Group $groupId)
    {
        $this->group_Id = $groupId;

        return $this;
    }

    /**
     * Get groupId
     *
     * @return \Myvendor\CoreBundle\Entity\Group
     */
    public function getGroupId()
    {
        return $this->group_Id;
    }

    /**
     * Add precondition
     *
     * @param \Myvendor\CoreBundle\Entity\Category $precondition
     *
     * @return $this
     */
    public function addPrecondition(\Myvendor\CoreBundle\Entity\Category $precondition)
    {
        $this->preconditions[] = $precondition;

        return $this;
    }

    /**
     * Get preconditions
     *
     * @return \Doctrine\Common\Collections\Collection
     */
    public function getPreconditions()
    {
        return $this->preconditions;
    }
/**
 * Group
 *
 * @ORM\Table(name="group", indexes={@ORM\Index(name="homepage", columns={"homepage"}), @ORM\Index(name="theme", columns={"theme"})})
 * @ORM\Entity
 */
class Group
{
    /**
     * @var string
     *
     * @ORM\Column(name="descr", type="string", length=60, nullable=true)
     */
    private $descr;

    /**
     * @var integer
     *
     * @Assert\NotBlank()
     * @ORM\Column(name="groupid", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    public $groupid;

    /**
     * Set descr
     *
     * @param string $descr
     * @return Group
     */
    public function setDescr($descr)
    {
        $this->descr = $descr;

        return $this;
    }

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

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

The solution was that the type of the selecting choice entities, must be not a choicelist, but really an collection type.

So use something like this

->add('preconditions', 'collection', array(
        'entry_type' => 'entity',
        'entry_options' => array(
            'class' => 'MyVendorCoreBundle:EduStructItem',
            'choice_label' => 'title'
        ),
        'allow_add' => true,
        'allow_delete' => true
    ))

instead of

->add('preconditions', 'choice', array(
        'choices' => $categories_choice,
        'multiple' => true,
        'mapped' => false
    ))

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