ULID is falsely stored as UUID in database (vanilla symfony app)

Viewed 163

Yesterday I tried to do what Symfony shouted out in some blog post (https://symfony.com/blog/new-in-symfony-5-2-doctrine-types-for-uuid-and-ulid) but failed hard. I want to store ULID (format "TTTTTTTTTTRRRRRRRRRRRRRRRR") in the database because they are not only sorteable but also contain a timestamp which is just perfect for my application. But when I tell a property to be "type=ulid" then it is stored as UUID (format: "xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx") in the database.

I debugged half day long and got so annoyed by this that I started from scratch and still the problem exists.

Where did I go wrong?

(skip to the ULID heading if you like, the following may look long but 50% of it is just basic stuff)

Symfony

The thing I do over and over again taken from https://symfony.com/doc/5.4/setup.html :

  1. stat shyt # does not exist
  2. composer create-project symfony/skeleton:5.4.* shyt
  3. cd shyt; composer require webapp
  4. Do you want to include Docker configuration from recipes? YES (default)
  5. bin/console about shows Symfony Version 5.4.10 and PHP 7.4

ORM

Taken from https://symfony.com/doc/5.4/doctrine.html :

  1. composer require symfony/orm-pack
  2. composer require --dev symfony/maker-bundle
  3. docker-compose up -d

ERROR: could not find an available, non-overlapping IPv4 address pool among the defaults to assign to the network

So I add some lines to the docker-compose.override.yml file:

networks:
  default:
    ipam:
      config:
        - subnet: 10.1.2.1/24

services:
  database:
  # [...] doctrine/doctrine-bundle stuff [...]
  networks:
    default:
      ipv4_address: 10.1.2.3
  1. In ".env" set DATABASE_URL for the host "10.1.2.3"
  2. bin/console doctrine:database:create (silly but as documented)

Could not create database "app" for connection named default An exception occurred while executing a query: SQLSTATE[42P04]: Duplicate database: 7 ERROR: database "app" already exists

Well, yes. Docker already did this.

  1. The make:entity is postponed until we have ULID capabilities.

ULID

We already lean into https://symfony.com/doc/5.4/components/uid.html (especially section ULIDs) :

  1. composer require symfony/uid
  2. bin/console make:entity Product
  • "someProperty" as "ulid" not nullable
  1. Inspect the Product entity

Looks almost like in the documentation except it has an additional field (the primary key, an integer) and some getter/setter.

  1. bin/console make:migration

Test the ULID entity

In between we use tests to programatically create a DB entry:

  1. composer require phpunit to programatically create a database entry
  2. bin/console --env=test doctrine:migrations:migrate
  3. bin/console --env=test doctrine:database:create
  4. The file "tests/FooTest.php" contains:
<?php

namespace App\Tests;

use App\Entity\Product;
use App\Repository\ProductRepository;
use Doctrine\ORM\EntityManager;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Uid\Ulid;

class FooTest extends KernelTestCase
{
    public function testFoo(): void
    {
        $product = new Product();
        $product->setSomeProperty(new Ulid());
        static::assertNotNull($product->getSomeProperty());

        self::getContainer()->get(ProductRepository::class)
            ->add($product);

        self::getContainer()->get('doctrine.orm.entity_manager')
            ->flush();
    }
}
  1. bin/console --env=test doctrine:query:sql 'TRUNCATE product' just to be sure
  2. bin/phpunit
  3. bin/console --env=test doctrine:query:sql 'SELECT * FROM product'

A UUID is shown instead of a ULID.

Shows ULID instead of UUID in the database

Using ULID as Primary Key

First clean up a bit then do the example shown in https://symfony.com/doc/5.4/components/uid.html#ulids :

  1. rm migrations/* to start again
  2. bin/console --env=test doctrine:database:drop --force
  3. bin/console --env=test doctrine:database:create
  4. bin/console doctrine:database:drop --force
  5. bin/console --env=test doctrine:database:create
  6. Edit "src/Entity/Product.php" to only contain the second ULID example from the documentation:
<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Uid\Ulid;
use App\Repository\ProductRepository;

/**
 * @ORM\Entity(repositoryClass=ProductRepository::class)
 */
class Product
{
    /**
     * @ORM\Id
     * @ORM\Column(type="ulid", unique=true)
     * @ORM\GeneratedValue(strategy="CUSTOM")
     * @ORM\CustomIdGenerator(class="doctrine.ulid_generator")
     */
    private $id;

    public function getId(): ?Ulid
    {
        return $this->id;
    }

    // ...

}

(example from documentation was missing the repository line)

  1. bin/console make:migration
  2. bin/console --env=test doctrine:migrations:migrate
  3. Test is now a bit simpler:
    public function testFoo(): void
    {
        self::getContainer()->get(ProductRepository::class)
            ->add(new Product());

        self::getContainer()->get('doctrine.orm.entity_manager')
            ->flush();
    }
  1. bin/phpunit (Risky is ok)
  2. bin/console --env=test doctrine:query:sql 'SELECT * FROM product'

Again those UUID instead of ULID

Database shows UUID instead of ULID

1 Answers

I'm trying out the ULID as well and followed the same documentation (https://symfony.com/blog/new-in-symfony-5-2-doctrine-types-for-uuid-and-ulid). I noticed you did one thing different in your code compared to the documentation.

In the entity change:

* @ORM\CustomIdGenerator(class="doctrine.ulid_generator")

to

* @ORM\CustomIdGenerator(class=UlidGenerator::class)

And add the following

use Symfony\Bridge\Doctrine\IdGenerator\UlidGenerator;

This should the make the ULID work.

The dump of this would be something like this (sorry this is a dump in the console as I was testing the ulid, but I hope it gives you the information you need):

^ Symfony\Component\Uid\Ulid^ {#842
  #uid: "01G7MCSCANWA2XXZ1NT1TZV6KQ"
  toBase58: "1BoCbKTaPcpbAuQVieEW54"
  toRfc4122: "0181e8cc-b155-e285-defc-35d075fd9a77"
  time: "2022-07-10 15:48:57.813 UTC"
}

In the database it looks as followed: Ulid in mariaDB

I hope this helps.

Related