Question

I use Silex with Form and Validator provider.

I have got a very simple register form :

  • username
  • password
  • confirmation of password

The username must be unique, how to check that ?

This my code :

$form = $app['form.factory']->createBuilder('form')
      ->add('username', 'text', array(
        'constraints' => array(
          new Assert\NotBlank(),
          new Assert\Length(array('min' => 6, 'max' => 20)))
        ))
      ->add('password', 'repeated', array(
        'constraints' => array(
          new Assert\NotBlank(),
          new Assert\Length(array('min' => 6, 'max' => 20))
          )
        ))
      ->getForm();

With Symfony, I know there is the UniqueEntity constraint but it comes with Doctrine and I only use Doctrine DBAL (Silex provider)

I think I can use the Callback constraint but I would like to know if there is a better solution than write a callback with a SQL query... or write my own Constraint object

Thank you

Was it helpful?

Solution

Ok, I decided to create my own Constraint because I can reuse it for email for example. So, this is my implementation :

UniqueEntry.php:

<?php
/**
 * Ensure a value is unique in the database
 *
 * @author Thomas KELLER
 */

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Exception\MissingOptionsException;
use Symfony\Component\Validator\Exception\InvalidArgumentException;

/**
 * @Annotation
 */
class UniqueEntry extends Constraint {

  public $notUniqueMessage = 'This value already exist in the database';
  public $dbal_connection;
  public $table;
  public $field;

  public function __construct($options = null)
  {
    parent::__construct($options);

    if ($this->dbal_connection === null)
      throw new MissingOptionsException(sprintf('The option "dbal_connection" is mandatory for constraint %s', __CLASS__), array('dbal_connection'));

    if (!$this->dbal_connection instanceof \Doctrine\DBAL\Connection)
      throw new InvalidArgumentException(sprintf('The option "dbal_connection" must be an instance of Doctrine\DBAL\Connection for constraint %s', __CLASS__));

    if ($this->table === null)
      throw new MissingOptionsException(sprintf('The option "table" is mandatory for constraint %s', __CLASS__), array('table'));

    if (!is_string($this->table) OR $this->table == '')
      throw new InvalidArgumentException(sprintf('The option "table" must be a valid string for constraint %s', __CLASS__));

    if ($this->field === null)
      throw new MissingOptionsException(sprintf('The option "field" is mandatory for constraint %s', __CLASS__), array('field'));

    if (!is_string($this->field) OR $this->field == '')
      throw new InvalidArgumentException(sprintf('The option "field" must be a valid string for constraint %s', __CLASS__));
  }
}

UniqueEntryValidator.php:

<?php
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;

class UniqueEntryValidator extends ConstraintValidator
{
  public function validate($value, Constraint $constraint)
  {
    $query = sprintf('SELECT `%s` FROM `%s` WHERE `%s` = ?',
      $constraint->field,
      $constraint->table,
      $constraint->field);

    $stmt = $constraint->dbal_connection->executeQuery($query, array($value));

    if ($stmt->fetch())
      $this->context->addViolation($constraint->notUniqueMessage);
  }
}

I post my implementation to have suggestion so feel free to comment it ;)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top