One-to-Many-to-One with attributes Form with Symfony 3 / Doctrine

偶尔善良 提交于 2020-06-14 19:25:32

问题


Here is the problem :

I have a model with 3 classes

  • person
  • person_job
  • job

A person can have several jobs, any job-person relation can have a "date_start" attribute, "date_end", and "comment". So I built this model with a jointable (person_job) holding these attributes, and making the relationship on 2 manyToOne attributes called person and job (generated with doctrine annotations)

Person attributes looks like :

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

/**
* @var string
* @ORM\Column(name="firstname",type="string",length=255,nullable=true)
*/
private $firstname;
/**
* @var bool
* @ORM\Column(name="active", type="boolean")
*/
private $active;

Job attributes looks like this :

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

person_job looks like this :

/**
* @ORM\ManyToOne(targetEntity="...\Person")
* @ORM\JoinColumn(nullable=false)
*/
private $person;

/**
* @ORM\ManyToOne(targetEntity="...\Job")
* @ORM\JoinColumn(nullable=false)
*/
private $job;

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

/**
* @var \DateTime
* @ORM\Column(name="startdate",type="datetime")
*/
private $datestart;

/**
* @var \DateTime
* @ORM\Column(name="enddate",type="datetime")
*/
private $dateend;

Now I'd like to build a form, for my "person" where I can choose jobs in a list, and add (if needed) date_start, date_end, or comment related to this job. My FormBuilder looks like this for "Person" :

$builder
  ->add('name')
  ->add('firstname')
  ->add('jobs',Job::class,array('label'=>'Job'));

This fails. My Person class has no "jobs" attribute.. So, how can I achieve such things? do I have to add a jobs attribute, with a oneToMany relation on it, declared with "mappedBy"?

These relationships in doctrine still make me confused, I'm new to symfony, I looked on the internet but didn't find a decent solution/example yet...

Thanks for reading/help


回答1:


You have come accross one of the hardest problems with Symfony forms. Fortunately, there is some good documentation. Let me summarize the important steps.

You’re right: The entity Person needs to know about its relationship with PersonJob if you want to manipulate that relationship from a Person’s point of view. So you need to add a property:

// src/AppBundle/Entity/Person.php
/**
 * @ORM\OneToMany(targetEntity="PersonJob", mappedBy="person")
 */
private $personJobs;

public function __construct()
{
    $this->personJobs = new \Doctrine\Common\Collections\ArrayCollection();
}

and then you will have in the form type

// src/AppBundle/Form/PersonType.php
$builder
    ->add('name')
    ->add('firstname')
    ->add('personJobs', CollectionType::class, array(
        'entry_type'   => PersonJobType::class,
        'allow_add' => true,
    )
;

Note the type of the personJobs field. Since a person can have many PersonJobs, you need a form type that can handle collections. This is the purpose of the built-in CollectionType (check out its documentation!). You also need the form type PersonJobType, so that CollectionType knows how to build the sub-forms:

class PersonJobType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('comment')
            ->add('datestart', DateTimeType::class)
            ->add('dateend', DateTimeType::class)
            ->add('job') // requires Job::__toString() to be defined!
        ;
    }

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

For debugging purposes, change your controller to

 public function testAction()
 {
    $person = new Person();
    $form = $this->createForm(PersonType::class, $person);
    $form->add('submit', SubmitType::class);

    $form->handleRequest($request);

    if ($form->isSubmitted() && $form->isValid()) {
        print '<pre>';
        var_dump($form->getData());
        die();
    }

    return $this->render('default/index.html.twig', [
        'form' => $form->createView(),
    ]);
}

Now go ahead and copy & paste the Twig and Javascript code from Adding and Removing Items (you have to make minor changes such as replacing form.emails with form.personJobs).

The form

The form will look like

Just the Person form with a “Add another PersonJob” link:

Adding a PersonJob:

Adding anothing PersonJob:

The data received

Submit the form and see the output of var_dump:

object(AppBundle\Entity\Person)#247 (5) {
  ["id":"AppBundle\Entity\Person":private]=>
  NULL
  ["name":"AppBundle\Entity\Person":private]=>
  string(12) "Charles Dude"
  ["firstName":"AppBundle\Entity\Person":private]=>
  string(7) "Charles"
  ["active":"AppBundle\Entity\Person":private]=>
  bool(true)
  ["personJobs":"AppBundle\Entity\Person":private]=>
  object(Doctrine\Common\Collections\ArrayCollection)#248 (1) {
    ["elements":"Doctrine\Common\Collections\ArrayCollection":private]=>
    array(2) {
      [0]=>
      object(AppBundle\Entity\PersonJob)#962 (6) {
        ["id":"AppBundle\Entity\PersonJob":private]=>
        NULL
        ["comment":"AppBundle\Entity\PersonJob":private]=>
        string(19) "Something important"
        ["datestart":"AppBundle\Entity\PersonJob":private]=> 
        object(DateTime)#1088 (3) { … }
        ["dateend": …] => …
        ["person":"AppBundle\Entity\PersonJob":private]=>
        NULL
        ["job":"AppBundle\Entity\PersonJob":private]=>
        object(AppBundle\Entity\Job)#1171 (2) {
          ["id":"AppBundle\Entity\Job":private]=>
          int(2)
          ["name":"AppBundle\Entity\Job":private]=>
          string(5) "Job 2"
        }
      }
      [1]=> …
  }
}

Two things remain to be done:

  1. Set the person property of the nested PersonJob entities properly to the new (but not yet persisted) Person.

  2. Tell Doctrine about the new PersonJob entities by calling $em->persist(…) on them.

Relevant documentation:

  • CollectionType Field
  • How to Embed a Collection of Forms


来源:https://stackoverflow.com/questions/38048806/one-to-many-to-one-with-attributes-form-with-symfony-3-doctrine

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!