2012-07-27 6 views
5

Ich habe ein einfaches Modell bekommt (die Quelle vereinfacht):Form: Vermeiden Sie nicht eingereicht Feld null Einstellung

class Collection 
{ 
    public $page; 
    public $limit; 
} 

Und einen Formulartyp:

class CollectionType extends AbstractType 
{ 
    public function buildForm(FormBuilderInterface $builder, array $options) 
    { 
     $builder->add('page', 'integer'); 
     $builder->add('limit', 'integer'); 
    } 

    public function setDefaultOptions(OptionsResolverInterface $resolver) 
    { 
     $resolver->setDefaults(array(
      'data_class' => 'FSC\Common\Rest\Form\Model\Collection', 
     )); 
    } 
} 

Mein Controller:

public function getUsersAction(Request $request) 
{ 
    $collection = new Collection(); 
    $collection->page = 1; 
    $collection->limit = 10; 

    $form = $this->createForm(new CollectionType(), $collection) 
    $form->bind($request); 

    print_r($collection);exit; 
} 

Wenn ich POST /users/?form[page]=2&form[limit]=20 bin, ist die Antwort, was ich erwarte:

Jetzt
Collection Object 
(
    [page:public] => 2 
    [limit:public] => 20 
) 

, wenn ich POST /users/?form[page]=3, die Antwort ist:

Collection Object 
(
    [page:public] => 3 
    [limit:public] => 
) 

limit wird null, weil es nicht vorgelegt wurde.

Ich wollte

Collection Object 
(
    [page:public] => 3 
    [limit:public] => 10 // The default value, set before the bind 
) 

Frage bekommen: Wie kann ich das Formular Verhalten zu ändern, so dass es nicht vorgelegten Werte ignoriert?

Antwort

10

Wenn nur ein Problem der Parameter (GET-Parameter) können Sie den Standardwert in Routing-Datei definieren können

route_name: 
pattern: /users/?form[page]={page}&form[limit]={limit} 
defaults: { _controller: CompanyNameBundleName:ControllerName:ActionName, 
         limit:10 } 

Eine alternative Möglichkeit, einen Haken (dh PRE_BIND) und manuell aktualisieren verwenden könnte sein, dass Wert in dieses Ereignis. Auf diese Weise haben Sie nicht die "Logik" in mehrere Codeabschnitte aufgeteilt.

Schluss Code - vorgeschlagen von Adrien - wird

<?php 

use Symfony\Component\Form\FormEvent; 
use Symfony\Component\Form\FormFactoryInterface; 
use Symfony\Component\EventDispatcher\EventSubscriberInterface; 
use Symfony\Component\Form\FormEvents; 

class IgnoreNonSubmittedFieldSubscriber implements EventSubscriberInterface 
{ 
    private $factory; 

    public function __construct(FormFactoryInterface $factory) 
    { 
     $this->factory = $factory; 
    } 

    public static function getSubscribedEvents() 
    { 
     return array(FormEvents::PRE_BIND => 'preBind'); 
    } 

    public function preBind(FormEvent $event) 
    { 
     $submittedData = $event->getData(); 
     $form = $event->getForm(); 

     // We remove every child that has no data to bind, to avoid "overriding" the form default data 
     foreach ($form->all() as $name => $child) { 
      if (!isset($submittedData[$name])) { 
       $form->remove($name); 
      } 
     } 
    } 
} 
+0

Mein Formular wird in vielen Controllern verwendet, so dass dies zu Wiederholungen führen wird. – AdrienBrault

+0

@AdrienBrault Ja, aber Sie müssen eine Route für jeden gleich definieren ... Eine bessere Lösung könnte sein, nur einen Controller zu verwenden, der darin einen Dispatcher aufruft, der Sie zum richtigen Controller führt .... – DonCallisto

+0

Ich denke dass ein Zuhörer besser wäre als ein Dispatcher. Wie auch immer, hier, ich frage nach einer Lösung auf der Formularebene. – AdrienBrault

2

Hier ist eine Änderung der ursprünglichen Antwort ist. Der wichtigste Vorteil dieser Lösung besteht darin, dass sich Validierer jetzt so verhalten können, als ob der Form-Post immer vollständig wäre, was bedeutet, dass es keine Probleme mit Fehler-Sprudeln und dergleichen gibt.

Beachten Sie, dass die Objektfeldnamen mit den Feldnamen des Formulars identisch sein müssen, damit dieser Code funktioniert.

<?php 
namespace Acme\DemoBundle\Form; 

use Symfony\Component\Form\FormEvent; 
use Symfony\Component\Form\FormFactoryInterface; 
use Symfony\Component\EventDispatcher\EventSubscriberInterface; 
use Symfony\Component\Form\FormEvents; 

class FillNonSubmittedFieldsWithDefaultsSubscriber implements EventSubscriberInterface 
{ 
    private $factory; 

    public function __construct(FormFactoryInterface $factory) 
    { 
     $this->factory = $factory; 
    } 

    public static function getSubscribedEvents() 
    { 
     return array(FormEvents::PRE_BIND => 'preBind'); 
    } 

    public function preBind(FormEvent $event) 
    { 
     $submittedData = $event->getData(); 
     $form = $event->getForm(); 

     // We complete partial submitted data by inserting default values from object 
     foreach ($form->all() as $name => $child) { 
      if (!isset($submittedData[$name])) { 
       $obj = $form->getData(); 

       $getter = "get".ucfirst($name); 
       $submittedData[$name] = $obj->$getter(); 
      } 
     } 
     $event->setData($submittedData); 

    } 
} 
+1

Ja, das ist die Lösung, die ich am Ende benutzt habe. Siehe https://github.com/adrienbrault/symfony-hateoas-sandbox/blob/master/src/AdrienBrault/ApiBundle/Form/EventListener/ReplaceNotSubmittedValuesByDefaultsListener.php. Stellen Sie sicher, dass Sie nicht direkt mit den Daten interagieren, wie Sie es in Ihrer Antwort tun – AdrienBrault