1

I am using EasyAdmin in my SF 3.3 project but I need to achieve something different from how EasyAdmin has been built for. Take a look at the following picture:

enter image description here

As you might notice a user can be in more than one GroupingRole. Having that information the challenge is:

  • Check if the user has been assigned to any other GroupingRole
  • If the criteria meets the condition then show a warning message saying "The user A is already assigned to GroupingRole A" and prevent the record to be created. (this message could be in a popup, a javascript alert or an alert from Bootstrap - since EA already uses it)
  • When the admin click once again on "Save changes" the record should be created.

What I want to achieve with this approach is to alert the admin that the user is already to any other group but not stop him for create the record.

I have achieve some part of it already by override the prePersist method for just that entity (see below):

class AdminController extends BaseAdminController
{
    /**
     * Check if the users has been assigned to any group
     */
    protected function prePersistGroupingRoleEntity($entity)
    {
        $usersToGroupRoleEntities = $this->em->getRepository('CommonBundle:UsersToGroupRole')->findAll();
        $usersToGroupRole         = [];

        /** @var UsersToGroupRole $groupRole */
        foreach ($usersToGroupRoleEntities as $groupRole) {
            $usersToGroupRole[$groupRole->getGroupingRoleId()][] = $groupRole->getUsersId();
        }

        $usersInGroup = [];

        /** @var Users $userEntity */
        foreach ($entity->getUsersInGroup() as $userEntity) {
            foreach ($usersToGroupRole as $group => $users) {
                if (\in_array($userEntity->getId(), $users, true)) {
                    $usersInGroup[$group][] = $userEntity->getId();
                }
            }
        }

        $groupingRoleEnt = $this->em->getRepository('CommonBundle:GroupingRole');
        $usersEnt        = $this->em->getRepository('CommonBundle:Users');

        $message = [];
        foreach ($usersInGroup as $group => $user) {
            foreach($user as $usr) {
                $message[] = sprintf(
                    'The user %s already exists in %s group!',
                    $usersEnt->find($usr)->getEmail(),
                    $groupingRoleEnt->find($group)->getName()
                );
            }
        }
    }
}

What I don't know is how to stop the record to be created and instead show the warning just the first time the button is clicked because the second time and having the warning in place I should allow to create the record.

Can any give me some ideas and/or suggestions?

UPDATE: adding entities information

In addition to the code displayed above here is the entities involved in such process:

/**
 * @ORM\Entity
 * @ORM\Table(name="grouping_role")
 */
class GroupingRole
{
    /**
     * @ORM\Id
     * @ORM\Column(name="id", type="integer",unique=true,nullable=false)
     * @ORM\GeneratedValue
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="role_name", type="string", nullable=false)
     */
    private $name;

    /**
     * @var string
     *
     * @ORM\Column(name="role_description", type="string", nullable=false)
     */
    private $description;

    /**
     * @var ArrayCollection
     *
     * @ORM\ManyToMany(targetEntity="Schneider\QuoteBundle\Entity\Distributor", inversedBy="groupingRole")
     * @ORM\JoinTable(name="grouping_to_role",
     *   joinColumns={
     *     @ORM\JoinColumn(name="grouping_role_id", referencedColumnName="id")
     *   },
     *   inverseJoinColumns={
     *     @ORM\JoinColumn(name="DistributorID", referencedColumnName="DistributorID", nullable=false)
     *   }
     * )
     *
     * @Assert\Count(
     *      min = 1,
     *      minMessage = "You must select at least one Distributor"
     * )
     */
    private $distributorGroup;

    /**
     * @var ArrayCollection
     *
     * @ORM\ManyToMany(targetEntity="CommonBundle\Entity\Users", inversedBy="usersGroup")
     * @ORM\JoinTable(name="users_to_group_role",
     *   joinColumns={
     *     @ORM\JoinColumn(name="grouping_role_id", referencedColumnName="id")
     *   },
     *   inverseJoinColumns={
     *     @ORM\JoinColumn(name="users_id", referencedColumnName="users_id", nullable=false)
     *   }
     * )
     *
     * @Assert\Count(
     *      min = 1,
     *      minMessage = "You must select at least one user"
     * )
     */
    private $usersInGroup;

    /**
     * Constructor
     */
    public function __construct()
    {
        $this->distributorGroup = new ArrayCollection();
        $this->usersInGroup     = new ArrayCollection();
    }
}

/**
 * @ORM\Entity()
 * @ORM\Table(name="users_to_group_role")
 */
class UsersToGroupRole
{
    /**
     * @var int
     *
     * @ORM\Id()
     * @ORM\Column(type="integer",nullable=false)
     * @Assert\Type(type="integer")
     * @Assert\NotNull()
     */
    protected $usersId;

    /**
     * @var int
     *
     * @ORM\Id()
     * @ORM\Column(type="integer", nullable=false)
     * @Assert\Type(type="integer")
     * @Assert\NotNull()
     */
    protected $groupingRoleId;
}
ReynierPM
  • 17,594
  • 53
  • 193
  • 363

1 Answers1

1

A little example by using form validation approach in EasyAdminBundle:

class AdminController extends EasyAdminController
{
    // ...

    protected function create<EntityName>EntityFormBuilder($entity, $view)
    {
        $builder = parent::createEntityFormBuilder($entity, $view);

        $builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
            $data = $event->getData();

            $flag = false;
            if (isset($data['flag'])) {
                $flag = $data['flag'];
                unset($data['flag']);
            }
            $key = md5(json_encode($data));

            if ($flag !== $key) {
                $event->getForm()->add('flag', HiddenType::class, ['mapped' => false]);
                $data['flag'] = $key;
                $event->setData($data);
            }
        });

        return $builder;
    }

    protected function get<EntityName>EntityFormOptions($entity, $view)
    {
        $options = parent::getEntityFormOptions($entity, $view);

        $options['validation_groups'] = function (FormInterface $form) {
            if ($form->has('flag')) {
                return ['Default', 'CheckUserGroup'];
            }

            return ['Default'];
        };

        $options['constraints'] = new Callback([
            'callback' => function($entity, ExecutionContextInterface $context) {
                // validate here and adds the violation if applicable.

                $context->buildViolation('Warning!')
                    ->atPath('<field>')
                    ->addViolation();
            },
            'groups' => 'CheckUserGroup',
        ]);

        return $options;
    }
}

Note that PRE_SUBMIT event is triggered before the validation process happen.

The flag field is added (dynamically) the first time upon submitted the form, so the validation group CheckUserGroup is added and the callback constraint do its job. Later, the second time the submitted data contains the flag hash (if the data does not changes) the flag field is not added, so the validation group is not added either and the entity is saved (same if the callback constraint does not add the violation the first time).

Also (if you prefer) you can do all this inside a custom form type for the target entity.

yceruto
  • 9,230
  • 5
  • 38
  • 65
  • this is working like a charm but I am having only one "small" issue, what happen if I add users: `u1` and `u2`, then click on save, validation triggers and said they exists in another group but after that I add `u3`, then as soon as click on save and because the flag was already set it will pass and skip the validation, any workaround this? – ReynierPM Nov 16 '17 at 18:24
  • 2
    well, you could add information to the flag field like a hash key with the submitted data the first time, later if next time the data changes then the flag field is added again, until it match (meaning no data change) so the flag is skipped and the validation too. – yceruto Nov 16 '17 at 18:45