sirix / cycle-orm-extensions
Practical extensions for Cycle ORM: base repositories, entity traits, typecasts and more
Fund package maintenance!
sirix777
buymeacoffee.com/sirix
Installs: 275
Dependents: 1
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/sirix/cycle-orm-extensions
Requires
- php: ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0
- cycle/orm: ^2.10
- ramsey/uuid: ^4.7
Requires (Dev)
- bamarni/composer-bin-plugin: ^1.8
- cakephp/chronos: ^3.1
- cycle/annotated: ^4.2
- cycle/entity-behavior: ^1.4
- phpunit/phpunit: ^10.5
- sirix/money: ^1.2
- vjik/cycle-typecast: ^3.0
Suggests
- cakephp/chronos: Required for Chronos datetime support
- cycle/annotated: Required for annotated entity support
- cycle/entity-behavior: Required for entity behaviors and lifecycle hooks support
- sirix/money: Required for Money and Currency typecast support
- vjik/cycle-typecast: Required for Typecast support
README
Practical extensions for Cycle ORM: base repositories, entity traits, typecasts, and more.
Overview
This package provides a collection of practical extensions for Cycle ORM, including:
- Entity traits for common functionality (timestamps, UUIDs, etc.)
- Base repository implementations
- Custom typecasts
- Event listeners
- And more
Requirements
- PHP 8.1, 8.2, 8.3, or 8.4
- Cycle ORM 2.10+
- Ramsey UUID 4.7+
Installation
Install the package via composer:
composer require sirix/cycle-orm-extensions
Features
Entity Traits
The package provides several traits for common entity functionality:
Standard Traits
HasChronosCreateTrait- Adds creation timestamp functionalityHasChronosUpdateTrait- Adds update timestamp functionalityHasChronosDeleteTrait- Adds deletion timestamp functionalityHasIdIdentifierTrait- Adds integer ID primary key functionalityHasUuidIdentifierTrait- Adds UUID primary key functionality
Annotated Traits
Annotated versions of the traits that include Cycle ORM annotations:
HasChronosCreateTimestampAnnotatedTrait- Adds creation timestamp with annotationsHasChronosCreateDatetimeAnnotatedTrait- Adds creation datetime with annotationsHasChronosUpdateTimestampAnnotatedTrait- Adds update timestamp with annotationsHasChronosUpdateDatetimeAnnotatedTrait- Adds update datetime with annotationsHasChronosDeleteTimestampAnnotatedTrait- Adds deletion timestamp with annotationsHasChronosDeleteDatetimeAnnotatedTrait- Adds deletion datetime with annotationsHasIdIdentifierAnnotatedTrait- Adds integer ID primary key with annotationsHasUuidIdentifierAnnotatedTrait- Adds UUID primary key with annotationsHasUuidIdentifierStringAnnotatedTrait- Adds UUID primary key (string) with annotationsHasUuidIdentifierByteAnnotatedTrait- Adds UUID primary key (binary) with annotations
Repositories
Base repository implementations for common operations:
AbstractReadRepository- Base repository for read-only operationsAbstractWriteRepository- Base repository for read-write operations
SelectFactory
The SelectFactory is a utility class used by repositories to create Cycle\ORM\Select instances. It ensures that the role passed to the select is a valid entity class implementing EntityInterface.
In your application, you should register SelectFactory in your dependency injection container. Registration example:
use Sirix\Cycle\Extension\Factory\SelectFactory; use Cycle\ORM\ORMInterface; /** @var ORMInterface $orm */ $selectFactory = new SelectFactory($orm);
Repositories provided by this package require SelectFactory in their constructor:
use Sirix\Cycle\Extension\Repository\AbstractReadRepository; use Sirix\Cycle\Extension\Factory\SelectFactory; class MyRepository extends AbstractReadRepository { public function __construct(SelectFactory $selectFactory) { parent::__construct($selectFactory); } protected function getEntityClass(): string { return MyEntity::class; } }
Typecasts
Custom typecasts for various data types, implemented as vjik/cycle-typecast types.
Annotation-based Typecasting
The package supports typecasting via PHP 8 attributes. This allows you to define typecasting logic directly on entity properties.
To use this feature, you need to configure Vjik\CycleTypecast\AttributeTypecastHandler for your entity and use the provided type attributes:
use Cycle\Annotated\Annotation\Entity; use Sirix\Cycle\Extension\Typecast\Uuid\UuidToStringType; use Vjik\CycleTypecast\AttributeTypecastHandler; use Ramsey\Uuid\UuidInterface; #[Entity( typecast: AttributeTypecastHandler::class, )] class User { #[UuidToStringType] private UuidInterface $uuid; }
Available type attributes:
- Arrays:
#[ArrayToDelimitedStringType(delimiter: ',')]- Converts array to string and vice versa.#[EnumArrayToDelimitedStringType(enumClass: MyEnum::class, delimiter: ',')]- Converts array of backed enums to string.#[ArrayToJsonType]- Converts array to JSON string.
- Boolean:
#[BooleanToIntType]- Converts boolean to integer (0 or 1).
- Chronos (CakePHP Chronos):
#[ChronosToTimestampType]- Converts Chronos to UNIX timestamp (string/int).#[ChronosToDateTimeStringType]- Converts Chronos to 'Y-m-d H:i:s' string.#[ChronosToDateStringType]- Converts Chronos to 'Y-m-d' string.
- Currency (Brick\Money):
#[CurrencyType]- ConvertsBrick\Money\Currencyto numeric code.#[CurrencyCodeType]- ConvertsSirix\Money\CurrencyCode(fiat/crypto) to value.
- Money (Brick\Money):
#[MoneyCurrencyCodeType(currencyCode: FiatCurrencyCode::Eur)]- ConvertsBrick\Money\Moneyto string amount using specified currency.#[MoneyCurrencyNumericCodeColumnType(currencyCodeEntityProperty: 'currencyCode')]- ConvertsBrick\Money\Moneyto string amount, using another entity property for currency code.#[MoneyMinorCurrencyCodeType(currencyCode: FiatCurrencyCode::Eur)]- ConvertsBrick\Money\Moneyto integer (minor units).#[MoneyMinorCurrencyNumericCodeColumnType(currencyCodeEntityProperty: 'currencyCode')]- ConvertsBrick\Money\Moneyto integer (minor units), using another entity property for currency code.
- UUID (Ramsey\Uuid):
#[UuidToBytesType]- Converts UUID to binary.#[UuidToStringType]- Converts UUID to string.
Typecast Traits
For common scenarios, the package provides traits that combine property definition, Cycle ORM annotations, and typecasting:
HasUuidIdentifierTypecastTrait- UUID primary key (string 36).HasUuidIdentifierStringTypecastTrait- UUID primary key (explicit string 36).HasUuidIdentifierByteTypecastTrait- UUID primary key (binary 16).HasChronosCreateTimestampTypecastTrait,HasChronosCreateDatetimeTypecastTrait- Creation timestamps.HasChronosUpdateTimestampTypecastTrait,HasChronosUpdateDatetimeTypecastTrait- Update timestamps.HasChronosDeleteTimestampTypecastTrait,HasChronosDeleteDatetimeTypecastTrait- Soft delete timestamps.
Event Listeners
The EventListeners attribute lets you declare Cycle ORM entity listeners directly on the entity class. It injects the configured listeners into the entity schema under SchemaInterface::LISTENERS.
Note: To use listeners, you must install the Cycle Entity Behavior package:
composer require cycle/entity-behavior
You can provide listeners as simple class-strings or as [class, args] tuples. You may also mix both forms in one array. Passing an empty args array [] is equivalent to specifying the class directly; the attribute normalizes this for you.
Simple listeners (class-strings only)
<?php use Sirix\Cycle\Extension\Behavior\EventListeners; use Sirix\Cycle\Extension\Listener\ChronosCreateListener; use Sirix\Cycle\Extension\Listener\ChronosUpdateListener; #[EventListeners(listeners: [ ChronosCreateListener::class, ChronosUpdateListener::class, ])] final class User {}
Listeners with constructor arguments (mixed usage)
<?php use Sirix\Cycle\Extension\Behavior\EventListeners; use Sirix\Cycle\Extension\Listener\ChronosSoftDeleteListener; use App\Cycle\Listener\AuditListener; #[EventListeners(listeners: [ ChronosSoftDeleteListener::class, [AuditListener::class, ['timezone' => 'UTC', 'captureOldValues' => true]], ])] final class User {}
Example listener class consuming the named arguments:
<?php namespace App\Cycle\Listener; final class AuditListener { public function __construct( private readonly string $timezone = 'UTC', private readonly bool $captureOldValues = false, ) {} // Implement handle methods as required by Cycle ORM's listener contracts }
For a complete entity example that uses listeners, see the annotated entity example below.
Usage Examples
The package includes several example files in the src/Example directory that demonstrate how to use its features:
Example Files
- AnnotatedEntityWithAttributesExample.php: Demonstrates how to create an entity with attributes for typecasting, using traits for UUID identifier and Chronos timestamps.
- AnnotatedEntityWithTypecastHandlerExample.php: Demonstrates how to create an entity using a custom
TypecastHandlerclass. - ReadRepositoryExample.php: Demonstrates how to create a read-only repository.
- WriteRepositoryExample.php: Shows how to create a repository with writing capabilities.
Creating an Annotated Entity with Attribute Typecast
<?php declare(strict_types=1); namespace Sirix\Cycle\Extension\Example; use Cycle\Annotated\Annotation\Column; use Cycle\Annotated\Annotation\Entity; use Sirix\Cycle\Extension\Behavior\EventListeners; use Sirix\Cycle\Extension\Domain\Contract\EntityInterface; use Sirix\Cycle\Extension\Entity\Trait\Annotated\Typecast\HasChronosCreateTimestampTypecastTrait; use Sirix\Cycle\Extension\Entity\Trait\Annotated\Typecast\HasChronosDeleteTimestampTypecastTrait; use Sirix\Cycle\Extension\Entity\Trait\Annotated\Typecast\HasChronosUpdateTimestampTypecastTrait; use Sirix\Cycle\Extension\Entity\Trait\Annotated\Typecast\HasUuidIdentifierTypecastTrait; use Sirix\Cycle\Extension\Listener\ChronosCreateListener; use Sirix\Cycle\Extension\Listener\ChronosSoftDeleteListener; use Sirix\Cycle\Extension\Listener\ChronosUpdateListener; use Vjik\CycleTypecast\AttributeTypecastHandler; #[Entity( repository: WriteRepositoryExample::class, table: 'users', database: 'default', typecast: AttributeTypecastHandler::class, )] #[EventListeners( listeners: [ ChronosCreateListener::class, ChronosUpdateListener::class, ChronosSoftDeleteListener::class, ], )] class AnnotatedEntityWithAttributesExample implements EntityInterface { // Include the annotated traits (with attributes for typecasting) use HasUuidIdentifierTypecastTrait; use HasChronosCreateTimestampTypecastTrait; use HasChronosUpdateTimestampTypecastTrait; use HasChronosDeleteTimestampTypecastTrait; // Define additional properties with annotations #[Column(type: 'string')] private string $name; #[Column(type: 'string')] private string $email; public function getName(): string { return $this->name; } public function setName(string $name): void { $this->name = $name; } public function getEmail(): string { return $this->email; } public function setEmail(string $email): void { $this->email = $email; } }
Creating an Annotated Entity with Typecast Handler
<?php declare(strict_types=1); namespace Sirix\Cycle\Extension\Example; use Cycle\Annotated\Annotation\Column; use Cycle\Annotated\Annotation\Entity; use Sirix\Cycle\Extension\Behavior\EventListeners; use Sirix\Cycle\Extension\Domain\Contract\EntityInterface; use Sirix\Cycle\Extension\Entity\Trait\Annotated\HasChronosCreateTimestampAnnotatedTrait; use Sirix\Cycle\Extension\Entity\Trait\Annotated\HasChronosDeleteTimestampAnnotatedTrait; use Sirix\Cycle\Extension\Entity\Trait\Annotated\HasChronosUpdateTimestampAnnotatedTrait; use Sirix\Cycle\Extension\Entity\Trait\Annotated\HasUuidIdentifierAnnotatedTrait; use Sirix\Cycle\Extension\Listener\ChronosCreateListener; use Sirix\Cycle\Extension\Listener\ChronosSoftDeleteListener; use Sirix\Cycle\Extension\Listener\ChronosUpdateListener; #[Entity( repository: WriteRepositoryExample::class, table: 'users_with_handler', database: 'default', typecast: AnnotatedEntityExampleTypecastHandler::class, )] #[EventListeners( listeners: [ ChronosCreateListener::class, ChronosUpdateListener::class, ChronosSoftDeleteListener::class, ], )] class AnnotatedEntityWithTypecastHandlerExample implements EntityInterface { // Include the annotated traits (without attributes for typecasting) use HasUuidIdentifierAnnotatedTrait; use HasChronosCreateTimestampAnnotatedTrait; use HasChronosUpdateTimestampAnnotatedTrait; use HasChronosDeleteTimestampAnnotatedTrait; // Define additional properties with annotations #[Column(type: 'string')] private string $name; #[Column(type: 'string')] private string $email; public function getName(): string { return $this->name; } public function setName(string $name): void { $this->name = $name; } public function getEmail(): string { return $this->email; } public function setEmail(string $email): void { $this->email = $email; } }
Creating a Write Repository
<?php declare(strict_types=1); namespace Sirix\Cycle\Extension\Example; use Cycle\ORM\ORMInterface; use DateTimeInterface; use Sirix\Cycle\Extension\Factory\SelectFactory; use Sirix\Cycle\Extension\Repository\AbstractWriteRepository; class WriteRepositoryExample extends AbstractWriteRepository { public function __construct(ORMInterface $orm, SelectFactory $selectFactory) { parent::__construct($orm, $selectFactory); } /** * Example of finding entities created after a certain date. */ public function findUsersCreatedAfter(DateTimeInterface $date): array { $select = $this->select() ->where('createdAt', '>', $date->getTimestamp()); return $select->fetchAll(); } protected function getEntityClass(): string { return AnnotatedEntityWithAttributesExample::class; } }
Optional Dependencies
The package suggests the following dependencies for additional functionality:
cakephp/chronos: Required for Chronos datetime supportcycle/annotated: Required for annotated entity supportcycle/entity-behavior: Required for entity behaviors and lifecycle hooks supportsirix/money: Required for Money and Currency typecast supportvjik/cycle-typecast: Required for Typecast support
License
This package is licensed under the MIT License - see the LICENSE file for details.