Here is the problem :
I have a model with 3 classes
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
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 will look like
Just the Person form with a “Add another PersonJob” link:
Adding a PersonJob:
Adding anothing PersonJob:
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:
Set the person
property of the nested PersonJob
entities properly to the new (but not yet persisted) Person.
Tell Doctrine about the new PersonJob
entities by calling $em->persist(…)
on them.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With