Встроенные формы Symfony Forms

Следуя документации symfony, я пытаюсь встроить коллекцию в форму. Но при отправке почтового запроса на мою конечную точку встроенная форма с коллекцией People остается пустой. Мои сущности:

user.php

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\UserInterface;

/**
 * User
 *
 * @ORM\Entity
 * @UniqueEntity(fields = "email", message = "Email already taken")
 * @ORM\HasLifecycleCallbacks()
 */
class User implements UserInterface
{
    use Mapping\UserTrait;

    /**
     * @param Person $person
     */
    public function addPeople(Person $person)
    {
        $this->people->add($person);
        $person->setOwner($this);
    }

    /**
     * @param Person $person
     */
    public function removePeople(Person $person)
    {
        $this->people->removeElement($person);
    }
}

UserTrait.php

<?php

namespace App\Entity\Mapping;

use App\Entity\Person;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * User
 *
 * @ORM\Table()
 */
trait UserTrait
{
    /**
     * @var int
     *
     * @ORM\Column(name = "id", type = "integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy = "AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name = "hash", type = "string")
     */
    private $hash;

    /**
     * @var bool
     *
     * @ORM\Column(name = "is_active", type = "boolean")
     */
    private $isActive;

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

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

    /**
     * @var string
     * @ORM\Column(name = "email", type = "string")
     * @Assert\NotBlank
     * @Assert\Email
     */
    private $email;

    /**
     * @var \Doctrine\Common\Collections\Collection
     *
     * @ORM\OneToMany(targetEntity = "App\Entity\Person", mappedBy = "owner", cascade = {"persist"})
     */
    private $people;

    /**
     * @var string
     * @Assert\NotBlank
     * @Assert\Length(max=4096)
     */
    private $plainPassword;

    /**
     * Constructor
     */
    public function __construct()
    {
        $this->people = new ArrayCollection();
        $this->roles = ['ROLE_USER'];
        $this->isActive = 1;
    }

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

    /**
     * @param int $id
     */
    public function setId(int $id): void
    {
        $this->id = $id;
    }

    /**
     * @return bool
     */
    public function isActive(): bool
    {
        return $this->isActive;
    }

    /**
     * @param bool $isActive
     */
    public function setIsActive(bool $isActive): void
    {
        $this->isActive = $isActive;
    }

    /**
     * @return string
     */
    public function getEmail()
    {
        return $this->email;
    }

    /**
     * @param string $email
     */
    public function setEmail(string $email): void
    {
        $this->email = $email;
    }

    /**
     * @return \Doctrine\Common\Collections\Collection
     */
    public function getPeople(): \Doctrine\Common\Collections\Collection
    {
        return $this->people;
    }

    /**
     * @param \Doctrine\Common\Collections\Collection $people
     */
    public function setPeople(\Doctrine\Common\Collections\Collection $people): void
    {
        $this->people = $people;
    }

    /**
     * @return string
     */
    public function getPlainPassword()
    {
        return $this->plainPassword;
    }

    /**
     * @param string $plainPassword
     */
    public function setPlainPassword(string $plainPassword): void
    {
        $this->plainPassword = $plainPassword;
    }

    /**
     * Implementation of UserInterface: Get password hash.
     * @return string
     */
    public function getPassword()
    {
        return $this->hash;
    }

    /**
     * Implementation of UserInterface
     */
    public function eraseCredentials()
    {
        $this->plainPassword = null;
    }

    /**
     * Implementation of UserInterface
     */
    public function getUsername()
    {
        return $this->email;
    }

    public function getSalt()
    {
        return null;
    }

    public function getRoles()
    {
        return ["User"];
    }

    /**
     * @return string
     */
    public function getHash(): string
    {
        return $this->hash;
    }

    /**
     * @param string $hash
     */
    public function setHash(string $hash): void
    {
        $this->hash = $hash;
    }

    /**
     * @ORM\PrePersist
     * @ORM\PreUpdate
     */
    public function updatedTimestamps(): void
    {
        $dateTimeNow = new \DateTime('now');
        $this->setUpdatedAt($dateTimeNow);
        if ($this->getCreatedAt() === null) {
            $this->setCreatedAt($dateTimeNow);
        }
    }

    /**
     * @return \DateTime
     */
    public function getUpdatedAt(): \DateTime
    {
        return $this->updatedAt;
    }

    /**
     * @param \DateTime $updatedAt
     */
    public function setUpdatedAt(\DateTime $updatedAt): void
    {
        $this->updatedAt = $updatedAt;
    }

    /**
     * @return \DateTime
     */
    public function getCreatedAt()
    {
        return $this->createdAt;
    }

    /**
     * @param \DateTime $createdAt
     */
    public function setCreatedAt(\DateTime $createdAt): void
    {
        $this->createdAt = $createdAt;
    }

}

Person.php

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * Person
 *
 * @ORM\Entity
 */
class Person
{
    use Mapping\PersonTrait;
}

PersonTrait.php

<?php

namespace App\Entity\Mapping;

use Doctrine\ORM\Mapping as ORM;

use Symfony\Component\Validator\Constraints as Assert;


/**
 * Person
 *
 * @ORM\Table()
 */
trait PersonTrait
{
    /**
     * @var int
     *
     * @ORM\Column(name = "id", type = "integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy = "AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name = "family_name", type = "string")
     * @Assert\NotBlank
     */
    private $familyName;

    /**
     * @var string
     *
     * @ORM\Column(name = "given_name", type = "string")
     * @Assert\NotBlank
     */
    private $givenName;

    /**
     * @var string
     *
     * @ORM\Column(name = "title", type = "string")
     */
    private $title;

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

    /**
     * @var string
     *
     * @ORM\Column(name = "salutation", type = "string")
     */
    private $salutation;

    /**
     * @var string
     *
     * @ORM\Column(name = "gender", type = "string")
     */
    private $gender;

    /**
     * @var \App\Entity\User
     *
     * @ORM\ManyToOne(targetEntity = "App\Entity\User", inversedBy = "people")
     * @ORM\JoinColumns({
     *   @ORM\JoinColumn(name = "owner_id", referencedColumnName = "id")
     * })
     */
    private $owner;

    /**
     * @return string
     */
    public function getFamilyName()
    {
        return $this->familyName;
    }

    /**
     * @param string $familyName
     */
    public function setFamilyName($familyName)
    {
        var_dump($familyName);
        $this->familyName = $familyName;
    }

    /**
     * @return string
     */
    public function getGivenName()
    {
        return $this->givenName;
    }

    /**
     * @param string $givenName
     */
    public function setGivenName($givenName)
    {
        $this->givenName = $givenName;
    }

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

    /**
     * @param string $title
     */
    public function setTitle(string $title): void
    {
        $this->title = $title;
    }

    /**
     * @return \DateTime
     */
    public function getBirthDate(): \DateTime
    {
        return $this->birthDate;
    }

    /**
     * @param \DateTime $birthDate
     */
    public function setBirthDate(\DateTime $birthDate): void
    {
        $this->birthDate = $birthDate;
    }

    /**
     * @return string
     */
    public function getSalutation(): string
    {
        return $this->salutation;
    }

    /**
     * @param string $salutation
     */
    public function setSalutation(string $salutation): void
    {
        $this->salutation = $salutation;
    }

    /**
     * @return string
     */
    public function getGender(): string
    {
        return $this->gender;
    }

    /**
     * @param string $gender
     */
    public function setGender(string $gender): void
    {
        $this->gender = $gender;
    }

    /**
     * @return \App\Entity\User
     */
    public function getOwner(): \App\Entity\User
    {
        return $this->owner;
    }

    /**
     * @param \App\Entity\User $owner
     */
    public function setOwner(\App\Entity\User $owner): void
    {
        $this->owner = $owner;
    }
}

Теперь о моих типах форм:

UserType.php

<?php

namespace App\Form;


use App\Entity\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class UserType extends AbstractType
{


    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('email', EmailType::class)
            ->add('plainPassword', PasswordType::class)
            ->add(
                "people",
                CollectionType::class,
                [
                    'entry_type' => PersonType::class,
                    'allow_add' => true,
                    'by_reference' => false,
                ]
            );
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(
            [
                'data_class' => User::class,
                'csrf_protection' => false
            ]
        );
    }

}

PersonType.php

<?php

namespace App\Form;


use App\Entity\Person;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class PersonType extends AbstractType
{


    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('familyName', TextType::class)
            ->add('givenName', TextType::class);
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(
            [
                'data_class' => Person::class,
                'csrf_protection' => false,
            ]
        );
    }

}

Используя эти типы, я сейчас пытаюсь зарегистрировать пользователя, создать человека и добавить его пользователю, используя этот код:

/**
     * @Route("/register")
     * @param Request $request
     * @param UserPasswordEncoderInterface $passwordEncoder
     */
    public function register(Request $request, UserPasswordEncoderInterface $passwordEncoder)
    {
        $user = new User();
        $form = $this->createForm(UserType::class, $user);
        $form->submit($request->request->all());

        if ($form->isSubmitted() && $form->isValid()) {
            $password = $passwordEncoder->encodePassword($user, $user->getPlainPassword());
            $user->setHash($password);

            $entityManager = $this->getDoctrine()->getManager();
            $entityManager->persist($user);
            $entityManager->flush();
            return new Response("ok", 300);
        }
        return new Response("not ok", 500);

    }

Моя проблема сейчас в том, что когда я отправляю почтовый запрос почтальону со следующими параметрами и содержимым:

email: [email protected] 
plainPassword: test1234  
people.familyName: testLastname 
people.givenName: testFirstname

Я получаю следующую ошибку, которая означает, что он не распознает данные для физического лица

"This form should not contain extra fields."

Как заставить формы symfony распознавать, что people.givenName и people.familyName предназначены для создания экземпляра Person

Обновлено: комментарий u_mulder предложил изменить тело сообщения на person [0] .givenName, и теперь я получаю сообщение об ошибке

This value is not valid.

Коллекция означает наличие массива подэлементов. Итак, подэлементы должны быть как-то проиндексированы, например, people[0].familyName: testLastname или что-то подобное.

u_mulder 19.12.2018 09:59

Спасибо, на самом деле хороший намек, спасибо за это! Я это протестирую!

Kbi 19.12.2018 10:01

После изменения я получаю «это значение недействительно»

Kbi 19.12.2018 10:09

Утверждение, что givenName "TestFirstname" недействительно

Kbi 19.12.2018 10:15

не могли бы вы опубликовать вид вашей формы, пожалуйста?

hoover_D 19.12.2018 10:36

Как я объяснил в вопросе, я использую почтальон для создания запроса POST

Kbi 19.12.2018 10:56

Сделайте рендеринг вашей формы, введите несколько значений и опубликуйте. Убедитесь, что все работает, а затем посмотрите опубликованные данные. Компонент форм Symfony использует несколько сложное соглашение об именах элементов, чтобы отображать опубликованные данные обратно в объекты. Как только вы узнаете точный формат, вы можете вернуться к использованию почтальона, если хотите. И да, ответ ниже пытается исправить симптомы, а не основную причину.

Cerad 19.12.2018 14:38

@Cerad, а вам не достаточно отладки данных запроса в контроллере? Зачем мне для этого нужна отрисованная форма?

Kbi 19.12.2018 14:47

Но на самом деле вы правы, что возникла проблема с содержанием запроса. : D

Kbi 19.12.2018 14:59

Потому что я почти уверен, что имена ваших элементов, которые вы используете, неверны. Визуализация формы точно скажет вам, каковы их имена. Как только вы узнаете их наверняка, вы сможете избавиться от визуализированной формы. Опять же, компонент формы использует собственный шаблон именования. И не удивляйся, что я был прав. Юмор.

Cerad 19.12.2018 15:00

@Cerad, проблема на самом деле была очень простой: мне нужно назвать параметр people [0] [familyName] вместо people [0] ["familyName"]

Kbi 19.12.2018 15:08
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
0
11
195
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

У меня было две проблемы, которые нужно было исправить. Первый намек от u_mulder заключался в том, что я должен использовать индекс для людей, так как это коллекция. Так что вместо

people["givenName"]

Мне пришлось использовать

people[0]["givenName"]

Вторая ошибка заключалась в неправильной структуре тела почтальона. Вместо

people[0]["givenName"]

Пришлось убрать кавычки.

people[0][givenName]

После устранения обеих проблем формы работают должным образом.

Другие вопросы по теме