一意な文字列作るよ、"Uid"

[Symfony Component Advent Calendar 2023](https://qiita.com/advent-calendar/2023/symfony-component)の8日目の記事です。


## 一意な文字列作るよ、"Uid"

[Uid](https://symfony.com/doc/current/components/uid.html)は、いろいろな種類のUID、一意な文字列を作成します。Symfony以外でも使えます。

### インストール

```shell
composer require symfony/uid
```

### UUID

[UUID](https://ja.wikipedia.org/wiki/UUID)にはいくつかの種類があります。`Uuid`クラスはそれぞれの種類の`Uuid` オブジェクトを返すスタティックなメソッドを用意しています。オブジェクトはそれぞれの種類ごとに用意されており、`Uuid` クラスを継承しています。オブジェクトにはそれぞれの形式に変換するメソッドが用意されています。

```php

use Symfony\Component\Uid\Uuid;

$v1 = Uuid::v1();

/*
Symfony\Component\Uid\UuidV1^ {#335
  #uid: "93056510-9300-11ee-b590-3f9709f8f317"
  toBase58: "K9yiBFUCkY8UmVSvyjij7C"
  toBase32: "4K0NJH14R027QBB41ZJW4ZHWRQ"
  time: "2023-12-04 23:55:17.095144 UTC"
}
*/

$v1->toBinary(); // バイナリ型
$v1->toBase31(); // Base32
$v1->toBase58(); // Base58
$v1->toRfc4122(); // RFC4122
$v1->toHex(); // HEX

$v4 = Uuid::v4();
/*
Symfony\Component\Uid\UuidV4^ {#317
  #uid: "a7054aef-45ce-4f19-9c35-8515b40d5d2a"
  toBase58: "MdDZoW61MwenJcs373WF2H"
  toBase32: "570N5EYHEE9WCSRDC52PT0TQ9A"
}
*/

// 以下それぞれのバージョンの呼び方、それぞれUuid{バージョン}オブジェクトを返す。

$v6 = Uuid::v6(); // 1ee93017-5d8c-6468-80cc-a7456e80f6a3
$v7 = Uuid::v7(); // 018c374a-9de5-704d-8935-8e598e1d3834

// v3, v5
$name = 'hogehoge';
$v3 = Uuid::v3($v4, $name); // ed22e12a-60f3-3b86-a8e2-f860b3dc5cc8
$v5 = Uuid::v5($v4, $name); // 4f443fd8-57b0-5f2c-a566-6ec4c055d79d

```

文字列からの`Uuid`オブジェクトの生成も可能です。

```php
$uuid = Uuid::fromString('a7054aef-45ce-4f19-9c35-8515b40d5d2a');
```

#### UuidFactory
Symfonyの場合、`config/packages/uid.yaml`に設定を記載すれば、`UuidFactory`をDIして自動で指定のバージョンのUUIDを作成できます。

```yaml:config/packages.uid.yaml
# config/packages/uid.yaml
framework:
    uid:
        default_uuid_version: 6
        name_based_uuid_version: 5
        name_based_uuid_namespace: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
        time_based_uuid_version: 6
        time_based_uuid_node: 121212121212
```

```php
use Symfony\Component\Uid\Factory\UuidFactory;

class SomeService
{
    public function __construct(private readonly UuidFactory $uuidFactory)
    {
    }

    public function hoge()
    {
        $uuid = $this->uuidFactory()->create(); // v6で生成
        $this->uuidFactory->nameBased(''); // v5で生成
    }
}
```

#### Entity

Doctrineを使っている場合は、プロパティの方に`Uuid`が使えます。PKにする際は自動生成も可能です。

```php
#[ORM\Entity(repositoryClass: ItemRepository::class)]
class Item
{
    #[ORM\Id]
    #[ORM\Column(type: UuidType::NAME, unique: true)]
    #[ORM\GeneratedValue(strategy: 'CUSTOM')]
    #[ORM\CustomIdGenerator(class: 'doctrine.uuid_generator')]
    private ?Uuid $id;
}
```

### ULID

`UUID`の他に[ULID](https://github.com/ulid/spec)も生成可能です。上記`Uuid`でやったこと全てができます。ただ、種類がないので生成がスタティックなメソッドではなく、直接newします。

```php
use Symfony\Component\Uid\NilUlid;

$ulid = new Ulid();
/*
Symfony\Component\Uid\Ulid^ {#312
  #uid: "01HGVNFQ75YTTEFZA4FX338GGK"
  toBase58: "1C5vcP9thGAhNddxs2Ab94"
  toRfc4122: "018c3757-dce5-f6b4-e7fd-447f46344213"
  time: "2023-12-05 00:19:14.277 UTC"
}
*/

$ulid = Ulid::fromString('01HGVNFQ75YTTEFZA4FX338GGK'); // 文字列から生成

// それぞれの型に変換
$ulid->toBinary();
$ulid->toBase32();
$ulid->toBase58();
$ulid->toRfc4122();
$ulid->toHex();
```

#### UlidFactory

```php
use Symfony\Component\Uid\Factory\UlidFactory;

class SomeService
{
    public function __construct(private readonly UlidFactory $ulidFactory)
    {
    }

    public function hoge()
    {
        $uuid = $this->ulidFactory()->create();
    }
}
```

#### Entity
```php
#[ORM\Entity(repositoryClass: ItemRepository::class)]
class Item
{
    #[ORM\Id]
    #[ORM\Column(type: UlidType::NAME, unique: true)]
    #[ORM\GeneratedValue(strategy: 'CUSTOM')]
    #[ORM\CustomIdGenerator(class: 'doctrine.ulid_generator')] <- ulid_generatorになってるので注意
    private ?Ulid $id;
}
```

## まとめ

今回は`Uid`をご紹介しました。一意な文字列の生成が簡単に作成できるので、ファイル名やslug、ユニークコードとして重宝します。