duanhunlou7051 2016-09-21 11:35
浏览 63
已采纳

在entityType中添加选项

I'm learning by myself the symfony framework (my job is not about developing, I'm not a developer) and I find out most of case the solution but here, is one what I didn't know how to manage.

I have 2 entity :

Product:

/**
 * Product
 *
 * @ORM\Table(name="product")
 * @ORM\Entity(repositoryClass="ProductBundle\Repository\ProductRepository")
 * @UniqueEntity("productNumber")
 */
class Product
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="productNumber", type="string", length=255, unique=true)
     * @Assert\Regex(
     *     pattern="/[0-9][.][0-9]{3}/",
     *     message="It should be like 1.234"
     * )
     */
    private $productNumber;    

    /**
     * @ORM\ManyToOne(targetEntity="ProductGroup")
     */
    private $productGroup;

    /**
     * Constructor
     */    
    public function __construct()
    {       

    }
}

Camera :

/**
 * Camera
 *
 * @ORM\Table(name="camera")
 * @ORM\Entity(repositoryClass="ProductBundle\Repository\CameraRepository")
 */
class Camera
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="modele", type="string", length=255, unique=true)
     */
    private $modele;

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

    /**
     *
     * @ORM\ManyToOne(targetEntity="Product")
     */
    private $product;

    /**
     * @ORM\ManyToMany(targetEntity="CustomField", inversedBy="camera", cascade={"persist", "remove"}, orphanRemoval=true)
     */
    protected $customFields;

    /**
     * Constructor
     */
    public function __construct()
    {
        $this->customFields = new ArrayCollection();
    }
}

My form :

namespace ProductBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;

use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Doctrine\ORM\EntityRepository;

class CameraType extends AbstractType {

    /**
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options) {

        $builder
            ->add('product', EntityType::class, [
                'class' => 'ProductBundle:Product',

                'query_builder' => function (EntityRepository  $er) {
                    return $er->createQueryBuilder('p')
                        ->select('p')
                        ->leftJoin('ProductBundle:Camera', 'c', 'WITH', 'c.product = p.id')
                        ->where('c.product IS NULL')
                        ;
                    },
                'attr' => [     
                    'required' => true, 
                ],

                'choice_label' => 'productNumber',

            ])
            ->add('modele', TextType::class, [
                'label' => "Modele",
            ])
            ->add('description', TextType::class, [
                'label' => "Description",
            ])
            ->add('customFields', CollectionType::class, [
                'entry_type' => CustomFieldType::class,
                'allow_add' => true,
                'allow_delete' => true,
                'prototype' => true,
                'required' => false,
                'attr' => [
                    'class' => 'customfield'
                ]
            ])
           ;
    }

    /**
     * @param OptionsResolver $resolver
     */
    public function configureOptions(OptionsResolver $resolver) {
        $resolver->setDefaults(array(
            'data_class' => 'ProductBundle\Entity\Camera'
        ));
    }

}

When I add a camera, I would like only the Product:productNumber where are available (not take by a camera), the querybuilder is working but my issue concern the edit form, it show only available productNumber so it's changing every time I need to edit this camera.

What can I handle this ? Should I try to found another way to add a productNumber ? do you have a "trick" ?

I hope you will understand the problem and my english because it's not my first language.

Have a nice day.

Edit : I'm on Symfony 3.1.4

  • 写回答

1条回答 默认 最新

  • duanaidang6197 2016-09-21 13:22
    关注

    I presume on new form your choice field shows only unused ProductBundle:Camera entity, and on edit form it should show saved ProductBundle:Camera entity and all unused ones.

    You should look into Form Event Subscribers

    You need to implement two event listeners PRE_SET_DATA and PRE_SUBMIT.

    Here is one way to do it. Something like this works on SF 2.8 First you will have to create product entity form from custom ProductFieldSubscriber which becomes EventSubscriberInterface:

    $builder->addEventSubscriber(new ProductFieldSubscriber('product', [])

    Now ProductFieldSubscriber should look something like this (untested)

    namespace ProductBundle\Form\EventListener;
    
    use Symfony\Component\Form\FormInterface,
        Symfony\Component\Form\FormEvent,
        Symfony\Component\EventDispatcher\EventSubscriberInterface,
        Symfony\Component\Form\FormEvents,
        Doctrine\ORM\EntityRepository,
        Symfony\Bridge\Doctrine\Form\Type as DoctrineTypes
    ;
    
    
    class ProductFieldSubscriber implements EventSubscriberInterface
    {
        private $propertyPathToSelf;
    
        public function __construct($propertyPathToSelf, array $formOptions=[]) {
            $this->propertyPathToSelf = $propertyPathToSelf;
            $this->formOptions = $formOptions;
        }
    
        public static function getSubscribedEvents() {
            return [
                FormEvents::PRE_SET_DATA => 'onPreSetData',
                FormEvents::PRE_SUBMIT => 'onPreSubmit',
            ];
        }
    
        private function addForm(FormInterface $form, $selfId = null) {
    
            $formOptions = array_replace_recursive ([
                        'class'         => 'ProductBundle:Product',
                        'placeholder'   => null,
                        'compound'      => false,
                        'query_builder' => function (EntityRepository $er) use ($selfId) {
                            $qb = $er->createQueryBuilder('p')
                                ->select('p')
                                ->leftJoin('ProductBundle:Camera', 'c', 'WITH', 'c.product = p.id')
                                ->where('c.product IS NULL')
                            ;
    
                            if (null !== $selfId) {
                                $qb
                                    ->orWhere($qb->expr()->eq('p.product', ':existingId'))
                                    ->setParameter('existingId', $selfId->getId())
                                ;
                            }
    
                            return $qb;
                        },
                    ],
                    $this->formOptions
                );
    
            if ($selfId) {
                $formOptions['data'] = $selfId;
            }
    
            $form->add($this->propertyPathToSelf, DoctrineTypes\EntityType::class, $formOptions);
    
        }
    
        public function onPreSetData(FormEvent $event) {
            $data = $event->getData();
            $form = $event->getForm();
    
            if (null === $data) {
                return;
            }
    
            $selfIdTypeMethod = "get{$this->propertyPathToSelf}";
            $selfId = $data->$selfIdTypeMethod();
    
            $this->addForm($form, $selfId);
        }
    
        public function onPreSubmit(FormEvent $event) {
            $data = $event->getData();
            $form = $event->getForm();
    
            $selfId = array_key_exists($this->propertyPathToSelf, $data) ? $data[$this->propertyPathToSelf] : null;
    
            $this->addForm($form, $selfId);
        }
    }
    

    Query builder would be simpler if you had mapped entity relations.

    Bonus update:

    1. form option 'placeholder' => null, takes care that no default 'empty' option is available.
    2. form option 'required' => true, forces html5 form popup validation.
    3. Then you should use something like entity @assert notations and use validator constraints on entity attribute:

      use Symfony\Component\Validator\Constraints as Assert;
      
      /**
       * @var string
       *
       * @Assert\NotNull()
       * @ORM\Column(name="modele", type="string", length=255, unique=true)
       */
      private $modele;
      
    4. You could also disallow edit form from opening via controller editAction (maybe some redirect) and twig, where you could hide edit button.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 rs485的上拉下拉,不会对a-b<-200mv有影响吗,就是接受时,对判断逻辑0有影响吗
  • ¥15 使用phpstudy在云服务器上搭建个人网站
  • ¥15 应该如何判断含间隙的曲柄摇杆机构,轴与轴承是否发生了碰撞?
  • ¥15 vue3+express部署到nginx
  • ¥20 搭建pt1000三线制高精度测温电路
  • ¥15 使用Jdk8自带的算法,和Jdk11自带的加密结果会一样吗,不一样的话有什么解决方案,Jdk不能升级的情况
  • ¥15 画两个图 python或R
  • ¥15 在线请求openmv与pixhawk 实现实时目标跟踪的具体通讯方法
  • ¥15 八路抢答器设计出现故障
  • ¥15 opencv 无法读取视频