diff --git a/docs/en/annotated/entity.md b/docs/en/annotated/entity.md index 75afed3..8d60c80 100644 --- a/docs/en/annotated/entity.md +++ b/docs/en/annotated/entity.md @@ -116,8 +116,7 @@ class Pivot ``` > Read how to use non-incremental primary keys in the Advanced section: -> - [UUID](/docs/en/entity-behaviors/uuid.md) -> - [Snowflake IDs](/docs/en/entity-behaviors/uuid.md#snowflake-ids) +> - [Identifiers](/docs/en/entity-behaviors/identifiers.md) You can use multiple attributes at the same time with shorter syntax: @@ -296,7 +295,7 @@ class User #[Column(type: 'enum', values: UserStatus::class, default: 'active')] private string $status; - + // Or pass a specific enum instance #[Column(type: 'enum', values: UserStatus::Active)] private string $defaultActive; @@ -311,7 +310,7 @@ class User { #[Column(type: 'enum', values: ['active', 'disabled', 'banned'])] private string $status; - + // Mix of strings and enum values #[Column(type: 'enum', values: [UserStatus::Active, 'custom'])] private string $mixedStatus; @@ -337,15 +336,15 @@ class PostgresEntity // PostgreSQL JSONB with indexing support #[Column(type: 'jsonb')] private array $metadata; - + // PostgreSQL timestamp with timezone #[Column(type: 'timestamptz')] private \DateTimeInterface $scheduledAt; - + // PostgreSQL geometric types #[Column(type: 'point')] private string $location; - + // PostgreSQL network address types #[Column(type: 'inet')] private string $ipAddress; @@ -421,7 +420,7 @@ class User #[Column(type: 'primary')] #[GeneratedValue(onInsert: true)] private int $id; - + // Database default timestamp (PostgreSQL: DEFAULT NOW()) #[Column(type: 'datetime', default: 'CURRENT_TIMESTAMP')] #[GeneratedValue(onInsert: true)] @@ -446,7 +445,7 @@ class Document #[Column(type: 'uuid', primary: true)] #[GeneratedValue(beforeInsert: true)] private string $id; - + #[Column(type: 'datetime')] #[GeneratedValue(beforeInsert: true)] private \DateTimeInterface $createdAt; @@ -460,7 +459,7 @@ class Document - Creation timestamps set by ORM - Custom business logic for initial values -> Read more about UUID generation in [Entity Behaviors: UUID](/docs/en/entity-behaviors/uuid.md). +> Read more about identifier generation in [Entity Behaviors: Identifiers](/docs/en/entity-behaviors/identifiers.md). #### 3. PHP-Generated Before Update (`beforeUpdate: true`) @@ -473,11 +472,11 @@ class Article #[Column(type: 'primary')] #[GeneratedValue(onInsert: true)] private int $id; - + #[Column(type: 'datetime')] #[GeneratedValue(beforeInsert: true)] private \DateTimeInterface $createdAt; - + // Automatically updated on every save #[Column(type: 'datetime')] #[GeneratedValue(beforeInsert: true, beforeUpdate: true)] @@ -502,7 +501,7 @@ class Post #[Column(type: 'datetime')] #[GeneratedValue(beforeInsert: true, beforeUpdate: true)] private \DateTimeInterface $lastModified; // Set on create AND update - + #[Column(type: 'datetime')] #[GeneratedValue(beforeInsert: true)] private \DateTimeInterface $createdAt; // Set only on create @@ -603,7 +602,7 @@ class UserPost { #[Column(type: 'integer')] private int $user_id; - + #[Column(type: 'integer')] private int $post_id; } @@ -676,7 +675,7 @@ class Post { #[Column(type: 'primary')] public int $id; - + #[Column(type: 'integer')] #[ForeignKey(target: User::class, action: 'CASCADE')] private int $userId; @@ -699,7 +698,7 @@ class Post { #[Column(type: 'primary')] public int $id; - + #[Column(type: 'integer')] private int $user_id; } diff --git a/docs/en/entity-behaviors/identifiers.md b/docs/en/entity-behaviors/identifiers.md new file mode 100644 index 0000000..1001746 --- /dev/null +++ b/docs/en/entity-behaviors/identifiers.md @@ -0,0 +1,279 @@ +# Identifiers + +The `cycle/entity-behavior-identifier` package provides the ability to use `ramsey/identifier` as various Cycle ORM entity column types. + +## Installation + +> **Note:** Due to a dependency on `ramsey/identifier` this package requires PHP `8.2` or newer. + +Install this package as a dependency using Composer. + +```bash +composer require cycle/entity-behavior-identifier +``` + +## Usage + +The package provides various types of identifiers, for generating unique values or as alternatives to auto-increment +IDs, helping ensure uniqueness and flexibility across an application. + +> **Note: ** Most identifiers encode metadata such as node ID and epoch offset. These values are typically +> derived from the platform or system rather than defined within an entity. Each applicable listener class provides a +> `setDefaults` method to allow these values to be set at an appropriate time within your application. + +For example: + +```php +\Cycle\ORM\Entity\Behavior\Identifier\Listener\SnowflakeGeneric::setDefaults(0, 1_446_940_800_000); +\Cycle\ORM\Entity\Behavior\Identifier\Listener\Uuid1::setDefaults('00000fffffff', 0xffff); +``` + + +### Snowflake Examples + +**Snowflake (Generic):** A flexible Snowflake implementation that generates globally unique, time-ordered 64-bit IDs +without adhering to any specific platform’s conventions, suitable for general distributed systems. + +```php +use Cycle\Annotated\Annotation\Column; +use Cycle\Annotated\Annotation\Entity; +use Cycle\ORM\Entity\Behavior\Identifier; +use Ramsey\Identifier\Snowflake; + +#[Entity] +#[Identifier\SnowflakeGeneric(field: 'id')] +class User +{ + #[Column(type: 'id', primary: true)] + public Snowflake $id; +} +``` + +**Snowflake (Discord):** Implements Discord’s Snowflake format, generating 64-bit IDs that encode a timestamp, +worker ID, and sequence number. Useful when interoperating with Discord’s API or matching its ID structure. + +```php +use Cycle\Annotated\Annotation\Column; +use Cycle\Annotated\Annotation\Entity; +use Cycle\ORM\Entity\Behavior\Identifier; +use Ramsey\Identifier\Snowflake; + +#[Entity] +#[Identifier\SnowflakeDiscord(field: 'id')] +class User +{ + #[Column(type: 'id', primary: true)] + public Snowflake $id; +} +``` + +**Snowflake (Instagram):** Follows Instagram’s Snowflake structure to produce unique, sortable 64-bit IDs suitable for +applications that need compatibility with Instagram-style ID sequences. + +```php +use Cycle\Annotated\Annotation\Column; +use Cycle\Annotated\Annotation\Entity; +use Cycle\ORM\Entity\Behavior\Identifier; +use Ramsey\Identifier\Snowflake; + +#[Entity] +#[Identifier\SnowflakeInstagram(field: 'id')] +class User +{ + #[Column(type: 'id', primary: true)] + public Snowflake $id; +} +``` + +**Snowflake (Mastodon):** Generates IDs compatible with Mastodon’s distributed Snowflake system, encoding time and node +information to ensure uniqueness across instances. + +```php +use Cycle\Annotated\Annotation\Column; +use Cycle\Annotated\Annotation\Entity; +use Cycle\ORM\Entity\Behavior\Identifier; +use Ramsey\Identifier\Snowflake; + +#[Entity] +#[Identifier\SnowflakeMastodon(field: 'id')] +class User +{ + #[Column(type: 'id', primary: true)] + public Snowflake $id; +} +``` + +**Snowflake (Twitter):** Produces 64-bit IDs in the format used by Twitter, encoding timestamp, machine ID, and sequence +number for globally unique, time-sortable identifiers. Ideal for high-throughput distributed systems. + +```php +use Cycle\Annotated\Annotation\Column; +use Cycle\Annotated\Annotation\Entity; +use Cycle\ORM\Entity\Behavior\Identifier; +use Ramsey\Identifier\Snowflake; + +#[Entity] +#[Identifier\SnowflakeTwitter(field: 'id')] +class User +{ + #[Column(type: 'id', primary: true)] + public Snowflake $id; +} +``` + +### ULID Examples + +**ULID (Universally Unique Lexicographically Sortable Identifier):** A 128-bit identifier designed for high uniqueness +and lexicographical sortability. It combines a timestamp component with random data, allowing for ordered IDs that can +be generated rapidly and are human-readable, making it ideal for databases and distributed systems. + +```php +use Cycle\Annotated\Annotation\Column; +use Cycle\Annotated\Annotation\Entity; +use Cycle\ORM\Entity\Behavior\Identifier; +use Ramsey\Identifier\Ulid; + +#[Entity] +#[Identifier\Ulid(field: 'id')] +class User +{ + #[Column(type: 'ulid', primary: true)] + private Ulid $id; +} +``` + +### UUID Examples + +**UUID Version 1 (Time-based):** Generated using the current timestamp and the MAC address of the computer, ensuring +unique identification based on time and hardware. + +```php +use Cycle\Annotated\Annotation\Column; +use Cycle\Annotated\Annotation\Entity; +use Cycle\ORM\Entity\Behavior\Identifier; +use Ramsey\Identifier\Uuid; + +#[Entity] +#[Identifier\Uuid1(field: 'id')] +class User +{ + #[Column(type: 'uuid', primary: true)] + private Uuid $id; +} +``` + +**UUID Version 2 (DCE Security):** Similar to version 1 but includes a local identifier such as a user ID or group ID, +primarily used in DCE security contexts. + +```php +use Cycle\Annotated\Annotation\Column; +use Cycle\Annotated\Annotation\Entity; +use Cycle\ORM\Entity\Behavior\Identifier; +use Ramsey\Identifier\Uuid; + +#[Entity] +#[Identifier\Uuid2(field: 'id')] +class User +{ + #[Column(type: 'uuid', primary: true)] + private Uuid $id; +} +``` + +**UUID Version 3 (Name-based, MD5):** Created by hashing a namespace identifier and name using MD5, resulting in a +deterministic UUID based on input data. + +```php +use Cycle\Annotated\Annotation\Column; +use Cycle\Annotated\Annotation\Entity; +use Cycle\ORM\Entity\Behavior\Identifier; +use Ramsey\Identifier\Uuid; + +#[Entity] +#[Identifier\Uuid3( + field: 'id', + namespace: '6ba7b810-9dad-11d1-80b4-00c04fd430c8', + name: 'example.com', +)] +class User +{ + #[Column(type: 'uuid', primary: true)] + private Uuid $id; +} +``` + +**UUID Version 4 (Random):** Generated entirely from random or pseudo-random numbers, offering high unpredictability +and uniqueness. + +```php +use Cycle\Annotated\Annotation\Column; +use Cycle\Annotated\Annotation\Entity; +use Cycle\ORM\Entity\Behavior\Identifier; +use Ramsey\Identifier\Uuid; + +#[Entity] +#[Identifier\Uuid4(field: 'id')] +class User +{ + #[Column(type: 'uuid', primary: true)] + private Uuid $id; +} +``` + +**UUID Version 5 (Name-based, SHA-1):** Similar to version 3 but uses SHA-1 hashing, providing a different deterministic +UUID based on namespace and name. + +```php +use Cycle\Annotated\Annotation\Column; +use Cycle\Annotated\Annotation\Entity; +use Cycle\ORM\Entity\Behavior\Identifier; +use Ramsey\Identifier\Uuid; + +#[Entity] +#[Identifier\Uuid5( + field: 'id', + namespace: '6ba7b810-9dad-11d1-80b4-00c04fd430c8', + name: 'example.com', +)] +class User +{ + #[Column(type: 'uuid', primary: true)] + private Uuid $id; +} +``` + +**UUID Version 6 (Draft/Upcoming):** An experimental or proposed version focused on improving time-based UUIDs with more +sortable properties (not yet widely adopted). + +```php +use Cycle\Annotated\Annotation\Column; +use Cycle\Annotated\Annotation\Entity; +use Cycle\ORM\Entity\Behavior\Identifier; +use Ramsey\Identifier\Uuid; + +#[Entity] +#[Identifier\Uuid6(field: 'id')] +class User +{ + #[Column(type: 'uuid', primary: true)] + private Uuid $id; +} +``` + +**UUID Version 7 (Draft/Upcoming):** A newer proposal designed to incorporate sortable features based on Unix timestamp, +enhancing performance in database indexing. + +```php +use Cycle\Annotated\Annotation\Column; +use Cycle\Annotated\Annotation\Entity; +use Cycle\ORM\Entity\Behavior\Identifier; +use Ramsey\Identifier\Uuid; + +#[Entity] +#[Identifier\Uuid7(field: 'id')] +class User +{ + #[Column(type: 'uuid', primary: true)] + private Uuid $id; +} +``` diff --git a/docs/en/entity-behaviors/install.md b/docs/en/entity-behaviors/install.md index 5e68462..b7a4579 100644 --- a/docs/en/entity-behaviors/install.md +++ b/docs/en/entity-behaviors/install.md @@ -35,8 +35,8 @@ $container = new Container(); $commandGenerator = new EventDrivenCommandGenerator($schema, $container); $orm = new ORM( - factory: $factory, - schema: $schema, + factory: $factory, + schema: $schema, commandGenerator: $commandGenerator ); ``` @@ -45,6 +45,7 @@ That's it. Now you can use all benefits of this package. ### Available behaviors +- [Identifiers](/docs/en/entity-behaviors/identifiers.md) - [UUID](/docs/en/entity-behaviors/uuid.md) - [CreatedAt and UpdatedAt](/docs/en/entity-behaviors/timestamps.md) - [SoftDelete](/docs/en/entity-behaviors/soft-delete.md) diff --git a/docs/en/entity-behaviors/uuid.md b/docs/en/entity-behaviors/uuid.md index 3148a10..80f0701 100644 --- a/docs/en/entity-behaviors/uuid.md +++ b/docs/en/entity-behaviors/uuid.md @@ -2,6 +2,9 @@ The `cycle/entity-behavior-uuid` package provides the ability to use `ramsey/uuid` as a Cycle ORM property type. +> **Note** This package has been superseded by `cycle/entity-behavior-identifier`. +> Read more about identifier generation in [Entity Behaviors: Identifiers](/docs/en/entity-behaviors/identifiers.md). + ## Installation Install this package as a dependency using Composer. diff --git a/docs/en/manifest.json b/docs/en/manifest.json index 87ae9b4..90fb784 100644 --- a/docs/en/manifest.json +++ b/docs/en/manifest.json @@ -61,6 +61,7 @@ "title": "Entity behaviors", "links": { "entity-behaviors/install": "Install", + "entity-behaviors/identifiers": "Identifiers", "entity-behaviors/uuid": "UUID", "entity-behaviors/timestamps": "Auto Timestamps", "entity-behaviors/soft-delete": "Soft delete", diff --git a/docs/en/readme.md b/docs/en/readme.md index 955bbb1..d28ed77 100644 --- a/docs/en/readme.md +++ b/docs/en/readme.md @@ -39,6 +39,7 @@ Table of Contents * [Security](/docs/en/query-builder/security.md) * Entity behaviors * [Install](/docs/en/entity-behaviors/install.md) + * [Identifiers](/docs/en/entity-behaviors/identifiers.md) * [UUID](/docs/en/entity-behaviors/uuid.md) * [Auto Timestamps](/docs/en/entity-behaviors/timestamps.md) * [Soft delete](/docs/en/entity-behaviors/soft-delete.md)