0

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.

  • Wrong values submitted: It seems to work for string value forms, but sometimes a boolean value form submits a value for the wrong setting.

  • Duplicate IDs: The block_prefixes for each form is set to the class name (e.g settings_value).

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 :)

4
  • Can you post createForm method?
    – HTMHell
    Sep 18, 2018 at 14:10
  • @HtmHell The createForm method is derived from the ControllerInterface. I can't change the code behind it :) In case you haven't seen it in my code, it's in the third code block: array_push($settingForms, $this->createForm(...)
    – DerpyNerd
    Sep 18, 2018 at 14:15
  • Oh sorry, I'm not familiar with Symfony. About the ID, why not using a random value? pastebin.com/nXDa8mDk (I also rearranged your code a bit, couldn't stand all the else/if xD)
    – HTMHell
    Sep 18, 2018 at 14:31
  • It doesn't matter what ID the form has, the name is important because Symfony uses it to check wether the form has been submitted. So the name has to be the same when the form is submitted back to the server
    – DerpyNerd
    Sep 18, 2018 at 14:54

1 Answer 1

2

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;
    }
}
6
  • Hmmm, I still have a lot to learn and this is exactly what the docs don't show you. I will try it your way, thanks in advance
    – DerpyNerd
    Sep 18, 2018 at 17:38
  • did you try it ? Sep 20, 2018 at 9:50
  • I will try it in a few days. I'm currently not working on this part. I'll keep you posted
    – DerpyNerd
    Sep 20, 2018 at 13:52
  • Legend says @DerpyNerd is still trying it to this day
    – Max
    Mar 9, 2022 at 20:57
  • 1
    @Max lol, you've snapped me out of it. Now I can leave my room and go outside
    – DerpyNerd
    Mar 24, 2022 at 20:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.