Search code examples
symfonysymfony4easyadmin

EasyAdmin - How to show custom Entity property properly which use EntityRepository


I would like to show on EasyAdmin a custom property, here is an example :

class Book
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue(strategy="AUTO")
     * @ORM\Column(type="integer")
     */
    public $id;
    /**
     * @ORM\Column(type="string")
     */
    public $name;
    /**
     * @ORM\Column(type="float")
     */
    public $price;
    public function getBenefit(): float
    {
        // Here the method to retrieve the benefits
    }
}

In this example, the custom parameter is benefit it's not a parameter of our Entity and if we configure EasyAdmin like that, it works !

easy_admin:
    entities:
        Book:
            class: App\Entity\Book
            list:
                fields:
                    - { property: 'title', label: 'Title' }
                    - { property: 'benefit', label: 'Benefits' }

The problem is if the function is a bit complexe and need for example an EntityRepository, it becomes impossible to respect Controller > Repository > Entities.

Does anyone have a workaround, maybe by using the AdminController to show custom properties properly in EasyAdmin ?


Solution

  • You shouldn't put the logic to retrieve the benefits inside the Book entity, especially if it involves external dependencies like entityManager.

    You could probably use the Doctrine events to achieve that. Retrieve the benefits after a Book entity has been loaded from the DB. Save the benefits before or after saving the Book entity in the DB.

    You can find out more about it here https://symfony.com/doc/current/doctrine/event_listeners_subscribers.html

    class Book
    {
        ...
        public $benefits;
    }
    
    // src/EventListener/RetrieveBenefitListener.php
    namespace App\EventListener;
    
    use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
    use App\Entity\Book;
    
    class RetrieveBenefitListener
    {
        public function postLoad(LifecycleEventArgs $args)
        {
            $entity = $args->getObject();
    
            // only act on some "Book" entity
            if (!$entity instanceof Book) {
                return;
            }
    
            // Your logic to retrieve the benefits
            $entity->benefits = methodToGetTheBenefits();
        }
    }
    
    // src/EventListener/SaveBenefitListener.php
    namespace App\EventListener;
    
    use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
    use App\Entity\Book;
    
    class SaveBenefitListener
    {
        public function postUpdate(LifecycleEventArgs $args)
        {
            $entity = $args->getObject();
    
            // only act on some "Book" entity
            if (!$entity instanceof Book) {
                return;
            }
    
            // Your logic to save the benefits
            methodToSaveTheBenefits($entity->benefits);
        }
    }
    
    // services.yml
    services:
        App\EventListener\RetrieveBenefitListener:
            tags:
                - { name: doctrine.event_listener, event: postLoad }
        App\EventListener\SaveBenefitListener:
            tags:
                - { name: doctrine.event_listener, event: postUpdate }
    

    This is just an example, I haven't tested the code. You will probably have to add the logic for the postPersist event if you create new Book objects.

    Depending on the logic to retrieve the benefits (another DB call? loading from an external API?), you might want to to approach the problem differently (caching, loading them in your DB via a cron job, ...)