haskel/map-serializer

Object Serializer by map

v0.1.2 2020-10-17 10:37 UTC

This package is auto-updated.

Last update: 2024-10-17 19:09:22 UTC


README

Use different tiny and simple schemas to serialize your objects and other structs



Latest Stable Version License

Installation

composer require haskel/map-serializer

Example

/** Define a class that you want to serialize */
class User 
{
    private $id;
    private $name;
    private status = 0;
    private $role;
    private $phone;
    private $mail;
    private group;
    
    public function __construct($id, $name, $role)
    {
        $this->id = $id;
        $this->name = $name;
        $this->role = $role;
    }
    
    /**
     * ... Boring code with getters and setters ...
     */
}

/** Specify the schema */
$schema = [
    'id'     => 'int',
    'name'   => 'string',
    'status' => 'int',
    'role'   => 'string',
];
/** Add this schema definition, uniq schema name and class name to serializer */
$serializer->addSchema(User::class, 'default', $schema);

/** serialize some instance of the class */
$result = $serializer->serialize(new User('Alice', 'user'));
{
  id: 1,
  name: 'Alice',
  status: 0,
  role: 'user'
}

Usage

Basic usage

Register different schemas for specific class. Use second argument of serialize() method to specify a schema.

use Haskel\MapSerializer\Serializer;

$serializer = new Serializer();

$schemas = [
    'default' => [
        'id'     => 'int',
        'name'   => 'string',
        'status' => 'int',
        'role'   => 'string',
    ],
    'short' => [
        'id'   => 'int',
        'name' => 'string',
    ]
];
foreach ($orderSchemas as $schemaName => $schema) {
    $serializer->addSchema(User::class, $schemaName, $schema);
}

$users = [
    new User(1, 'Alice'),
    new User(2, 'Bob'),
];
$result = $serializer->serialize($users, 'short');
[
  {
    id: 1,
    name: 'Alice'
  },
  {
    id: 2,
    name: 'Bob'
  }
]

Nested objects

  1. add schema with name
  2. use this name to nest an object
use Haskel\MapSerializer\Serializer;

$serializer = new Serializer();

$userSchema = [
    'id'     => 'int',
    'name'   => 'string',
    'group'  => 'short',
];
$groupSchema = [
  'id'   => 'int',
  'name' => 'string',
];
$serializer->addSchema(User::class, 'default', $userSchema);
$serializer->addSchema(Group::class, 'short', $groupSchema);

$group = new Group('sales');
$user = new User('Alice');
$user->addToGroup($group);

$result = $serializer->serialize($user);
{
  id: 1,
  name: 'Alice',
  group: {
    id: 1,
    name: 'sales'
  }
}



Format you object as you really want

Formatter is an object which define specific transformation rules for entity. Formatter should implement Haskel\MapSerializer\Formatter interface

Look at this example

interface Formatter
{
    public function format($value, $schemaName);
}

class DatetimeFormatter implements Formatter
{
    public function format($value, $schemaName)
    {
        if (!$value instanceof DateTime) {
            throw new FormatterException(sprintf('wrong value type'));
        }

        switch ($schemaName) {
            case 'default':
            case 'datetime':
            default:
                return $value->format("Y-m-d H:i:s");

            case 'date':
                return $value->format('Y-m-d');

            case 'time':
                return $value->format('H:i:s');
        }
    }
}

How it works

use Haskel\MapSerializer\Formatter\DatetimeFormatter;
use Haskel\MapSerializer\Serializer;

$serializer = new Serializer();
$serializer->addFormatter(new DatetimeFormatter());

$datetime = new DateTime('2015-10-21 12:00:00');
$serializer->format($datetime, 'date');
'2015-10-21'

How to accelerate extracting

Extractors are autogenerated classes that help you to extract fields from an object of some class.
ExtractorGenerator generates classes those can extract fields fastly without using reflection and without analysing of class structure. It looks like that:

final class AppEntityUserExtractor extends \Haskel\MapSerializer\EntityExtractor\BaseExtractor
{
    /** @var \App\Entity\User */
    protected $entity;

    protected function extract()
    {
        return [
            "id" => $this->entity->getId(),
            "name" => $this->entity->getName(),
        ];
    }
    
    public function exists($fieldName)
    {
        if (count($this->fields) === 0) {
            $this->fields = $this->extract();
        }

        return array_key_exists($fieldName, $this->fields);
    }

    /**
     * @param $fieldName
     *
     * @return mixed
     */
    public function get($fieldName)
    {
        if (count($this->fields) === 0) {
            $this->fields = $this->extract();
        }

        return $this->fields[$fieldName];
    }
}

Extracting works automatically, but if you want to specify your own extractor it would be easy. Just implement an interface \Haskel\MapSerializer\EntityExtractor\Extractor.

namespace Haskel\MapSerializer\EntityExtractor;

interface Extractor
{
    public function get($fieldName);
    public function exists($fieldName);
}

class UserExtractor implements Extractor 
{
    public function get($fieldName)
    {
        return 42;
    }
    
    public function exists($fieldName)
    {
        return true;
    }
}

And register it in your serializer for specific scheme

$schema = [
    'id'     => 'int',
    'name'   => 'string',
    'status' => 'int',
    'role'   => 'string',
];
$serializer->addSchema(User::class, 'default', $schema);
$serializer->addExtractor(User::class, 'default', UserExtractor::class);
$result = $serializer->serialize(new User('Alice'));
{
  id: 42,
  name: '42',
  status: 42,
  role: '42'
}