0

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.

MrCujo
  • 1,218
  • 3
  • 31
  • 56

1 Answers1

0

I'm sure you've already solved this as it's been months ago, but first you don't need the Attribute mutator as you have already defined the cast which will correctly map the string value to the right enum case.

Then in the resource, you just get the value from the enum like so.

'status' => $this->status->value,
Esi're
  • 66
  • 3