2012-07-27 4 views
5

Tôi đã có một mô hình đơn giản (đơn giản hóa các nguồn):Mẫu: Tránh thiết null để phi lĩnh vực nộp

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

Và một loại hình thức:

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

điều khiển của tôi:

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

Khi tôi POST /users/?form[page]=2&form[limit]=20, phản hồi là những gì tôi mong đợi:

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

Bây giờ, khi tôi POST /users/?form[page]=3, phản ứng là:

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

limit trở nên vô giá trị, bởi vì nó không được nộp.

tôi muốn để có được

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

Câu hỏi: Làm thế nào tôi có thể thay đổi hành vi hình thức, vì vậy mà nó bỏ qua các giá trị không nộp?

Trả lời

10

Nếu chỉ là một vấn đề của tham số (tham số GET), bạn có thể xác định giá trị mặc định vào tập tin định tuyến

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

Một cách khác có thể được sử dụng một móc (tức PRE_BIND) và cập nhật bằng tay mà giá trị vào sự kiện này. Theo cách đó, bạn không có "logic" trải rộng thành nhiều phần mã.

cuối cùng mã - được đề xuất bởi Adrien - sẽ

<?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

Biểu mẫu của tôi sẽ được sử dụng trong nhiều bộ điều khiển, vì vậy điều này sẽ dẫn đến sự lặp lại. – AdrienBrault

+0

@AdrienBrault Có nhưng bạn phải xác định một lộ trình cho mỗi một bằng nhau ... Một giải pháp tốt hơn có thể là chỉ sử dụng bộ điều khiển, bên trong nó gọi là bộ điều phối sẽ dẫn bạn đến bộ điều khiển phù hợp .... – DonCallisto

+0

Tôi nghĩ rằng người nghe sẽ tốt hơn người điều phối. Dù sao, ở đây, tôi yêu cầu một giải pháp ở cấp biểu mẫu. – AdrienBrault

2

Dưới đây là một biến thể của câu trả lời ban đầu. Lợi ích quan trọng nhất của giải pháp này là các trình duyệt tính hợp lệ bây giờ có thể hoạt động như thể bài đăng biểu mẫu sẽ luôn hoàn thành, điều đó có nghĩa là không có vấn đề gì với lỗi tạo bọt và như vậy.

Lưu ý rằng tên trường đối tượng phải giống hệt nhau để tạo thành tên trường cho mã này hoạt động.

<?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

Đúng, đó là giải pháp mà tôi đã sử dụng. Xem https://github.com/adrienbrault/symfony-hateoas-sandbox/blob/master/src/AdrienBrault/ApiBundle/Form/EventListener/ReplaceNotSubmittedValuesByDefaultsListener.php. Đảm bảo không tương tác với dữ liệu trực tiếp như bạn đang làm trong câu trả lời của mình – AdrienBrault