1
votes

I'm using symfony 2.8 and FOSUserBundle. I want to allow admins to edit users' usernames and emails. If the new username or email is already taken then the database gives an error

SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry

which is good, but I don't know how to communicate that back to the admin who tried to change it to tell them what went wrong (the production version of the app will just give an error 500). What I want to do is show an error message of some kind (preferable like the one FOSUserBundle has in its forms) to say the username (or email) is taken.

The relevant portions of the form is built here:

    $userManager = $this->get('fos_user.user_manager');

    $user = $userManager->findUserBy(array('id' => $id));

    $form = $this->createFormBuilder()
          ->add('username', TextType::class, array(
              'label' => 'Username',
              'data' => $user->getUsername(),
          ))
          ->add('email', EmailType::class, array(
              'label' => 'Email',
              'data' => $user->getEmail(),
          ))
          ->getForm();

and the database is handled here:

    if ($form->isSubmitted() and $form->isValid()) {

        // set new username if different
        $newUsername = $form['username']->getData();
        if ($user->getUsername() !== $newUsername) {
            $user->setUsername($newUsername);
        }

        // set new email if different
        $newEmail = $form['email']->getData();
        if ($user->getEmail() !== $newEmail) {
            $user->setEmail($newEmail);
        }

        $userManager->updateUser($user);
    }

I have tried a number of things, like also setting username_canonical and email_canonical, or adding @UniqueEntity in my User.php class, but they haven't helped (which makes sense since the error is correct - I just can't translate it into a useful message).

1
if you uses FOSUser them look how the register method do it.abdiel
Did you solve your problem?sonja

1 Answers

0
votes

If you doesn't want override anymore for make some validation, you need to implement an EventListener that catch the exceptions of your need by listening on the onKernelResponse event.

DBALExceptionResponseListener.php

// src/AcmeBundle/EventListner/DBALExceptionResponseListener.php

<?php

namespace AcmeBundle\EventListener;

use Symfony\Component\HttpFoundation\RedirectResponse;
use Doctrine\DBAL\DBALException;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpFoundation\Router\RouterInterface;

class DBALExceptionResponseListener
{
    public function __construct(SessionInterface $session, RouterInterface $router) 
    {
        $this->session = $session;
        $this->router  = $router;
    }

    /**
     * @param GetResponseForExceptionEvent $event
     */
    public function onKernelResponse(GetResponseForExceptionEvent $event)
    {
        $request = $event->getRequest();
        $exception =  $event->getException();
        $message = $exception->getMessage();

        // Maybe some checks on the route
        if ($request->get('_route') !== 'your_route' || $request->headers->get('referer') !== 'your_referer') {
            return;
        }

        // Listen only on the expected exception
        if (!$exception instanceof DBALException) {
            return;
        }

        // You can make some checks on the message to return a different response depending on the MySQL error given.
        if (strpos($message, 'Integrity constraint violation')) {
            // Add your user-friendly error message
            $this->session->getFlashBag()->add('error', 'SQL Error: '.$message);   
        }

        // Create your custom response to avoid the error page.
        $response = new RedirectResponse($this->router->generate('your_route'));

        // Update the Event Response with yours
        $event->setResponse($response);
    }
}

services.yml

# app/config/services.yml

services:
    acme.kernel.listener.dbal_exception_response_listener:
        class: AcmeBundle\EventListener\DBALExceptionResponseListener
        tags:
            - {name: kernel.event_listener, event: kernel.exception, method: onKernelResponse}
        arguments: 
            session: "@session"
            router: "@router"

By looking more at the Exception::$message, you can easily find which property causes the problem.

The most common message contains something like :

... column 'propertyname' cannot be null ...