Sonata用户管理员 - 自定义字段依赖项

时间:2013-05-13 16:10:00

标签: symfony fosuserbundle sonata-admin

我已为FOSUser扩展了SonataAdmin类,并添加了2个自定义字段(来自外部数据源的选项类型):CompanySector

我希望Sector依赖于Company,因此如果用户选择公司,则会过滤可用的部门。

我虽然在页面加载时使用FormEvents进行过滤,但我甚至不知道如何获取当前表单的Company值。

以下是我的自定义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;
}

所以问题是:

如何从我的自定义Company中获取所选SectorType


之后,我需要能够使用Ajax刷新扇区,但这将是另一个问题

1 个答案:

答案 0 :(得分:1)

我有类似的问题。我需要创建一个销售实体,该实体需要与企业实体建立多对一关系,并与服务实体建立多对多的关系。这是销售实体:

根据所选公司的不同,服务可用。例如,服务a和b只能提供给公司x。服务b和c只能提供给公司y。所以在我的管理员中,根据所选择的公司,我必须显示可用的服务。对于这些我需要做两件事:

首先使用我的销售管理员创建一个动态表单,以便在服务器端我可以为我的销售记录中指定的公司获得正确的服务。第二,我必须为我的公司表单元素创建一个自定义表单类型,这样当用户在客户端更改它时,它会发送一个ajax请求以获得所选公司的正确服务。

对于我的第一个问题,我做了类似于你想要实现的事情,但是我没有为我的服务元素创建特定的自定义类型,而是直接在管理员中添加了de event listener。

这是Sale实体:

/**
 *
 * @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;
    }

然后在Admin表单构建器中:

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)));
        }
    );
}

诀窍是传递表单数据。在事件监听器的函数中使用evet-> getData()是行不通的。相反,我通过admin-> getSubject()方法传递了它。然后,我不得不在事件监听器的函数中添加一个sonataadmin表单类型,而是使用普通的symfony表单类型。

你提到的Ajax部分是另一个问题。表单构建器中的分支add方法中的所有奇怪的东西都与此问题的自定义字段类型相关。别担心。