2012-08-15 16 views
9

Sto usando i moduli di Symfony 2.1 con PropelBundle e sto cercando di ridefinire un modulo con un elenco a discesa di oggetti (da cui selezionare) per utilizzare invece un campo di completamento automatico jquery (che funziona con AJAX). Per l'elenco a discesa che stavo usando il seguente codice (che ha funzionato perfettamente per la discesa) nel mio tipo di modulo:Come aggiungere un campo di completamento automatico in un modulo Symfony2 per una raccolta e l'utilizzo di Propel?

$builder->add('books', 'collection', array(
    'type'   => 'model', 
    'options'  => array(
     'class'  => 'MyVendor\MyBundle\Model\Book', 
     'property' => 'title', 
    ), 
    'allow_add'  => true, 
    'allow_delete' => true, 
    'by_reference' => false, 
    'required'  => false, 
)); 

Per il bene di dare un po 'di contesto, diciamo che stiamo creando un nuovo "Reader "oggetto e che vorremmo selezionare i libri preferiti di Reader da un elenco di oggetti" Libro "disponibili. Viene utilizzato un tipo di raccolta in modo che molti "libri preferiti" possano essere selezionati nel nuovo modulo "Reader". Ora, vorrei cambiare quanto sopra per usare il completamento automatico. Per fare ciò, ho provato a implementare un Data Transformer to be able to get a Book object from a simple text field che potrebbe essere utilizzato per la funzione di completamento automatico per passare l'ID del libro come indicato in the answer to this Question. Tuttavia, non ero in grado di capire come far funzionare Data Transformer con un tipo di raccolta e le classi Propel. Ho creato una classe BookToIdTransformer come indicato nel Symfony Cookbook e provato quanto segue nel file "ReaderType":

$transformer = new BookToIdTransformer(); 
$builder->add(
     $builder->create('books', 'collection', array(
      'type'   => 'text', 
      'allow_add'  => true, 
      'allow_delete' => true, 
      'by_reference' => false, 
      'required'  => false, 
     ))->addModelTransformer($transformer) 
); 

Con quanto sopra, ho un "Call to metodo non definito: getId" eccezione (a quanto pare il trasformatore si aspetta una PropelCollection of Books, non un singolo oggetto Book ..). Qualcuno sa come fare? o fammi sapere se ci sono altri modi per implementare il completamento automatico in Symfony utilizzando Propel e consentendo di selezionare più oggetti (ad esempio una raccolta di libri)?

risposta

14

La soluzione alla quale alla fine sono andato è leggermente diversa dalla mia precedente risposta. Ho finito per utilizzare un tipo di campo "testo" anziché un tipo di campo "raccolta" nel mio modulo "ReaderType", dal momento che ho finito con lo Loopj Tokeninput jQuery plugin che consente di selezionare più oggetti (ad esempio "Libro preferito") da associare al mio principale oggetto (ad es. oggetto "Reader") nel modulo. Considerato ciò, ho creato un "Data Transformer" per trasformare gli ID degli oggetti passati (in un elenco separato da virgole nel campo di testo) in una collezione di oggetti Propel. Il codice è condiviso come segue, incluso un controller di oggetti ajax di esempio.

La parte fondamentale del "ReaderType" forma appare come segue:

$transformer = new BooksToIdsTransformer(); 
$builder->add(
    $builder->create('books', 'text', array(
     'required' => false, 
    ))->addModelTransformer($transformer) 
); 

Il file "Data Transformer" assomiglia a questo:

// src/MyVendor/MyBundle/Form/DataTransformer/BooksToIdsTransformer.php 
namespace MyVendor\MyBundle\Form\DataTransformer; 

use \PropelObjectCollection; 
use Symfony\Component\Form\DataTransformerInterface; 
use Symfony\Component\Form\Exception\UnexpectedTypeException; 
use MyVendor\MyBundle\Model\BookQuery; 

class BooksToIdsTransformer implements DataTransformerInterface 
{ 
    public function transform($books) 
    { 
     if (null === $books) { 
      return ""; 
     } 

     if (!$books instanceof PropelObjectCollection) { 
      throw new UnexpectedTypeException($books, '\PropelObjectCollection'); 
     } 
     $idsArray = array(); 
     foreach ($books as $book) { 
      $idsArray[] = $book->getId(); 
     } 
     $ids = implode(",", $idsArray); 
     return $ids; 
    } 

    public function reverseTransform($ids) 
    { 
     $books = new PropelObjectCollection(); 

     if ('' === $ids || null === $ids) { 
      return $books; 
     } 

     if (!is_string($ids)) { 
      throw new UnexpectedTypeException($ids, 'string'); 
     } 
     $idsArray = explode(",", $ids); 
     $idsArray = array_filter ($idsArray, 'is_numeric'); 
     foreach ($idsArray as $id) { 
      $books->append(BookQuery::create()->findOneById($id)); 
     } 
     return $books; 
    } 
} 

Il controller ajax che restituisce un insieme JSON di "libri" alla funzione di completamento automatico Tokeninput è la seguente:

namespace MyVendor\MyBundle\Controller; 

use Symfony\Bundle\FrameworkBundle\Controller\Controller; 
use Symfony\Component\HttpFoundation\Request; 
use Symfony\Component\HttpFoundation\Response; 
use MyVendor\MyBundle\Model\BookQuery; 


class ClassAjaxController extends Controller 
{ 
    public function bookAction(Request $request) 
    { 
     $value = $request->get('q'); 

     $books = BookQuery::create()->findByName('%'.$value.'%'); 

     $json = array(); 
     foreach ($books as $book) { 
      $json[] = array(
       'id' => $book->getId(), 
       'name' => $book->getName() 
      ); 
     } 

     $response = new Response(); 
     $response->setContent(json_encode($json)); 

     return $response; 
    } 
} 

E, infine, th e router nel file "routing.yml":

ajax_book: 
    pattern: /ajax_book 
    defaults: { _controller: MySiteBundle:ClassAjax:book } 
+0

Grazie per la spiegazione approfondita. Se hai molte azioni del controller che dovrebbero restituire le risposte JSON, ti consiglio FOSRestBundle – Narretz

+0

puoi aggiungere uno snippet della tua vista/modulo, per favore? – timaschew

+0

@timaschew se ricordo bene (ho finito per non averne bisogno), il codice nella vista è solo quello standard usato per i moduli di symfony. Il campo è un normale inserimento di testo. La funzionalità di completamento automatico viene aggiunta a tale input dal plugin Tokeninput utilizzando javascript utilizzando l'id dell'input: $ ("# my-text-input"). TokenInput ("/ url/to/your/script /"); – RayOnAir

Problemi correlati