kikwik/json-form-bundle

Helpers and listeners for using forms with dunglas/doctrine-json-odm

Installs: 7

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 1

Forks: 0

Open Issues: 0

Type:symfony-bundle

v1.0.1 2023-12-08 13:38 UTC

This package is auto-updated.

Last update: 2024-04-08 14:24:06 UTC


README

Helpers and listeners for using forms with dunglas/doctrine-json-odm

Installation

Open a command console, enter your project directory and execute the following command to download the latest stable version of this bundle:

$ composer require kikwik/json-form-bundle

Usage

Suppose to have some models defined:

// first model
namespace App\Model;

class Dimension
{
    private ?int $height = null;
    private ?int $width = null;
    
    // getter and setter...
}
// second model
namespace App\Model;

class TechData
{
    private ?string $someData = null;
    private ?string $otherData = null;
    
    // getter and setter...
}

Then you have to define a form for each model:

// first model form
namespace App\Form\Model;

use App\Model\Dimension;

class DimensionType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('height')
            ->add('width')
        ;
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'data_class'=>Dimension::class
        ]);
    }
}
// second model form
namespace App\Form\Model;

use App\Model\TechData;

class TechDataType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('someData')
            ->add('otherData')
        ;
    }

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

Now you can to have an entity with some json_document fields defined as single model or an array of arbitrary models (the entity must have an updatedAt timestamp field)

// the entity
namespace App\Entity;

use App\Model\Dimension;

#[ORM\Entity(repositoryClass: ProductRepository::class)]
class Product
{    
    #[ORM\Column(type: Types::STRING)]
    private ?string $name = null;

    #[ORM\Column(type: 'json_document', nullable: true)]
    private ?Dimension $dimension = null;
    
    #[ORM\Column(type: 'json_document', nullable: true)]
    private array $techData = [];
    
    #[ORM\Column(type: Types::DATETIME_MUTABLE)]
    protected $updatedAt;
    
    // getter and setter...
}

To handle correctly forms that has json_document fields you must autowire the JsonDocumentFormSubscriber service and add to the FormBuilderInterface as event subscriber.

The subscriber will set the updatedAt field to the current timestamp in case of one of the json_document fields has changed. This will force doctrine to persist the main entity.

// the entity form
namespace App\Form;

use Kikwik\JsonFormBundle\EventListener\JsonDocumentFormSubscriber;

class ProductFormType extends AbstractType
{
    public function __construct(private JsonDocumentFormSubscriber $jsonDocumentFormSubscriber) # autowire here
    {
    }
    
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('name')
            ->add('dimension',DimensionType::class)
            ->addEventSubscriber($this->jsonDocumentFormSubscriber) # add as event subscriber
        ;
    }
     
    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'data_class' => Product::class,
        ]);
    }
}

JsonDocumentCollectionType

To handle collections of models you must define the mapping from a model to the relative form in config/packages/kikwik_json_form.yaml:

kikwik_json_form:
    model_map:
        App\Model\Dimension:    App\Form\Model\DimensionType
        App\Model\TechData:     App\Form\Model\TechDataType

Then you can use the JsonDocumentCollectionType to store heterogeneous types of models in one field:

namespace App\Form;

use Kikwik\JsonFormBundle\EventListener\JsonDocumentFormSubscriber;

class ProductFormType extends AbstractType
{
    public function __construct(private JsonDocumentFormSubscriber $jsonDocumentFormSubscriber)
    {
    }
    
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('name')
            ->add('techData',JsonDocumentCollectionType::class, [
                'model_labels' => [
                    'App\Model\Dimension' => 'Size of product',
                    'App\Model\TechData' => 'Technical data',
                ]
            ])
            ->addEventSubscriber($this->jsonDocumentFormSubscriber)
        ;
    }
     
    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'data_class' => Product::class,
        ]);
    }
}

To force the initial data use the data_models option inside a PRE_SET_DATA listener:

namespace App\Form;

use Kikwik\JsonFormBundle\EventListener\JsonDocumentFormSubscriber;

class ProductFormType extends AbstractType
{
    public function __construct(private JsonDocumentFormSubscriber $jsonDocumentFormSubscriber)
    {
    }
    
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('name')
            ->add('techData',JsonDocumentCollectionType::class, [
                'model_labels' => [
                    'App\Model\Dimension' => 'Size of product',
                    'App\Model\TechData' => 'Technical data',
                ]
            ])
            ->addEventSubscriber($this->jsonDocumentFormSubscriber)
        ;
        
        $builder->addEventListener(FormEvents::PRE_SET_DATA, [$this, 'onPreSetData']);
    }
     
    public function onPreSetData(PreSetDataEvent $event)
    {
        /** @var Product $product */
        $product = $event->getData();
        $form = $event->getForm();

        if(!$product || $product->getId() == null)
        {
            $form->add('techData',JsonDocumentCollectionType::class, [
                'model_labels' => [
                    'App\Model\Dimension' => 'Size of product',
                    'App\Model\TechData' => 'Technical data',
                ],
                'data_models' => [
                    'App\Model\Dimension',
                    'App\Model\TechData'
                ]
            ]);
        }
    } 
     
    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'data_class' => Product::class,
        ]);
    }
}