PHP code example of zero-to-prod / service-model

1. Go to this page and download the library: Download zero-to-prod/service-model library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

zero-to-prod / service-model example snippets


use Zerotoprod\ServiceModel\ServiceModel;

class Order
{
    use ServiceModel;

    public readonly int $id;
}

$Order = Order::make(['id' => 1]);
$Order->id; // 1

$Order = Order::make('{"id":1}');
$Order->id; // 1

$order = Order::factory()->make();
$order->id; // 1

$Order = Order::make(['id' => 1]);
$Order->id; // 1

$Order = Order::make('{"id":1}');
$Order->id; // 1

use Zerotoprod\ServiceModel\Attributes\Cast;
use Zerotoprod\ServiceModel\Attributes\CastUsing;
use Zerotoprod\ServiceModel\Attributes\MapFrom;
use Zerotoprod\ServiceModel\Attributes\ArrayOf;
use Zerotoprod\ServiceModel\ServiceModel;

class Order
{
    use ServiceModel;

    /**
     * Automatically cast OrderDetails to a model by using
     * the ServiceModel trait in OrderDetails.
     */
    public readonly OrderDetails $details;
    
    /**
     * Define you own value caster.
     */
     #[Cast(ToJson::class)]
    public readonly string $metadata;

    /**
     * If you have a class the can cast the value, 
     * you can use the CastUsing attribute to 
     * define the method to pass values to.
     */
    #[CastUsing('set')]
    public readonly TimeClass $time;
    
    /**
     * Rename your values.
     */
    #[MapFrom('AcknowledgedAt')]
    public readonly string $acknowledged_at;
    
    /**
     * Remap your values.
     */
    #[MapFrom('vendor_details.serial_number')]
    public readonly string $serial_number;
    
    /**
     * Use a value-backed enum to automatically cast the value.
     */
    public readonly Status $status;
    
    /**
     * Casts to an array of enums.
     * @var Tag[] $tags
     */
    #[ArrayOf(Tag::class)]
    public readonly array $tags;

    /**
     * Unpacks the array into the constructor of the type-hinted class.
     * NOTE: PickupInfo does not use the ServiceModel trait.
     */
    public readonly PickupInfo $PickupInfo;
    
    /**
     * Casts to an array of PickupInfo.
     * NOTE: PickupInfo does not use the ServiceModel trait.
     * 
     * @var PickupInfo[] $previous_pickups
     */
    #[ArrayOf(PickupInfo::class)]
    public readonly array $previous_pickups;
    
    /**
     * Because Carbon uses the static method `parse`, this will 
     * cast the value to a Carbon instance for free.
     */
    public readonly Carbon $created_at;

    /**
     * Creates an array of Items.
     * @var Item[] $items
     */
    #[ArrayOf(Item::class)]
    public readonly array $items;
    
    /**
     * Use a custom cast. 
     * @var Collection<int, View> $views
     */
    #[CollectionOf(View::class)]
    public readonly Collection $views;
}

$order = Order::make([
    'details' => ['id' => 1, 'name' => 'Order 1'],
    'metadata' => ['id' => 1, 'name' => 'Order 1'],
    'time' => '2021-01-01 00:00:00',
    'vendor_details' => ['serial_number' => '123456789'],
    'status' => 'pending',
    'tags' => ['important', 'rush'],
    'PickupInfo' => ['location' => 'Location 1', 'time' => '2021-01-01 00:00:00'],
    'created_at' => '2021-01-01 00:00:00',
    'items' => [
        ['id' => 1,'name' => 'Item 1'],
        ['id' => 2,'name' => 'Item 2']],
    'views' => [
        ['id' => 1,'name' => 'View 1'],
        ['id' => 2,'name' => 'View 2']],
]);

// Nested Models
$details = $order->details; // Order::class
$details = $order->details->name; // 'Order 1'

// Custom Casters
$metadata = $order->metadata; // '{"id":1,"name":"Order 1"}'

// CastUsing
$time = $order->time; // TimeClass::class
$time = $order->time->value; // '2021-01-01 00:00:00'

// Remapped Properties
$serial_number = $order->serial_number; // '123456789'

// Enums
$status = $order->status; // Status::pending
$status = $order->status->value; // 'pending'

// Array of Enums
$tags = $order->tags[0]; // Tag::important
$tags = $order->tags[0]->value; // 'important'

// Value Casting
$created_at = $order->created_at; // Carbon::class
$created_at = $order->created_at->toDateTimeString(); // '2021-01-01 00:00:00'

// One-to-many array Casting
$item_id = $order->items[0]; // Item::class
$item_id = $order->items[0]->id; // 1

// One-to-many custom Casting
$view_name = $order->views->first(); // View::class
$view_name = $order->views->first()->name; // 'View 1'

$order = Order::factory()->make();

$order->status; // Status::pending

use Zerotoprod\ServiceModel\ServiceModel;

class Order
{
    use ServiceModel;

    /**
     * Using the `ServiceModel` trait in the child class (OrderDetails)
     * class will automatically instantiate new class.
     */
    public readonly OrderDetails $details;
}

use Zerotoprod\ServiceModel\ServiceModel;

class OrderDetails
{
    use ServiceModel;

    public readonly int $id;
    public readonly string $name;
}

$order = Order::make([
    'details' => [
        'id' => 1, 
        'name' => 'Order 1'
    ],
]);

// This is also equivalent.
$order = Order::make([
    'details' => OrderDetail::make([
        'id' => 1, 
        'name' => 'Order 1'
    ]),
]);

$order->details->id; // 1
$order->details->name; // 'Order 1'

use Zerotoprod\ServiceModel\ServiceModel;

class Order
{
    use ServiceModel;
    
    /**
     * Casts to a Status enum
     */
    public readonly Status $status;
    
    /**
     * Casts to an array of Status enum.
     * @var Status[] $statuses
     */
    #[CastToArray(Status::class)]
    public readonly array $statuses;
}

enum Status: string
{
    case pending = 'pending';
    case completed = 'completed';
}

$order = Order::make([
    'status' => 'pending',
    'statuses' => ['pending', 'completed'],
]);

$order->status; // Status::pending
$order->status->value; // 'pending'

$order->statuses[0]; // Status::pending
$order->statuses[1]->value; // completed

use Zerotoprod\ServiceModel\ServiceModel;

class Order
{
    use ServiceModel;
    
    /**
     * Unpacks the array into the constructor of the type-hinted class.
     */
    public readonly PickupInfo $pickups;
}

class PickupInfo
{
    public function __construct(public readonly string $location, public readonly string $time)
    {
    }
}

$order = Order::make([
    'pickups' => [
        'location' => 'Location 1',
        'time' => '2021-01-01 00:00:00',
    ]
]);

$order->pickups; // PickupInfo::class
$order->pickups->location; // Location 1
$order->pickups->time; // 2021-01-01 00:00:00


use Zerotoprod\ServiceModel\Attributes\CastUsing;
use Zerotoprod\ServiceModel\ServiceModel;

class MyModel
{
    use ServiceModel;

    // The 'set' method of the TimeClass will be used for parsing the value
    #[CastUsing('set')]
    public readonly TimeClass $time;
}

class TimeClass
{
    public string $value;

    public static function set($value): self
    {
        $self = new self();
        $self->value = $value;

        return $self;
    }
}

use Zerotoprod\ServiceModel\Attributes\ArrayOf;
use Zerotoprod\ServiceModel\ServiceModel;

class Order
{
    use ServiceModel;
    
    /**
     * Casts to an array of PickupInfo.
     * @var PickupInfo[] $pickups
     */
    #[ArrayOf(PickupInfo::class)]
    public readonly array $pickups;
}

class PickupInfo
{
    public function __construct(public readonly string $location, public readonly string $time)
    {
    }
}

$order = Order::make([
    'pickups' => [
        [
            'location' => 'Location 1',
            'time' => '2021-01-01 00:00:00',
        ],
        [
            'location' => 'Location 2',
            'time' => '2021-01-01 00:00:00',
        ],
    ],
]);

$order->pickups[0]->location; // Location 1
$order->pickups[0]->time; // 2021-01-01 00:00:00

use Zerotoprod\ServiceModel\ServiceModel;

class Order
{
    use ServiceModel;

    /**
     * Transforms the value to a custom instance.
     */
    #[Cast(ToCustomTime::class)]
    public readonly ToCustomTime $ordered_at;

    /**
     * Because Carbon uses the static method `parse`, this will 
     * cast the value to a Carbon instance for free.
     */
    public readonly Carbon $created_at;

use Zerotoprod\ServiceModel\Contracts\CanParse;

class ToCustomTime implements CanParse
{
    public function parse(array $values): Carbon
    {
        return ToCustomTime::parse($values[0]);
    }
}

$order = Order::make([
    'ordered_at' => '2021-01-01 00:00:00',
    'created_at' => '2021-01-01 00:00:00',
]);

$order->ordered_at; // Carbon::class
$order->ordered_at->toDateTimeString(); // '2021-01-01 00:00:00'

$order->created_at; // Carbon::class
$order->created_at->toDateTimeString(); // '2021-01-01 00:00:00'

use Zerotoprod\ServiceModel\ServiceModel;
use Illuminate\Support\Collection;

class Order
{
    use ServiceModel;

    /**
     * Casts to a Collection containing View classes.
     * @var Collection<int, View> $views
     */
    #[CollectionOf(View::class)]
    public Collection $views;
}

use Zerotoprod\ServiceModel\Contracts\CanParse;

#[Attribute]
class CollectionOf implements CanParse
{
    public function __construct(public readonly string $class)
    {
    }

    public function parse(array $values): Collection
    {
        return collect($values)->map(fn(array $item) => $this->class::make($item));
    }
}

$order = Order::make([
    'views' => [
        [
            'id' => 1,
            'name' => 'View 1'
        ],
        [
            'id' => 2,
            'name' => 'View 2'
        ]
    ],
]);

$order->views->first(); // View::class
$order->views->first()->name; // 'View 1'

use Zerotoprod\ServiceModel\ServiceModel;

class MyModel
{
    use ServiceModel;

    #[CustomCaster(1)]
    public readonly int $add_one;
    
    #[CustomValueCaster(1, 2)]
    public readonly int $add_two;
}

$MyModel = MyModel::make(['add_one' => 1, 'add_two' => 1]);
$MyModel->add_one; // 2
$MyModel->add_two; // 4

use Attribute;
use Zerotoprod\ServiceModel\Contracts\CanParse;

#[Attribute]
class CustomCaster implements CanParse
{
    public function __construct(public readonly int $attribute_constructor_value)
    {
    }

    public function parse(array $values): int
    {
        return $values[0] + $this->attribute_constructor_value;
    }
}

use Attribute;
use Zerotoprod\ServiceModel\Contracts\CanParse;

#[Attribute]
class CustomValueCaster implements CanParse
{
    public function __construct(public readonly int $value_1, public readonly int $value_2)
    {
    }

    public function parse(array $values): int
    {
        return $values[0] + $this->value_1 + $this->value_2;
    }
}

use Zerotoprod\ServiceModel\HasFactory;
use Zerotoprod\ServiceModel\ServiceModel;

class Order
{
    use ServiceModel;
    use HasFactory;

    public static string $factory = OrderFactory::class;
    
    public OrderDetails $details;
    public Status $status;
}

use Zerotoprod\ServiceModel\Factory;

class OrderFactory extends Factory
{
    public string $model = Order::class;

    public function definition(): array
    {
        return [
            'details' => ['id' => 1, 'name' => 'Order 1'],
            'status' => 'pending',
        ];
    }

    public function setStatus(Status $status): self
    {
        return $this->state(fn() => [
            'status' => $status->value
        ]);
    }
}

$order = Order::factory()->make();
$order->status; // Status::pending
$order->details->name; // 'Order 1'

$order = Order::factory()->setStatus(Status::completed)->make();
$order->status; // Status::completed



namespace App\Channels\Amazon\ServiceModels\Support;

use Illuminate\Support\Collection;

trait ServiceModel
{
    use \Zerotoprod\ServiceModel\ServiceModel;

    public function toArray(): array
    {
        return $this->collect()->toArray();
    }

    public function toJson(): string
    {
        return $this->collect()->toJson();
    }

    public function collect(): Collection
    {
        return collect($this);
    }
}

Order::make([...])->toJson();

use Zerotoprod\ServiceModel\Attributes\MapFrom;
use Zerotoprod\ServiceModel\ServiceModel;

class MyModel
{
    use ServiceModel;

    #[MapFrom('MyValue')]
    public readonly string $my_value;
}

$MyModel = MyModel::make(['MyValue' => 'value']);
$MyModel->my_value; // 'value'

use Zerotoprod\ServiceModel\Attributes\MapFrom;
use Zerotoprod\ServiceModel\ServiceModel;

class MyModel
{
    use ServiceModel;

    #[MapFrom('value.nested')]
    public readonly string $value;
}

$MyModel = MyModel::make(['value' => ['nested' => 'value']]);
$MyModel->value; // 'value'

use Zerotoprod\ServiceModel\ServiceModel;
use Zerotoprod\ServiceModel\Strict;

class MyModel
{
    use ServiceModel;
    use Strict;

    /** 
     * Throws Zerotoprod\ServiceModel\Exceptions\ValidationException 
     * if the ersion;
    public readonly null|string $date;
    public ?string $time;
    public null|string $data;
}

MyModel::make()->validate()

use Zerotoprod\ServiceModel\ServiceModel;

class MyModel
{
    use ServiceModel;

    public readonly string $value;
    
    public function afterMake($attributes): void
    {
        // Example of manual assignment.
        $this->value = $attributes['Value'];
    }
}

use Zerotoprod\ServiceModel\Attributes\MapOutputNames;
use Zerotoprod\ServiceModel\Attributes\ToSnakeCase;
use Zerotoprod\ServiceModel\ServiceModel;

#[MapOutputNames(ToSnakeCase::class)]
class MyModel
{
    use ServiceModel;

    public readonly string $LastName;
}

$MyModel = MyModel::make(['LastName' => 'Doe']);
$MyModel->toResource(); // ['last_name' => 'Doe']

use Attribute;
use UnitEnum;
use Zerotoprod\ServiceModel\Contracts\CanParse;

#[Attribute]
class ToCustomCase implements CanParse
{
    public function parse(array $values): array
    {
        return $values;
    }
}

use Zerotoprod\ServiceModel\Attributes\MapOutputNames;
use Zerotoprod\ServiceModel\ServiceModel;

#[MapOutputNames(ToCustomCase::class)]
class MyModel
{
    use ServiceModel;

    public readonly string $LastName;
}

use Zerotoprod\ServiceModel\Cast;
use Zerotoprod\ServiceModel\CastToArray;
use Zerotoprod\ServiceModel\CastToClasses;
use Zerotoprod\ServiceModel\CanCast;

use Zerotoprod\ServiceModel\Attributes\Cast;
use Zerotoprod\ServiceModel\Attributes\CastToArray;
use Zerotoprod\ServiceModel\Attributes\CastToClasses;
use Zerotoprod\ServiceModel\Contracts\CanParse;

use Zerotoprod\ServiceModel\CanCast;

use Zerotoprod\ServiceModel\Contracts\CanParse;

use Zerotoprod\ServiceModel\CanCast;

class ToCarbon implements CanCast
{
    public function set(array $values): Carbon
    {
        return Carbon::parse($values[0]);
    }
}

use Zerotoprod\ServiceModel\Contracts\CanParse;

class ToCarbon implements CanParse
{
    public function parse(array $values): Carbon
    {
        return Carbon::parse($values[0]);
    }
}

use Zerotoprod\ServiceModel\CanCast;

#[Attribute]
class CollectionOf implements CanCast
{
    public function __construct(public readonly string $class)
    {
    }

    public function set(array $values): Collection
    {
        return collect($values)->map(fn(array $item) => $this->class::make($item));
    }
}

use Zerotoprod\ServiceModel\Contracts\CanParse;

#[Attribute]
class CollectionOf implements CanParse
{
    public function __construct(public readonly string $class)
    {
    }

    public function parse(array $values): Collection
    {
        return collect($values)->map(fn(array $item) => $this->class::make($item));
    }
}