Revision: Thu, 25 Apr 2024 11:06:45 GMT

Typecasting

Cycle ORM v1.x has the only ability to add custom column types via Column wrappers. Moreover, it doesn't have an ability to use proper type casting for autogenerated values on a database side: lastInsertID couldn't have proper type after inserting a new record to the database, and it could make some problem with strict type code. Since v2.x you can use more configurable way to use custom types.

Schema typecast handlers

Cycle ORM v2.x has a new key SchemaInterface::TYPECAST_HANDLER in a schema declaration.

php
$schema = new Schema([
    User::class => [
        // ..
        Schema::TYPECAST_HANDLER => //...
    ],
]);

By default, Schema::TYPECAST_HANDLER has null value. In this case the ORM uses default typecast handler - Cycle\ORM\Parser\Typecast. You can define custom handler or array of handlers (int might be class string or handler alias (in this case the ORM will try to load class from application container)).

php
$schema = new Schema([
    User::class => [
        // ..
        Schema::TYPECAST_HANDLER => App\MyCustomTypecast::class
    ],
    Post::class => [
        // ..
        Schema::TYPECAST_HANDLER => [
            \Cycle\ORM\Parser\Typecast::class,
            App\UuidTypecast::class,
            'carbon_typecast'
        ]
    ],
]);

Array of handlers will be wrapped in to Cycle\ORM\Parser\CompositeTypecast and then applied in order they were set.

Custom typecast example

Handler class should implement at least one of presented interfaces:

  • \Cycle\ORM\Parser\CastableInterface
  • \Cycle\ORM\Parser\UncastableInterface
php
use Cycle\ORM\Parser\CastableInterface;
use Cycle\ORM\Parser\UncastableInterface;

final class UuidTypecast implements CastableInterface, UncastableInterface
{
    private array $rules = [];

    public function __construct(
        private DatabaseInterface $database
    ) {
    }

    public function setRules(array $rules): array
    {
        foreach ($rules as $key => $rule) {
            if ($rule === 'uuid') {
                unset($rules[$key]);
                $this->rules[$key] = $rule;
            }
        }

        return $rules;
    }

    public function cast(array $values): array
    {
        foreach ($this->rules as $column => $rule) {
            if (! isset($values[$column])) {
                continue;
            }

            $values[$column] = Uuid::fromString($values[$column]);
        }

        return $values;
    }

    public function uncast(array $values): array
    {
        foreach ($this->rules as $column => $rule) {
            if (! isset($values[$column]) || !$values[$column] instanceof UuidInterface) {
                continue;
            }

            $values[$column] = $values[$column]->toString();
        }

        return $values;
    }
}

Attribute typecast handlers

Finally, you should add typecast attribute to property which should processed by UuidTypecast.

php
use Cycle\Annotated\Annotation\Entity;
use Ramsey\Uuid\UuidInterface;

#[Entity(
    typecast: [
        \Cycle\ORM\Parser\Typecast::class,
        App\UuidTypecast::class,
        'carbon_typecast'
    ]
)]
class User
{
    #[Cycle\Column(type: 'string', typecast: 'uuid')]
    public UuidInterface $uuid;
}
Edit this page