Chris Rockwell
Chris Rockwell

Reputation: 1852

Not getting expected data for Laravel JSON:API

I am trying to create an API based on the following two Models:

Product.php

<?php

namespace App\Models\Commerce;

use ...

class Product extends Model {

    use HasFactory;
    use SoftDeletes;

    public mixed $id;

    protected $fillable = ['user_id', 'title', 'default_variation', 'status'];

    /**
    +     * @return HasMany
    +     */
    public function productVariations() : HasMany {
        return $this->hasMany('App\Models\Commerce\ProductVariation');
    }

    public function defaultVariation() : HasOne {
        return $this->hasOne('App\Models\Commerce\ProductVariation', 'default_variation');
    }
}

ProductVariation.php

<?php

namespace App\Models\Commerce;

use ...

class ProductVariation extends Model {
    use HasFactory;
    protected $fillable = ['user_id', 'product_id', 'sku', 'title', 'price', 'status'];

    public function product() : BelongsTo {
        return $this->belongsTo(Product::class);
    }
}

I have configured the JSON:API according to the documents, as best I can tell:

routes/api.php

<?php

use ...

JsonApiRoute::server('v2')->prefix('v2')->resources(function ($server) {
    $server->resource('products', JsonApiController::class)->readOnly();
    $server->resource('product-variations', JsonApiController::class)->readOnly();
});

ProductSchema.php

<?php
// For authentication see ProductPolicy
namespace App\JsonApi\V2\Products;

use ...

class ProductSchema extends Schema
{
    public static string $model = Product::class;
    public function fields(): array
    {
        return [
            ID::make(),
            DateTime::make('createdAt')->sortable()->readOnly(),
            DateTime::make('updatedAt')->sortable()->readOnly(),
            Str::make('title'),
            Number::make('user_id'),
            Number::make('defaultVariation'),
            HasOne::make('defaultVariation', 'default_variation'),
            Boolean::make('status'),
            HasMany::make('productVariations')
        ];
    }

    public function filters(): array
    {
        return [
            WhereIdIn::make($this),
        ];
    }

    public function pagination(): ?Paginator
    {
        return PagePagination::make();
    }

}

ProductVariationSchema.php

<?php

namespace App\JsonApi\V2\ProductVariations;

use ...

class ProductVariationSchema extends Schema
{

    public static string $model = ProductVariation::class;

    public function fields(): array
    {
        return [
            ID::make(),
            DateTime::make('createdAt')->sortable()->readOnly(),
            DateTime::make('updatedAt')->sortable()->readOnly(),
            Number::make('userId'),
            Number::make('productId'),
            Str::make('sku'),
            Str::make('title'),
            Number::make('price'),
            Boolean::make('status'),
            BelongsTo::make('product')
        ];
    }


    public function filters(): array
    {
        return [
            WhereIdIn::make($this),
        ];
    }

    public function pagination(): ?Paginator
    {
        return PagePagination::make();
    }

}

With some dummy data in the database, I can access the endpoint /api/v2/products and receive the following:

{
    "jsonapi": {
        "version": "1.0"
    },
    "data": [
        {
            "type": "products",
            "id": "3",
            "attributes": {
                "createdAt": null,
                "updatedAt": null,
                "title": "foo-product",
                "user_id": 1,
                "status": 1
            },
            "relationships": {
                "defaultVariation": {
                    "links": {
                        "related": "domain/api/v2/products/3/default-variation",
                        "self": "domain/api/v2/products/3/relationships/default-variation"
                    }
                },
                "productVariations": {
                    "links": {
                        "related": "domain/api/v2/products/3/product-variations",
                        "self": "domain/api/v2/products/3/relationships/product-variations"
                    }
                }
            },
            "links": {
                "self": "domain/api/v2/products/3"
            }
        }
    ]
}

/api/v2/product-variations

{
    "jsonapi": {
        "version": "1.0"
    },
    "data": [
        {
            "type": "product-variations",
            "id": "3",
            "attributes": {
                "createdAt": null,
                "updatedAt": null,
                "userId": 1,
                "productId": 3,
                "sku": "foo-bar",
                "title": "foo bar ",
                "price": "11.11",
                "status": 1
            },
            "relationships": {
                "product": {
                    "links": {
                        "related": "domain/api/v2/product-variations/3/product",
                        "self": "domain/api/v2/product-variations/3/relationships/product"
                    }
                }
            },
            "links": {
                "self": "domain/api/v2/product-variations/3"
            }
        }
    ]
}

However, if I try api/v2/products/3/product-variations or /api/v2/products/3/relationships/product-variations I get a 404 error:

{
    "jsonapi": {
        "version": "1.0"
    },
    "errors": [
        {
            "status": "404",
            "title": "Not Found"
        }
    ]
}

Additionally, if I run a test I also get a 404 but I do see that the variations are correctly related: enter image description here

I think that somewhere I have misconfigured something, but this is my first time doing this in Laravel so I might be completely missing a configuration also.

Upvotes: 0

Views: 794

Answers (1)

Kolovos Konstantinos
Kolovos Konstantinos

Reputation: 564

You only set a JsonApiRoute (starting with api) and prefix('v2') meaning api/v2 group. In this group you add 2 more product-variations meaning /api/v2/product-variations and products /api/v2/products. So anything else don't exists and laravel returns 404.

You need to add

$server->resource('products/{id}/product-variations', JsonApiController::class)->readOnly();

for api/v2/products/3/product-variations

and

$server->resource('products/{id}/relationships/product-variations', JsonApiController::class)->readOnly();

for /api/v2/products/3/relationships/product-variations

Also you can add (int $id) in your controller methods to read this id you pass.

Upvotes: 0

Related Questions