I know I should use services and patterns to refactor this controller action, but I can't imagine one that could fit/be appropriate for this problem.
It seems a strategy or template, but a strategy is supposed to be changed if needed; in my case, it should be always the same:
- Get the customer
- If doesn't exist store the error key (and dispatch/redirect)
- Otherwise continue
- Get current agent permission
- If special privileges, just continue
- Otherwise, check customer agent against current logged agent. In case of error, store the errore key (and dispatch/redirect)
- Check if customer is enabled, otherwise store the error key (and dispatch/redirect)
- Get customer users
- If there are no users, store the error key (and dispatch/redirect)
- Otherwise continue
- Pick the first user and check his privileges
- In case of errors, store the error key (and dispatch/redirect)
- Success, redirect
Actual code:
public function impersonateAction($id, Request $request)
{
$referer = $this->redirect($request->headers->get('referer'));
/** @var \CommonBundle\Model\CustomerInterface $customer */
$customer = $this->get('common.repository.customer')
->find($id);
/** @var \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher */
$dispatcher = $this->get('event_dispatcher');
$error = false;
// Customer doesn't exist
if (!$error && empty($customer)) {
$error = Impersonate::CUSTOMER_NOT_FOUND_ERROR;
}
// Check current agent privileges
if (!$error && !$this->isGranted(AgentInterface::ROLE_SUPER_AGENT)
&& $customer->getAgent() !== $this->getUser()) {
$error = Impersonate::DENIED_ERROR;
}
// Customer is disabled
if (!$error && !$customer->isEnabled()) {
$error = Impersonate::CUSTOMER_DISABLED_ERRROR;
}
// There are no users for this customer
/** @var \CommonBundle\Entity\Repository\UserRepositoryInterface $repository */
$repository = $this->get('common.repository.user');
if (!$error && !($users = $repository->findByCustomer($customer))) {
$error = Impersonate::NO_USERS_ERROR;
}
// We have a candidate, forward the check to Symfony user checker
if (!$error) {
try {
$this->get('security.user_checker')->checkPreAuth(current($users));
} catch (\Exception $e) {
$error = Impersonate::USER_UNKOWN_ERROR;
if ($e instanceof LockedException) {
$error = Impersonate::USER_LOCKED_ERROR;
}
if ($e instanceof DisabledException) {
$error = Impersonate::USER_DISABLED_ERROR;
}
if ($e instanceof AccountExpiredException) {
$error = Impersonate::USER_ACCOUNT_EXPIRED;
}
}
}
// Dispatch the error and redirect to referer
if ($error) {
$dispatcher->dispatch(
AgentEvents::CUSTOMER_IMPERSONATE_ERROR,
new ImpersonateEvent($this->trans($error))
);
}
// Success
$this->loginUser(
$user,
$this->container->getParameter('agent.impersonate.firewall_name')
);
return $this->redirect($this->generateUrl(
$this->container->getParameter('agent.impersonate.target_path')
));
}