zero-to-prod / data-model-helper
Helpers for a DataModel.
Requires
- php: >=8.1.0
- zero-to-prod/data-model: ^v81.0.0
Requires (Dev)
- phpunit/phpunit: ^10.0
Suggests
- zero-to-prod/transformable: Transform a class into different types.
README
Utilities for casting values using the DataModel package.
Installation
Install the package via Composer:
composer require zero-to-prod/data-model-helper
Additional Packages
- DataModel: Transform data into a class.
- DataModelFactory: A factory helper to set the value of your
DataModel
. - Transformable: Transform a
DataModel
into different types.
Quick Start
Here’s how to use the mapOf
helper with all its arguments:
class User { use \Zerotoprod\DataModel\DataModel; use \Zerotoprod\DataModelHelper\DataModelHelper; /** @var Collection<int, Alias> $Aliases */ #[Describe([ 'cast' => [self::class, 'mapOf'], // Casting method to use 'type' => Alias::class, // Target type for each item 'coerce' => true, // Coerce single elements into an array 'using' => [self::class, 'map'], // Custom mapping function 'map_via' => 'mapper', // Custom mapping method (defaults to 'map') 'level' => 1, // The dimension of the array. Defaults to 1. ])] public Collection $Aliases; }
Usage
Including the Trait
Include the DataModelHelper
trait in your class to access helper methods:
class DataModelHelper { use \Zerotoprod\DataModelHelper\DataModelHelper; }
mapOf()
The mapOf()
method returns an array of Alias
instances.
class User { use \Zerotoprod\DataModel\DataModel; use \Zerotoprod\DataModelHelper\DataModelHelper; /** @var Alias[] $Aliases */ #[Describe([ 'cast' => [self::class, 'mapOf'], // Use the mapOf helper method 'type' => Alias::class, // Target type for each item ])] public array $Aliases; } class Alias { use \Zerotoprod\DataModel\DataModel; public string $name; } $User = User::from([ 'Aliases' => [ ['name' => 'John Doe'], ['name' => 'John Smith'], ] ]); echo $User->Aliases[0]->name; // Outputs: John Doe echo $User->Aliases[1]->name; // Outputs: John Smith
Laravel Collection Example
The mapOf
helper is designed to work will with the \Illuminate\Support\Collection
class.
class User { use \Zerotoprod\DataModel\DataModel; use \Zerotoprod\DataModelHelper\DataModelHelper; /** @var Collection<int, Alias> $Aliases */ #[Describe([ 'cast' => [self::class, 'mapOf'], 'type' => Alias::class, ])] public \Illuminate\Support\Collection $Aliases; } class Alias { use \Zerotoprod\DataModel\DataModel; public string $name; } $User = User::from([ 'Aliases' => [ ['name' => 'John Doe'], ['name' => 'John Smith'], ] ]); echo $User->Aliases->first()->name; // Outputs: John Doe
Coercing
Sometimes, an attribute may contain either a single element or an array of elements. By setting 'coerce' => true
, you can ensure that single
elements
are coerced into an array.
class User { use \Zerotoprod\DataModel\DataModel; use \Zerotoprod\DataModelHelper\DataModelHelper; /** @var Alias[] $Aliases */ #[Describe([ 'cast' => [self::class, 'mapOf'], 'type' => Alias::class, 'coerce' => true, // Coerce single elements into an array ])] public array $Aliases; } class Alias { use \Zerotoprod\DataModel\DataModel; public string $name; } $User = User::from([ 'Aliases' => ['name' => 'John Doe'], // Single element instead of an array ]); echo $User->Aliases[0]->name; // Outputs: John Doe
Using a Custom Mapping Function
Specify your mapping function by setting the using
option.
class User { use \Zerotoprod\DataModel\DataModel; use \Zerotoprod\DataModelHelper\DataModelHelper; /** @var Collection $Aliases */ #[Describe([ 'cast' => [self::class, 'mapOf'], 'type' => Alias::class, 'using' => [self::class, 'map'], // Use custom mapping function ])] public Collection $Aliases; public static function map(array $values): Collection { // Map each value to an Alias instance $items = array_map(fn($value) => Alias::from($value), $values); // Return as a Collection return new Collection($items); } } class Alias { use \Zerotoprod\DataModel\DataModel; public string $name; } class Collection { public array $items; public function __construct(array $items = []) { $this->items = $items; } } $User = User::from([ 'Aliases' => [ ['name' => 'John Doe'], ], ]); echo $User->Aliases->items[0]->name; // Outputs: John Doe
Specifying a Custom Mapping Method
By default, the map method is used to map over elements. You can specify a different method using the map_via
option.
class User { use \Zerotoprod\DataModel\DataModel; use \Zerotoprod\DataModelHelper\DataModelHelper; /** @var Collection $Aliases */ #[Describe([ 'cast' => [self::class, 'mapOf'], 'type' => Alias::class, 'map_via' => 'mapper', // Use custom mapping method for the `Collection` class. ])] public Collection $Aliases; } class Alias { use \Zerotoprod\DataModel\DataModel; public string $name; } class Collection { public array $items; public function __construct(array $values) { $this->items = $values; } public function mapper(callable $callable): Collection { $this->items = array_map($callable, $this->items); return $this; } } $User = User::from([ 'Aliases' => [ ['name' => 'John Doe'], ], ]); echo $User->Aliases->items[0]->name; // Outputs: John Doe
Deep Mapping
You can set the level for mapping deep arrays.
class User { use \Zerotoprod\DataModel\DataModel; use \Zerotoprod\DataModelHelper\DataModelHelper; /** @var Alias[] $Aliases */ #[Describe([ 'cast' => [self::class, 'mapOf'], // Use the mapOf helper method 'type' => Alias::class, // Target type for each item 'level' => 2, // The dimension of the array. Defaults to 1. ])] public array $Aliases; } class Alias { use \Zerotoprod\DataModel\DataModel; public string $name; } $User = User::from([ 'Aliases' => [ [ ['name' => 'John Doe'], ['name' => 'John Smith'], ] ] ]); echo $User->Aliases[0][0]->name; // Outputs: John Doe echo $User->Aliases[0][1]->name; // Outputs: John Smith