2012-07-16 16 views
11

इम Symfony2 और इम abit अनिश्चित के साथ प्रयोग करना कैसे Symfony2 देखें घटक में बहुरूपी संग्रह संभालती है। ऐसा लगता है कि मैं सार तत्वों के संग्रह के साथ एक इकाई बना सकता हूं, लेकिन यह सुनिश्चित नहीं करता कि फ़ॉर्म प्रकार वर्ग के अंदर इसके साथ क्या किया जाए।Symfony2 फ़ॉर्म और बहुरूपी संग्रह

उदाहरण के लिए, मैं निम्नलिखित इकाई रिश्ता है।

/** 
* @ORM\Entity 
*/ 
class Order 
{ 
    /** 
    * @ORM\OneToMany(targetEntity="AbstractOrderItem", mappedBy="order", cascade={"all"}, orphanRemoval=true) 
    * 
    * @var AbstractOrderItem $items; 
    */ 
    $orderItems; 
    ... 
} 


/** 
* Base class for order items to be added to an Order 
* 
* @ORM\Entity 
* @ORM\InheritanceType("JOINED") 
* @ORM\DiscriminatorColumn(name="discr", type="string") 
* @ORM\DiscriminatorMap({ 
*  "ProductOrderItem" = "ProductOrderItem", 
*  "SubscriptionOrderItem " = "SubscriptionOrderItem " 
* }) 
*/ 
class AbstractOrderItem 
{ 
    $id; 
    ... 
} 

/** 
* @ORM\Entity 
*/ 
class ProductOrderItem extends AbstractOrderItem 
{ 
    $productName; 
} 

/** 
* @ORM\Entity 
*/ 
class SubscriptionOrderItem extends AbstractOrderItem 
{ 
    $duration; 
    $startDate; 
    ... 
} 

सरल पर्याप्त है, लेकिन जब im मेरे आदेश वर्ग के लिए एक फ़ॉर्म बनाते

class OrderType extends AbstractType 
{ 
    public function buildForm(FormBuilder $builder, array $options) 
    { 
     $builder->add('items', 'collection', array('type' => AbstractOrderItemType())); 
    } 
} 

मैं कैसे इस स्थिति आप को प्रभावी ढंग से में आइटम के प्रत्येक वर्ग के लिए एक अलग रूप प्रकार की जरूरत है, जहां संभाल करने को लेकर अनिश्चित हूं संग्रह?

उत्तर

9

मैंने हाल ही में एक समान समस्या का सामना किया - सिम्फनी स्वयं पॉलिमॉर्फिक संग्रह के लिए कोई रियायत नहीं देता है, लेकिन फ़ॉर्म का विस्तार करने के लिए इवेंट लिस्टनर का उपयोग करके उनके लिए समर्थन प्रदान करना आसान है।

namespace Acme\VariedCollectionBundle\EventListener; 

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

class VariedCollectionSubscriber implements EventSubscriberInterface 
{ 
    protected $factory; 
    protected $type; 
    protected $typeCb; 
    protected $options; 

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

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

    public function fixChildTypes(FormEvent $event) 
    { 
     $form = $event->getForm(); 
     $data = $event->getData(); 

     // Go with defaults if we have no data 
     if($data === null || '' === $data) 
     { 
      return; 
     } 

     // It's possible to use array access/addChild, but it's not a part of the interface 
     // Instead, we have to remove all children and re-add them to maintain the order 
     $toAdd = array(); 
     foreach($form as $name => $child) 
     { 
      // Store our own copy of the original form order, in case any are missing from the data 
      $toAdd[$name] = $child->getConfig()->getOptions(); 
      $form->remove($name); 
     } 
     // Now that the form is empty, build it up again 
     foreach($toAdd as $name => $origOptions) 
     { 
      // Decide whether to use the default form type or some extension 
      $datum = $data[$name] ?: null; 
      $type = $this->type; 
      if($datum) 
      { 
       $calculatedType = call_user_func($this->typeCb, $datum); 
       if($calculatedType) 
       { 
        $type = $calculatedType; 
       } 
      } 
      // And recreate the form field 
      $form->add($this->factory->createNamed($name, $type, null, $origOptions)); 
     } 
    } 
} 
:

नीचे मेरी EventListener, जो Symfony \ घटक \ फार्म \ एक्सटेंशन \ कोर \ EventListener \ ResizeFormListener, घटना श्रोता जो संग्रह प्रपत्र प्रकार की सामान्य सुविधा प्रदान करता है के लिए एक समान दृष्टिकोण का उपयोग करता है की सामग्री

इस दृष्टिकोण का उपयोग करने के लिए नकारात्मक पक्ष यह है कि इस पर अपनी बहुरूपी संस्थाओं के प्रकार की पहचान करने के लिए भेजने के बाद, आप चाहिए यह बाध्यकारी पहले संबंधित संस्थाओं के साथ अपने फार्म पर डेटा सेट किया गया है, नहीं तो श्रोता किस प्रकार पता लगाने का कोई तरीका नहीं डेटा वास्तव में है। आप FormTypeGuesser सिस्टम के साथ इस काम के आसपास संभावित रूप से काम कर सकते हैं, लेकिन यह मेरे समाधान के दायरे से बाहर था।

इसी प्रकार, जबकि इस प्रणाली का उपयोग करने वाला संग्रह अभी भी पंक्तियों को जोड़ने/हटाने का समर्थन करता है, तो यह मान लेगा कि सभी नई पंक्तियां मूल प्रकार के हैं - यदि आप उन्हें विस्तारित इकाइयों के रूप में सेट करने का प्रयास करते हैं, तो यह आपको एक त्रुटि देगा अतिरिक्त फ़ील्ड वाले फॉर्म के बारे में।

सादगी के लिए, मैं इस कार्यक्षमता को संपुटित करने के लिए एक सुविधा प्रकार का उपयोग करें - कि के लिए नीचे देखें और एक उदाहरण:

namespace Acme\VariedCollectionBundle\Form\Type; 

use Acme\VariedCollectionBundle\EventListener\VariedCollectionSubscriber; 
use JMS\DiExtraBundle\Annotation\FormType; 
use Symfony\Component\OptionsResolver\OptionsResolverInterface; 
use Symfony\Component\Form\FormBuilderInterface; 
use Symfony\Component\Form\AbstractType; 

/** 
* @FormType() 
*/ 
class VariedCollectionType extends AbstractType 
{ 
    public function buildForm(FormBuilderInterface $builder, array $options) 
    { 
     // Tack on our event subscriber 
     $builder->addEventSubscriber(new VariedCollectionSubscriber($builder->getFormFactory(), $options['type'], $options['type_cb'])); 
    } 

    public function getParent() 
    { 
     return "collection"; 
    } 

    public function setDefaultOptions(OptionsResolverInterface $resolver) 
    { 
     $resolver->setRequired(array('type_cb')); 
    } 

    public function getName() 
    { 
     return "varied_collection"; 
    } 
} 

उदाहरण: नाम स्थान एक्मे \ VariedCollectionBundle \ फार्म;

use Acme\VariedCollectionBundle\Entity\TestModelWithDate; 
use Acme\VariedCollectionBundle\Entity\TestModelWithInt; 
use JMS\DiExtraBundle\Annotation\FormType; 
use Symfony\Component\Form\FormBuilderInterface; 
use Symfony\Component\Form\AbstractType; 

/** 
* @FormType() 
*/ 
class TestForm extends AbstractType 
{ 
    public function buildForm(FormBuilderInterface $builder, array $options) 
    { 
     $typeCb = function($datum) { 
      if($datum instanceof TestModelWithInt) 
      { 
       return "test_with_int_type"; 
      } 
      elseif($datum instanceof TestModelWithDate) 
      { 
       return "test_with_date_type"; 
      } 
      else 
      { 
       return null; // Returning null tells the varied collection to use the default type - can be omitted, but included here for clarity 
      } 
     }; 

     $builder->add('demoCollection', 'varied_collection', array('type_cb' => $typeCb, /* Used for determining the per-item type */ 
                    'type' => 'test_type', /* Used as a fallback and for prototypes */ 
                    'allow_add' => true, 
                    'allow_remove' => true)); 
    } 

    public function getName() 
    { 
     return "test_form"; 
    } 
} 
+0

क्या आपको इस बारे में कोई जानकारी है कि सिम्फनी 2 के साथ काम करने के लिए इसे कैसे प्राप्त किया जाए? विशेष रूप से, "$ child-> getConfig() -> getOptions();" 2.0 में उपलब्ध नहीं है और इस तरह मैं फॉर्म के लिए मूल विकल्प नहीं प्राप्त कर सकता हूं। यदि मैं विकल्पों को छोड़ देता हूं तो मुझे अंततः "न तो संपत्ति" 0 "न ही विधि" get0() "न ही विधि" is0() "कक्षा में मौजूद है" सिद्धांत \ ORM \ PersistentCollection " – CriticalImpact

+0

@CriticalImpact मैंने देखा है 2.0 फॉर्म घटक के लिए स्रोत, और मैं वास्तव में एक ही प्रभाव को प्राप्त करने के लिए कोई रास्ता नहीं देख सकता (विकल्प उसमें लंबी अवधि के लिए संग्रहीत नहीं हैं)। यदि आप साथ रह सकते हैं तो आप ऐसा करने में सक्षम हो सकते हैं हमेशा डिफ़ॉल्ट विकल्पों का उपयोग करके - जो त्रुटि आप ऊपर प्राप्त कर रहे हैं उसे हल करने के लिए, आपको केवल property_path को उचित रूप से सेट करना होगा (दुर्भाग्यवश मुझे संग्रह में संपत्ति पथों के लिए कौन सा स्वरूप 2.0 उपयोग करता है यह जानने के लिए मुझे इसे छोड़ना होगा - ए कुछ अच्छी तरह से रखा var_dump की चाल चलनी चाहिए, हालांकि) –

+2

मैं वास्तव में एक और समाधान के साथ आने में कामयाब रहा। मैंने FormEvents :: PRE_SET_DATA के लिए एक ईवेंट श्रोता जोड़ा, बैकिंग ऑब्जेक्ट (मेरे मामले में एक प्रश्न ऑब्जेक्ट) मिला, जिस प्रकार का निर्धारण किया सवाल (मेरे पास मेरी क्यू में कुछ सेट है स्टियन जो कहता है कि यह एक चेकबॉक्स है, हां/नहीं, टेक्स्ट फ़ील्ड इत्यादि) और फिर प्रश्न ऑब्जेक्ट में सेट किए गए प्रकार के आधार पर फॉर्म में फ़ील्ड जोड़ा गया है। – CriticalImpact

0

उदाहरण आप दे दिया है में, आप उन ProductOrder और SubscriptionOrder

class ProductOrderType extends AbstractType 
{ 
    public function buildForm(FormBuilder $builder, array $options) 
    { 
     //Form elements related to Product Order here 
    } 
} 

और

class SubsciptionOrderType extends AbstractType 
{ 
    public function buildForm(FormBuilder $builder, array $options) 
    { 
     //Form elements related SubscriptionOrder here 
    } 
} 

आप इन दोनों रूपों को जोड़ने के अपने OrderType प्रपत्र वर्ग में के लिए अलग रूप वर्ग बनाना होगा, इस

class OrderType extends AbstractType 
{ 
    public function buildForm(FormBuilder $builder, array $options) 
    { 
     $builder->add('product',new ProductOrderType()) 
     $builder->add('subscription',new SubsciptionOrderType()) 
     //Form elements related to order here 
    } 
} 

अब यह दो रूपों को जोड़ता है SubciptionOrderType, ProductOrder मुख्य फॉर्म ऑर्डर टाइप टाइप करें। तो बाद में नियंत्रक में यदि आप इस फॉर्म को प्रारंभ करते हैं तो आपको ऑर्डर टाइप के साथ सदस्यता और उत्पाद फ़ॉर्म के सभी फ़ील्ड मिलेंगे।

मैं इस अपने प्रश्नों के उत्तर देता है, तो अभी भी स्पष्ट नहीं प्रलेखन के माध्यम से यहां कई रूपों एम्बेड करने के लिए जाना कृपया उम्मीद है। http://symfony.com/doc/current/cookbook/form/form_collections.html

+2

इस समाधान से हालांकि मैं केवल एक ही उत्पाद और/या एकल सदस्यता प्राप्त नहीं कर पाऊंगा? आईडी उन ऑब्जेक्ट्स का संग्रह करना पसंद करती है जो उत्पाद या सब्सक्रिप्शन हो सकती हैं और सिम्फनी को यह तय करने दें कि संग्रह में इकाई के लिए कौन सा फॉर्म टाइप उपयुक्त है – vcetinick

संबंधित मुद्दे