Skip to content

Custom Casters

Carapace ships with several built‑in casters and also lets you provide your own by implementing CasterInterface. Custom casters are used via the CastWith attribute during the hydration stage.

Creating a Custom Caster

Implement the CasterInterface from Alamellama\Carapace\Contracts.

php
use Alamellama\Carapace\Contracts\CasterInterface;

class MyCaster implements CasterInterface
{
    public function cast(mixed $value): mixed
    {
        // Validate input and transform
        if ($value === null) {
            // Let CastWith handle nulls based on property type; usually return null as-is
            return null;
        }

        // ... your casting logic
        $transformed = $value; // replace with real logic

        return $transformed;
    }
}

TIP

Throw InvalidArgumentException for unsupported inputs. CastWith will surface helpful error messages.

Example: Carbon date caster

Here's an example of a custom caster for Carbon dates:

php
use Alamellama\Carapace\Contracts\CasterInterface;
use Carbon\Carbon;
use Carbon\CarbonInterface;
use DateTimeInterface;
use InvalidArgumentException;

final class CarbonCaster implements CasterInterface
{
    public function __construct(
        private string $format = 'Y-m-d H:i:s'
    ) {}

    public function cast(mixed $value): CarbonInterface
    {
        if ($value instanceof CarbonInterface) {
            return $value;
        }

        if ($value instanceof DateTimeInterface) {
            return Carbon::instance($value);
        }

        if (is_string($value)) {
            // Try explicit format first
            $carbon = Carbon::createFromFormat($this->format, $value);
            if ($carbon !== false) {
                return $carbon;
            }

            // Fallback to flexible parser
            return Carbon::parse($value);
        }

        if (is_int($value)) {
            return Carbon::createFromTimestamp($value);
        }

        throw new InvalidArgumentException('Cannot cast to Carbon: unsupported type ' . gettype($value));
    }
}

Using your caster with CastWith

CastWith accepts one of the following:

  • A DTO class‑string (e.g. User::class) — arrays/objects/JSON will be cast to that DTO, lists to arrays of DTOs
  • A caster class‑string (e.g. PrimitiveCaster::class) — CastWith will instantiate it with no arguments
  • A caster instance (e.g. new PrimitiveCaster('int'))
php
use Alamellama\Carapace\Attributes\CastWith;
use Alamellama\Carapace\Data;
use Carbon\CarbonInterface;

final class Event extends Data
{
    public function __construct(
        public string $name,

        // Pass an instance when your caster has constructor args
        #[CastWith(new CarbonCaster('Y-m-d'))]
        public CarbonInterface $date,
    ) {}
}

$event = Event::from([
    'name' => 'Conference',
    'date' => '2025-08-15', // Will be cast to Carbon
]);