Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use Sonata Field Type on normal Form Class

I'm trying to insert custom sonata form field type on the front page, not in SonataAdmin, something like this:

 $form = $this->createFormBuilder($content)
            ->add('titleEs', 'text', array('required' => true, 'label' => 'label.title.spanish', 'attr' => array('class' => 'col-xs-12 form-control input-lg')))
            ->add('contentEs', 'ckeditor', array('required' => true,'label' => 'label.content.spanish', 'attr' => array('class' => 'col-xs-12')))
            ->add('titleEn', 'text', array('required' => true,'label' => 'label.title.english', 'attr' => array('class' => 'col-xs-12 form-control input-lg')))
            ->add('contentEn', 'ckeditor', array('required' => true, 'label' => 'label.content.english', 'attr' => array('class' => 'col-xs-12')))
            ->add('header', 'sonata_type_model', array('required' => true,'label' => 'label.content.headerImage'), array('link_parameters' => array('context' => 'content/front', 'size' => 'big')))
            //->add('coverImage', 'sonata_type_model_list', array('required' => true,'label' => 'label.content.coverImage'), array('link_parameters' => array('context' => 'content/front', 'size' => 'small')))
            //->add('sliderImage', 'sonata_type_model_list', array('required' => false,'label' => 'label.content.sliderImage'), array('link_parameters' => array('context' => 'content/slider', 'size' => 'normal')))
            ->getForm(); 

But when I execute that, it throws an error:

Catchable Fatal Error: Argument 1 passed to Sonata\AdminBundle\Form\ChoiceList\ModelChoiceList::__construct() must implement interface Sonata\AdminBundle\Model\ModelManagerInterface, null given

I can't understand why Symfony throws that error, if the Sonata Form Field Types are services.

like image 944
Borja Pombo Avatar asked Oct 19 '22 23:10

Borja Pombo


1 Answers

Thanks to @gabtzi's answer I poked around in the source code of Sonata Admin and came up with a very similar solution. Assuming that we have two entities Movie and Genre with a many-to-many relation between them (Movie is the owning side), the solution in Symfony 4 and Sonata Admin 3.x would look like this:

<?php

namespace App\Form\Type;

use App\Entity\Movie;
use App\Entity\Genre;
use Symfony\Component\Form\AbstractType;
use Sonata\AdminBundle\Form\Type\ModelType;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class MovieType extends AbstractType
{
    private $container;

    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            // add fields
            ->add('genres', ModelType::class, [
                'multiple' => true,
                'class' => Genre::class,
                'property' => 'name',  // assuming Genre has property name
                'model_manager' => $this->container->get('sonata.admin.manager.orm'),
                'by_reference' => false
            ])
            // add more fields
        ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => Movie::class,
        ));
    }
}

This is a very basic example, but should give an idea how to proceed further. Important things are:

  • you don't have to register the form type as a service if you use autowiring. Check that autowire is set to true in your config/services.yaml. Read the official documentation for more detailed information;

  • pass ContainerInterface to the constructor to get the container;

  • you don't use sonata_type_model anymore. You have to use ModelType::class. Pay attention to the use statements;

  • you can set mutiple to true for a M2M relation, otherwise it defaults to false;

  • you have to pass the entity class to class - in this case Movie::class;

  • you can specify property to use certain property of Genre. You don't have to declare this if you have defined __toString method in the entity class. Then the return value of this method will be used;

  • the most important thing: now that you have the container, get the service sonata.admin.manager.orm and pass it to model_manager. Without this everything falls in water.

I haven't however managed to display the button + Add new. It's worth mentioning that admin class for the related property must exist and be accessible (proper permissions set) - in this case GenreAdmin would be required, otherwise the button couldn't even theoretically work.

like image 124
cezar Avatar answered Oct 22 '22 23:10

cezar