2012-06-05 6 views
6

Accesso al mio percorso /message/new Ho intenzione di mostrare un modulo per l'invio di un nuovo messaggio a uno o più clienti. modello di forma ha (tra gli altri) un insieme di entità Customer:Symfony2 e ParamConverter (s)

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

mi piacerebbe implementare selezione clienti automatici utilizzando customers parametri GET, in questo modo:

message/new?customers=2,55,543 

questo è ora di lavoro dividendo semplicemente su , e facendo una query per ottenere clienti:

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 
} 

Come posso fare lo stesso usando Symfony 2 converters? Mi piace:

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

risposta

10

Risposta a me stesso: non esiste una cosa che ti renda la vita facile. Ho codificato una soluzione rapida e sporca (e probabilmente buggy) che mi piacerebbe condividere, in attesa di una migliore.

AVVISO DI EDIT: non funziona con due convertitori di parametri della stessa classe.

esempio URL

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

NOTE:

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

Opzione delimiter è usato per separare GET parametro mentre id viene utilizzato per l'aggiunta di una clausola WHERE id IN.... Ci sono entrambi facoltativi.

L'opzione class viene utilizzata solo come "firma" per indicare che il convertitore deve essere support. entity deve essere un FQCN di un'entità Doctrine mentre finder è un metodo di repository da richiamare e deve restituire un generatore di query (quello predefinito fornito).

Converter

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

definizione Servizio

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

Probabilmente dovresti usare 'preg_quote()' per l'inserimento di '$ opzioni ['delimitatore']' –

+0

@PeterBailey grazie per il tuo suggerimento! – gremo

3

È tardi, ma secondo latest documentazione sui @ParamConverter, è possibile raggiungerlo seguire la via:

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

basta assicurati che il metodo di repository possa gestire il valore separato da virgole (,) s