2015-09-23 63 views
1

我知道類似的問題已經在這裏問過,但答案不滿足我。Symfony2 - 驗證多個字段,DTO,沒有註釋

有以下幾點:

  1. 標準表格字段foobarbazbatx
  2. DTO沒有實體,虛擬對象,沒有註釋
  3. 約束一些字段使用FormBuilder
  4. 表單在多個地方使用。

在一些地方形成時,我想單驗證添加到域foobarbaz。這個驗證器只能得到那些字段或整個傳播的DTO的值。它應該有權訪問DI容器來調用將根據數據庫檢查數據的服務。

現在我正在考慮兩種解決方案之一:

  1. 在需要它的控制器增加額外的約束形式(音髒)
  2. 增加額外領域形成的構造函數/ DTO(不布爾值,但告訴業務邏輯是否需要額外的驗證),並添加額外的約束形式。

問題是我無法弄清楚如何處理這些情況。

最後,我想強調一點,我不想使用驗證組和註釋 - 兩者都會向DTO添加額外的依賴關係和邏輯。

回答

1

找到了答案。簡而言之,您可以查看This Matt Daum post

下面是完整的示例,不僅介紹如何創建自定義表單驗證程序,還介紹如何注入服務和額外數據以形成表單(因爲這是我的情況)。

如果你想簡單receipe,直接去底部

讓我們DTO:

class MyFormDTO 
{ 
    /** @var string */ 
    private $name; 

    /** @var string */ 
    private $surname; 

    /** @var string */ 
    private $phone; 

    /** getters and setters ommited */ 

} 

現在,在窗體中定義的依賴。前兩個是服務,最後一個(Calendar)是控制器所需的一些額外數據。

class MyForm extends AbstractType 
{ 
    (fields hidden) 

    /** 
    * @param Sender     $sender 
    * @param TranslatorInterface $translator 
    * @param Calendar    $calendar 
    */ 
    public function __construct(Sender $sender, TranslatorInterface $translator, Calendar $calendar) 
    { 
     $this->translator = $translator; 
     $this->sender  = $sender; 
     $this->calendar = $calendar; 
    } 
} 

現在有兩種方法 - 如果您只需要表單中的服務,則可以將表單定義爲服務。如果你和我一樣,都需要額外的數據,你需要編寫形式工廠服務:

class MyFormFactory 
{ 

    (fields hidden) 

    /** 
    * @param Sender     $sender 
    * @param TranslatorInterface $translator 
    */ 
    public function __construct(Sender $sender, TranslatorInterface $translator) 
    { 
     $this->sender     = $sender; 
     $this->translator    = $translator; 
    } 

    /** 
    * @param Calendar $calendar 
    * 
    * @return MyForm 
    */ 
    public function getMyForm(Calendar $calendar) 
    { 
     return new MyForm($this->sender, $this->translator, $calendar); 
    } 

} 

讓我們定義這家工廠與正確的依賴服務:

mybundle.form.myform_factory: 
    class: MyBundle\Service\FormFactory\MyFormFactory 
    arguments: [ @text_message.sender, @translator ] 

如何獲得在控制器形式?易爲:

class MyController extends Controller 
{ 
    /** 
    * @ParamConverter("calendar", options={"mapping"={"calendarId":"id"}}) 
    * 
    * @param Request $request 
    * @param Calendar $calendar 
    * 
    * @return Response 
    * @throws Exception 
    */ 
    public function myAction(Request $request, Calendar $calendar) 
    { 
     $formDTO = new MyFormDto(); 

     $myForm = $this->get('mybundle.form.myform_factory')->getMyForm($calendar); 
     $form = $this->createForm($myForm, $formDTO); 

     (handling post hidden) 
    } 
} 

而現在最重要的部分 - 我們必須適當注入到我們的形式服務。如何使用它們並驗證選定的數據?像這樣:

class MyForm extends AbstractType 
{ 
    (fields hidden, constructor shown in previous example) 

    /** 
    * @param FormBuilderInterface $builder 
    * @param array    $options 
    */ 
    public function buildForm(FormBuilderInterface $builder, array $options) 
    { 
     /** @var MyFormDTO $myDTO */ 
     $myDTO = $options['data']; 

     (build form as usual, using services and data from $options and $this->calendar injected by controller and factory) 
    } 

    /** 
    * @param OptionsResolverInterface $resolver 
    */ 
    public function setDefaultOptions(OptionsResolverInterface $resolver) 
    { 
     parent::setDefaultOptions($resolver); 

     $resolver->setDefaults([ 
      'csrf_protection' => true, 
      'constraints'  => [ 
       new Callback(function (MyFormDTO $data, ExecutionContextInterface $context) //notice that we have access to fully propageted DTO here 
       { 
        //use injected service 
        $isValid = $this->sender->validateSomething($data->getSurname(), $data->getPhone()); 

        if (false === $isValid) 
        { 
         $context 
          ->buildViolation($this->translator->trans('wrong_surname_phone_pair')) 
          ->addViolation(); 
        } 

        return $isValid; 
       }) 
      ], 
     ]); 
    } 
} 
+0

而只是一點提示。如果你的'Callback'處理重邏輯,你可以將它移出來分開'Constraint'。 [以下是如何操作](http://symfony.com/doc/current/cookbook/validation/custom_constraint.html) – ex3v