I've got a Company that has many Employees. In my form, I want the user to be able to dynamically add employees (easy enough). EmployeeType (an AbstractType) is compound, containing a first and last name. On form submission, Symfony doesn't seem to carry over the data from the form into the constructor for the "new" Employee. I get an erro
ArgumentCountError: Too few arguments to function Employee::__construct() ... 0 passed in ... and exactly 3 expected
Showing and editing existing Employees works, so I'm confident my relationships, etc., are all correct.
Abbreviated code:
Company
class Company
{
protected $employees;
public function __construct()
{
$this->employees = new ArrayCollection();
}
public function addEmployee(Employee $employee)
{
if ($this->employees->contains($employee)) {
return;
}
$this->employees->add($employee);
}
public function removeEmployee(Employee $employee)
{
if (!$this->employees->contains($employee)) {
return;
}
$this->employees->removeElement($employee);
}
}
Employee
class Employee
{
// ... firstName and lastName properties...
public function __construct(Company $company, $firstName, $lastName)
{
$this->company = $company;
$this->company->addEmployee($this);
}
// ...getter and setter for firstName / lastName...
}
CompanyType
class CompanyType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('employees', CollectionType::class, [
'entry_type' => EmployeeType::class,
'allow_add' => true,
'allow_delete' => false,
'required' => false,
]);
// ...other fields, some are CollectionType of TextTypes that work correctly...
}
}
EmployeeType
class EmployeeType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('firstName')
->add('lastName');
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Employee::class,
]);
}
}
CompanyController
class CompanyController
{
// Never mind that this is a show and not edit, etc.
public function showAction()
{
// Assume $this->company is a new or existing Company
$form = $this->createForm(CompanyType::class, $this->company);
$form->handleRequest($this->request);
if ($form->isSubmitted() && $form->isValid()) {
$company = $form->getData();
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($company);
$entityManager->flush();
}
// set flash message, redirect, etc.
}
// ...render view...
}
The above will work when modifying existing Employees, just not when creating new ones. Debugging from within the Symfony code, I can see that no data exists for the new employees, so it's trying to find a closure or definition for empty_data in CompanyType. I've tried this every which way (via configureOptions, and empty_data option when building the CompanyType::buildForm form), e.g. https://symfony.com/doc/current/form/use_empty_data.html. My gut tells me I don't even need to do this, because the form data should not be empty (I explicitly filled out the fields).
I tried using a model transformer as well. In that case, the transformation from the form (second function argument passed to new CallbackTransformer) isn't even hit.
The view properly sets name attributes when adding new employee fields, e.g. form[employees][1][firstName], etc. That isn't the problem. It also sends the right data to the controller. I confirmed this by inspecting the form submission data via CompanyType::onPreSubmit (using an event listener).
I also have a CollectionType of TextTypes for other things in CompanyType, those work fine. So the issue seems to be related to the fact that EmployeeType is compound (containing multiple fields).
Hopefully the above is enough to illustrate the problem. Any ideas?
UPDATE:
It seems the issue is there isn't an instantiation of Employee for Symfony to work with. Internally, each field gets passed to Symfony\Component\Form\Form::submit(). For existing employees, there is also an Employee passed in. For the new one, it's null. That explains why it's looking for empty_data, but I don't know why I can't get empty_data to work.