Skip to content

[Validator] UniqueEntity allow boolean value for repositoryMethod parameter #60441

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
ViPErCZ opened this issue May 16, 2025 · 4 comments
Open

Comments

@ViPErCZ
Copy link

ViPErCZ commented May 16, 2025

Description

I have this code

#[UniqueEntity(
    fields: ['alias'],
    entityClass: ParameterValue::class,
    repositoryMethod: 'findMethod',
    identifierFieldNames: ['id' => 'id'],
)]

and the repository method could be

public function findMethodi(array $criteria): bool
{
        $entity = $this->createQueryBuilder('o')
             ->select('count(o.id)');
            ->andWhere('o.alias = :alias')
            ->setParameter('alias', $criteria['alias'])
            ->getQuery()
            ->getOneOrNullResult()
        ;

        /** @var int $count */
        $count = $qb->getQuery()->getSingleScalarResult();

        return $count > 0;
}

The idea is that I don't need to download the complete data and certainly not hydrate it.
Sometimes just a method like this with COUNT(...) and returning a boolean is enough.
What do you think? Is it OK or would you rather write a new class and your own validator? Or integrate this?
If someone approves, I'll feel free to make a pull request. But if not, I don't want to waste time.
Thank you.

Example

No response

@MatTheCat
Copy link
Contributor

Returning a boolean wouldn’t work for updates since you wouldn’t know if your method returns true because of the entity you’re updating (in which case everything is fine) or another (in which case there is a violation).

@ViPErCZ
Copy link
Author

ViPErCZ commented May 18, 2025

Yes I know. Of course this would require some thought.
For example

$result = $repository->{$constraint->repositoryMethod}(...$arguments);
If repositoryMethod !== 'findBy' , then for example

$result = $repository->{$constraint->repositoryMethod}(...$arguments, $value);

For example, when $value is entity object and identifierFieldNames is 'id':

public function findMethod(array $criteria, object $value): bool
{
        $entity = $this->createQueryBuilder('o')
             ->select('count(o.id)');
            ->andWhere('o.alias = :alias')
            ->andWhere('o.id != :pk')
            ->setParameter('alias', $criteria['alias'])
            ->setParameter('pk', $value->getId())
            ->getQuery()
            ->getOneOrNullResult()
        ;

        /** @var int $count */
        $count = $qb->getQuery()->getSingleScalarResult();

        return $count > 0;
}

This is only example.
I need to figure out how to pass either the object I'm testing or the values ​​and keys for identifierFieldNames to the method from the repositoryMethod parameter and then insert that into the SQL query. So that I can eliminate the fact that when it comes to updates, I find the correct data. And I can then make a decision based on the boolean value.

In this example above, just a COUNT(id) SQL query is enough, instead of SELECT *. It seemed more optimal to me. But this validator doesn't allow this. If it makes sense, I'd be happy to try a pull request. ;-)
Thank you very much.

@wkania
Copy link
Contributor

wkania commented May 19, 2025

For example, when $value is entity object and identifierFieldNames is 'id':

If $value is an entity object, you don't need to use the identifierFieldNames option. Doctrine knows by itself what the identifier is in such a case. This option is required only during updates when $value is not an entity.

Is hydration really that costly? If the entities are too large, it's probably because the database was poorly designed or Doctrine shouldn't be used in that part of the code.

This solution would only make sense when adding new records. It’s enough to create a method like the one you created and then return something or nothing. However, I don’t think it requires additional code. Anyone who has performance issues can define such a method in the repository and provide its name.

@ViPErCZ
Copy link
Author

ViPErCZ commented May 20, 2025

Hi, it probably doesn't matter if it's a Doctrine entity or not. But I understand that identifierFieldNames is only specified if it's not a Doctrine entity. My mistake for writing it that way.

My idea comes from real usage. For example, we have it over an entity where there is a Uuid and 8x scalar column + one OneToOne relation.

It's nothing major. But I still think that the classic SELECT * + hydration will be a little less efficient than COUNT(*).

But yes, maybe that's an unnecessary hammer to a mosquito.
Of course, nothing prevents me from writing something of my own... UniqueEntityCallback validator and passing original data according to which I can find duplicates in the DB. Some parts will be identical to the UniqueEntity validator, but that doesn't matter. The logic will be to pass the name of the callback method in the appropriate repository, which will be passed as a class-string, just like in the case of UniqueEntity.
The idea was to have it all "under one roof" and of course contribute to open source ;-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants