Search code examples
phpsymfonysymfony-formssymfony6

When using Symfony Forms, how to render inputs for one entity type but with multiple input types


I am having a hard time making a form to edit a already existing subset of the Entity "Setting". The idea is to render only a determined collection of "Settings" in one template, each with his custom input type, at least on template level changing the widget.

The Setting Entity looks like this:

App\Entity\Setting.php

<?php

namespace App\Entity;

use App\Repository\SettingRepository;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Uid\Ulid;

#[ORM\Entity(repositoryClass: SettingRepository::class)]
class Setting
{
    #[ORM\Id]
    #[ORM\Column(type: 'ulid', unique: true)]
    #[ORM\GeneratedValue(strategy: 'CUSTOM')]
    #[ORM\CustomIdGenerator(class: 'doctrine.ulid_generator')]
    private ?Ulid $id = null;

    #[ORM\Column(length: 255)]
    private ?string $name = null;

    #[ORM\Column(length: 255)]
    private ?string $slug = null;

    #[ORM\Column(type: Types::TEXT, nullable: true)]
    private ?string $value = null;

    public function getId(): ?Ulid
    {
        return $this->id;
    }

    public function getName(): ?string
    {
        return $this->name;
    }

    public function setName(string $name): self
    {
        $this->name = $name;

        return $this;
    }

    public function getSlug(): ?string
    {
        return $this->slug;
    }

    public function setSlug(string $slug): self
    {
        $this->slug = $slug;

        return $this;
    }

    public function getValue(): ?string
    {
        return $this->value;
    }

    public function setValue(?string $value): self
    {
        $this->value = $value;

        return $this;
    }
}

The Form Type:

App\Form\SettingGeneralType.php

<?php

namespace App\Form;

use App\Entity\Setting;
use App\Form\SettingType;
use Doctrine\ORM\EntityRepository;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class SettingGeneralType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('name')
            ->add('value')
        ;
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'data_class' => Setting::class,
        ]);
    }
}

For the "Set" that needs to be in the form i could give following data:

id name value slug
App name AppName app_name
App logo NULL app_logo
App description NULL app_description
Maintenance mode false app_maintenance
Default date and time format d.m.Y H:i app_datetime_format
PHP bin path pathToPhp.bin app_php_bin_path
Default color mode auto app_theme_default

The value types vary between string,choice (Dropdowns),boolean (checkboxes).

I would have no problem in formatting the different form widgets manually in the template to fit the required value, but i can't find any solution to how to render only a little collection each with his own "input" from this entity.

My only idea was using the "CollectionType" since it can render a set of simple forms i can render in the template and handle on submit, but i could not get it to filter entites that i wanted be rendered in those forms since i dont want every setting to be rendered...

This is my first question, so no hate if i missed something please :)


Solution

  • Hello what you're trying to do is called Embded Forms, to achieve what you need you have to create a custom FormType with inputs you need then you can use that custom formType in you formBuilder check documentation here for normal forms check here and here for Embded Collection form check here

    your code must look something like :

    class CustomType extends AbstractType
    {
         public function buildForm(FormBuilderInterface $builder, array $options): void
         {
             $builder->add('name')
                     ->add('description');
         }
    
          public function configureOptions(OptionsResolver $resolver): void
          {
              $resolver->setDefaults([
              'data_class' => Custom::class,
          ]);
       }
    }
    

    And then in your builder use customType :

    $builder->add('settings', CollectionType::class, [
            'entry_type' => CustomType::class,
        ]);