Package

Zero To Prod Data Model Helper

Helpers for a DataModel.

Package

zero-to-prod/data-model-helper

View on GitHub →

Zerotoprod\DataModelHelper

Utilities for casting values using the DataModel package.

Installation

Install the package via Composer:

composer require zero-to-prod/data-model-helper

Usage

Including the Trait

Include the DataModelHelper trait in your class to access helper methods:

class DataModelHelper
{
use \Zerotoprod\DataModelHelper\DataModelHelper;
}

Helper Methods

  • mapOf: Create a map of any type by using
  • pregReplace: Perform a regular expression search and replace.
  • pregMatch: Perform a regular expression match.
  • isUrl: Validates a url.
  • isEmail: Validates an email.

mapOf

Create a map of any type by using the DataModelHelper::mapOf() method.

use Zerotoprod\DataModel\Describe;
 
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
'required', // Throws PropertyRequiredException when value not present
'coerce' => true, // Coerce single elements into an array
'using' => [self::class, 'map'], // Custom mapping function
'map_via' => 'mapper', // Custom mapping method (defaults to 'map')
'map' => [self::class, 'keyBy'], // Run a function for that value.
'level' => 1, // The dimension of the array. Defaults to 1.
'key_by' => 'key', // Key an associative array by a field.
])]
public Collection $Aliases;
}
Usage

In this case the mapOf() method returns an array of Alias instances.

use Zerotoprod\DataModel\Describe;
 
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
'required', // Throws PropertyRequiredException when value not present
])]
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.

use Zerotoprod\DataModel\Describe;
 
class User
{
use \Zerotoprod\DataModel\DataModel;
use \Zerotoprod\DataModelHelper\DataModelHelper;
 
/** @var Collection<int, Alias> $Aliases */
#[Describe([
'cast' => [self::class, 'mapOf'],
'type' => Alias::class,
'required', // Throws PropertyRequiredException when value not present
])]
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.

use Zerotoprod\DataModel\Describe;
 
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
'required', // Throws PropertyRequiredException when value not present
])]
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.

use Zerotoprod\DataModel\Describe;
 
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
'required', // Throws PropertyRequiredException when value not present
])]
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.

use Zerotoprod\DataModel\Describe;
 
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.
'required', // Throws PropertyRequiredException when value not present
])]
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.

use Zerotoprod\DataModel\Describe;
 
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.
'required', // Throws PropertyRequiredException when value not present
])]
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
KeyBy

Key an array by an element value by using the key_by argument.

This also supports deep mapping.

Note: this only applies to arrays.

class User
{
use \Zerotoprod\DataModel\DataModel;
use \Zerotoprod\DataModelHelper\DataModelHelper;
 
/** @var Alias[] $Aliases */
#[Describe([
'cast' => [self::class, 'mapOf'],
'type' => Alias::class,
'key_by' => 'id',
'required', // Throws PropertyRequiredException when value not present
])]
public array $Aliases;
}
 
class Alias
{
use \Zerotoprod\DataModel\DataModel;
 
public string $id;
public string $name;
}
 
$User = User::from([
'Aliases' => [
[
'id' => 'jd1',
'name' => 'John Doe',
],
[
'id' => 'js1',
'name' => 'John Smith'
],
]
]);
 
echo $User->Aliases['jd1']->name; // 'John Doe'
echo $User->Aliases['js1']->name); // 'John Smith'
Map

Call a function for that value.

Note: This does not work with arrays.

class User
{
use \Zerotoprod\DataModel\DataModel;
use \Zerotoprod\DataModelHelper\DataModelHelper;
 
/** @var Alias[] $Aliases */
#[Describe([
'cast' => [self::class, 'mapOf'],
'type' => Alias::class,
'map' => [self::class, 'keyBy'],
'required', // Throws PropertyRequiredException when value not present
])]
public Collection $Aliases;
 
public static function keyBy(Collection $values): Collection
{
return $values->keyBy('id');
}
}
 
class Alias
{
use \Zerotoprod\DataModel\DataModel;
 
public string $id;
public string $name;
}
 
$User = User::from([
'Aliases' => [
[
'id' => 'jd1',
'name' => 'John Doe',
]
]
]);
 
echo $User->Aliases->get('jd1')->name; // 'John Doe'

pregReplace

Use pregReplace to perform a regular expression search and replace.

class User
{
use \Zerotoprod\DataModel\DataModel;
use \Zerotoprod\DataModelHelper\DataModelHelper;
 
public const ascii_only = '/[^\x00-\x7F]/';
 
#[Describe([
'cast' => [self::class, 'pregReplace'],
'pattern' => ascii_only,
'replacement' => '!' // defaults to '' when not specified
'required', // Throws PropertyRequiredException when value not present
])]
public string $name;
}
 
$User = User::from([
'name' => 'Trophy🏆',
]);
 
echo $User->name; // Outputs: 'Trophy!'

pregMatch

Use pregMatch to perform a regular expression match.

class User
{
use \Zerotoprod\DataModel\DataModel;
use \Zerotoprod\DataModelHelper\DataModelHelper;
 
#[Describe([
'cast' => [self::class, 'pregMatch'],
'pattern' => '/s/', // Required
'match_on' => 0 // Index of the $matches to return
'flags' => PREG_UNMATCHED_AS_NULL
'offset' => 0,
'required', // Throws PropertyRequiredException when value not present
])]
public string $name;
}
 
$User = User::from([
'name' => 'sarah',
]);
 
echo $User->name; // Outputs: 's'

isUrl

Use isUrl to validate a url.

class User
{
use \Zerotoprod\DataModel\DataModel;
use \Zerotoprod\DataModelHelper\DataModelHelper;
 
#[Describe([
'cast' => [self::class, 'isUrl'],
'protocols' => ['http', 'udp'], // Optional. Defaults to all.
'on_fail' => [MyAction::class, 'method'], // Optional. Invoked when validation fails.
'exception' => MyCustomException::class, // Optional. Throws an exception when not url.
'required' // Optional. Throws \Zerotoprod\DataModel\PropertyRequiredException::class
])]
public string $url;
}

isEmail

Use isEmail to validate an email.

class User
{
use \Zerotoprod\DataModel\DataModel;
use \Zerotoprod\DataModelHelper\DataModelHelper;
 
#[Describe([
'cast' => [self::class, 'isEmail'],
'on_fail' => [MyAction::class, 'method'], // Optional. Invoked when validation fails.
'exception' => MyCustomException::class, // Optional. Throws an exception when not url.
'required' // Optional. Throws \Zerotoprod\DataModel\PropertyRequiredException::class
])]
public string $url;
}

Zero To Prod Data Model Helper Related Articles

How to add unlimited custom domains to Laravel Vapor image

How to add unlimited custom domains to Laravel Vapor

Read article
Inertia.js v3.0.0 Is Here with Optimistic Updates, useHttp, and More image

Inertia.js v3.0.0 Is Here with Optimistic Updates, useHttp, and More

Read article
Cloudflare has been leaking custom HTTPS sessions image

Cloudflare has been leaking custom HTTPS sessions

Read article
Get Free Access to all 70+ Vue School Courses - 1-2 March image

Get Free Access to all 70+ Vue School Courses - 1-2 March

Read article
No Compromises logo

No Compromises

Joel and Aaron, the two seasoned devs from the No Compromises podcast, are now available to hire for your Laravel project. ⬧ Flat rate of $9500/mo. ⬧ No lengthy sales process. ⬧ No contracts. ⬧ 100% money back guarantee.

No Compromises
Pushsilver logo

Pushsilver

Pushsilver is simple invoicing for freelancers and small studios — send polished invoices, automate reminders, and get paid on time without the bloat.

Pushsilver
PhpStorm logo

PhpStorm

The go-to PHP IDE with extensive out-of-the-box support for Laravel and its ecosystem.

PhpStorm
Acquaint Softtech logo

Acquaint Softtech

Acquaint Softtech offers AI-ready Laravel developers who onboard in 48 hours at $3000/Month with no lengthy sales process and a 100 percent money-back guarantee.

Acquaint Softtech
Harpoon: Next generation time tracking and invoicing logo

Harpoon: Next generation time tracking and invoicing

The next generation time-tracking and billing software that helps your agency plan and forecast a profitable future.

Harpoon: Next generation time tracking and invoicing
Kirschbaum logo

Kirschbaum

Providing innovation and stability to ensure your web application succeeds.

Kirschbaum