2012-08-15 10 views
9

Ich benutze Symfony 2.1 Formen mit PropelBundle und ich versuche, ein Formular, das eine Dropdown-Liste von Objekten (zur Auswahl) hatte, um ein jquery Autocomplete-Feld (arbeiten mit AJAX) verwenden. Für die Drop-Down-Liste, die ich den folgenden Code wurde mit (die sich perfekt für die Dropdown gearbeitet) in meiner Form Typ:Wie füge ich ein Autocomplete-Feld in einem Symfony2-Formular für eine Sammlung hinzu und verwende 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, 
)); 

Aus Gründen ein wenig Kontext zu geben, lassen Sie uns sagen, dass wir eine neue „Reader erstellen "Objekt und dass wir die Lieblingsbücher des Lesers aus einer Liste verfügbarer" Buch "-Objekte auswählen möchten. Ein Sammlungstyp wird verwendet, so dass viele "Lieblingsbücher" in dem neuen "Leser" -Formular ausgewählt werden können. Nun möchte ich das obige ändern, um Autocomplete zu verwenden. Dafür habe ich versucht, eine Data Transformer to be able to get a Book object from a simple text field zu implementieren, die für die Autocomplete-Funktion verwendet werden könnte, um die Buch-ID wie in the answer to this Question angegeben zu übergeben. Ich konnte jedoch nicht herausfinden, wie der Data Transformer mit einem Sammlungstyp und Propel-Klassen arbeiten kann. Ich habe eine BookToIdTransformer Klasse wie in der Symfony-Kochbuch angegeben und versucht, die folgenden in der „ReaderType“ file:

$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) 
); 

Mit dem oben, erhalte ich eine „Call to undefined Methode: getId“ Ausnahme (anscheinend der Transformator erwartet eine PropelCollection von Büchern, kein einziges Buchobjekt ..). Weiß jemand, wie man das macht? Oder lassen Sie mich wissen, ob es andere Möglichkeiten gibt, die automatische Vervollständigung in Symfony mit Propel zu implementieren und mehrere Objekte auszuwählen (z. B. eine Sammlung von Büchern)?

Antwort

14

Die Lösung, für die ich mich letztendlich entschieden habe, unterscheidet sich etwas von meiner vorherigen Antwort. Am Ende habe ich in meinem "ReaderType" -Formular einen "text" -Feldtyp anstelle eines "collection" -Feldtyps verwendet, da ich am Ende die Loopj Tokeninput jQuery plugin verwendet habe, die es ermöglicht, mehrere Objekte (zB "Favorite Book") auszuwählen Objekt (zB "Reader" -Objekt) im Formular. In Anbetracht dessen habe ich einen "Data Transformer" erstellt, der die übergebenen Objekte (in einer durch Kommas getrennten Liste im Textfeld) in eine Propel-Objekt-Sammlung transformiert. Der Code wird wie folgt geteilt, einschließlich eines Beispiel-Ajax-Objekt-Controllers.

Der wichtigste Teil des "ReaderType" sieht Form wie folgt:

// 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; 
    } 
} 

Der Ajax-Controller, der eine json Sammlung kehrt aus:

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

Die "Data Transformer" Datei sieht wie folgt aus "Bücher" zu der Tokeninput-Autocomplete-Funktion ist wie folgt:

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

Und schließlich th e Router in der "routing.yml" Datei:

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

Danke für die gründliche Erklärung. Wenn Sie viele Controller-Aktionen haben, die JSON-Antworten zurückgeben sollen, empfehle ich FOSRestBundle – Narretz

+0

können Sie bitte ein Snippet Ihrer Ansicht/Ihres Formulars hinzufügen? – timaschew

+0

@timaschew Wenn ich mich gut erinnere (ich brauchte es nicht), ist der Code in der Ansicht nur der Standard, der für Symfony-Formulare verwendet wird. Das Feld ist eine normale Texteingabe. Die Autocomplete-Funktionalität wird zu dieser Eingabe vom Tokeninput-Plugin hinzugefügt, indem JavaScript mit der ID der Eingabe verwendet wird: $ ("# my-text-input"). TokenInput ("/ url/to/your/script /"); – RayOnAir