1

I am using EasyAdmin 3 and I am trying to create a validation system where:

  • ROLE_EDITOR can edit an Entity (e.g. Operateur) via EasyAdmin (OperateurCrudCrontroller) and the edited data of this Operator is saved in another temporary table (Operateur_Temp).
  • Later on, ROLE_ADMIN can access the saved data in Operateur_Temp via EasyAdmin, and validate the changes. The validated changes will be added to the Operateur table.

In the OperateurCrudController, I have created a button updateOperator and a function associated to this button called saveOperator.

I can create a new OperateurTemp object and save some data in it. My problem is that I do not know how to get the data from the edit form in EasyAdmin. I would like to retrieve the values of all the fields in the edit form to be able to save them in the OperateurTemp table.

Here is my OperateurCrudController:

<?php

namespace App\Controller\Admin;

use App\Entity\Operateur;
use App\Entity\OperateurTemp;
use App\Entity\User;
use App\Form\OperateurType;
use EasyCorp\Bundle\EasyAdminBundle\Dto\ActionDto;
use EasyCorp\Bundle\EasyAdminBundle\Dto\CrudDto;
use EasyCorp\Bundle\EasyAdminBundle\Form\Type\CrudFormType;
use EasyCorp\Bundle\EasyAdminBundle\Provider\AdminContextProvider;
use EasyCorp\Bundle\EasyAdminBundle\Router\CrudUrlGenerator;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use App\Repository\OperateurRepository;
use App\Repository\OperateurTempRepository;
use App\Repository\UserRepository;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\QueryBuilder;
use EasyCorp\Bundle\EasyAdminBundle\Collection\FieldCollection;
use EasyCorp\Bundle\EasyAdminBundle\Collection\FilterCollection;
use EasyCorp\Bundle\EasyAdminBundle\Config\Filters;
use EasyCorp\Bundle\EasyAdminBundle\Config\KeyValueStore;
use EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;


use EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto;
use EasyCorp\Bundle\EasyAdminBundle\Dto\SearchDto;
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
use EasyCorp\Bundle\EasyAdminBundle\Field\IdField;
use EasyCorp\Bundle\EasyAdminBundle\Field\AssociationField;
use EasyCorp\Bundle\EasyAdminBundle\Field\DateTimeField;
use EasyCorp\Bundle\EasyAdminBundle\Field\NumberField;
use EasyCorp\Bundle\EasyAdminBundle\Field\TextareaField;
use EasyCorp\Bundle\EasyAdminBundle\Field\EmailField;
use EasyCorp\Bundle\EasyAdminBundle\Field\TelephoneField;
use EasyCorp\Bundle\EasyAdminBundle\Field\UrlField;
use EasyCorp\Bundle\EasyAdminBundle\Field\TextEditorField;

use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
use EasyCorp\Bundle\EasyAdminBundle\Filter\EntityFilter;
use EasyCorp\Bundle\EasyAdminBundle\Router\AdminUrlGenerator;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Validator\Constraints\Date;

use EasyCorp\Bundle\EasyAdminBundle\Event\BeforeEntityUpdatedEvent;
use EasyCorp\Bundle\EasyAdminBundle\Event\BeforeCrudActionEvent;
use EasyCorp\Bundle\EasyAdminBundle\Factory\EntityFactory;
use EasyCorp\Bundle\EasyAdminBundle\Event\AfterCrudActionEvent;
use EasyCorp\Bundle\EasyAdminBundle\Orm\EntityUpdater;
use EasyCorp\Bundle\EasyAdminBundle\Event\AfterEntityUpdatedEvent;
use EasyCorp\Bundle\EasyAdminBundle\Event\BeforeEntityPersistedEvent;
use App\EventSubscriber\EasyAdminSubscriber;

class OperateurCrudController extends AbstractCrudController
{

    private $adminContextProvider;
    private $entityManager;

    public function __construct(AdminUrlGenerator $adminUrlGenerator, AdminContextProvider $adminContextProvider, EntityManagerInterface $entityManager)
    {
        $this->adminUrlGenerator = $adminUrlGenerator;
        $this->adminContextProvider = $adminContextProvider;
        $this->entityManager = $entityManager;
    }

    public static function getEntityFqcn(): string
    {
        return Operateur::class;
    }

    public function configureFields(string $pageName): iterable
    {
        return [
            IdField::new('id')->hideOnForm(),
            TextField::new('name', 'Nom'),
            TextEditorField::new('description', 'Description')->hideOnIndex(),
            TextareaField::new('address', 'Adresse'),
            TextField::new('city', 'Ville')->hideOnIndex(),
            TextField::new('postal_code', 'Code Postal')->hideOnIndex(),
            TextField::new('email', 'Email'),
            TelephoneField::new('phone', 'Téléphone'),
            UrlField::new('website', 'Site Web')->hideOnIndex(),
            TextEditorField::new('opening_hours', 'Horaires d\'Ouverture'),
            NumberField::new('latitude', 'Latitude')->hideOnIndex()->setNumDecimals(15),
            NumberField::new('longitude', 'Longitude')->hideOnIndex()->setNumDecimals(15),
            TextField::new('slug', 'Slug')->hideOnIndex()->setPermission('ROLE_ADMIN'),
            DateTimeField::new('created_at', 'Date Création')->onlyOnIndex(),
            DateTimeField::new('updated_at', 'Date Modification')->onlyOnIndex(),
            AssociationField::new('thematiques', 'Thématiques')

        ];
    }

    public function configureActions(Actions $actions): Actions
    {
        $batchAction = Action::new('approve', 'Approuver', 'fa fa-user-check')
            ->linkToUrl('approveOperators');

        $updateOperator = Action::new('update', 'Enregistrer les modifications', 'fa fa-save')
            ->linkToCrudAction('saveOperator');


        if ($this->isGranted('ROLE_ADMIN')) {
            return $actions
                // ->add(Crud::PAGE_INDEX, $batchAction)
                ->add(Crud::PAGE_INDEX, Action::DETAIL)
                ->setPermission(Action::DELETE, 'ROLE_ADMIN')
                ->setPermission(Action::NEW, 'ROLE_ADMIN')
                ->setPermission(Action::EDIT, 'ROLE_ADMIN')
                ->setPermission($batchAction, 'ROLE_ADMIN');
        }

        if ($this->isGranted('ROLE_EDITOR')) {
            return $actions
                ->add(Crud::PAGE_INDEX, Action::DETAIL)
                ->add(Crud::PAGE_EDIT, $updateOperator)
                ->setPermission(Action::DELETE, 'ROLE_ADMIN')
                ->setPermission(Action::NEW, 'ROLE_ADMIN')
                ->setPermission($updateOperator, 'ROLE_EDITOR')
                ->disable(Action::SAVE_AND_RETURN, Action::SAVE_AND_CONTINUE);
        }
    }

    public function approveOperators(): Response
    {
        $this->addFlash('notice', '<span style="color: green"><i class="fa fa-check"></i>Modification effecuté </span>');

        $url = $this->adminUrlGenerator
            ->setAction(Action::INDEX)
            ->generateUrl();

        return $this->redirect($url);
    }

    public function saveOperator(AdminContext $context): Response
    {   
        $operator = $context->getEntity()->getInstance();

        $operator_name = $operator->getName();
        $operator_description = $operator->getDescription();
        $operator_latitude = $operator->getLatitude();
        $operator_longitude = $operator->getLongitude();

        $operator_temp = new OperateurTemp();
        $operator_temp->setName($operator_name);
        $operator_temp->setDescription($operator_description);
        $operator_temp->setLatitude($operator_latitude);
        $operator_temp->setLongitude($operator_longitude);

        $em = $this->getDoctrine()->getManager();
        $em->persist($operator_temp);
        $em->flush();

        //$this->addFlash('notice', '<span style="color: green"><i class="fa fa-check"></i>Modification prise en compte ! </span>');

        //Create my own save button in page edit

        $url = $this->adminUrlGenerator
            ->setAction(Action::INDEX)
            ->generateUrl();

        return $this->redirect($url);
    }


    public function configureCrud(Crud $crud): Crud
    {
        return $crud
            ->setEntityPermission('ROLE_EDITOR');
    }



    public function createIndexQueryBuilder(SearchDto $searchDto, EntityDto $entityDto, FieldCollection $fields, FilterCollection $filters): QueryBuilder
    {
        $response = parent::createIndexQueryBuilder($searchDto, $entityDto, $fields, $filters);
        if (!$this->isGranted('ROLE_ADMIN')) {
            $response->where('entity.id = :id');
            $response->setParameter('id', $this->getUser()->getOperateur());
        }

        return $response;
    }

    public function updateEntity(EntityManagerInterface $entityManager, $entityInstance): void
    {
        parent::updateEntity($entityManager, $entityInstance); // TODO: Change the autogenerated stub
    }
}

I have tried to use EasyAdminSubscriber and the BeforeEntityUpdatedEvent; but it did not worked. I get the impression that the updateOperateur function is never executed. Here is my EasyAdminSubscriber.php file:

<?php

namespace App\EventSubscriber;

use App\Entity\User;
use App\Entity\Operateur;
use App\Entity\OperateurTemp;
use Doctrine\ORM\EntityManagerInterface;
use EasyCorp\Bundle\EasyAdminBundle\Event\BeforeEntityPersistedEvent;
use EasyCorp\Bundle\EasyAdminBundle\Event\BeforeEntityUpdatedEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;

class EasyAdminSubscriber implements EventSubscriberInterface
{

    private $entityManager;
    private $passwordEncoder;

    public function __construct(EntityManagerInterface $entityManager, UserPasswordEncoderInterface $passwordEncoder)
    {
        $this->entityManager = $entityManager;
        $this->passwordEncoder = $passwordEncoder;
    }

    public static function getSubscribedEvents()
    {
        return [
            BeforeEntityPersistedEvent::class => ['addUser'],
            BeforeEntityUpdatedEvent::class => ['updateUser'],
            BeforeEntityUpdatedEvent::class => ['updateOperateur'],
        ];
    }

    public function updateUser(BeforeEntityUpdatedEvent $event)
    {
        $entity = $event->getEntityInstance();

        if (!($entity instanceof User)) {
            return;
        }
        $this->setPassword($entity);
    }

    public function updateOperateur(BeforeEntityUpdatedEvent $event)
    {
        $entity = $event->getEntityInstance();
        if (!($entity instanceof Operateur)) {
            return;
        }
        $this->setOperateurTemp($entity);
    }

    public function addUser(BeforeEntityPersistedEvent $event)
    {
        $entity = $event->getEntityInstance();

        if (!($entity instanceof User)) {
            return;
        }
        $this->setPassword($entity);
    }

    /**
     * @param User $entity
     */
    public function setPassword(User $entity): void
    {
        $pass = $entity->getPassword();

        $entity->setPassword(
            $this->passwordEncoder->encodePassword(
                $entity,
                $pass
            )
        );
        $this->entityManager->persist($entity);
        $this->entityManager->flush();
    }

    /**
     * @param Operateur $entity
     */
    public function setOperateurTemp(Operateur $entity): void
    {
        $operator_temp = new OperateurTemp();

        $operator_temp->setName($entity->getName());
        $operator_temp->setDescription($entity->getDescription());
        $operator_temp->setLatitude($entity->getLatitude());
        $operator_temp->setLongitude($entity->getLongitude());

        var_dump($entity);
        var_dump($operator_temp);

        $this->entityManager->persist($operator_temp);
        $this->entityManager->flush();
    }
}

I have been also checking the AbstractCrudController.php file in the EasyAdminBundle file to check the main edit, new, etc. functions to know how to retrieve the form values or how to get the form and its attributes, but without success.

If anyone know how to get the edit form and its values in EasyAdmin 3, it would be hardly appreciated.

Thanks in advance

Atalaia
  • 45
  • 1
  • 6
  • To make your [`use` imports](https://www.php.net/manual/en/language.namespaces.importing.php) more manageable, consider you can use by group (`use /some/path/{a, b, c}`) or a parent and then use a partial (`use some/path` and `path/a()` ). That way there isn't a hundred lines at the top dedicated to every single use. – Jared Farrish Oct 29 '21 at 14:01

1 Answers1

0

i cant comment so i answer instead. here is my proposal.

Use BeforeEntityUpdatedEvent. Make a validate boolean field, setup it as false if role = ROLE_EDITOR.

ROLE_ADMIN have to be able to see this validate field and just have to change it. So add in your entity the nullable field, and in the controller this:

  BooleanField::new('Validate')->setPermission('ROLE_ADMIN')

Once this is done, setup your BeforeEntityPersistedEvent

public function prePostChargeActuelle(BeforeEntityPersistedEvent $event){

    $entity=$event->getEntityInstance();

    $user = $this->getUser();
    $userRole = $user->getRole();

    if ($entity instanceof Operateur) {

           if($userRole == "ROLE_EDITOR){
                    $entity->setValidate(false);
             }
    }

And to prevent these non validate operators to be displayed in your crud for the wrong user , create in your repository :

 public function getValidateOperator($user,$role)
    {
      if($role == "ROLE_ADMIN"){
        return $this->createQueryBuilder('t')
          ->select('t')
          ->andWhere('t.validate = false')
          ->orderBy('t.id', 'DESC')
          ->setMaxResults(5)
          ->getQuery()
          ->getResult()
          ;
      }elseif($role == "ROLE_EDITOR"){
        return $this->createQueryBuilder('t')
        ->select('t')
        ->andWhere('t.validate = false')
        ->orderBy('t.id', 'DESC')
        ->setMaxResults(5)
        ->getQuery()
        ->getResult()
          ;
      }

Add this in your crudController the field to display it:

TextField::new('getNonValidateOperator'),

So you have your validate object visible only by editor or whatever other role you want instead, and the no validate only visible by admin, which will have to validate it. My code maybe contain some mistake and can be way better/clean, I did it quickly. Feel free to ask me if you dont understand something !

Cephalopodius
  • 117
  • 1
  • 10
  • Hi Cephalopodius, Thanks a lot for your answer! I wanted to implement a system with temporary operator tables in the database to really separate validated and non validated data. That is why I wanted to retrieve edit form data to be able to persist them in another table. I am not sure if this option would work for me because I need to send to the front app all Operators, including those that would be potentially "Not validated' because they would contain the previous data. So not sure I can manage this with a boolean. Do you know how to create a customized update btn in EasyAdmin? – Atalaia Jul 30 '21 at 08:32
  • Hi Atalaia, I'm not sure to understand what you are really aiming for. I dont understand why you really need 2 table (close to be identical by the way). You can send all your data, but choose to only display only some , filter by the current user connected. – Cephalopodius Jul 30 '21 at 08:51
  • If you reallly need 2 table, just make a new entity with a new crudController for this. Use an EntityPersistedEvent to make a copy in your other table , wih a validate field on false. – Cephalopodius Jul 30 '21 at 09:00