简体   繁体   中英

Sonata User Admin - Custom field dependency

I have extended the SonataAdmin class for FOSUser and added 2 custom fields (choice type from external data source): Company and Sector

I'd like to make Sector dependent on Company , so if the user selects a Company it filters the available Sectors.

I though about using FormEvents for filtering at page load, but I don't even know how to get the Company value of the current form.

Here is a part of my custom SectorType

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder->addEventListener(FormEvents::PRE_SET_DATA
    , function(FormEvent $event) {
        $data = $event->getData();
        $form = $event->getForm();
        // Need to get the company value here if set
    });
}

public function setDefaultOptions(OptionsResolverInterface $resolver)
{
    $resolver->setDefaults(array(
        'choices' => $this->getSectors(),
    ));
}

public function getSectors()
{
    $sects = array();
    // Need to pass the selected company value to my getList
    // (which gets the list of sector as you can imagine)
    if (($tmp_sects = $this->ssrs->getList('Sector'))) {
        foreach ($tmp_sects as $sect) {
            $label = $sect['id'] ? $sect['label'] : '';
            $sects[$sect['id']] = $label;
        }
    }
    return $sects;
}

So the question is:

How to get the selected Company from my custom SectorType ?


After that I'll need to be able to refresh the Sector with Ajax, but that will be another question

I had a similar problem. I needed to create a sale entity that needed to be associated in a many to one relationship with an enterprise entity and a many to many relationship with services entities. Here is the Sale Entity:

The thing is that services where available depending on the companies chosen. For instance services a and b could only be provided to company x. And services b and c could only be provided to company y. So in my admin, depending on the chosen company I had to display the available services. For these I needed to do 2 things:

First create a dynamic form with my sale admin, so that on the server side I could get the right services available for the company specified in my sale record. And second, I had to create a custom form type for my company form element, so that when it was changed by the user on the client side, It would send an ajax request to get the right services for the company chosen.

For my first problem, I did something similar to what you were trying to achieve, but instead of creating an specific custom type for my services element, I added de event listener directly in the admin.

Here is the Sale entity:

/**
 *
 * @ORM\Table(name="sales")
 * @ORM\Entity
 * @ORM\HasLifecycleCallbacks()
 */
class Sale
{
    /**
     * @var integer $id
     *
     * @ORM\Column(name="id", type="integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
     public $id;
    /**
     * @ORM\ManyToOne(targetEntity="Branch")
     * @ORM\JoinColumn(name="branch_id", referencedColumnName="id", nullable = false)
     * @Assert\NotBlank(message = "Debe especificar una empresa a la cual asignar el precio de este exámen!")
     */
    private $branch;

    /** Unidirectional many to many
     * @ORM\ManyToMany(targetEntity="Service")
     * @ORM\JoinTable(name="sales_services",
     *      joinColumns={@ORM\JoinColumn(name="sale_id", referencedColumnName="id")},
     *      inverseJoinColumns={@ORM\JoinColumn(name="service_id", referencedColumnName="id")}
     *      )
     * @Assert\Count(min = "1", minMessage = "Debe especificar al menos un servicio a realizar!")
     */
    private $services;


    public function __construct() {
        $this->services = new \Doctrine\Common\Collections\ArrayCollection();
    }

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

    /**
     * Set branch
     *
     * @param Astricom\NeurocienciasBundle\Entity\Branch $branch
     */
     //default value always have to be null, because when validation constraint is set to notblank, 
     //if default is not null, before calling the validation constraint an error will pop up explaining
     //that no instance of Branch was passed to the $branch argument.
    public function setBranch(\Astricom\NeurocienciasBundle\Entity\Branch $branch = null)
    {
        $this->branch = $branch;
    }

    /**
     * Get branch
     *
     * @return Astricom\NeurocienciasBundle\Entity\Branch 
     */
    public function getBranch()
    {
        return $this->branch;
    }

     /**
     * Add service
     *
     * @param \Astricom\NeurocienciasBundle\Entity\Service|null $service
     */
    public function addServices(\Astricom\NeurocienciasBundle\Entity\Service $service = null)
    {
        $this->services[] = $service;
    }

    /**
     * Get services
     *
     * @return Doctrine\Common\Collections\Collection 
     */
    public function getServices()
    {
        return $this->services;
    }


    /**
     * Sets the creation date
     *
     * @param \DateTime|null $createdAt
     */
    public function setCreatedAt(\DateTime $createdAt = null)
    {
        $this->createdAt = $createdAt;
    }

    /**
     * Returns the creation date
     *
     * @return \DateTime|null
     */
    public function getCreatedAt()
    {
        return $this->createdAt;
    }

    /**
     * Sets the last update date
     *
     * @param \DateTime|null $updatedAt
     */
    public function setUpdatedAt(\DateTime $updatedAt = null)
    {    
        $this->updatedAt = $updatedAt;
    }

So then in the Admin form builder:

protected function configureFormFields(FormMapper $formMapper)  {
    $em = $this->container->get('doctrine')->getEntityManager();
    $branchQuery = $em->createQueryBuilder();

    $branchQuery->add('select', 'b')
       ->add('from', 'Astricom\NeurocienciasBundle\Entity\Branch b')
       ->add('orderBy', 'b.name ASC');

    $formMapper
      ->with('Empresa/Sucursal')
         ->add('branch','shtumi_ajax_entity_type',array('required' => true, 'label'=>'Empresa/Sucursal','error_bubbling' => true, 'empty_value' => 'Seleccione una empresa/sucursal', 'empty_data'  => null, 'entity_alias'=>'sale_branch', 'attr'=>array('add_new'=>false), 'model_manager' => $this->getModelManager(), 'class'=>'Astricom\NeurocienciasBundle\Entity\Branch', 'query' => $branchQuery)) 
      ->end()
    ;

    $builder = $formMapper->getFormBuilder();
    $factory = $builder->getFormFactory(); 
    $sale = $this->getSubject();
    $builder->addEventListener(FormEvents::PRE_SET_DATA, 
        function(DataEvent $event) use ($sale,$factory, $em) {

            $form = $event->getForm();
            $servicesQuery = $em->createQueryBuilder();
            $servicesQuery->add('select','s')
                ->add('from','Astricom\NeurocienciasBundle\Entity\Service s');

            if (!$sale || !$sale->getId()) {
                $servicesQuery
                    ->where($servicesQuery->expr()->eq('s.id', ':id'))
                    ->setParameter('id', 0);
            }
            else {
                $servicesQuery
                    ->join('s.branch', 'b')
                    ->where($servicesQuery->expr()->eq('b.id', ':id'))
                    ->setParameter('id', $sale->getBranch()->getId());
            }

            $form->add($factory->createNamed('services','entity',null,array('required' => true, 'label'=>'Servicios','error_bubbling' => true, 'attr'=>array('show_value_label'=>true),'class'=>'Astricom\NeurocienciasBundle\Entity\Service','multiple'=>true,'expanded'=>true,'query_builder'=>$servicesQuery)));
        }
    );
}

The trick thing was to pass the forms data. It doesn't work to use evet->getData() in the event listener's function. Instead I passed it through the admin->getSubject() method. Then instead of adding a sonataadmin form type, inside the event listener's function, I had to use a plain symfony form type.

The Ajax part as you mentioned is another question. All the weird things on the branch add method in the form builder is related to a customized field type for this matter. Don't worry about it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM