2016-02-17 21 views
6

trong dự án của tôi Tôi có một số biểu mẫu với các loại lựa chọn với nhiều tùy chọn.Symfony 2,8 tùy chọn Lựa chọn năng động

Vì vậy, tôi đã quyết định tạo loại lựa chọn tự động hoàn tất dựa trên tự động hoàn thành jquery, bổ sung thêm các phần tử HTML <option> mới vào <select> ban đầu khi chạy. Khi được chọn chúng được gửi chính xác, nhưng không thể được xử lý trong mặc định ChoicesToValuesTransformer, vì không tồn tại trong biểu mẫu của tôi khi tôi tạo nó.

Tôi làm cách nào để symfony chấp nhận các giá trị được thêm động?

Tôi tìm thấy câu trả lời này Validating dynamically loaded choices in Symfony 2, trong đó các giá trị đã gửi được sử dụng để sửa đổi biểu mẫu trên sự kiện biểu mẫu PRE_SUBMIT, nhưng không thể chạy nó cho tình huống của tôi. Tôi cần phải thay đổi sự lựa chọn nổi tiếng với loại hiện tại thay vì thêm một widget mới mẫu

Trả lời

19

để đối phó với các giá trị tự động thêm vào sử dụng 'choice_loader' tùy chọn kiểu lựa chọn. It's new in symfony 2.7 và đáng tiếc là không có bất kỳ tài liệu nào cả.

Về cơ bản nó là một dịch vụ thực hiện ChoiceLoaderInterface trong đó xác định ba chức năng:

  • loadValuesForChoices(array $choices, $value = null)
    • được kêu gọi xây dựng hình thức và nhận các giá trị cài đặt trước của đối tượng bị ràng buộc vào mẫu
  • loadChoiceList($value = null)
    • được kêu gọi xây dựng quan điểm và phải trả lại toàn bộ danh sách các lựa chọn nói chung
  • loadChoicesForValues(array $values, $value = null)
    • được gọi vào mẫu nộp và nhận dữ liệu gửi

Bây giờ ý tưởng là giữ ArrayChoiceList là thuộc tính riêng tư trong bộ chọn lựa. Trên hình thức xây dựng loadValuesForChoices(...) được gọi, ở đây chúng tôi thêm tất cả các lựa chọn đặt trước vào danh sách lựa chọn của chúng tôi để chúng có thể được hiển thị cho người dùng. Trên chế độ xem xây dựng, loadChoiceList(...) được gọi, nhưng chúng tôi không tải bất cứ thứ gì, chúng tôi chỉ trả lại danh sách lựa chọn riêng tư của chúng tôi được tạo trước đó.

Bây giờ người dùng tương tác với biểu mẫu, một số lựa chọn bổ sung được tải thông qua tính năng tự động hoàn tất và được đưa vào HTML thứ. Khi gửi biểu mẫu, các giá trị đã chọn được gửi và trong hành động điều khiển của chúng tôi trước tiên, biểu mẫu được tạo và sau đó, $form->handleRequest(..)loadChoicesForValues(...) được gọi, nhưng các giá trị đã gửi có thể hoàn toàn khác với các giá trị được bao gồm trong phần đầu.Vì vậy, chúng tôi thay thế danh sách lựa chọn nội bộ của chúng tôi bằng một danh sách mới chỉ chứa các giá trị đã gửi.

Biểu mẫu của chúng tôi giờ đây hoàn toàn giữ dữ liệu được thêm bằng tự động hoàn thành.

Phần khó khăn là, chúng ta cần một phiên bản mới của trình chọn lựa chọn của chúng tôi bất cứ khi nào chúng tôi sử dụng loại biểu mẫu, nếu không danh sách lựa chọn nội bộ sẽ chứa hỗn hợp của tất cả các lựa chọn.

Vì mục tiêu là viết loại lựa chọn tự động hoàn thành mới, bạn thường sử dụng tính năng tiêm phụ thuộc để chuyển bộ chọn lựa của bạn vào dịch vụ kiểu. Nhưng đối với các loại này, điều này là không thể nếu bạn luôn cần một phiên bản mới, thay vào đó chúng tôi phải bao gồm nó thông qua các tùy chọn. Việc đặt trình chọn lựa chọn trong các tùy chọn mặc định sẽ không hoạt động, vì chúng cũng được lưu trữ. Để giải quyết vấn đề mà bạn phải viết một hàm nặc danh mà cần phải lấy các tùy chọn như các thông số:

$resolver->setDefaults(array(
    'choice_loader' => function (Options $options) { 
     return AutocompleteFactory::createChoiceLoader(); 
    }, 
)); 

Edit: Đây là một phiên bản thu gọn của lớp lựa chọn bộ nạp:

use Symfony\Component\Form\ChoiceList\ArrayChoiceList; 
use Symfony\Component\Form\ChoiceList\ChoiceListInterface; 
use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface; 

class AutocompleteChoiceLoader implements ChoiceLoaderInterface 
{ 
    /** @var ChoiceListInterface */ 
    private $choiceList; 

    public function loadValuesForChoices(array $choices, $value = null) 
    { 
     // is called on form creat with $choices containing the preset of the bound entity 
     $values = array(); 
     foreach ($choices as $key => $choice) { 
      // we use a DataTransformer, thus only plain values arrive as choices which can be used directly as value 
      if (is_callable($value)) { 
       $values[$key] = (string)call_user_func($value, $choice, $key); 
      } 
      else { 
       $values[$key] = $choice; 
      } 
     } 

     // this has to be done by yourself: array(label => value) 
     $labeledValues = MyLabelService::getLabels($values); 

     // create internal choice list from loaded values 
     $this->choiceList = new ArrayChoiceList($labeledValues, $value); 

     return $values; 
    } 


    public function loadChoiceList($value = null) 
    { 
     // is called on form view create after loadValuesForChoices of form create 
     if ($this->choiceList instanceof ChoiceListInterface) { 
      return $this->choiceList; 
     } 

     // if no values preset yet return empty list 
     $this->choiceList = new ArrayChoiceList(array(), $value); 

     return $this->choiceList; 
    } 


    public function loadChoicesForValues(array $values, $value = null) 
    { 
     // is called on form submit after loadValuesForChoices of form create and loadChoiceList of form view create 
     $choices = array(); 
     foreach ($values as $key => $val) { 
      // we use a DataTransformer, thus only plain values arrive as choices which can be used directly as value 
      if (is_callable($value)) { 
       $choices[$key] = (string)call_user_func($value, $val, $key); 
      } 
      else { 
       $choices[$key] = $val; 
      } 
     } 

     // this has to be done by yourself: array(label => value) 
     $labeledValues = MyLabelService::getLabels($values); 

     // reset internal choice list 
     $this->choiceList = new ArrayChoiceList($labeledValues, $value); 

     return $choices; 
    } 
} 
+0

Ồ, nếu chỉ tôi biết những gì các phím mảng và giá trị nên được! –

+0

@IanPhillips tùy thuộc vào mảng bạn muốn nói. Đối với các giá trị trả về của các hàm, hãy xem phpDoc của 'ChoiceLoaderInterface'. Các khóa luôn nằm trong mảng tham số và giá trị hoặc là các lựa chọn hoặc giá trị. Lưu ý rằng bạn vẫn cần một [DataTransformer] (http://symfony.com/doc/current/cookbook/form/data_transformers.html) nếu bạn làm việc với các thực thể! Mảng được sử dụng để tạo 'ArrayChoiceList' nội bộ phải chứa các nhãn'

+0

@IanPhillips Tôi đã thêm phiên bản rút gọn của trình tải lựa chọn tự động hoàn thành – SBH

0

Một cơ bản (và có lẽ không phải là tốt nhất) tùy chọn sẽ được unmap lĩnh vực này trong hình thức của bạn như:

->add('field', choiceType::class, array(
     ... 
     'mapped' => false 
    )) 

trong bộ điều khiển, sau khi xác nhận, lấy dữ liệu và gửi chúng đến các thực thể như thế này:

$data = request->request->get('field'); 
// OR 
$data = $form->get('field')->getData(); 
// and finish with : 
$entity = setField($data); 
+1

Setting ''ánh xạ' => false' không giải quyết được vấn đề. Ngoài ra tôi đang tìm một giải pháp chung trong lớp loại tùy chỉnh của tôi mà không cần thêm mã vào bất kỳ bộ điều khiển nào – SBH

+0

Đúng, bạn nói đúng, tôi trộn lẫn với một thứ khác được kết hợp với trình xử lý sự kiện: [dynamic form modify] (http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html#adding-an-event-listener-to-a-form-class) trong đó ý tưởng là lấy tất cả trường được chọn trong biểu mẫu của bạn, và thêm nó vào chocie của bạn – Aridjar

Các vấn đề liên quan