Skip to content

Commit

Permalink
add Carbon dates & objects properties mapping
Browse files Browse the repository at this point in the history
  • Loading branch information
d8vjork committed Sep 7, 2023
1 parent 7cf0da1 commit 41ff48f
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 1 deletion.
23 changes: 22 additions & 1 deletion src/PropertiesMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@
namespace OpenSoutheners\LaravelDto;

use BackedEnum;
use Carbon\CarbonImmutable;
use Carbon\CarbonInterface;
use Illuminate\Support\Carbon;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use OpenSoutheners\LaravelDto\Attributes\BindModelWith;
use OpenSoutheners\LaravelDto\Attributes\NormaliseProperties;
use ReflectionClass;
use stdClass;
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
use Symfony\Component\PropertyInfo\PropertyInfoExtractor;
Expand Down Expand Up @@ -86,10 +90,15 @@ public function run(): static
}

$this->data[$key] = match (true) {
default => $value,
$preferredType->isCollection() || $preferredTypeClass === Collection::class => $this->mapIntoCollection($propertyTypes, $key, $value),
is_subclass_of($preferredTypeClass, Model::class) => $this->mapIntoModel($preferredTypeClass, $key, $value),
is_subclass_of($preferredTypeClass, BackedEnum::class) => $preferredTypeClass::tryFrom($value),
is_subclass_of($preferredTypeClass, CarbonInterface::class) || $preferredTypeClass === CarbonInterface::class => $this->mapIntoCarbonDate($preferredTypeClass, $value),
$preferredTypeClass === stdClass::class && is_array($value) => (object) $value,
$preferredTypeClass === stdClass::class && Str::isJson($value) => json_decode($value),
$preferredTypeClass && class_exists($preferredTypeClass) && (new ReflectionClass($preferredTypeClass))->isInstantiable() && is_array($value) && is_string(array_key_first($value)) => new $preferredTypeClass(...$value),
$preferredTypeClass && class_exists($preferredTypeClass) && (new ReflectionClass($preferredTypeClass))->isInstantiable() && Str::isJson($value) => new $preferredTypeClass(...json_decode($value, true)),
default => $value,
};
}

Expand Down Expand Up @@ -170,6 +179,18 @@ protected function mapIntoModel(string $modelClass, string $propertyKey, mixed $
return $this->getModelInstance($modelClass, $value, $bindModelWithRelationships);
}

/**
* Map data value into Carbon date/datetime instance.
*/
public function mapIntoCarbonDate($carbonClass, mixed $value): CarbonInterface|null
{
if ($carbonClass === CarbonImmutable::class) {
return CarbonImmutable::make($value);
}

return Carbon::make($value);
}

/**
* Map data value into collection of items with subtypes.
*
Expand Down
8 changes: 8 additions & 0 deletions tests/Fixtures/CreatePostData.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@
namespace OpenSoutheners\LaravelDto\Tests\Fixtures;

use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use OpenSoutheners\LaravelDto\DataTransferObject;
use stdClass;

class CreatePostData extends DataTransferObject
{
public mixed $authorEmail = '[email protected]';

/**
* @param \Illuminate\Support\Collection<\Illuminate\Support\Carbon>|null $dates
*/
public function __construct(
public string $title,
public array|null $tags,
Expand All @@ -19,6 +24,9 @@ public function __construct(
public $description = '',
public ?Collection $subscribers = null,
public ?Authenticatable $currentUser = null,
public ?Carbon $publishedAt = null,
public ?stdClass $content = null,
public ?Collection $dates = null,
$authorEmail = null
) {
$this->authorEmail = $authorEmail;
Expand Down
75 changes: 75 additions & 0 deletions tests/Unit/DataTransferObjectTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Illuminate\Auth\AuthManager;
use Illuminate\Config\Repository;
use Illuminate\Container\Container;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Mockery;
use OpenSoutheners\LaravelDto\Tests\Fixtures\CreatePostData;
Expand Down Expand Up @@ -137,4 +138,78 @@ public function testDataTransferObjectArrayPropertiesGetsMappedAsCollection()
$this->assertContains($rubenUser, $data->subscribers);
$this->assertContains($taylorUser, $data->subscribers);
}

public function testDataTransferObjectDatePropertiesGetMappedFromStringsIntoCarbonInstances()
{
$data = CreatePostData::fromArray([
'title' => 'Hello world',
'tags' => '',
'post_status' => PostStatus::Published->value,
'published_at' => '2023-09-06 17:35:53',
'content' => '{"type": "doc", "content": [{"type": "paragraph", "attrs": {"textAlign": "left"}, "content": [{"text": "dede", "type": "text"}]}]}'
]);

$this->assertTrue($data->publishedAt instanceof Carbon);
$this->assertTrue(now()->isAfter($data->publishedAt));
}

public function testDataTransferObjectDatePropertiesGetMappedFromJsonStringsIntoGenericObjects()
{
$data = CreatePostData::fromArray([
'title' => 'Hello world',
'tags' => '',
'post_status' => PostStatus::Published->value,
'content' => '{"type": "doc", "content": [{"type": "paragraph", "attrs": {"textAlign": "left"}, "content": [{"text": "hello world", "type": "text"}]}]}'
]);

$this->assertTrue($data->content instanceof \stdClass);
$this->assertObjectHasProperty('type', $data->content);
}

public function testDataTransferObjectDatePropertiesGetMappedFromArraysIntoGenericObjects()
{
$data = CreatePostData::fromArray([
'title' => 'Hello world',
'tags' => '',
'post_status' => PostStatus::Published->value,
'content' => [
"type" => "doc",
"content" => [
[
"type" => "paragraph",
"attrs" => [
"textAlign" => "left",
],
"content" => [
[
"text" => "hello world",
"type" => "text",
],
],
],
],
]
]);

$this->assertTrue($data->content instanceof \stdClass);
$this->assertObjectHasProperty('type', $data->content);
}

public function testDataTransferObjectDatePropertiesGetMappedFromArraysOfObjectsIntoCollectionOfGenericObjects()
{
$data = CreatePostData::fromArray([
'title' => 'Hello world',
'tags' => '',
'post_status' => PostStatus::Published->value,
'dates' => [
'2023-09-06 17:35:53',
'2023-09-07 06:35:53',
]
]);

$this->assertTrue($data->dates instanceof Collection);
$this->assertTrue($data->dates->first() instanceof Carbon);
$this->assertTrue(now()->isAfter($data->dates->first()));
$this->assertTrue(now()->isAfter($data->dates->last()));
}
}

0 comments on commit 41ff48f

Please sign in to comment.