6

访问我的路线/message/new,我将展示一个用于向一个或多个客户发送新消息的表单。表单模型具有(除其他外)一组Customer实体:

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

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

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 转换器做同样的事情?像:

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

2 回答 2

13

对我自己的回答:没有这样的事情可以让你的生活变得轻松。我已经编写了一个快速而肮脏(并且可能有问题)的解决方案,我想分享,等待一个最好的解决方案。

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

网址示例

/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}
于 2012-06-06T01:12:55.903 回答
4

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

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

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

于 2015-07-24T16:50:57.443 回答