Skip to content

Commit

Permalink
feat: add custom casts
Browse files Browse the repository at this point in the history
  • Loading branch information
edipoReboucas committed Oct 20, 2023
1 parent 282bb83 commit 2f0612e
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 7 deletions.
57 changes: 57 additions & 0 deletions docs/docs/casting.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,64 @@ $user->getOriginalDocumentAttributes()['birthdate']; // Returns birthdate as an
// To set a new birthdate, you can pass both UTCDateTime or native's PHP DateTime
$user->birthdate = new \MongoDB\BSON\UTCDateTime($anyDateTime);
$user->birthdate = DateTime::createFromFormat('d/m/Y', '01/03/1970');
```

## Custom Casting


With Mongolid, you can define a custom cast implement a CastInterface.

```php
class MoneyCast implements \Mongolid\Model\Casts\CastInterface
{
public function __construct(
private string $curreny
) {
}

/**
* @param string|int|null $value
*/
public function get(mixed $value): ?Money
{
if (is_null($null)) {
return null;
}

return new Money\Money::{$this->currency}($value);
}

/**
* @param Money\Money $value
*/
public function set(mixed $value): ?string
{
if (is_null($mixed)) {
return null;
}

// "At the moment, if you want to provide support for legacy models, it is necessary to support raw values in the 'set'."
if (is_string($value) || is_int($value)) {
return Money\Money::{$this->currency}($value)->getAmount();
}

return $value->getAmount();
}
}

class Order extends \Mongolid\Model\AbstractModel {
protected $casts = [
'total' => Money::class . ':BRL',
];
}
```

Check out some usages and examples:
```php

$order = Order::first();
$order->total; // Returns a money object

// Set '-10' string
$order->total = Money::BRL('-10');
```
20 changes: 13 additions & 7 deletions src/Model/Casts/CastResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,24 @@ class CastResolver
self::IMMUTABLE_DATE_TIME,
];

public static function resolve(string $castName): CastInterface
public static function resolve(string $castIdentifier): CastInterface
{
if ($cast = self::$cache[$castName] ?? null) {
if ($cast = self::$cache[$castIdentifier] ?? null) {
return $cast;
}

self::$cache[$castName] = match($castName) {
self::DATE_TIME => new DateTimeCast(),
self::IMMUTABLE_DATE_TIME => new ImmutableDateTimeCast(),
default => throw new InvalidCastException($castName),
$parts = explode(':', $castIdentifier);

$castName = $parts[0];
$castOptions = array_slice($parts, 1);

self::$cache[$castIdentifier] = match (true) {
self::DATE_TIME === $castName => new DateTimeCast(),
self::IMMUTABLE_DATE_TIME === $castName => new ImmutableDateTimeCast(),
is_subclass_of($castName, CastInterface::class) => new $castName(...$castOptions),
default => throw new InvalidCastException($castIdentifier)
};

return self::$cache[$castName];
return self::$cache[$castIdentifier];
}
}
7 changes: 7 additions & 0 deletions tests/Unit/Model/Casts/CastResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,17 @@ public function testShouldResolveCast(): void
// Actions
$dateTimeCast = CastResolver::resolve('datetime');
$dateTimeImmutableCast = CastResolver::resolve('immutable_datetime');
// Alterar para BackedEnum após o merge para testar parâmetro
//$customCast = CastResolver::resolve(BackedEnumCast::class .':' . 'Size::class);
$customCast = CastResolver::resolve(ImmutableDateTimeCast::class);


// Assertions
$this->assertInstanceOf(DateTimeCast::class, $dateTimeCast);
$this->assertInstanceOf(ImmutableDateTimeCast::class, $dateTimeImmutableCast);
// Alterar para BackedEnum após o merge para testar parâmetro
//$this->assertInstanceOf(BackedEnumCast::class, $customCast);
$this->assertInstanceOf(ImmutableDateTimeCast::class, $customCast);
}

public function testShouldResolveFromCache(): void
Expand Down

0 comments on commit 2f0612e

Please sign in to comment.