orm: Bug if you want to have an AUTO_INCREMENT id and an UUID
Scenario:
I want to have an AUTO_INCREMENT id
from my DB layer, and also have a hash
as Uuid.
If you have your entity like this:
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
class Foo
{
/**
* @var int
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* @var Uuid
* @ORM\Column(type="uuid", unique=true)
* @ORM\GeneratedValue(strategy="CUSTOM")
* @ORM\CustomIdGenerator(class="Ramsey\Uuid\Doctrine\UuidGenerator")
*/
private $uuid;
// ...
}
When you try to persist a new entity:
$foo = new Foo();
$em->persist($foo);
$em->flush($foo);
Problem:
You will get an error saying:
“detail”: “An exception occurred while executing ‘INSERT INTO db_name.foo (id, hash) VALUES (?, ?)’ with params ["431548bf-1a89-4552-90dd-a1ee89b659f8", "36710515-8595-49c0-86b9-8c504b7fb243"]: Notice: Object of class Ramsey\Uuid\Uuid could not be converted to int”,
I was wondering why this was happening so I went deeper into Doctrine, and inside UnitOfWord::persistNew($class, $entity)
I found something interesting:
$idGen = $class->idGenerator; [line: 895]
is a UuidGenerator
!?
But shoulnd’t it be the generator for the id
property the @ORM\Id | @ORM\GeneratedValue(strategy="IDENTITY")
auto increment by default? Why it seems to be overrite it by the new Ramsey Uuid Generator
?
After a little research I came with the conclusion that Doctrine annotations -in order to determine the idGenerator- it loads by order which is the idGenerator for the entity. Which means the last one (annotation) override the previous one.
I came with this solution:
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
class Foo
{
/**
* @var Uuid
* @ORM\Column(type="uuid", unique=true)
* @ORM\GeneratedValue(strategy="CUSTOM")
* @ORM\CustomIdGenerator(class="Ramsey\Uuid\Doctrine\UuidGenerator")
*/
private $uuid;
/**
* IMPORTANT! This field annotation must be the last one in order to prevent
* that Doctrine will use UuidGenerator as $`class->idGenerator`!
*
* @var int
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
// ...
}
Conclusion: the solution was just moving as last annotation the id
property from the entity which it is actually the real id (as AUTO_INCREMENT int from the DB) and still have the Uuid
from Ramsey\Uuid\Uuid
.
I think we should avoid this ordering problem in our entities files and takes the idGenerator from the property which has @ORM\Id
.
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Reactions: 24
- Comments: 17 (10 by maintainers)
Only identifiers can have
@GeneratedValue
This is currently by design but it could become supported in ORM 3.0. Note that in ORM 3.0 the UUID generator strategy will be removed, please use constructor injection for passing entity (uu)IDs.
That’s true, but actually, an AUTO_INCREMENT can be applied only to one key in a table, but not necessarily the PK.
Consider this example:
So, technically
Only identifiers can have @GeneratedValue
is only for Doctrine by design, because at a lower level(SQL) we could say something like:Any Key can have a @GeneratedValue, but only one per Entity/Table.
In such case, this would allow this logic:
And what if you want UUID as Your IDENTITY and have internal AUTO GENERATED int in entity? It cries: