Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Sinres's avatar

API Resource Collection - How use realtionship?

Hello!

In my REST API I have a Sale Resource and DeviceInspection Resource and I need use data from Sale in DeviceInspection for display records. I trying like this but this is not work :-( Where I make a mistake?

Sale model

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use App\DeviceInspection;

class Sale extends Model
{
    use SoftDeletes;
    protected $fillable = [
        'user_id', 
        'sales_number', 
        'customer_institution', 
        'institution_address', 
        'product_name', 
        'manufacturer', 
        'serial_number', 
        'amount_of_sales', 
        'sale_date', 
        'guarantee_period', 
        'directory_link',
    ];
    public function inspection() {
        return $this->hasMany(DeviceInspection::class);
    }
}

Device Inspection model

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use App\Sale;

class DeviceInspection extends Model
{
    use SoftDeletes;
    /**
    * The atributes that should be muted to dates.
    *
    * @var array
    */
    protected $fillable = [
        'sale_id',
        'inspection_date',
        'service_man',
        'status'
    ];
    protected $dates = ['deleted_at'];
    
    public $sortable = ['created_at'];
    
    public function sale()
    {
        return $this->belongsTo(Sale::class);
    }
}

Sale Resource

<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

class Sale extends JsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */
    public function toArray($request)
    {
        return [
            'id' => $this->id,
            'sale_number' => $this->sale_number,
            'user_id' => $this->user_id,
            'customer_institution' => $this->customer_institution,
            'institution_address' => $this->institution_address,
            'product_name' => $this->product_name,
            'manufacturer' => $this->manufacturer,
            'serial_number' => $this->serial_number,
            'amount_of_sales' => $this->amount_of_sales,
            'sale_date' => $this->sale_date,
            'term_realization' => $this->term_realization,
            'guarantee_period' => $this->guarantee_period,
            'directory_link' => $this->directory_link,
            'file_sales' => $this->file_sales,
            'sale_type' => $this->sale_type,
        ];
    }

    public function with($request) {
        return [
            'version' => '1.0.0'
        ];
    }
}

DeviceInspection Resource

<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

class DeviceInspection extends JsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */
    public function toArray($request)
    {
        return [
            'id' => $this->id,
            'sale_id' => $this->sale_id,
            'inspection_date' => $this->inspection_date,
            'service_man' => $this->service_man,
            'status' => $this->status,
            'warranty_service' => $this->warranty_service,
            'created_at' => $this->created_at,
            'updated_at' => $this->updated_at,
            'sale' => Sale::collection($this->whenLoaded('sale'))
        ];
    }

    public function with($request) {
        return [
            'version' => '1.0.0'
        ];
    }
}

And my API Controller method show

/**
     * Display the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function show($id)
    {
        return new DeviceInspectionResource(DeviceInspection::with('sale')->findOrFail($id));
    }

And when I try to get data from API by postman receives an error:

Symfony\Component\Debug\Exception\FatalThrowableError: Call to a member function first() on null in file C:\xampp\htdocs\Customer-Service_v_2.0\vendor\laravel\framework\src\Illuminate\Http\Resources\CollectsResources.php on line 24

How fix this??

Thanks

0 likes
2 replies
bobbybouwmann's avatar

@sinres You can't use Sale::collection() here, because sale is a one-to-many relationship. In this case, a DeviceInspection only has one sale item. Because of the belongsTo relationship sale won't return a collection but a single item. Because of that, the first method fails here.

So instead you need to do this:

return [
    // Other fields

    'sale' => Sale::resource($this->whenLoaded('sale')),
];

Let me know if that works for you ;)

Sinres's avatar

I came to this, that's right I can't use in this case collection haha :-)

I tried your solution but receives an error:

Symfony\Component\Debug\Exception\FatalThrowableError: Call to undefined method App\Http\Resources\Sale::resource() in file C:\xampp\htdocs\Customer-Service_v_2.0\app\Http\Resources\DeviceInspection.php on line 27

I found a solution for my problem and I did it that way in my api controller:

<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

class DeviceInspection extends JsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */
    public function toArray($request)
    {
        return [
            'id' => $this->id,
            'sale_id' => $this->sale_id,
            'inspection_date' => $this->inspection_date,
            'service_man' => $this->service_man,
            'status' => $this->status,
            'warranty_service' => $this->warranty_service,
            'created_at' => $this->created_at,
            'updated_at' => $this->updated_at,
            'user' => $this->user,
            'sale'=> $this->sale
            
        ];
    }

    public function with($request) {
        return [
            'version' => '1.0.0'
        ];
    }
}

DeviceInspection Model:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class DeviceInspection extends Model
{
    use SoftDeletes;
    /**
    * The atributes that should be muted to dates.
    *
    * @var array
    */
    protected $fillable = [
        'sale_id',
        'inspection_date',
        'service_man',
        'status'
    ];

    protected $dates = ['deleted_at'];
    
    public $sortable = ['created_at'];

    public function sale()
    {
        return $this->belongsTo(Sale::class);
    }

    public function user() {
        return $this->belongsTo(User::class);
    }
}

And reply from API like this:

{
    "data": {
        "id": 1,
        "sale_id": 1,
        "inspection_date": "2020-02-21",
        "service_man": null,
        "status": "expectant",
        "warranty_service": 1,
        "created_at": "2019-03-08T13:21:43.000000Z",
        "updated_at": "2019-03-08T13:21:43.000000Z",
        "user": {
            "id": 1,
            "name": "Adrian",
            "email": "[email protected]",
            "sex": "m",
            "department": "integrator",
            "avatar": null,
            "created_at": null,
            "updated_at": "2019-03-14 11:37:25"
        },
        "sale": {
            "id": 1,
            "user_id": 1,
            "sale_number": "Sale 045/02/2019/AG",
            "customer_institution": "Institution of Customer",
            "institution_address": "Institution Address",
            "product_name": "Genano 310",
            "manufacturer": "---",
            "serial_number": "0000-0000",
            "amount_of_sales": "1",
            "sale_date": "2019-02-21",
            "term_realization": "2019-02-21",
            "guarantee_period": "24",
            "directory_link": "C:/",
            "status": "expectant",
            "file_sales": null,
            "sale_type": "national",
            "created_at": "2019-03-08 14:21:43",
            "updated_at": "2019-10-09 23:56:55",
            "deleted_at": "2019-10-09 23:56:55"
        }
    },
    "version": "1.0.0"
}

This is work! :-) But way your solution don't work and what is the difference between these two ways?

You won't believe it but the data from eloquent relationship was not displayed in the API response because I was using softDeletes, do you know why this happens? If he comments on "use softDeletes;" everything works correctly

Please or to participate in this conversation.