0

I've got a project that I'm converting from Rails REST API to Laravel GraphQL API (I'm new to Laravel), and am working on setting up the models and relationships.

I have a categories, category_items, and items table.

The category_items table has columns for category_id, item_id and position.

The items table has a column for category_id.

When I open artisan tinker and run App\Models\Category::find(1)->items()->get();, it returns everything expected (including the pivot table nested beneath the Item object). However, when I run it in the graphql-playground, I get the error:

"debugMessage": "SQLSTATE[42S02]: Base table or view not found: 1146 Table 'my_project.category_item' doesn't exist (SQL: select * from `category_item` where 0 = 1)",

I have a feeling it's something tiny that I'm overlooking like a typo or a naming convention that I'm unaware of.

What I'd also like to do is include the position column from the pivot table in the item object. Essentially, instead of having the "position" column nested inside of the pivot of an Item, I'd like "position" to be included as a part of the Item object, however I'm not sure if that's possible.

Schema.graphql

type Query {
    categories: [Category!]! @field(resolver: "CategoriesQuery@find_by_user")
    items: [Item!]! @field(resolver: "ItemsQuery@find_by_user")
    notifications: [Notification!]! @field(resolver: "NotificationQuery@find_by_user")
}

type Category {
    id: ID!
    name: String!
    created_at: DateTime!
    updated_at: DateTime!

    category_items: [CategoryItem!]! @hasMany
    items: [Item!]! @belongsToMany
}

type CategoryItem {
    id: ID!
    position: Int!
    created_at: DateTime!
    updated_at: DateTime!

    categories: [Category!]! @hasMany
    items: [Item!]! @hasMany
}

type Item {
    id: ID!
    name: String
    quantity: Int
    created_at: DateTime!
    updated_at: DateTime!

    categories: [Category]! @belongsToMany
    category_item: CategoryItem! @belongsTo
}

Category.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;

class Category extends Model
{
    protected $fillable = [
        'name'
    ];

    // automatically eager load items of a category
    protected $with = ['items'];

    // define relationships
    public function category_items(): HasMany
    {
        return $this->hasMany(CategoryItem::class);
    }

    public function items(): BelongsToMany
    {
        return $this
            ->belongsToMany('App\Models\Item', 'category_items', 'item_id', 'category_id')
            ->using('App\Models\CategoryItem')
            ->withPivot(['position'])
            ->withTimestamps();
    }
}

CategoryItem.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Relations\Pivot;
use Illuminate\Database\Eloquent\Relations\HasMany;

class CategoryItem extends Pivot
{
    protected $fillable = [
        'category_id', 'item_id', 'position'
    ];

    // public function category(): HasMany
    // {
    //     return $this->hasMany(Category::class);
    // }

    // public function item(): HasMany
    // {
    //     return $this->hasMany(Item::class);
    // }
}

Item.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;

class Item extends Model
{
    protected $fillable = [
        'name', 'price', 'category_id', 'quantity'
    ];

    // define relationships
    public function categories(): BelongsToMany
    {
        return $this
            ->belongsToMany('App\Models\Category', 'category_items', 'item_id', 'category_id')
            ->using('App\Models\CategoryItem')
            ->withPivot(['position'])
            ->withTimestamps();
    }

    public function category_item(): BelongsTo
    {
        return $this->belongsTo('App\Models\CategoryItem');
    }
}

So to wrap up, my questions are:

  1. What is causing the GraphQL error in the playground?
  2. Is it possible to append a pivot table property to its immediate parent object?
J. Jackson
  • 3,326
  • 8
  • 34
  • 74
  • 1
    You generally don't need to create a model for your pivot table, but if you want to you'll need to specify the table name as you're using a non-standard name. `protected $table = 'category_items';` A pivot table *should* be named with singulars in alphabetic order, so `category_item`. – miken32 Nov 10 '20 at 22:40
  • You know, I'd actually read that in the docs and it caused other errors, so I kept it. I can start going down that route to test it out again. Just in case though, which model would I apply the `protected $table` to? Also, are you saying that the table name itself should be "category_item" instead of "category_items"? Because all of my tables are pluralized: "categories", "category_items", "items".... is that just a Rails naming convention, and in Laravel those should all be singularized? – J. Jackson Nov 10 '20 at 22:50
  • 1
    Ok, just double checked the source and yes, pivot models do expect a singular table name, so you should put that property into your `CategoryItem` model definition. Sorry for the edit mess, I shouldn't have doubted myself! – miken32 Nov 10 '20 at 23:04
  • All good, I appreciate the help! It's fixed the original error, however now I'm getting `Cannot return null for non-nullable field Item.category_item` – J. Jackson Nov 10 '20 at 23:07

1 Answers1

1

@miken32 helped out a bunch with this. I removed the unnecessary CategoryItem.php model, renamed the db table from category_items to category_item, and in my schema.graphql redefined the types to the following:

type Category {
    id: ID!
    name: String!
    created_at: DateTime!
    updated_at: DateTime!

    pivot: CategoryItemPivot
    items: [Item!]! @belongsToMany
}

type CategoryItemPivot {
    id: ID!
    position: Int!
    created_at: DateTime!
    updated_at: DateTime!

    categories: [Category!]! @hasMany
    items: [Item!]! @hasMany
}

type Item {
    id: ID!
    name: String
    quantity: Int
    created_at: DateTime!
    updated_at: DateTime!

    categories: [Category!]! @belongsToMany
    pivot: CategoryItemPivot
}
J. Jackson
  • 3,326
  • 8
  • 34
  • 74