Type Converters

Type converters let the ORM turn one PHP type into another while it hydrates entity-like input into an entity.

They are useful when incoming data is close to the stored shape but not quite the same as the entity property type. A common example is accepting a string from a request DTO or seed file and assigning it to a DateTime property on an entity.

Type converters are not validators, serializers, relation loaders, or query-builder hooks. They live in the repository and entity-manager path where entity-like payloads become entity instances.

When converters run

Converters can run when the ORM creates or normalizes an entity through repository and entity-manager APIs such as:

  • create()
  • insert()
  • save()
  • update()
  • upsert()
  • soft-delete flows that hydrate entity-like values

The lower-level SQL query builder does not use type converters. Query-builder values are the exact values you pass into the builder.

Built-in conversions

The ORM includes a small default converter set for common cases such as:

  • string to DateTime
  • DateTime to string
  • int to bool
  • bool to int

Custom converters are checked before the built-in converters, so you can override a common conversion when your application needs a stricter rule.

Writing a custom converter

A converter is any class with methods marked by #[TypeConverter].

The method signature is the contract:

  • the first parameter type is the source type
  • the return type is the target type
  • the method receives the value and returns the converted value
<?php

namespace App\Orm\Converters;

use Assegai\Orm\Attributes\TypeConverter;
use DateTime;

final class ScheduleDateConverter
{
  #[TypeConverter]
  public function fromStringToDateTime(string $value): ?DateTime
  {
    $value = trim($value);

    if ($value === '') {
      return null;
    }

    return new DateTime($value);
  }
}

This converter accepts strings such as '2026-04-12 20:00:00' and returns a DateTime instance. It also deliberately turns an empty string into null, which is useful for nullable date columns.

Using the converter

Consider an entity with a nullable date property:

<?php

namespace App\Movies\Entities;

use Assegai\Orm\Attributes\Columns\Column;
use Assegai\Orm\Attributes\Entity;
use Assegai\Orm\Queries\Sql\ColumnType;
use DateTime;

#[Entity(table: 'screenings')]
class ScreeningEntity
{
  #[Column(name: 'starts_at', type: ColumnType::DATETIME, nullable: true)]
  public ?DateTime $startsAt = null;
}

Now a DTO or array payload can carry a string while the entity still receives a DateTime:

<?php

$screening = $screeningsRepository->create([
  'startsAt' => '2026-04-12 20:00:00',
]);

If ScheduleDateConverter is registered, the ORM sees a string source value, sees a DateTime target property, and calls the converter before assigning the property.

Registering converters in an Assegai app

In an Assegai module, register converter classes through module config:

<?php

namespace App\Movies;

use App\Orm\Converters\ScheduleDateConverter;
use Assegai\Core\Attributes\Modules\Module;

#[Module(
  providers: [MoviesService::class],
  controllers: [MoviesController::class],
  config: [
    'data_source' => 'sqlite:catalog',
    'converters' => [
      ScheduleDateConverter::class,
    ],
  ],
)]
class MoviesModule
{
}

That makes the converter available to repositories and entity managers resolved inside that module context.

Registering converters manually

For standalone scripts or tests, register converter instances on the repository or entity manager:

<?php

use App\Orm\Converters\ScheduleDateConverter;

$entityManager->useConverters([
  new ScheduleDateConverter(),
]);

or:

<?php

$screeningsRepository->useConverters([
  new ScheduleDateConverter(),
]);

useConverters() replaces the current custom converter list for that manager, so pass the full set of custom converters you need.

Nullable conversions

Converters can return null when the target property allows null.

That makes this pattern valid:

<?php

#[TypeConverter]
public function fromStringToDateTime(string $value): ?DateTime
{
  return trim($value) === '' ? null : new DateTime($value);
}

If the target property does not allow null, the ORM raises a conversion error instead of assigning null.

Matching rules

Converter matching is intentionally strict.

This converter:

<?php

#[TypeConverter]
public function fromStringToDateTime(string $value): DateTime
{
  return new DateTime($value);
}

matches:

  • source type: string
  • target type: DateTime

It will not match an int source value, and it will not run for a string target property.

Keep converter methods simple and use exactly one required parameter. If a conversion depends on application services, do that work before creating or updating the entity instead of hiding service calls inside a converter.

Practical advice

  • Use converters for type normalization, not business validation.
  • Keep converters deterministic and side-effect free.
  • Throw a clear exception when a non-empty value cannot be converted.
  • Prefer DTO validation for user-facing error messages.
  • Do not expect converters to run inside raw query-builder chains.
  • Register converters at the module level when a feature uses them in normal repository flows.

Next steps

Once type conversion is clear, continue with Relations or Working with Entity Manager.