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.
I just bumped into the same issue as you, but since it's the first hit I came across in Google, I'm just posting what I did to work around the issue.
I'm assuming you were dynamically creating the form in a controller. If not you would need to declare your class as a service and inject the sonata.admin.manager.orm
service to it.
$form = $this->createFormBuilder()
->add('<name_of_field>', 'sonata_type_model', array(
'multiple' => true,
'class' => <className>::class,
'property' => '<propertyName>',
'model_manager' => $this->get('sonata.admin.manager.orm')
))
;
After that it rendered correctly for me as it would in admin context.
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.
sonata_type_model need to know what kind of entity is related to your field.
If you define this in admin class, sonata use own internal method to check the relation.
So if you define it outside admin it is put null instead entity