- Published on
Serializing PHP Entities to JSON
- Authors
- Name
- Niko Granö
- Github
- @NikoGrano
- Socials
- Address
Mail
P.O. Box 5
FI-01711 VANTAA
FINLAND- Company details
Other
VAT Registered
FI29067989
So, as I fetch object from the database by using ORM and I might sometimes return results via REST. This means usually I end up mapping getters to array. This seems boring, time consuming, stupid. So I said enough of this and started looking for a solution.
The requirement was simple. Solution which makes possible to get entity values as array automatically. (As arrays can be always encoded into JSON). This would need reflection, but I decided already go lazy way, so performance loss is accepted. If you want high performance, you should just stay manually writing entities to array.
Solution
So I wrote the class. Source for this class is available here. The idea is to input EntityToArray::convert(object $entity)and output entity as array. The idea is to find all getters and call those. The key of the array will taken automatically from the name of property. The basic flow for the conversion is following.
This is the basic principle how it will work. Very simple, but a lot of looping and reflections. This means it will be a lot slower than just doing it manually. However, like said, this is not about performance.
Usage
It is easier to show the code first and talk after that. So in bellow example I’m going to declare two entities, Country and Flag. Every country needs a flag, so does our example Country also need a flag as a entity.
Entity: Flag
// Our Entities
final class Flag
{
/** @var string */
private $mainColor;
/** @var int */
private $height;
/** @var int */
private $width;
/** @var bool */
private $registered;
public function __construct(
string $mainColor,
int $height,
int $width,
bool $registered
)
{
$this->mainColor = $mainColor;
$this->height = $height;
$this->width = $width;
$this->registered = $registered;
}
public function getMainColor(): string
{
return $this->mainColor;
}
public function getHeight(): int
{
return $this->height;
}
public function getWidth(): int
{
return $this->width;
}
public function getRegistered(): bool
{
return $this->registered;
}
}
Entity: Country
// It is not mandatory to extend. Only if you want benefit from
// implementing \JsonSerializable and having toArray, toJson methods.
final class Country extends \Niko9911\Serializable\Serializable
{
// If you don't want implement \JsonSerializable,
// but you want methods `toArray & toJson` into
// you entity, you can add this trait.
use \Niko9911\Serializable\SerializableTrait;
/** @var string */
private $name;
/** @var int */
private $id;
/** @var Flag */
private $flag;
public function __construct(string $name, int $id, Flag $flag)
{
$this->name = $name;
$this->id = $id;
$this->flag = $flag;
}
public function getName(): string
{
return $this->name;
}
public function getId(): int
{
return $this->id;
}
public function getFlag(): Flag
{
return $this->flag;
}
}
Explaining the usage
Now we have declared the entities and you know what we are going to map into array and into JSON. First, let’s create instance of a Entity.
$entity = new Country('Finland', 358, new Flag('Blue', 150, 245, true));
So now we have new entity with values in the properties and getters set. Now we just want values in format of array. This can be done easily just by calling our static function.
$result1 = \Niko9911\Serializable\EntityToArray::convert($entity);
var_dump($result1);
/**
[
'name'=>self::NAME,
'id'=>self::CODE,
'flag'=>
[
'mainColor'=>self::MAIN,
'height' => 150,
'width' => 245,
'registered'=>true,
'options'=>[]
]
]
*/
// And now you can just get json by doing following
\json_encode($result1);
Advanced
Okey, that was easy. Do you remember how we did extend Serializable in the Country class? Well, that is how we can make sure our class also implements \JsonSerializable. This is automatically made when you extend the class. Also this will add two new useful functions into your class when you extend it toJson(): and toArray(): array. Example how to take advantage of these functions is bellow.
$result2 = $entity->toArray();
$result3 = $entity->toJson();
$result4 = \json_encode($entity);
var_dump($result1);
/**
[
'name'=>self::NAME,
'id'=>self::CODE,
'flag'=>
[
'mainColor'=>self::MAIN,
'height' => 150,
'width' => 245,
'registered'=>true,
'options'=>[]
]
]
**/
var_dump($result1 === $result2); // True
var_dump($result3);
/**
{"name":"Finland","id":358,"flag":{"options":[],"mainColor":"Blue","height":150,"width":245,"registered":true}}
**/
var_dump($result3 === $result4); // True
/** END **/
Summary
Should I use this? Well, it depends. In case you want just develop something quickly and performance is not most critical, then you can use this. However, I would still agree to write Entities to array manually when doing REST responses. Just prevent exposing data what you should not expose, but you exposed due it was automatically put there.
Loops, loops and loops. Biggest issue of this library. You’re creating reflections and looping trough the properties. Like, just think about it. You have 10 properties plus two properties which are also entities and those have 10 another properties totaling about 30 properties. Also there could be 5 arrays in total. (Now there is total 35 properties.) This means this code will looped 35 times, plus all array items plus creating Reflections on every object. Not good.
However, if you can accept this performance loss, just go for it. I’m doing my freetime non-critical apps in PHP like bots, hobby sites, etc. Nothing critical and those will never scale into level of enterprise applications. Also I’m 99.9% of the time working on those projects alone. This means everything what saves my time from typing entities with 35 properties manually into array is welcome.
Pros
- Faster development
- Good for small applications
Cons
- Bad performance
- Can lead into data expose if not used correctly
Notice
Source is available in Github and Packagist, but due of this beign legacy and I will not anymore link it here.