Symfony - 具有可变数量字段的表单

时间:2017-05-10 09:50:55

标签: forms symfony symfony-forms symfony-3.2

我想创建表单,用于添加,编辑(以及在url字段为空时删除)菜单项。问题是行/项的计数是可变的。 (正如你在第一张照片上看到的那样)

问题:

1)如何编写具有可变字段数的表单。

2)如何将数据解析为此表单中的字段。

class GalleryType extends AbstractType {

public function buildForm(FormBuilderInterface $builder, array $options) {
    $builder->add(
    //...some textType, fileType fields,...etc
    );
}

public function configureOptions(OptionsResolver $resolver) {
    $resolver->setDefaults([
        //...
        //some data/data_class option that parse data into the field
    ]);
}

example of form (administration part)

额外信息:

我正在使用Symfony 3框架开发自己的简单内容管理系统。我想允许用户添加菜单项,其中包含以下信息:URL,标题,例如FA图标,背景图片等等。

- 总是有一个空行用于添加项目,其余字段用现有数据(菜单项/ s)来实现。确认表单后,此行将添加到表单中(以及空行)。

- 有几种不同类型的菜单:主菜单,滑块,侧面菜单,具有不同类型的字段。 (你可以在第二张图片上看到它)

- 主菜单有:标题,网址和某些项目可以有子项目(作为子菜单)

-Slider有:标题,网址,标题颜色,背景图片

-Side菜单包含:title,url和Font Awesome Icon

我已经完成了导航菜单(页脚)的表单,其中只有2个字段(标题和链接),但我觉得这不是属性方式如何编程...为了说明目的,这里是我'做了导航 example of menus design 控制器:

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use AppBundle\Entity\SMBundle\Navigation;
use AppBundle\Entity\Sett;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer;
use Symfony\Component\Form\Extension\Core\Type\TextType;

class SettingsController extends Controller {

//....

/**
 * @Route("/admin/menu/navigation", name="navigation")
 */
public function navigationAction(Request $request) {
    $set = $this->getDoctrine()->getRepository('AppBundle:Sett')->findOneByName('navigation');
    $navigation = $this->deserializeFromStringToObject('navigation');

    if (!$navigation) {
        $set = new Sett();
        $navigation = new Navigation();
    }

    $form = $this->createFormFromArray($navigation->getLinksArray());
    $form->handleRequest($request);

    if ($form->isSubmitted() && $form->isValid()) {
        $set->setEditedAt(new \DateTime());
        $set->setName('navigation');

        $this->brutalHack($navigation, $form);

        $set->setContent($this->serializeFromObjectToString($navigation));

        // Save
        $this->save($set);
        return $this->redirect($this->generateUrl('navigation'));
    }
    return $this->render("viewSM/menu/navigation.html.twig", array('form' => $form->createView()));
}

 private function deserializeFromStringToObject($name) {
    $object = $this->getDoctrine()->getRepository('AppBundle:Sett')->findOneByName($name);
    if (!$object) {
        return null;
    }
    $serializer = new Serializer(array(new GetSetMethodNormalizer()), array('json' => new JsonEncoder()));
    return $serializer->deserialize($object->getContent(), 'AppBundle\\Entity\\SMBundle\\' . ucfirst($name), 'json');
}

private function serializeFromObjectToString($object) {
    $serializer = new Serializer(array(new GetSetMethodNormalizer()), array('json' => new JsonEncoder()));
    return $serializer->serialize($object, 'json');
}

   private function createFormFromArray(array $collection) {
    $i = 0;
    $formBuilder = $this->createFormBuilder();

    foreach ($collection as $key => $value) {
        $formBuilder
                ->add('url' . $i, TextType::class, ['label' => 'URL ', 'data' => '' . $key, 'attr' => ['class' => 'form-control']])
                ->add('name' . $i, TextType::class, ['label' => 'Titulek ', 'data' => '' . $value, 'attr' => ['class' => 'form-control']]);
        $i++;
    }
    $formBuilder
            ->add('url' . $i, TextType::class, ['label' => 'URL ', 'attr' => ['class' => 'form-control']])
            ->add('name' . $i, TextType::class, ['label' => 'Titulek ', 'attr' => ['class' => 'form-control']])
            ->add('submit', \Symfony\Component\Form\Extension\Core\Type\SubmitType::class, ['label' => 'Uložit', 'attr' => ['class' => 'btn btn-primary']]);
    $form = $formBuilder->getForm();
    return $form;
}

private function save($set) {
    $em = $this->getDoctrine()->getManager();
    $em->persist($set);
    $em->flush();
}

private function brutalHack($navigation, $form) {
    $nav = array();
    if (count($navigation->getLinksArray()) == 0) {
        $nav[$form['url0']->getData()] = $form['name0']->getData();
    }
    for ($i = 0; $i < count($navigation->getLinksArray()); $i++) {
        $key = $form['url' . $i]->getData();
        $value = $form['name' . $i]->getData();
        if ($key != NULL && $value != NULL) {
            $nav[$key] = $value;
        }
    }
    $key = $form['url' . $i]->getData();
    $value = $form['name' . $i]->getData();
    if ($key != NULL && $value != NULL) {
        $nav[$key] = $value;
    }
    $navigation->setLinksArray($nav);
}
//...
}

实体:

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 * @ORM\Table(name="sett")
 * @ORM\HasLifecycleCallbacks
 */
class Sett  
{
 /**
 * @ORM\Id
 * @ORM\Column(type="integer")
 * @ORM\GeneratedValue(strategy="AUTO")
 */
private $id;

/**
 * @ORM\Column(name="name", length=255)
 */
private $name;

/**
 * @ORM\Column(name="content", type="json_array")
 */
private $content;  

     /**
 * @ORM\Column(name="edited_at", type="datetime")
 */
private $editedAt;

 /**
 * @ORM\Column(name="created_at", type="datetime")
 */
private $createdAt;

/**
 * @ORM\PrePersist
 */
public function onPrePersist()
{
    $this->createdAt = new \DateTime();
}

/**
 * Get id
 *
 * @return integer
 */
public function getId()
{
    return $this->id;
}

/**
 * Set name
 *
 * @param string $name
 *
 * @return Set
 */
public function setName($name)
{
    $this->name = $name;

    return $this;
}

/**
 * Get name
 *
 * @return string
 */
public function getName()
{
    return $this->name;
}

/**
 * Set content
 *
 * @param array $content
 *
 * @return Set
 */
public function setContent($content)
{
    $this->content = $content;

    return $this;
}

/**
 * Get content
 *
 * @return array
 */
public function getContent()
{
    return $this->content;
}

/**
 * Set editedAt
 *
 * @param \DateTime $editedAt
 *
 * @return Set
 */
public function setEditedAt($editedAt)
{
    $this->editedAt = $editedAt;

    return $this;
}

/**
 * Get editedAt
 *
 * @return \DateTime
 */
public function getEditedAt()
{
    return $this->editedAt;
}

/**
 * Set createdAt
 *
 * @param \DateTime $createdAt
 *
 * @return Set
 */
public function setCreatedAt($createdAt)
{
    $this->createdAt = $createdAt;

    return $this;
}

/**
 * Get createdAt
 *
 * @return \DateTime
 */
public function getCreatedAt()
{
    return $this->createdAt;
}
}

数据类:

class Navigation 
{
    private $linksArray;

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

function getLinksArray() {
    return $this->linksArray;
}

function setLinksArray($linksArray) {
    $this->linksArray = $linksArray;
}

function add($key,$value){
    $this->linksArray[$key]=$value;
}


}

1 个答案:

答案 0 :(得分:1)

我不确定这是否有效,但你应该试一试。

2)如何将数据解析为具有可变字段数的表单。

您可以使用$ options格式发送数据。

  控制器中的

$oForm = $this->createForm(YourFormType::class, 
$FormObject, [
    'your_options' => [
        'Checkbox' => 'FieldName1',
        'TextArea' => 'FieldName2'
]);
  

在您的表单中

public function buildForm(FormBuilderInterface $builder, array $options)
{

foreach($options['your_options'] as $key, $option) { //you can name $option as $filedName or whatever you find convenient
    $builder->add($option, $key.Type::class);
}
...}


public function configureOptions(OptionsResolver $resolver)
{
    $resolver->setDefaults([
        'your_options' => null
    ])
}