@contra1

Как правильно построить Symfony Forms, при ManyToMany связи?

Здравствуйте, таблицы post и language связаны между собой через промежуточную таблицу language_post.
Сущность Post
#[ORM\Entity(repositoryClass: PostRepository::class)]
class Post
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(options: ['unsigned' => true])]
    private ?int $id = null;

    #[ORM\Column(length: 150, unique: true)]
    private ?string $slug = null;

    /**
     * @var Collection<int, LanguagePost>
     */
    #[ORM\OneToMany(targetEntity: LanguagePost::class, mappedBy: 'post')]
    private Collection $languagePost;

    public function __construct()
    {
        $this->languagePost = new ArrayCollection();
    }
}

Сущность Language
#[ORM\Entity(repositoryClass: LanguageRepository::class)]
class Language
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(type: 'tinyint', options: ['unsigned' => true, 'length' => 1])]
    private ?int $id = null;

    #[ORM\Column(length: 3)]
    private ?string $title = null;

    /**
     * @var Collection<int, LanguagePost>
     */
    #[ORM\OneToMany(targetEntity: LanguagePost::class, mappedBy: 'language')]
    private Collection $languagePost;

    public function __construct()
    {
        $this->languagePost = new ArrayCollection();
    }
}


Промежуточная сущность LanguagePost
class LanguagePost
{
    #[ORM\Id]
    #[ORM\ManyToOne(targetEntity: Post::class, inversedBy: 'languagePost')]
    #[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
    private ?Post $post = null;

    #[ORM\Id]
    #[ORM\ManyToOne(targetEntity: Language::class, inversedBy: 'languagePost')]
    #[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
    private ?Language $language = null;

    #[ORM\Column(length: 140)]
    private ?string $title = null;

    #[ORM\Column(length: 255, nullable: true)]
    private ?string $descr = null;

    #[ORM\Column(length: 255, nullable: true)]
    private ?string $keyw = null;

    #[ORM\Column(type: Types::TEXT, nullable: true)]
    private ?string $text = null;


Далее создание формы
Контроллер
$post = new Post();
        $languages = $this->languageService->getAll();

        foreach ($languages as $language) {
            $language_post = new LanguagePost();
            $language_post->setLanguage($language);
            $post->addLanguagePost($language_post);
        }

        $form = $this->createForm(PostType::class, $post);
        $form->handleRequest($request);


PostType
class PostType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('languagePost', CollectionType::class, [
                'entry_type' => LanguagePostType::class,
                'label' => 'Language Posts',
                'allow_add' => true,
                'allow_delete' => true,
                'by_reference' => false,
            ]);
    }

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


LanguagePostType
class LanguagePostType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('language', EntityType::class, [
                'class' => Language::class,
                'choice_label' => 'title',
                'label' => 'Select Language',
            ])
            ->add('title', TextType::class, [
                'label' => 'Title',
            ])
            ->add('descr', TextType::class, [
                'label' => 'Description',
            ])
            ->add('keyw', TextType::class, [
                'label' => 'Keywords',
            ]);
    }

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


Например в таблице language есть 2 языка. В итоге в форме выводятся 2 группы с полями (title, descr и т.д). Проблема в том, что в каждой группе, так же создается тег select со всеми языками из таблицы language.

Как мне реализовать такое, чтобы в каждой группе полей был 1 язык, а не весь список?
  • Вопрос задан
  • 148 просмотров
Решения вопроса 1
@contra1 Автор вопроса
Вдруг кому-то еще понадобится. В итоге сделал с помощью HiddenType и трансформера. HiddenType ожидает текст, в качестве значения, но в language у нас объект Language.
public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('language', HiddenType::class)
            ->get('language')
                ->addModelTransformer(new CallbackTransformer(
                    function ($language) {
                        return $language->getId();
                    },
                    function ($id): ?Language {
                        return $this->entityManager
                            ->getRepository(Language::class)
                            ->find($id);
                    }
                ));
    }


Поэтому когда подставляем значение в форму, с помощью трансформера передаем id. Когда форма отправляется на сервер, обратно переводим в объект Language. В идеале трансформер нужно перенести в отдельный класс, со всеми проверками и т.д, но тут просто в качестве примера.
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы