Comment ajouter un champ autocomplete sous une forme Symfony2 pour une collection et utiliser Propel?

j'utilise des formes Symfony 2.1 avec PropelBundle et j'essaye de reformater une forme qui avait une liste déroulante d'objets (à choisir) pour utiliser plutôt un champ jQuery autocomplete (travaillant avec AJAX). Pour la liste déroulante, j'ai été en utilisant le code suivant (qui a fonctionné parfaitement pour le déroulante) dans mon type de formulaire:

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

pour donner un peu de contexte, disons que nous sommes en train de créer un nouvel objet" lecteur " et que nous aimerions sélectionner les livres préférés du lecteur à partir d'une liste de "Réserver" des objets. Un type de collection est utilisé de sorte que de nombreux "livres préférés" peuvent être sélectionnés dans la nouvelle forme "lecteur". Maintenant, je voudrais changer le ci-dessus pour utiliser autocomplete. Pour ce faire, j'ai essayé de mettre en œuvre un Transformateur de données pour être en mesure d'obtenir un objet de livre à partir d'un simple champ de texte qui pourrait être utilisé pour la fonction Autocomplete pour passer L'ID du livre comme indiqué dans la réponse à cette Question. Cependant, je n'étais pas en mesure de comprendre comment pour faire fonctionner le transformateur de données avec un type de collecte et des classes de propulsion. J'ai créé une classe BookToIdTransformer comme indiqué dans le Livre de recettes Symfony et j'ai essayé ce qui suit dans le fichier" 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)
);

avec ce qui précède, j'obtiens une exception "appel à une méthode non définie: getId" (apparemment le transformateur s'attend à une collecte Propel de livres, pas un seul objet de Livre..). Personne ne sait comment s'y prendre? ou faites moi savoir s'il y a d'autres façons d'implémenter l'autocomplete en Symfony en utilisant Propel et en permettant de sélectionner plusieurs objets (par exemple une collection de livres)?

9
demandé sur Community 2012-08-15 04:13:08

1 réponses

la solution que j'ai finalement choisie est légèrement différente de ma réponse précédente. J'ai fini par utiliser un type de champ" text "au lieu d'un type de champ" collection "dans mon formulaire" ReaderType", puisque j'ai fini par utiliser le Loopj Tokeninput jQuery plugin qui permet de sélectionner plusieurs objets (par exemple "Livre préféré") à associer à mon objet principal (par exemple "objet lecteur") dans la forme. Considérant cela, j'ai créé un "transformateur de données" pour transformer les identifiants des objets passés (dans un virgule séparée de la liste dans le champ de texte) dans une Collection D'objets Propel. Le code est partagé comme suit, y compris un exemple de contrôleur d'objet ajax.

La partie clé de la "ReaderType" formulaire se présente comme suit:

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

le fichier" Data Transformer " ressemble à ceci:

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

le contrôleur ajax qui renvoie une collection json de "books" à la fonction autocomplete Tokeninput est comme suit:

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

Et enfin, le routeur dans la "routage.yml" fichier:

ajax_book:
    pattern:  /ajax_book
    defaults: { _controller: MySiteBundle:ClassAjax:book }
14
répondu RayOnAir 2012-08-23 14:43:22