I use PHP 8.3.11 and Laravel 11.22.0, and I want to have immutable DTOs that are also serializable.
My best idea so far was this:
trait SerializePrivate
{
public function jsonSerialize()
{
$vars = get_object_vars($this);
//dd($vars);
return $vars;
}
}
trait ImmutableAccess
{
public function __get($key)
{
return $this->$key;
}
}
class User
{
use SerializePrivate, ImmutableAccess;
protected int $id;
protected string $name;
protected string $surname;
//public function __construct(...) {....}
}
This seemed like a good idea but it doesn't work.
For some weird reason, when serializing the User
DTO, no properties get serialized.
I have tried like this:
public function getUser()
{
// ...
return response()->json($user);
}
...and like this:
public function getUser()
{
// ...
return new UserResource($user);
}
class UserResource extends \Illuminate\Http\Resources\Json\JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray(\Illuminate\Http\Request $request): array
{
//dd($this->resource); // <---- This shows the properties.
//dd(get_object_vars($this->resource)); // <---- This shows no properties.
return get_object_vars($this->resource);
}
}
Any idea how to fix my existing solution or use a better alternative?
The User
class needs to implement the JsonSerializable
interface, the return type also needs to be declared as mixed
:
trait SerializePrivate
{
public function jsonSerialize() : mixed
{
return get_object_vars($this);
}
}
class User implements \JsonSerializable
{
use SerializePrivate, ImmutableAccess;
protected int $id;
protected string $name;
protected string $surname;
}
You can also use the new initializer with readonly properties since PHP 8.1, or use readonly class since PHP 8.2:
# 8.1
class User
{
public function __construct(
public readonly int $id,
public readonly string $name,
public readonly string $surname,
) {}
}
# 8.2
readonly class User
{
public function __construct(
public int $id,
public string $name,
public string $surname,
) {}
}