Search code examples
eloquentlaravel-7eloquent-relationshiplaravel-relationslaravel-resource

is it possible to return different models in an API response


This is my first time posting here so please pardon my errors:

I have a search functionality whose route is:

Route::get('/search', 'SearchController@index');

Currently, I have an eloquent relationship where products has many deals. is it possible to return a single level deep array doing the following:

  • If the product has an active deal, return the deal only;
  • Otherwise, return the product itself.

here's what I earlier implemented in my Product.php:

public function deals()
    {
        return $this->hasMany(Deal::class, 'product_id');
    }

Deal.php

public function product()
    {
        return $this->hasOne(Product::class, 'id', 'product_id');
    }

SearchController:

public function index(Request $request)
        {
            $per_page = $request->per_page ?? 10;

            $products = Product::query()->latest()
            ->when($request->query('filter'), function ($query) use ($request) {
                $query->with('deals')->where('title', 'LIKE', "%$request->filter%");
            })
            ->when($request->query('category'), function ($query) use ($request) {
                $query->with('deals')->whereHas('categories', function ($q) use ($request) {
                    $q->where('title', 'LIKE', "%$request->category%");
                });
            })
            ->paginate($per_page);
            
            return new PaginatedCollection($products, ProductResource::class);
        }

and in my ProductResource:

<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

class ProductResource extends JsonResource
{
    public function toArray($request)
    {
        $details = array_filter($this->details ?: [], function ($d) {
            return $d != "";
        });

        $personalizedOptions = array_filter($this->personalized_options ?: [], function ($o) {
            return $o != "";
        });

        return [
            'id'                    => $this->id,
            'createdAt'             => $this->created_at,
            'updatedAt'             => $this->updated_at,
            'title'                 => $this->title,
            'sellerId'              => $this->sellerId,
            'description'           => $this->description,
            'categories'            => CategoryResource::collection($this->categories),
            'details'               => $details,
            'active'                => (bool) $this->active,
            'defaultPreviewImageId' => $this->default_preview_image_id,
            'originalPrice'         => $this->originalPrice,
            'shippingPrice'         => $this->shippingPrice,
            'shippingWeightLbs'     => $this->shippingWeightLbs,
            'shippingWeightOz'      => $this->shippingWeightOz,
            'shippingMaxDays'       => $this->shipping_max_days,
            'shippingMinDays'       => $this->shipping_min_days,
            'personalized'          => (bool) $this->personalized,
            'personalizedOptions'   => $personalizedOptions,
            'deals'                 => $this->deals ?? null,
            'options'               => ProductOptionResource::collection($this->productOptions),
            'images'                => ImageResource::collection($this->images->whereNull('meta')),
            'preview'               => new ImageResource($this->images->where('meta', '=', 'preview')->first()),
        ];
    }
}

Now, I have refactored the ProductResource to this but it's all returning null response

<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

class ProductResource extends JsonResource
{
    public function toArray($request)
    {
        $details = array_filter($this->details ?: [], function ($d) {
            return $d != "";
        });

        $personalizedOptions = array_filter($this->personalized_options ?: [], function ($o) {
            return $o != "";
        });

        if($this->deals){
            DealResource::collection($this->deals);
        }else{
            return [
                'id'                    => $this->id,
                'createdAt'             => $this->created_at,
                'updatedAt'             => $this->updated_at,
                'title'                 => $this->title,
                'sellerId'              => $this->sellerId,
                'description'           => $this->description,
                'categories'            => CategoryResource::collection($this->categories),
                'details'               => $details,
                'active'                => (bool) $this->active,
                'defaultPreviewImageId' => $this->default_preview_image_id,
                'originalPrice'         => $this->originalPrice,
                'shippingPrice'         => $this->shippingPrice,
                'shippingWeightLbs'     => $this->shippingWeightLbs,
                'shippingWeightOz'      => $this->shippingWeightOz,
                'shippingMaxDays'       => $this->shipping_max_days,
                'shippingMinDays'       => $this->shipping_min_days,
                'personalized'          => (bool) $this->personalized,
                'personalizedOptions'   => $personalizedOptions,
                // 'deals'                 => $this->deals ?? null,
                'options'               => ProductOptionResource::collection($this->productOptions),
                'images'                => ImageResource::collection($this->images->whereNull('meta')),
                'preview'               => new ImageResource($this->images->where('meta', '=', 'preview')->first()),
            ];
        }
    }
}

Solution

  • The reason why it may be giving the null result because of the condition check. it is returning an array you need to update it to this.

            if(count($this->deals))
    

    this will check if the deal array contains an element in the array. if not it will return products.