1
votes

I'm working on symfony(4.2) project with CMS option. So I decided to use Sonata bundles to achieve the CMS like WP. Currentl I've installed and using SonataAdminBundle, SonataMediaBundle, SonataBlockBundle...etc. Also I've created custom service with basic form fields for page blocks and it works fine for me. Now I want to create a form with repeater field like clone system

I've created a new form type named CloneType and customized the form template. Also added the CloneType field in block service file and now the form looks like this below image

https://i.stack.imgur.com/WmYFg.png

my inputs is looking like this when inspect in browser

 <input type="text" id="s9fdc9db89c_settings_cxi_different_title" name="s9fdc9db89c[settings][cxi_different][title_1]" class=" form-control" value="test">
 <input type="text" id="s9fdc9db89c_settings_cxi_different_title" name="s9fdc9db89c[settings][cxi_different][title_2]" class=" form-control" value="test">

src/Form/Type/CloneType.php

<?php
namespace App\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\OptionsResolver\OptionsResolver;

class CloneType extends AbstractType
{
    public function getParent()
    {
        return FormType::class;
    }
}

templates/sonataadmin/forms/clone.twig.html

{% block clone_widget %}
  {% spaceless %}        
    <div class="clone-wrapper">
        <div id="clonedInput1" class="clonedInput">
            {{- form_widget(form) -}}
            {{- form_errors(form) -}}
            <div class="actions">
                <a class="btn btn-info clone">Clone</a> 
                <a class="btn btn-warning remove">Remove</a>
            </div>
        </div>
    </div>      
 {% endspaceless %}
 {% block javascripts %}
    <script type="text/javascript"> 
        var regex = /^(.+?)(\d+)$/i;
        var cloneIndex = $(".clonedInput").length;

        function clone(){
            $(this).parents(".clonedInput").clone()
                .appendTo("clone-wrapper")
                .attr("id", "clonedInput" +  cloneIndex)
                .find("*")
                .each(function() {
                    var id = this.id || "";
                    var match = id.match(regex) || [];
                    if (match.length == 3) {
                        this.id = match[1] + (cloneIndex);
                    }
                })
                .on('click', 'a.clone', clone)
                .on('click', 'a.remove', remove);
            cloneIndex++;
        }
        function remove(){
            $(this).parents(".clonedInput").remove();
        }
        $("a.clone").on("click", clone);

        $("a.remove").on("click", remove);         
    </script>
 {% endblock %}        
{% endblock %}

src/Application/Sonata/BlockBundle/Block/CxiDifferentBlockService.php

<?php 
namespace App\Application\Sonata\BlockBundle\Block;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Sonata\Form\Type\ImmutableArrayType;
use Sonata\Form\Validator\ErrorElement;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\BlockBundle\Meta\Metadata;
use Sonata\BlockBundle\Block\BlockContextInterface;
use Sonata\BlockBundle\Model\BlockInterface;
use Sonata\BlockBundle\Block\Service\AbstractBlockService;
use App\Application\Sonata\PageBundle\Entity\Block;
use App\Form\Type\CloneType;
use Sonata\AdminBundle\Admin\AdminInterface;
class CxiDifferentBlockService extends AbstractBlockService
{
 public function buildEditForm(FormMapper $formMapper, BlockInterface $block)
 {
    $formMapper
        ->add('settings', ImmutableArrayType::class, [
            'keys' => [
                ['title', TextType::class, [
                    'label' => 'Title (H2)',
                    'required' => false,
                ]],
                [$this->getCloneBuilder($formMapper), null, []],                   
            ],
        ])
    ;        
  }
  protected function getCloneBuilder(FormMapper $formMapper)
  {
     return $formMapper->create('cxi_different', CloneType::class, ['required' => false,'by_reference' => false,'allow_extra_fields'=>true])
         ->add('title', TextType::class,['required' => false, 'allow_extra_fields'=>true, 'by_reference' => false])
    ;
  }
}

I want to update the cloned field values in DB. But its not working. I checked the data while submit the form. The sonata form is not added the cloned fields in the post array. The post array is looks like below actual result

Actual Result :

Array(
 [title] => test
 [cxi_different] => Array
    (
        [title_1] => test
    )
)

Expected Result :

Array(
 [title] => test
 [cxi_different] => Array
    (
        [title_1] => test
        [title_2] => test
    )
) 

Thanks in advance!

1
I found the solution for this, we should use sonata CollectionType and need to create a new formtype to achieve thisSimbu Dev

1 Answers

1
votes

I've used CollectionType to achieve this and I've followed the below steps to achieve this

Step 1 : I've added new FormType in src/Form/CloneFormType.php

<?php

namespace App\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Sonata\AdminBundle\Form\Type\CollectionType;

final class CloneFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('title_1', TextType::class)
            ->add('title_2', TextType::class)
            ;
    }
}

Step 2 : Now BlockService should be like this

<?php 
namespace App\Application\Sonata\BlockBundle\Block;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Sonata\Form\Type\ImmutableArrayType;
use Sonata\Form\Validator\ErrorElement;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\BlockBundle\Meta\Metadata;
use Sonata\BlockBundle\Block\BlockContextInterface;
use Sonata\BlockBundle\Model\BlockInterface;
use Sonata\BlockBundle\Block\Service\AbstractBlockService;
use App\Application\Sonata\PageBundle\Entity\Block;
use App\Form\CloneFormType;
use Sonata\AdminBundle\Admin\AdminInterface;
class CxiDifferentBlockService extends AbstractBlockService
{
 public function buildEditForm(FormMapper $formMapper, BlockInterface $block)
 {
    $formMapper
        ->add('settings', ImmutableArrayType::class, [
            'keys' => [
                ['title', TextType::class, [
                    'label' => 'Title (H2)',
                    'required' => false,
                ]],
                ['cxi_different', CollectionType::class, [
                    'required' => false,
                    'allow_add' => true,
                    'allow_delete' => true,
                    'prototype' => true,
                    'by_reference' => false,
                    'allow_extra_fields' => true,
                    'entry_type' => CloneFormType::class,
                ]],                  
            ],
        ])
    ;        
  }
}

Step 3 : You can remove the below files. It's not needed when you using this CollectionType

  1. src/Form/Type/CloneType.php
  2. templates/sonataadmin/forms/clone.twig.html
  3. Also don't forgot to remove this line(use App\Form\Type\CloneType;) from src/Application/Sonata/BlockBundle/Block/CxiDifferentBlockService.php and I've replaced this line with use App\Form\CloneFormType; which is created newly for using CollectionType