lingo
lingo

Reputation: 1908

Laravel manage different API responses

Explanation

User scans barcode and system responses with a barcodable model (can be Article, Package or Inventory Shelf).

return new BarcodeResource($barcode);

Barcode resource resolves barcodable resource based on barcodable class. Each barcodable model return different JSON resouce.

// BarcodeResource.php

$modelResource = app()->makeWith(__NAMESPACE__ . '\\' . class_basename($this->barcodable) . 'Resource', [
    'resource' => $this->barcodable
]);

return [
    'code' => $this->code,
    'model_type' => class_basename($this->barcodable),
    'model_data' => $modelResource
];

In case of...

Problem

I want to prevent infinity loops with recursive resources.

Article
  >> Package
      >> Article (infinity loop begins because package resource 
                  returns articles in spesific package)

Package
  >> Article
      >> Package (loop...)
  >> Inventory Shelf
      >> Package (loop...)
  >> Child package


Inventory Shelf
  >> Package
      >> Article
      >> Inventory Shelf (loop...)
      >> Child package

Eager loading and unsetting relations should be one solution, but how I can unset those in the correct phase? Is this even possible with one resources or should I make multiple resources (recursive/normal)?


Tries

Extra attribute containing relations

I tried this solution, but magically $this->relations attribute gets changed to integer 1 after couple recursions...

class PackageResource extends JsonResource
{
    private $relations;

    public function __construct($resource, array $relations = [])
    {
        parent::__construct($resource);
        $this->relations = $relations;
    }

    public function toArray($request)
    {
        return [
            'id' => $this->id,
            'articles' => $this->when(in_array('articles', $this->relations), ArticleResource::collection($this->articles, $this->relations)),
            'children' => PackageResource::collection($this->children, $this->relations),
        ];
    }

Upvotes: 2

Views: 129

Answers (1)

Zoli
Zoli

Reputation: 1081

My solution for a similar situation was as follows: In the Resource files, I allways return relationships based on a request property with. This is attached to the request as follows: I need the User with Orders and Profile, but I also need the Area for an order, than the request is something like this:

http://example.com/api/v1/user/234?with=user.orders,user.profile,orders.area

and in the Resource file something similar:

    public function toArray($request)
    {
        $return = [
            'id' => $this->id,
            'name' => $this->name,
            'email' => $this->email,
            'location' => $this->location,
            'active' => $this->isActive(),
            'level' => $this->level,
        ];

        if($request->has('with')){

            $relationships = [
                'orders'=>[OrderCollection::class, 'orders'],
                'area'=>[Area::class, 'area', 'area.admin'],
                'profile'=>[UserProfile::class, 'profile'],
            ];

            $with = explode(',', $request->with);

            foreach($relationships as $key => $relationship){
                if( in_array("user.".$key, $with) ){
                    $return[$key] = new $relationship[0]($this->{$relationship[1]});
                }
            }
        }

        $return['created'] = $this->created_at->toDateTimeString();

        return $return;
    }

An other solution is to add an extra property to the resource class:

    protected $with = "";
    public function __construct(mixed $resource, $with="")
    {
        parent::__construct($resource);
    }

Than, when you call that resource, you can filter it in the previous way. I just tested and it worked for me.

Hope that helps.

Upvotes: 1

Related Questions