首页 文章

Symfony2以symfony形式获取Unmapped字段值

提问于
浏览
1

在symfony2房地产项目上工作,我需要弄清楚如何在数据库中以动态创建的形式存储提交的数据 .

这是我到目前为止所做的工作,我有一个带有一组预定义字段的房地产 addListings 表单,为此我在其中嵌入了一组来自 options 的附加字段 . 这些'additional fields'由管理员动态决定,他可以添加x个字段,他可以选择下拉菜单或复选框或文本类型 .

这里是选项和类别表单类型,它们都很好,不用担心,只需要提交到数据库的常规表单值

/**
 * Inside OptionsType.php
 */
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('value')
        ->add('submit', 'submit', ['label'=>'Create Option'])
    ;
}

/**
 * Inside CategoriesType.php
 */
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('name')
        ->add('required')
        ->add('isMultiple')
        ->add('isText')
        ->add('submit', 'submit',['label'=>'Create Category'])
    ;
}

这里是代表附加字段的实体 PropertyCategory.php 它有吸气剂和定位器,这里不用担心

<?php
 namespace path\to\Entity;

 use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Table(name="property_category")
 * @ORM\Entity
 */
class PropertyCategory {

/**
 * @var type integer
 * 
 * @ORM\ID
 * @ORM\Column(name="id", type="integer")
 * @ORM\GeneratedValue(strategy="AUTO")
 */
private $id;

/**
 * @ORM\ManyToOne(targetEntity="University", inversedBy="propertyCategory")
 */
protected $university;

/**
 * @ORM\ManyToOne(targetEntity="Property", inversedBy="propertyCategory")
 */
protected $property;

/**
 * @ORM\ManyToOne(targetEntity="Category", inversedBy="propertyCategory")
 */
protected $category;

/**
 * @ORM\OneToOne(targetEntity="Options", inversedBy="propertyCategory")
 */
protected $options;

现在这里是真实的东西,在PropertyCategoryType中,Options对象作为数组传递给这个表单,数组的键成为标签,值成为表单字段的选项,忽略大部分,只看一下这条线

$builder->add($value->getCategory()->getName(), $type, $options);

这将创建一个 mapped=>false 字段 . 现在,如果您查看下面的控制器 AdditionalDetailsController ,我有一个名为 propertyCategoryFormAction 的动作,这是我将用户选择的数据插入数据库的地方

So question is how do i get the unmapped field names dynamically like $form['fieldName']->getData() so that i can use it to query the database and get the options_id and persist it to the propertyCategory table

namespace path\to\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class PropertyCategoryType extends AbstractType {

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

/**
 * @param FormBuilderInterface $builder
 * @param array $options
 */
public function buildForm(FormBuilderInterface $builder, array $options) {

    foreach ($this->options as $key => $value) {
        if($value->getCategory()->getIsMultiple()){
            $checkbox[$value->getCategory()->getName()][] = $value->getValue();
            $type = 'choice';
            $options = array('choices'=>$checkbox[$value->getCategory()->getName()],'multiple'=>true, "mapped"=>false, 'expanded'  => true, 'required'=>$value->getCategory()->getRequired());


        }elseif($value->getCategory()->getIsText()){
            $type = 'text';
            $options = array( "mapped"=>false, 'required'=>$value->getCategory()->getRequired());


        }elseif($value->getCategory()->getIsText() == false && $value->getCategory()->getIsMultiple() == false){
            $ddl[$value->getCategory()->getName()][] = $value->getValue();
            $type = 'choice';
            $options = array('choices'=>$ddl[$value->getCategory()->getName()],'multiple'=>false, "mapped"=>false, 'required'=>$value->getCategory()->getRequired()); 

        }

        $builder
            ->add($value->getCategory()->getName(), $type, $options);
    }
        $builder
            ->add('submit', 'submit', array('label'=>'submit', 'attr'=>array('class'=>'btn btn-danger')));
  }


  /**
   * @param OptionsResolverInterface $resolver
   */
   public function setDefaultOptions(OptionsResolverInterface $resolver)
   {
      $resolver->setDefaults(array(
          'data_class' => path\to\PropertyCategory'
      ));
   }


  /**
   * @return string
   */
  public function getName()
  {
    return 'eduflats_bundle_eduflatsbundle_propertycategory';
  }
}

这是控制器 AdditionalDetailsController.php

class AdditionalDetailsController extends Controller
{
   /**
    * @Route("/AddCategory", name="addCategory")
    * @Template()
    */
 public function addCategoryAction(Request $request) {

    $em = $this->getDoctrine()->getEntityManager();
    $category = new Category();
    $form = $this->createForm(new CategoryType(), $category);
    $form->handleRequest($request);

    if($form->isValid()){
        $em->persist($category);
        $em->flush();

        return $this->redirect($this->generateUrl('addOption', array('id'=>$category->getId())));
    }
    return array('form'=>$form->createView());
}

/**
 * @Route("/AddOption/{id}", name="addOption")
 * @Template()
 */
public function addOptionAction(Request $request, $id){

    $em = $this->getDoctrine()->getEntityManager();
    $options = new Options();
    $form = $this->createForm(new OptionsType(), $options);
    $form->handleRequest($request);

    if($form->isValid()){
        $category = $this->getDoctrine()->getRepository('EduflatsBundle:Category')->findOneById($id);
        $university = $this->getDoctrine()->getRepository('EduflatsBundle:University')->findOneById(siteConfig::$university_id);
        $options->setCategory($category);
        $options->setUniversity($university);
        $em->persist($options);
        $em->flush();

        $this->get('session')->getFlashBag()->set('success', 'Your options have been saved Successfully');
        return $this->redirect($this->generateUrl('addOption',array('id'=>$id)));
    }
    return array('form'=>$form->createView());

}

/**
 * @Route("/form", name="form")
 * @Template()
 */
public function propertyCategoryFormAction(Request $request) {

    $options = $this->getDoctrine()->getRepository('EduflatsBundle:Options')->findAll();

    $em = $this->getDoctrine()->getEntityManager();
    $propertyCategory = new PropertyCategory();
    $form = $this->createForm(new PropertyCategoryType($options), $propertyCategory);
    $form->handleRequest($request);

    if($form->isValid()){
        $property = $this->getDoctrine()->getRepository('EduflatsBundle:Property')->findOneById(1);
        $propertyCategory = new PropertyCategory();

        $propertyCategory->setProperty($property);
        $propertyCategory->setOptions();

        $em->persist($propertyCategory);
        $em->flush();
    }
    return array('form'=>$form->createView());
}
}

我希望我很清楚,我可以在评论中澄清一些内容 .

1 回答

  • 0

    这是我的想法 . 我实际上有一份想要做同样事情的工作,但我们当时最终放弃了它,因为这是一件很难管理的事情 . 但这就是我的计划 .

    从主实体开始

    class Listing
    {
        protected $name;
        protected $price;
    
        //Your custom options (One-to-Many)
        protected $options;
    
        public function __construct()
        {
            $this->options = new ArrayCollection();
        }
    
        // Getters and Setters ...
    }
    

    然后我们有两个实体负责处理自定义选项 . 选项本身与列表有关 .

    class Option
    {
        protected $name;
        protected $fieldType;
        protected $required;
        // Other form options you may want...
    
        //Related Values (One-to-many)
        protected $optionValues;
    
        // Many-to-one
        protected $listings;
    
        public function __construct()
        {
            $this->optionValues = new ArrayCollection();
        }
    
        // Getters and Setters ...
    }
    

    以及存储在数据库中的该选项的值 .

    class OptionValues
    {
        protected $value;
    
    
        // Many-to-one
        protected $option;
    }
    

    然后我们 Build 的是From for the listing .

    class ListingType extends AbstractType
    {
        public function buildForm(FormBuilderInterface $builder, array $options)
        {
            $builder->add('name' , 'text')
                ->add('price', 'text');
                //Your other standard fields...
                foreach($options['extraFields'] as $field)
                {
                    $builder->add($field->getName() , new OptionValueType(), array('mapped' => false, 'options' => array('field' => $field));
                }
        }
    
        public function setDefaultOptions(OptionsResolverInterface $resolver)
        {
            $resolver->setRequired('extraFields');
    
            $resolver->addAllowedTypes(array(
                'extraFields' => '\Doctrine\Common\Collections\Collection'
            ));
    
            $resolver->setDefaults(array(
                'data_class' => 'AppBundle\Entity\Listing',
                'cascade_validation' => true
            ));
        }
    
        public function getName()
        {
            return 'listing';
        }
    }
    

    我们要求您传递 $option['extraFields'] ,它应该是您在控制器中从 $yourListing->getOptions() 获得的 AppBundle\Entity\Option 的集合 . 然后我们可以循环遍历这些并创建一个新的未映射字段,将该字段的其余部分传递给我们 AppBundle\Entity\OptionValue 的表单 .

    class OptionValueType extends AbstractType
    {
        public function buildForm(FormBuilderInterface $builder, array $options)
        {
            //Obviously do whatever other settings you need.  The important part is remembering that this belongs in the $value
            //of our OptionValue entity.
            $builder->add('value' , $field->fieldType, array('required' => $field->getIsRequired()));
        }
    
        public function setDefaultOptions(OptionsResolverInterface $resolver)
        {
            $resolver->setRequired('field');
    
            $resolver->addAllowedTypes(array(
                'field' => '\AppBundle\Entity\Option'
            ));
    
            $resolver->setDefaults(array(
                'data_class' => 'AppBundle\Entity\OptionValue',
                'cascade_validation' => true
            ));
        }
    
        public function getName()
        {
            return '';
        }
    }
    

    一些重要的事情需要注意 . 在这两种形式中,我们使用 $resolver->setRequired()$resolver->setAddAllowedTypes() 来定义我们的额外选项字段,否则Symfony将抛出错误 . 此外,在清单表单中,我们设置 'cascade_validation' => true 以确保我们的子表单也得到验证,如果's what you want. You may also need to do some crazy custom validation, that'是另外一套技巧 .

    这最终允许在我们的控制器中使用的是限制疯狂的自定义表单操作的数量,相反,如果您在清单和选项实体中正确设置持久化级联,您应该能够只保留列表并且它将确保所有Option和OptionValue实体也持久保存到数据库中 . 只要你做的一切都正确 . 我遗漏了很多细节,因为写的很多,但希望这会给你一些想法 .

相关问题