I'm using PHP8.1 and Laravel 9 for a project in which I've got the following enum:
enum OrderStatuses : string
{
case New = 'new';
case Pending = 'pending';
case Canceled = 'canceled';
case Paid = 'paid';
case PaymentFailed = 'payment-failed';
public function createOrderStatus(Order $order) : OrderStatus
{
return match($this) {
OrderStatuses::Pending => new PendingOrderStatus($order),
OrderStatuses::Canceled => new CanceledOrderStatus($order),
OrderStatuses::Paid => new PaidOrderStatus($order),
OrderStatuses::PaymentFailed => new PaymentFailedOrderStatus($order),
default => new NewOrderStatus($order)
};
}
one of the classes listed in the enum looks like this:
abstract class OrderStatus
{
public function __construct(protected Order $order)
{
}
/**
* Determines whether an order can transition from one status into another
*
* @return bool
*/
abstract public function canBeChanged() : bool;
}
class PaidOrderStatus extends OrderStatus
{
public function canBeChanged(): bool
{
return false;
}
}
all others are basically the same, they just differ on the implementation of the canBeChanged
method.
Now, I've got the following resource:
class OrdersResource extends JsonResource
{
public function toArray($request): array
{
return [
'id' => (string)$this->id,
'type' => 'orders',
'attributes' => [
'status' => $this->status,
'payment_type' => $this->payment_type,
'payment_transaction_no' => $this->payment_transaction_no,
'subtotal' => $this->subtotal,
'taxes' => $this->taxes,
'total' => $this->total,
'items' => OrderItemsResource::collection($this->whenLoaded('orderItems')),
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
]
];
}
}
which is called from my controller like this:
return (new OrdersResource($order))
->response()->setStatusCode(ResponseAlias::HTTP_OK);
Before implementing the enum my resource was working correctly, it returned the expected data. But after the enum, it's returning []
for the status
field.
A sample return is currently looking like this:
"id" => "86b4e2da-76d4-4e66-8016-88a251513050"
"type" => "orders"
"attributes" => array:8 [
"status" => []
"payment_type" => "card"
"payment_transaction_no" => "3kaL92f5UwOG"
"subtotal" => 3005.76
"taxes" => 0
"total" => 3005.76
"created_at" => "2022-08-31T12:47:55.000000Z"
"updated_at" => "2022-08-31T12:47:55.000000Z"
]
]
notice again the value for status
.
I've got a casting and a attribute in my Order's model:
protected $casts = [
'status' => OrderStatuses::class,
];
protected function status(): Attribute
{
return new Attribute(
get: fn(string $value) =>
OrderStatuses::from($value)->createOrderStatus($this),
);
}
Furthermore, if I dd
the type of $this->status
in the toArray
method from OrdersResource
it says that it is of type Domain\Order\Enums\PaidOrderStatus
which is correct.
I tried adding __toString()
to PaidOrderStatus
class but had no luck. What am I missing?
Update
I've added a test()
method to PaidOrderStatus
:
class PaidOrderStatus extends OrderStatus
{
public function canBeChanged(): bool
{
return false;
}
public function test() : string
{
return OrderStatuses::Paid->value;
}
}
and then did:
public function toArray($request): array
{
return [
'id' => (string)$this->id,
'type' => 'orders',
'attributes' => [
'status' => ($this->status)->test(),
'payment_type' => $this->payment_type,
'payment_transaction_no' => $this->payment_transaction_no,
'subtotal' => $this->subtotal,
'taxes' => $this->taxes,
'total' => $this->total,
'items' => OrderItemsResource::collection($this->whenLoaded('orderItems')),
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
]
];
}
and that gave me:
[
"id" => "8a2d6024-a63f-44ba-a145-cede2ecf3aaa"
"type" => "orders"
"attributes" => array:8 [
"status" => "paid"
"payment_type" => "card"
"payment_transaction_no" => "kC9upaoGb2Nd"
"subtotal" => 657.26
"taxes" => 0
"total" => 657.26
"created_at" => "2022-08-31T13:17:25.000000Z"
"updated_at" => "2022-08-31T13:17:25.000000Z"
]
and that worked. But it's a very hacky solution and I'd like to do better.