2012-06-05 14 views
6

Accediendo a mi ruta /message/new voy a mostrar un formulario para enviar un mensaje nuevo a uno o más clientes. modelo de formulario tiene (entre otros) una colección de entidades Customer:Symfony2 y ParamConverter (s)

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

me gustaría implementar una selección clientes automáticos usando customers parámetros GET, así:

message/new?customers=2,55,543 

Esto está trabajando ahora simplemente dividiendo en , y hacer una consulta para obtener clientes:

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 
} 

¿Cómo puedo hacer lo mismo usando Symfony 2 converters? Me gusta:

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

Respuesta

10

Responda a mi auto: no hay tal cosa para hacerte la vida más fácil. He codificado una solución rápida y sucia (y posiblemente con errores) que me gustaría compartir, esperando la mejor.

EDITAR ADVERTENCIA: esto no funcionará con dos convertidores de parámetros con la misma clase.

ejemplo Url

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

Anotaciones:

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

Opción delimiter se utiliza para dividir GET parámetro mientras id se utiliza para añadir una cláusula WHERE id IN.... Hay ambos opcionales.

La opción class solo se utiliza como "firma" para indicar que el convertidor debe support. entity tiene que ser un FQCN de una entidad Doctrine, mientras que finder es un método de repositorio que debe invocarse y debe devolver un generador de consultas (se proporciona una predeterminada).

convertidor

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

Definición de servicio

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

Probablemente deberías usar 'preg_quote()' alrededor de la inserción de '$ opciones ['delimitador']' –

+0

@PeterBailey ¡gracias por tu sugerencia! – gremo

3

Es tarde, pero de acuerdo con latest documentación acerca @ParamConverter, se puede lograr que siga así:

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

sólo tiene asegúrese de que el método de repositorio pueda manejar valores separados por comas (,) s

Cuestiones relacionadas