Marshal Serializer
, (*1)
, (*2)
Introduction
Marshal is serializing / marshalling data structures to the desired format. It is also deserializing / unmarshalling the format back to the data structures., (*3)
Especially useful for building the raw response for web services which then can be formatted to JSON for example., (*4)
If you need to serialize directly to a format, use the appropriate Marshal library:, (*5)
Installation
Easiest way to install the library is via composer:, (*6)
composer require kingson-de/marshal-serializer
The following PHP versions are supported:
* PHP 7.0
* PHP 7.1
* PHP 7.2
* PHP 7.3, (*7)
Execute tests
Just run:, (*8)
composer test
Or without code coverage:, (*9)
composer quicktest
Usage
Mappers
The first thing to do is to create a mapper that takes care of mapping your entities / models to the correct format., (*10)
You always need to inherit from the abstract Mapper class and implement a map function with your type hinting., (*11)
There is also the option to use directly a callable to map data. This will be explained later., (*12)
It is always possible to use a callable in a mapper or vice versa., (*13)
<?php
use KingsonDe\Marshal\AbstractMapper;
class UserMapper extends AbstractMapper {
public function map(User $user) {
return [
'username' => $user->getUsername(),
'email' => $user->getEmail(),
'birthday' => $user->getBirthday()->format('Y-m-d'),
'followers' => count($user->getFollowers()),
];
}
}
Data Structures
Next step is to create the desired data structure either being an item/object or a collection., (*14)
Item/Object
<?php
use KingsonDe\Marshal\Data\Item;
$item = new Item(new UserMapper(), $user);
Collection
<?php
use KingsonDe\Marshal\Data\Collection;
$userCollection = [$user1, $user2, $user3];
$item = new Collection(new UserMapper(), $userCollection);
Serializing / Marshalling
The final step is to map the data structures to the actual format., (*15)
<?php
use KingsonDe\Marshal\Marshal;
$data = Marshal::serialize($item);
You are also not forced to create data structures on your own, you can use the appropriate Marshal functions instead:, (*16)
<?php
use KingsonDe\Marshal\Marshal;
$data = Marshal::serializeItem($mapper, $model);
// or
$data = Marshal::serializeCollection($mapper, $modelCollection);
// or
$data = Marshal::serializeCollectionCallable(function (User $user) {
return [
'username' => $user->getUsername(),
'email' => $user->getEmail(),
'birthday' => $user->getBirthday()->format('Y-m-d'),
'followers' => count($user->getFollowers()),
];
}, $modelCollection);
Symfony Example
<?php
use KingsonDe\Marshal\Data\Item;
use KingsonDe\Marshal\Marshal;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;
class UserController extends Controller {
public function indexAction(User $user) {
$item = new Item(new UserMapper(), $user);
$data = Marshal::serialize($item);
return new JsonResponse($data);
}
}
Advanced Mappers
Nested Data Structures
Mappers can even include other mappers with different data structures., (*17)
Therefore you can use item, itemCallable, collection or collectionCallable function from the AbstractMapper class., (*18)
<?php
use KingsonDe\Marshal\AbstractMapper;
class UserMapper extends AbstractMapper {
public function map(User $user) {
return [
'username' => $user->getUsername(),
'email' => $user->getEmail(),
'birthday' => $user->getBirthday()->format('Y-m-d'),
'followers' => $this->collection(new FollowerMapper(), $user->getFollowers),
'location' => $this->item(new LocationMapper(), $user->getLocation()),
];
}
}
Pass as many parameters as you like to the mappers
<?php
use KingsonDe\Marshal\Data\Item;
use KingsonDe\Marshal\Marshal;
$item = new Item(new UserMapper(), $user, $followers, $location);
$data = Marshal::serialize($item);
<?php
use KingsonDe\Marshal\AbstractMapper;
class UserMapper extends AbstractMapper {
public function map(User $user, FollowerCollection $followers, Location $location) {
return [
'username' => $user->getUsername(),
'email' => $user->getEmail(),
'birthday' => $user->getBirthday()->format('Y-m-d'),
'followers' => $this->collection(new FollowerMapper(), $followers),
'location' => $this->item(new LocationMapper(), $location),
];
}
}
For collections the first parameter passed is the one which Marshal will use for iterating., (*19)
All other parameters in a collection will stay as it is., (*20)
For items/objects all parameters retain., (*21)
Filter out single item's from the collection
Collection mappers can discard single item's by returning null., (*22)
<?php
use KingsonDe\Marshal\AbstractMapper;
class UserMapper extends AbstractMapper {
public function map(User $user) {
if ($user->isPrivate()) {
return null;
}
return [
'username' => $user->getUsername(),
];
}
}
Deserializing / Unmarshalling
To transform the actual format back to your structure use Marshal's deserialize functions.
You need a class extending the AbstractObjectMapper which will be passed to the deserialize function., (*23)
<?php
use KingsonDe\Marshal\AbstractObjectMapper;
use KingsonDe\Marshal\Data\FlexibleData;
use KingsonDe\Marshal\Example\Model\User;
class UserObjectMapper extends AbstractObjectMapper {
/**
* @inheritdoc
*
* @return User
*/
public function map(FlexibleData $flexibleData, ...$additionalData) {
return new User(
$flexibleData['id'] ?? 0,
$flexibleData['email'] ?? '',
$flexibleData->find('username', '')
);
}
}
<?php
use KingsonDe\Marshal\Marshal;
$data = Marshal::serializeItem(new UserMapper(), $user);
$user = Marshal::deserialize(new UserObjectMapper(), $data);
Another option would be to use the deserializeCallable function., (*24)
<?php
use KingsonDe\Marshal\Marshal;
$data = Marshal::serializeItem(new UserMapper(), $user);
$user = Marshal::deserializeCallable(function (FlexibleData $flexibleData) {
return new User(
$flexibleData->get('id'),
$flexibleData->get('email'),
$flexibleData->get('username')
);
}, $data);
License
This project is released under the terms of the Apache 2.0 license., (*25)