Symfony2和ParamConverter(s)

时间:2012-06-05 20:52:43

标签: symfony doctrine doctrine-orm symfony-forms

访问我的路线/message/new我将显示一个表单,用于向一个或多个客户发送新消息。表单模型(以及其他)包含Customer个实体的集合:

class MyFormModel
{
   /**
    * @var ArrayCollection
    */
    public $customers;
}

我想使用customers GET参数实施自动客户选择,如下所示:

message/new?customers=2,55,543

现在只需拆分,并进行查询即可获得客户:

public function newAction(Request $request)
{
    $formModel = new MyFormModel();

    // GET "customers" parameter
    $customersIds = explode($request->get('customers'), ',');

    // If something was found in "customers" parameter then get entities
    if(!empty($customersIds)) :

        $repo  = $this->getDoctrine()->getRepository('AcmeHelloBundle:Customer');
        $found = $repo->findAllByIdsArray($customersIds);

        // Assign found Customer entities
        $formModel->customers = $found;
    endif;

    // Go on showing the form
}

如何使用Symfony 2 converters执行相同的操作?像:

public function newAction(Request $request, $selectedCustomers)
{
}

2 个答案:

答案 0 :(得分:13)

回答我自己:没有让你生活轻松的事情。我编写了一个快速而肮脏(可能是错误)的解决方案,我想分享,等待最好的解决方案。

编辑警告:这不适用于具有相同类别的两个参数转换器。

Url示例

/mesages/new?customers=2543,3321,445

注解:

/**
 * @Route("/new")
 * @Method("GET|POST")
 * @ParamConverter("customers",
 *     class="Doctrine\Common\Collections\ArrayCollection", options={
 *         "finder"    = "getFindAllWithMobileByUserQueryBuilder",
 *         "entity"    = "Acme\HelloBundle\Entity\Customer",
 *         "field"     = "id",
 *         "delimiter" = ",",
 *     }
 * )
 */
public function newAction(Request $request, ArrayCollection $customers = null)
{
}

选项delimiter用于分割GET参数,而id用于添加WHERE id IN...子句。这两个都是可选的。

选项class仅用作“签名”来告诉转换器应该support它。 entity必须是Doctrine实体的FQCN,而finder是要调用的存储库方法,应返回查询构建器(默认提供)。

转换器

class ArrayCollectionConverter implements ParamConverterInterface
{
    /**
     * @var \Symfony\Component\DependencyInjection\ContainerInterface
     */
    protected $container;

    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }

    function apply(Request $request, ConfigurationInterface $configuration)
    {
        $name    = $configuration->getName();
        $options = $this->getOptions($configuration);

        // Se request attribute to an empty collection (as default)
        $request->attributes->set($name, new ArrayCollection());

        // If request parameter is missing or empty then return
        if(is_null($val = $request->get($name)) || strlen(trim($val)) === 0)
            return;

        // If splitted values is an empty array then return
        if(!($items = preg_split('/\s*'.$options['delimiter'].'\s*/', $val,
            0, PREG_SPLIT_NO_EMPTY))) return;

        // Get the repository and logged user
        $repo = $this->getEntityManager()->getRepository($options['entity']);
        $user = $this->getSecurityContext->getToken()->getUser();

        if(!$finder = $options['finder']) :
            // Create a new default query builder with WHERE user_id clause
            $builder = $repo->createQueryBuilder('e');
            $builder->andWhere($builder->expr()->eq("e.user", $user->getId()));

            else :
                // Call finder method on repository
                $builder = $repo->$finder($user);
        endif;

        // Edit the builder and add WHERE IN $items clause
        $alias   = $builder->getRootAlias() . "." . $options['field'];
        $wherein = $builder->expr()->in($alias, $items);
        $result  = $builder->andwhere($wherein)->getQuery()->getResult();

        // Set request attribute and we're done
        $request->attributes->set($name, new ArrayCollection($result));
    }

    public function supports(ConfigurationInterface $configuration)
    {
        $class = $configuration->getClass();

        // Check if class is ArrayCollection from Doctrine
        if('Doctrine\Common\Collections\ArrayCollection' !== $class)
            return false;

        $options = $this->getOptions($configuration);
        $manager = $this->getEntityManager();

        // Check if $options['entity'] is actually a Dcontrine one
        try
        {
            $manager->getClassMetadata($options['entity']);
            return true;
        }
        catch(\Doctrine\ORM\Mapping\MappingException $e)
        {
            return false;
        }
    }

    protected function getOptions(ConfigurationInterface $configuration)
    {
        return array_replace(
            array(
                'entity'         => null,
                'finder'         => null,
                'field'          => 'id',
                'delimiter'      => ','

            ),
            $configuration->getOptions()
        );
    }

    /**
     * @return \Doctrine\ORM\EntityManager
     */
    protected function getEntityManager()
    {
        return $this->container->get('doctrine.orm.default_entity_manager');
    }

    /**
     * @return \Symfony\Component\Security\Core\SecurityContext
     */
    protected function getSecurityContext()
    {
        return $this->container->get('security.context');
    }
}

服务定义

arraycollection_converter:
  class: Acme\HelloBundle\Request\ArrayCollectionConverter
  arguments: ['@service_container']
  tags:
    - { name: request.param_converter}

答案 1 :(得分:3)

已经很晚了,但根据latest关于@ParamConverter的文档,您可以按照以下方式实现:

 * @ParamConverter("users", class="AcmeBlogBundle:User", options={
 *    "repository_method" = "findUsersByIds"
 * })

您只需要确保存储库方法可以处理逗号(,)分隔值