Search code examples
symfonydependency-injectiondoctrine-ormeasyadmineasyadmin3

Easy Admin 3 (Symfony 4) AssociationField in OneToOne relationship shows already associated entities


Using Symfony 4.4 with Easy Admin 3:
I've a OneToOne relationship

class Usuario
{
...
    /**
     * @ORM\OneToOne(targetEntity=Hora::class, inversedBy="usuario", cascade={"persist", "remove"})
     */
    private $hora;
...
}
class Hora
{
...
    /**
     * @ORM\OneToOne(targetEntity=Usuario::class, mappedBy="hora", cascade={"persist", "remove"})
     */
    private $usuario;
...
}

I've got a CRUD Controller for Usuario:

class UsuarioCrudController extends AbstractCrudController
{
    public function configureFields(string $pageName): iterable
    {
    ...
    return [
    ...
            AssociationField::new('hora', 'Hora'),
        ];

Everything seems ok, but in the admin form for "Usuario", the field "hora" shows all values in database, even the ones already assigned to other "Usuario" entities:
I would like the dropdown control to show only not assigned values, PLUS the value of the actual "Usuario" entity, so the control be easy to use.

Which is the proper way to do this with easyadmin?

I've managed to code the field to show only the not associated "Hora" values, using $this->getDoctrine() and ->setFormTypeOptions([ "choices" => in UsuarioCrudController class,

but I am not able to access the actual entity being managed, nor in UsuarioCrudController class (maybe there it is not accesible) neither in Usuario class (I've tried here __construct(EntityManagerInterface $entityManager) to no avail as the value doesn't seem to be injected, dunno why).


Solution

  • It is possible to customize a few things in easy admin by either overriding EasyAdmin methods or listening to EasyAdmin events.

    Example of methods:

    public function createIndexQueryBuilder(SearchDto $searchDto, EntityDto $entityDto, FieldCollection $fields, FilterCollection $filters): QueryBuilder
    public function createEntity(string $entityFqcn)
    public function createEditForm(EntityDto $entityDto, KeyValueStore $formOptions, AdminContext $context): FormInterface
    //etc..
    

    Example of events:

    use EasyCorp\Bundle\EasyAdminBundle\Event\AfterCrudActionEvent;
    use EasyCorp\Bundle\EasyAdminBundle\Event\AfterEntityDeletedEvent;
    use EasyCorp\Bundle\EasyAdminBundle\Event\AfterEntityPersistedEvent;
    use EasyCorp\Bundle\EasyAdminBundle\Event\AfterEntityUpdatedEvent;
    use EasyCorp\Bundle\EasyAdminBundle\Event\BeforeCrudActionEvent;
    use EasyCorp\Bundle\EasyAdminBundle\Event\BeforeEntityDeletedEvent;
    use EasyCorp\Bundle\EasyAdminBundle\Event\BeforeEntityPersistedEvent;
    use EasyCorp\Bundle\EasyAdminBundle\Event\BeforeEntityUpdatedEvent;
    

    You could override easy admin createEditFormBuilder or createNewFormBuilder method, this way you could access the current form data and modify your hora field.

    Something like :

     use Symfony\Bridge\Doctrine\Form\Type\EntityType;
     use Symfony\Component\Form\FormBuilderInterface;
     use EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto;
     use EasyCorp\Bundle\EasyAdminBundle\Config\KeyValueStore;
     
     public function createEditFormBuilder(EntityDto $entityDto, KeyValueStore $formOptions, AdminContext $context): FormBuilderInterface {
        $formBuilder = parent::createEditFormBuilder($entityDto, $formOptions, $context);
    
        $unassignedValues = $this->yourRepo->findUnassignedValues();
        $data = $context->getEntity()->getInstance();
        if(isset($data) && $data->getHora()){
            //if your repo return an ArrayCollection
            $unassignedValues = $unassignedValues->add($data->getHora());
        }
        // if 'class' => 'App\Entity\Hora' is not passed as option, an error is raised (see //github.com/EasyCorp/EasyAdminBundle/issues/3095):
        //      An error has occurred resolving the options of the form "Symfony\Bridge\Doctrine\Form\Type\EntityType": The required option "class" is missing.
        $formBuilder->add('hora', EntityType::class, ['class' => 'App\Entity\Hora', 'choices' => $unassignedValues]);
    
        return $formBuilder;
    }
    

    Currently, easyadmin3 still lack documentation so sometimes the best way to do something is to look at how easy admin is doing things.