DerpyNerd
DerpyNerd

Reputation: 4813

Multiple forms for the same entity type

It's amazing that I can't find a solution for something that seems so straight forward.

My website has a simple settings page. All settings are stored in a simple table:

| id | name                | label                                             | type   | value                 | is_user_created | is_editable | edit_date           | original_name | category         | file                 | subcategory |
+----+---------------------+---------------------------------------------------+--------+-----------------------+-----------------+-------------+---------------------+---------------+------------------+----------------------+-------------+
| 21 | index_header_large  | Large header for index page                       | bool   | true                  |               0 |           1 | 2018-09-17 13:22:20 |               | Layout           |                      | Heading     |
| 25 | website_title       | Short title                                       | string | My website            |               0 |           1 | 2018-09-17 13:22:20 |               | Details website  |                      |             |
| 26 | website_owner       | Name of the owner                                 | string | Not specified         |               0 |           1 | 2018-09-17 13:22:20 |               | Gegevens website |                      |             |
+----+---------------------+---------------------------------------------------+--------+-----------------------+-----------------+-------------+---------------------+---------------+------------------+----------------------+-------------+

The settings table has a column called type. I use this to generate a FormType that has the correct fields / asserts for the type of value the form will have to render.

They all look very similar, this type of for a string type:

class SettingsType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        /* @var $entity \App\Entity\Setting */
        $entity=$builder->getData();
        $builder
            ->add('value', TextType::class, array(
                'label'             => $entity->getLabel(),
                'trim'              => true,
                'translation_domain' => 'app'
            ));
        ;
    }
}

In the controller I fetch the settings and iterate them to create the forms

    $settings = $repSettings->findAllOrderedByCategory();

    $settingForms = [];
    /* @var $setting Setting */
    foreach ($settings as $setting) {
        if ($setting->getType() === 'bool') {
            array_push($settingForms, $this->createForm(SettingsBoolType::class, $setting, array(
                'action' => $this->generateUrl(
                    'admin_set_setting_value',
                    array(
                        "_locale" => $request->getLocale(),
                        "_id" => $setting->getId()
                    )
                )
            ))->createView());
        } else if ($setting->getType() === 'file') {
            array_push($settingForms, $this->createForm(SettingsFileType::class, $setting, array(
                'action' => $this->generateUrl(
                    'admin_set_setting_file',
                    array(
                        "_locale" => $request->getLocale(),
                        "_id" => $setting->getId())
                )
            ))->createView());
        } else {
            array_push($settingForms, $this->createForm(SettingsType::class, $setting, array(
                'action' => $this->generateUrl(
                    'admin_set_setting_value',
                    array(
                        "_locale" => $request->getLocale(),
                        "_id" => $setting->getId())
                )
            ))->createView());
        }
    }

    return $this->render('admin/manage_settings.html.twig', array_merge(
        array(
            'settingForms'      => $settingForms,
        )
    ));

Good so far, the forms render and work because each form has a unique ID set in the action url. But this method has some issues.

I know this is not the way you should to this, but I'm clueless as to how I should. A collection maybe? Should I create a super class in order to render a CollectionType? In that case I need to know how I can apply my own layout, because I render headers for every category and subcategory column in the settings table.

A push in the right direction would be appreciated :)

Upvotes: 0

Views: 2191

Answers (1)

Fabien Papet
Fabien Papet

Reputation: 2319

I ran into the same problem today. Here's how I solved it :

You will have to adapt your code to fill in your application, the following code is just a demo I made about how things works. Do not hesitate to ask in comments for questions The $data is just an array with some Setting objects. To get faster, I used symfony built-in types for the Settings::type property. You're free to code an adapter in the ExtendedFieldCollectionType

<?php

namespace App\Controller;

use App\Model\Setting;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;

class DemoController extends AbstractController
{
    /**
     * @Route(name="demo_demo", path="/demo")
     * @Template()
     * @param Request $request
     * @return array
     */
    public function demo(Request $request)
    {
        $data = [
            new Setting('count', NumberType::class, 0.5),
            new Setting('text', TextType::class,'sample text'),
            new Setting('date', DateType::class, new \DateTime())
        ];

        $form = $this->createForm('App\Form\ExtendedFieldCollectionType', $data);

        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            // ...
        }

        return [
            'form' => $form->createView()
        ];
    }
}

And the form type:

<?php

namespace App\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;

class ExtendedFieldCollectionType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
            $form = $event->getForm();
            $data = $event->getData();

            foreach ($data as $name => $value) {
                $form->add($value->id, $value->type, [
                    'property_path' => '[' . $name . '].value', // the value property is my setting value
                ]);
            }
        });
    }
}

And the model I used

<?php

namespace App\Model;


class Setting
{
    public $id;
    public $type;
    public $value;

    /**
     * ProjectField constructor.
     * @param $id
     * @param $type
     * @param $value
     */
    public function __construct($id, $type, $value)
    {
        $this->id = $id;
        $this->type = $type;
        $this->value = $value;
    }
}

Upvotes: 2

Related Questions