Lighthouse GraphQL — hasManyThrough в сводной таблице

#laravel #eloquent #graphql #laravel-lighthouse

#laravel #красноречивый #graphql #laravel-lighthouse

Вопрос:

У меня есть проект, который я конвертирую из Rails REST API в Laravel GraphQL API (я новичок в Laravel), и я работаю над настройкой моделей и отношений.

У меня есть categories , category_items , и items таблица.

В category_items таблице есть столбцы для category_id , item_id и position .

В items таблице есть столбец для category_id .

Когда я открываю artisan tinker и запускаю AppModelsCategory::find(1)->items()->get(); , он возвращает все ожидаемое (включая сводную таблицу, вложенную под объектом Item). Однако, когда я запускаю его на graphql-playground, я получаю сообщение об ошибке:

 "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)",
  

У меня такое чувство, что это что-то крошечное, что я упускаю из виду, как опечатку или соглашение об именах, о котором я не знаю.

Что я также хотел бы сделать, так это включить position столбец из сводной таблицы в объект item. По сути, вместо того, чтобы иметь столбец «позиция», вложенный внутри оси элемента, я бы хотел, чтобы «позиция» была включена как часть объекта Item, однако я не уверен, возможно ли это.

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 AppModels;

use IlluminateDatabaseEloquentModel;
use IlluminateDatabaseEloquentRelationsBelongsTo;
use IlluminateDatabaseEloquentRelationsHasMany;
use IlluminateDatabaseEloquentRelationsBelongsToMany;

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('AppModelsItem', 'category_items', 'item_id', 'category_id')
            ->using('AppModelsCategoryItem')
            ->withPivot(['position'])
            ->withTimestamps();
    }
}
  

CategoryItem.php

 <?php

namespace AppModels;

use IlluminateDatabaseEloquentRelationsPivot;
use IlluminateDatabaseEloquentRelationsHasMany;

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 AppModels;

use IlluminateDatabaseEloquentModel;
use IlluminateDatabaseEloquentRelationsBelongsTo;
use IlluminateDatabaseEloquentRelationsBelongsToMany;

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

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

    public function category_item(): BelongsTo
    {
        return $this->belongsTo('AppModelsCategoryItem');
    }
}

  

Итак, в заключение, мои вопросы:

  1. Что вызывает ошибку GraphQL на игровой площадке?
  2. Можно ли добавить свойство сводной таблицы к ее непосредственному родительскому объекту?

Комментарии:

1. Обычно вам не нужно создавать модель для вашей сводной таблицы, но если вы хотите, вам нужно указать имя таблицы, поскольку вы используете нестандартное имя. protected $table = 'category_items'; Сводная таблица должна быть названа с помощью символов в алфавитном порядке, так category_item что .

2. Вы знаете, я действительно прочитал это в документах, и это вызвало другие ошибки, поэтому я сохранил это. Я могу начать идти по этому маршруту, чтобы снова его протестировать. На всякий случай, к какой модели я бы применил protected $table ? Кроме того, вы хотите сказать, что само имя таблицы должно быть «category_item» вместо «category_items»? Потому что все мои таблицы множественны: «категории», «category_items», «элементы» …. это просто соглашение об именовании Rails, и в Laravel все они должны быть разделены?

3. Хорошо, просто дважды проверил источник, и да, сводные модели ожидают единственное имя таблицы, поэтому вам следует включить это свойство в CategoryItem определение вашей модели. Извините за беспорядок при редактировании, я не должен был сомневаться в себе!

4. Все хорошо, я ценю помощь! Исправлена исходная ошибка, однако теперь я получаю Cannot return null for non-nullable field Item.category_item

Ответ №1:

@miken32 очень помог с этим. Я удалил ненужные CategoryItem.php модель, переименовал таблицу БД из category_items в category_item , и в моем schema.graphql переопределил типы следующим образом:

 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
}