empty_data не работает для составных форм или объект не создается (ArguementCountError: слишком мало аргументов для функции) - PullRequest
0 голосов
/ 07 сентября 2018

У меня есть Company, у которого много Employee с. В моей форме я хочу, чтобы пользователь мог динамически добавлять сотрудников (достаточно просто). EmployeeType (AbstractType) - соединение, содержащее имя и фамилию. При отправке формы Symfony, похоже, не переносит данные из формы в конструктор для «нового» сотрудника. Я получаю ArgumentCountError: Too few arguments to function Employee::__construct() ... 0 passed in ... and exactly 3 expected. Отображение и редактирование работ сотрудников, поэтому я уверен, что мои отношения и т. Д. Все верны.

Сокращенный код:

Компания

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...
}

Выше будет работать при изменении существующих сотрудников, но не при создании новых. Отлаживая из кода Symfony, я вижу, что для новых сотрудников не существует данных, поэтому он пытается найти закрытие или определение для empty_data в CompanyType. Я пробовал это в разные стороны (с помощью configureOptions и empty_data при создании формы CompanyType::buildForm), например. https://symfony.com/doc/current/form/use_empty_data.html. Моя интуиция говорит мне, что мне даже не нужно этого делать, потому что данные формы не должны быть пустыми (я явно заполнил поля).

Я тоже пытался использовать модельный трансформатор. В этом случае преобразование из формы (второй аргумент функции, переданный в new CallbackTransformer) даже не выполняется.

Представление правильно устанавливает атрибуты имени при добавлении новых полей сотрудника, например, form[employees][1][firstName] и т. Д. Это не проблема. Он также отправляет правильные данные в контроллер. Я подтвердил это, проверив данные отправки формы через CompanyType::onPreSubmit (используя прослушиватель событий).

У меня также есть CollectionType из TextType с для других вещей в CompanyType, они работают нормально. Таким образом, проблема, похоже, связана с тем, что EmployeeType является составным (содержит несколько полей).

Надеюсь, вышесказанного достаточно, чтобы проиллюстрировать проблему. Есть идеи?

UPDATE:

Кажется, проблема в том, что * Symanfony не работает с Employee. Внутри каждое поле передается в Symfony\Component\Form\Form::submit(). Для существующих сотрудников также передается Employee. Для нового это null. Это объясняет, почему он ищет empty_data, но я не знаю, почему я не могу заставить empty_data работать.

1 Ответ

0 голосов
/ 10 сентября 2018

Решением было определить empty_data в составной форме , а не в форме CollectionType.

Моя ситуация немного странная, потому что мне также нужен экземпляр Company в моем EmployeeType, так как он должен быть передан конструктору для Employee. Я выполнил это, передав компанию в качестве опции формы в configureOptions (предоставляется контроллером), а затем в entry_options. Я не знаю, является ли это лучшей практикой, но она работает:

CompanyController

Убедитесь, что мы передаем экземпляр Company, чтобы его можно было использовать в EmployeeType при создании нового Employee:

$form = $this->createForm(CompanyType::class, $this->company, [
    // This is a form option, valid because it's in CompanyType::configureOptions()
    'company' => $this->company,
]);

CompanyType

class CompanyType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('employees', CollectionType::class, [
            // ...

            // Pass the Company instance to the EmployeeType.
            'entry_options' => [ 'company' => $options['company'] ],

            // This was also needed, apparently.
            'by_reference' => false,
        ]);
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            // Allows the controller to pass in a Company instance.
            'company' => null,
        ]);
    }
}

EmployeeType

Здесь мы убедимся, что empty_data правильно строит Employee из данных формы.

class EmployeeType extends AbstractType
{
    private $company;

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('firstName')
            ->add('lastName');

        // A little weird to set a class property here, but we need the Company
        // instance in the 'empty_data' definition in configureOptions(),
        // at which point we otherwise wouldn't have access to the Company.
        $this->company = $options['company'];
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => Employee::class,
            'empty_data' => function (FormInterface $form) use ($resolver) {
                return new Employee(
                    $this->company,
                    $form->get('firstName')->getData(),
                    $form->get('lastName')->getData(),
                );
            },
        ]);
    }
}

Viola! Теперь я могу добавлять новых сотрудников.

Надеюсь, это поможет другим людям!

...