Created
April 17, 2024 11:17
-
-
Save klkvsk/f3ad81c274968144e3e0ac34732f1696 to your computer and use it in GitHub Desktop.
UniqueSafeRepositoryTrait
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Framework\Doctrine; | |
use Doctrine\DBAL\Exception\UniqueConstraintViolationException; | |
use Doctrine\ORM\EntityRepository; | |
use Doctrine\ORM\Persisters\Entity\BasicEntityPersister; | |
trait UniqueSafeRepositoryTrait | |
{ | |
/** | |
* @template T of object | |
* @param array $uniqueData | |
* @param callable():T $factory | |
* @return object | |
* @psalm-return T | |
*/ | |
public function findOrCreate(array $uniqueData, callable $factory): object | |
{ | |
assert($this instanceof EntityRepository); | |
// optimistic read | |
$entity = $this->findOneBy($uniqueData); | |
if ($entity) { | |
return $entity; | |
} | |
// try inserting | |
$entity = $factory(); | |
$this->uniqueSafeInsert($entity); | |
// retry read if write failed | |
$entity = $this->findOneBy($uniqueData); | |
if (!$entity) { | |
throw new \LogicException('Failed to find existing unique entity: ' . json_encode($uniqueData)); | |
} | |
return $entity; | |
} | |
protected function uniqueSafeInsert(object $entity): bool | |
{ | |
assert($this instanceof EntityRepository); | |
$em = $this->getEntityManager(); | |
$classMetadata = $em->getClassMetadata($entity::class); | |
// prepare insert data | |
$em->persist($entity); | |
$em->getUnitOfWork()->computeChangeSet($classMetadata, $entity); | |
// manually insert single entity via separate persister | |
$entityPersister = new BasicEntityPersister($em, $classMetadata); | |
$entityPersister->addInsert($entity); | |
try { | |
$entityPersister->executeInserts(); | |
return true; | |
} catch (UniqueConstraintViolationException $e) { | |
return false; | |
} finally { | |
// cleanup so it won't be persisted on next flush | |
$em->detach($entity); | |
$em->getUnitOfWork()->clearEntityChangeSet(spl_object_id($entity)); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment