Unique Validation - How to implement RepositoryInterface

Hallo everybody,

at github I found a validator to make sure, that a variable is unique. After implementation I get the error “The option “repository” must implement RepositoryInterface” in the frondend.

In my model the variable to validate looks like that:

/**        
  * 
  * @Flow\Validate(type="Itoop.Customers:Unique", options={ "repository"="CustomerRepository",  "propertyName"="company" })
  * @var string
  */
protected $company;

In the validator I find this:

 if (!$repository instanceof \TYPO3\Flow\Persistence\RepositoryInterface) {
            throw new \TYPO3\Flow\Validation\Exception\InvalidValidationOptionsException('The option "repository" must implement RepositoryInterface.', 1336499435);
        }

Can anybody give a hint to me how to implement the RepositoryInterface?
Thank you!

Maybe you have to use the fully qualified class name of CustomerRepository?
Did you use the “use namespace\ofMy\Repository” statement?

Hallo Philipp,
yes, I used the “use \Itoop\Customers\Domain\Repository\CustomerRepository;” statement in the model.
I also tried different annotations in my model, too, for example like this:

@Flow\Validate(type="Itoop.Customers:Unique", options={ "repository"="\Itoop\Customers\Domain\Repository\CustomerRepository", "propertyName"="company" })

I think it should be “use Itoop\Customers\Domain\Repository\CustomerRepository;” without the leading \

Oh, yes, of course. I tried that without success.

Plz debug the isValid of the validator. It seems like the options you apply are strings and no one is making an instance of your repository. Maybe you have to make an instance of the repository (using the objectManager) first.

Heh seems you have found a pretty old one there. There’s one in the core which is pretty flexible you will probably have more luck using.

http://flowframework.readthedocs.org/en/stable/TheDefinitiveGuide/PartV/ValidatorReference.html?highlight=UniqueEntityValidator#uniqueentityvalidator
https://github.com/neos/flow/blob/master/Classes/TYPO3/Flow/Validation/Validator/UniqueEntityValidator.php

1 Like

Good morning Aske,
thank you. This Validator is to validate an entity - I just want to validate, that there’s only one object with the same “company name”.
Now I wrote my own validator, which is completely sufficient for my special case:

<?php
namespace Itoop\Customers\Validation\Validator;

use TYPO3\Flow\Annotations as Flow;


class UniqueCustomerValidator extends \TYPO3\Flow\Validation\Validator\AbstractValidator {
  
    /**
      * @Flow\Inject
      * @var \Itoop\Customers\Domain\Repository\CustomerRepository
      */
     protected $customerRepository;  
     
     
    /**
     * This validator always needs to be executed even if the given value is empty.
     * See AbstractValidator::validate()
     *
     * @var boolean
     */
    protected $acceptsEmptyValues = FALSE;    
             
  
    /**
     * @param mixed $value The value that should be validated
     * @return void
     * @throws \TYPO3\Flow\Validation\Exception\InvalidValidationOptionsException
     *
     * 
     */
    protected function isValid($value) {
                
        if(empty($value)) {
            $this->addError('Dieses Feld darf nicht leer sein.', 1456984915);    
        }
        
        $query = $this->customerRepository->createQuery();
        $numberOfResults = $query->matching($query->equals('company', $value))->count();
        if ($numberOfResults > 0) {
            $message = "Eine Firma mit dem Namen \"$value\" existiert schon.";
            $this->addError($message, 1456983326);
            
        }
    }
}
?>

Thank you all for your help.

Hi all,

the snippet was quite old indeed. But it should work nevertheless. It was just meant to be used in conjunction with the Flow Form Framework where it’s possible to use complex types in validator options.

I’ve just updated the gist to allow class names, too.
But using your custom validator makes a lot of sense here anyways.

PS: One more remark. If a company name must be unique, you might want to make it part of the identity (or at least specify a unique constraint like @ORM\Table(uniqueConstraints={@ORM\UniqueConstraint(name="company_unique", columns={"company"})})

Hi Bastian,
I tried your new version with these different annotations in the model:

@Flow\Validate(type="Itoop.Customers:Unique", options={ "repository"="CustomerRepository",  "propertyName"="company" })
@Flow\Validate(type="Itoop.Customers:Unique", options={ "repository"="customerRepository",  "propertyName"="company" })
@Flow\Validate(type="Itoop.Customers:Unique", options={ "repository"="Itoop\Customers\Repository\CustomerRepository",  "propertyName"="company" })

I always get a whitescreen and the apache-logfile says something like that:

PHP Fatal error:  Class 'CustomerRepository' not found in /var/www/html/customers/Data/Temporary/Development/Cache/Code/Flow_Object_Classes/Itoop_Customers_Validation_Validator_UniqueValidator.php on line 19

Line 19 is this:

$repository = new $this->options['repository']()

Although maybe it’s a trivial question: Which exact annotation options do I have to use? I think the propertyName should be correct, but at the repository I’m not sure.

PS.: If the class “customer” in my model is already marked as entity like the following - the property company should be part of the identity already, should’nt it?

<?php
namespace Itoop\Customers\Domain\Model;

/*
 * This file is part of the Itoop.Customers package.
 */

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use TYPO3\Flow\Annotations as Flow;
use Doctrine\ORM\Mapping as ORM;



/**
 * @Flow\Entity
 */
class Customer 
{
    
     
    /**
     * @var string
     */
    protected $customernumber;

    /**
      * @var string
      * @Flow\Validate(type="Itoop.Customers:Unique", options={ "repository"="CustomerRepository",  "propertyName"="company" })
     */
    protected $company;

I think the problem is the same. If you use the validator via annotation, you can’t pass php objects. Just plain strings.

If there is no typeconverter you have to create an instance of your repository on your own as shown in the new Gist.

I didn’t test it, but it should work if you specify the Fully Qualified Classname like:

@Flow\Validate(type="Itoop.Customers:Unique", options={ "repository"="Itoop\\Customers\\Repository\\CustomerRepository",  "propertyName"="company" })

But are you sure that that’s the right namespace? It should probably be Itoop\Customers\Domain\Repository\CustomerRepository.

No, for this you’ll have to add a @Flow\Identity annotation

I tried it with Itoop\Customers\Repository\CustomerRepository as namespace - so I think I forgot Domain\ in the namespace, sorry :confused:
I´ll try it later with the correct namespace Itoop\Customers\Domain\Repository\CustomerRepository.

Ah ok, thank you for the hint!

Yeees…with the correct namespace now it works :grinning: :+1:

Thank you very much.

Hey Andre,

Which is exactly what the unique entity validator does, hence why I mentioned it to you. You don’t need to write a custom validator to achieve what you need.

Here’s an example where it validates that the domain property of a created Domain object is unique:
https://github.com/neos/neos/blob/master/Classes/TYPO3/Neos/Controller/Module/Administration/SitesController.php#L328

Good morning Aske,
I think I didn’t understood exactly at first, what this validator does. You’re right, with…

@Flow\Validate(argumentName="$newCustomer", type="UniqueEntity")

…in my controller it works for me, too :ok_hand: :clap:

I think if you don’t need to initiate the validator in the model this is probably the easiest way.
Thank you!