Extending mapper

Data mapper can be extended to understand other classes or by using first-hand crafted classes.

There is 2 ways of extending the data mapper.

Use MapeableObject interface

There are cases where there is classes created within the Laravel application (not being 3rd party) or some extensions over other 3rd party classes, in these cases you can use the interface with the mappingFrom method:

<?php

use OpenSoutheners\LaravelDataMapper\Contracts\MapeableObject;
use OpenSoutheners\LaravelDataMapper\MappingValue;

class MyMapeableClass implements MapeableObject
{
    public function mappingFrom(MappingValue $mappingValue): void
    {
        $mappedValue->data = /** mapping logic here */;
    }
}

The MappingValue class has this data where it stores original and modified values, mapped values should be stored on this so they will be returned by the data mapper.

Creating a DataMapper class

When treating with 3rd party classes that aren't extended or modified on the Laravel application this can be used to teach the mapper about these new types.

<?php

namespace App\Mappers;

use Carbon\CarbonImmutable;
use Carbon\CarbonInterface;
use Illuminate\Support\Carbon;
use OpenSoutheners\LaravelDataMapper\MappingValue;

final class CarbonDataMapper extends DataMapper
{
    /**
     * Assert that this mapper resolves property with types given.
     */
    public function assert(MappingValue $mappingValue): bool
    {
        return in_array(gettype($mappingValue->data), ['string', 'integer'], true)
            && ($mappingValue->preferredTypeClass === CarbonInterface::class
                || is_subclass_of($mappingValue->preferredTypeClass, CarbonInterface::class));
    }

    /**
     * Resolve mapper that runs once assert returns true.
     */
    public function resolve(MappingValue $mappingValue): void
    {
        $mappingValue->data = match (true) {
            gettype($mappingValue->data) === 'integer' || is_numeric($mappingValue->data) => Carbon::createFromTimestamp($mappingValue->data),
            default => Carbon::make($mappingValue->data),
        };

        if ($mappingValue->preferredTypeClass === CarbonImmutable::class) {
            $mappingValue->data = $mappingValue->data->toImmutable();
        }
    }
}

This is the same as the interface method above but adding the assert method which ensures all types and data is right.

Now this mapper class should be registered in the boot method of any of your application service provider:

use OpenSoutheners\LaravelDataMapper\ServiceProvider;
use App\Mappers\CarbonDataMapper;

/**
 * Bootstrap any application services.
 *
 * @return void
 */
public function boot()
{
    ServiceProvider::registerMapper(CarbonDataMapper::class);
}

This package makes extensive use of Symfony's PropertyInfo package which analyses the types of each class/object property (these are stored on the MappingValue object passed on each mapping method).

Last updated

Was this helpful?