3

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).

circulosmeos
  • 424
  • 1
  • 6
  • 19

2 Answers2

3

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.

circulosmeos
  • 424
  • 1
  • 6
  • 19
Dylan KAS
  • 4,840
  • 2
  • 15
  • 33
  • thanks, but may be I must do something more?: `Compile Error: Could not check compatibility between App\Controller\Admin\UsuarioCrudController::createFormBuilder($data = NULL, array $options = Array): App\Controller\Admin\FormBuilderInterface and Symfony\Bundle\FrameworkBundle\Controller\AbstractController::createFormBuilder($data = NULL, array $options = Array): Symfony\Component\Form\FormBuilderInterface, because class App\Controller\Admin\FormBuilderInterface is not available`. Adding `use App\Controller\Admin\FormBuilderInterface;` in UsuarioCrudController.php doesn't solve this. – circulosmeos Apr 08 '21 at 12:47
  • You seem to have the wrong import of FormBuilderInterface, can you try the code I udpated (with the modification you need to make it work) – Dylan KAS Apr 08 '21 at 13:29
  • Yes, you're right, (`: FormBuilderInterface` is necessary), the correct import is `use Symfony\Component\Form\FormBuilderInterface;`. But now, **how do I show the field?** If I don't use `configureFields()` it doesn't appear, and if I use it, an `AssociationField::new('hora', 'Hora')` shows the same content as before. Should I see the change just using `AssociationField`? – circulosmeos Apr 08 '21 at 17:40
  • Using configureFields and overriding createFormBuilder at the same time works, you are just overriding the form when modifying it in your form builder so the point is just to modify your hora field in createFormBuilder as you would a regular form in symfony – Dylan KAS Apr 09 '21 at 13:30
  • thank you very much, with **createEditFormBuilder()** this runs ok! I wonder why the dropdown control doesn't have a search field as it does when using the AssociationField::new() method :-o – circulosmeos Apr 13 '21 at 10:50
3

fwiw, the actual entity being edited can be accessed in a Symfony easyadmin CrudController's configureFields() method using:

if ( $pageName === 'edit' ) {
    ...
    $this->get(AdminContextProvider::class)->getContext()->getEntity()->getInstance()
    ...

This way in configureFields() I could add code to filter my entities:

$horas_libres = $this->getDoctrine()->getRepository(Hora::class)->findFree();

and then add the actual entity value also, which is what I was trying to do:

array_unshift( $horas_libres, 
$this->get(AdminContextProvider::class)->getContext()->getEntity()->getInstance()->getHora() );

Now the field can be constructed in the returned array with "choices":

return [ ...
            AssociationField::new('hora', 'Hora')->setFormTypeOptions([
                "choices" => $horas_libres
            ]),
       ]
circulosmeos
  • 424
  • 1
  • 6
  • 19
  • Please share more details. How does this resolve the original problem? – Nico Haase Apr 12 '21 at 07:35
  • Ok, all details added. Any help to improve this code the "Symfony way" is welcome :-) – circulosmeos Apr 13 '21 at 10:26
  • This hint on how to access the entity from the context was extremely helpful! It should be mentioned somewhere in the official documentation. Thank you! – kcm Nov 02 '21 at 16:14