Walidacja w Symfony2 – Custom Validation Constraint

Piszę właśnie aplikację do automatycznej obsługi rejestracji, zarządzania i płatności dla strony http://document-management.pl/ i napotkałem pewien problem z walidacją danych. Potrzebowałem sprawdzić w unikalność Stringa w dwóch różnych tabelach. Niestety Doctrine domyślnie tego nie obsługuje więc pośpieszyłem z napisaniem własnego walidatora. Dziarsko przeczytałem “How to create a Custom Validation Constraint“, napisałem walidator i … zonk

Fatal error: Class 'Symfony\Component\Validator\Constraints\UniqueDomain' not found in /home/kirkor/Projects/Beriko Software/document-management.pl/public_html_register/Symfony/vendor/symfony/src/Symfony/Component/Validator/Mapping/Loader/FileLoader.php on line 73


Szybki pogląd sytuacji. Utworzyłem ‘Bundle’ RegistrationBundle w przestrzeni nazw Beriko\DocMgm\RegistrationBundle. Układ katalogów:
/src
-Beriko
–DocMgm
—-RegistrationBundle
—–Validation
——UniqueDomain.php
——UniqueDomainValidator.php

Mój plik /src/Beriko/DocMgm/RegistrationBundle/Resources/config/validation.yml:

Beriko\DocMgm\RegistrationBundle\Entity\CustomerForConfirmation:
  properties:
    name:
      - NotBlank: ~
      - MinLength: 3
    surename:
      - NotBlank: ~
      - MinLength: 3
    username:
      - NotBlank: ~
      - MinLength: 3
    password:
      - NotBlank: ~
      - MinLength: { limit: 8 }
    phone:
      - NotBlank: ~
    domain:
      - NotBlank: ~
      - MinLength: 1
      - MaxLength: 50
      - UniqueDomain: ~

Dla przykładu mój plik /src/Beriko/DocMgm/RegistrationBundle/Validation/UniqueDomain.php:

namespace Beriko\DocMgm\RegistrationBundle\Validation;

use Symfony\Component\Validator\Constraint;

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

	public $message = 'Podana domena jest już zajęta.';
	public $em = null;

	public function getRequiredOptions() {
		return array();
	}

	public function validatedBy() {
		return 'UniqueDomainValidator';
	}

}

Oraz /src/Beriko/DocMgm/RegistrationBundle/Validation/UniqueDomainValidator.php:

namespace Beriko\DocMgm\RegistrationBundle\Validation;

use Symfony\Bridge\Doctrine\RegistryInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
use Symfony\Component\Validator\ConstraintValidator;

class UniqueDomainValidator extends ConstraintValidator {
	/**
	 * @var RegistryInterface
	 */
	private $registry;

	/**
	 * @param RegistryInterface $registry
	 */
	public function __construct(RegistryInterface $registry) {
		$this -> registry = $registry;
	}

	/**
	 * @param string $domain
	 * @param Constraint $constraint
	 * @return bool
	 */
	public function isValid($domain, Constraint $constraint) {
		// customer_for_confirmation.domain check
		$em = $this -> registry -> getEntityManager($constraint -> em);
		$repository = $em -> getRepository('RegistrationBundle:CustomerForConfirmation');
		$result = $repository -> getCustomerByDomain($domain);

		if (0 != count($result)) {
			$this -> setMessage($constraint -> message);

			return false;
		}

		// TODO: add customer.domain check

		return true;
	}

}

Jak widzicie ta klasa jeszcze nie jest skończona – brakuje jeszcze sprawdzenia drugiej tabeli.
I na koniec ostatni plik /src/Beriko/DocMgm/RegistrationBundle/Resources/config/services.yml:

parameters:
  beriko.registration.validator.unique.domain.class: Beriko\DocMgm\RegistrationBundle\Validation\UniqueDomainValidator
  beriko.registration.validator.unique.username.class: Beriko\DocMgm\RegistrationBundle\Validation\UniqueUsernameValidator

services:
  beriko.registration.validator.unique.domain:
    class: %beriko.registration.validator.unique.domain.class%
    arguments: [@doctrine]
    tags:
      - { name: validator.constraint_validator, alias: UniqueDomainValidator }
  beriko.registration.validator.unique.username:
    class: %beriko.registration.validator.unique.username.class%
    arguments: [@doctrine]
    tags:
      - { name: validator.constraint_validator, alias: UniqueUsernameValidator }

Należy pamiętać, że mój walidator działa jako serwis. Potrzebuje do działania wstrzyknąć Doctrine.
Pytanie brzmi – co w tym kodzie jest nie tak, że dostaję wyjątek?
Odpowiedź prosta:

- UniqueDomain: ~

zamień na

- Beriko\DocMgm\RegistrationBundle\Validation\UniqueDomain: ~

i pomyśleć, że straciłem na to tyle czasu…