symfony4 Form -- Сохранение non-mapped поля (не относящегося к сущности, для которой генерируется форма)

В Symfony существует компонент Forms, предназначенный для создания форм, основанных на сущностях (Entity). Если корректно написать класс формы, то можно максимально упростить рендеринг, сохранение и автозаполнение формы. Но чем сложнее поставленная задача, тем более неоднозначно решение.

Здесь, например, мы научились выводить сложную форму, основанную на сущности (Entity) и связанной с ней (вложенной) сущности.

Задача

А теперь мы рассмотрим, как добавить в форму поле, которого нет в объекте сущности, а также как потом сохранить его, не нарушая автоматического сохранения полей, принадлежащих сущности.

Это требуется, когда в качестве поля выступает, например, файл, а сохранить в объект нам нужно, например, путь к файлу.

Решение

  • Чтобы добавить поле, которого нет в сущности, редактируем класс формы:
        // ProductType.php:
    
        /**
         * @param FormBuilderInterface $builder
         * @param array $options
         */
        public function buildForm(FormBuilderInterface $builder, array $options): void
        {
            $builder->add('name')
                ->add('backgroundColor')
                ->add('fields')
                ->add('photo', FileType::class, [
                    // опция <em>mapped</em> говорит Symfony о том, что это поле 
                    // не нужно искать в сущности, но нужно добавить в форму
                    'mapped' => false,
                ]);
        }
        
        /**
         * @param OptionsResolver $resolver
         */
        public function configureOptions(OptionsResolver $resolver): void
        {
            $resolver->setDefaults([
                'data_class' => Product::class,
            ]);
        }
  • В контроллере объект сущности сохраняется автоматически, а non-mapped поле мы получим вручную, также вручную сохраняем сам файл:
        // ProductController.php:
    
        $product = new Product();
        $form = $this->createForm(ProductType::class, $product);
        $form->handleRequest($request);
            
        if ($form->isSubmitted() && $form->isValid()) {
           /** @var Symfony\Component\HttpFoundation\File\UploadedFile $photo */
           $photo = $form->get('photo')->getData();
            
            if (!empty($photo)) {  
                 $product->setPathToPhoto($this->getParameter('images_directory')
                      .$product->getName()
                      .'.'.$photo->getClientOriginalExtension());
    
                 // и сохраняем файл (как можно сделать, см в ссылке выше)
            }
            
            $em = $this->getDoctrine()->getManager();
            $em->persist($adProduct);
            $em->flush();
    
            return $this->redirectToRoute('admin_product_list');
       }
    
       return $this->render('admin/product/new.html.twig', [
            'productForm' => $form->createView(),
      ]);   

Вот и всё) Всем счастья ♥!

Источники