rabrowne
rabrowne

Reputation: 96

Lighthouse Nested Mutations FK Field not being filled

I'm having some issues wrapping my head around nested mutations with Lighthouse.

I've got two models that are related: Ams\Account hasMany Ams\Contact. This is the Ams\Contact model:

namespace App\Models\Ams;

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

class Contact extends Model
{
    use SoftDeletes;

    public $table = 'ams_contacts';

    protected $dates = ['deleted_at'];

    public $fillable = [
        'ams_account_id',
        'forename',
        'surname',
        'active',
        'email'
    ];

    protected $casts = [
        'forename' => 'string',
        'surname' => 'string',
        'active' => 'boolean',
        'email' => 'string'
    ];

    public static $rules = [
        'ams_account_id' => 'required',
        'forename' => 'required',
        'surname' => 'required',
        'email' => 'required|email'
    ];

    public function amsAccount(): BelongsTo
    {
        return $this->belongsTo(\App\Models\Ams\Account::class, 'ams_account_id', 'id');
    }

}

My GraphQL schema file looks like this:

"A datetime string with format `Y-m-d H:i:s`, e.g. `2018-01-01 13:00:00`."
scalar DateTime @scalar(class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\DateTime")

"A date string with format `Y-m-d`, e.g. `2011-05-23`."
scalar Date @scalar(class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\Date")

type Query {
    amsAccounts: [AmsAccount!]! @paginate(defaultCount: 10 model: "App\\Models\\Ams\\Account")
    amsAccount(id: ID @globalId): AmsAccount @find(model: "App\\Models\\Ams\\Account")

    amsContacts: [AmsContact!]! @paginate(defaultCount: 10 model: "App\\Models\\Ams\\Contact")
    amsContact(id: ID @globalId): AmsContact @find(model: "App\\Models\\Ams\\Contact")
}

type Mutation {
    createAmsAccount(
        input: CreateAmsAccountInput! @spread
    ): AmsAccount @create(model: "App\\Models\\Ams\\Account")

    updateAmsAccount(
        input: UpdateAmsAccountInput! @spread
    ): AmsAccount @update(model: "App\\Models\\Ams\\Account")

    deleteAmsAccount(
        id: ID! @rules(apply: ["required"])
    ): AmsAccount @delete(model: "App\\Models\\Ams\\Account")

    createAmsContact(
        input: CreateAmsContactInput! @spread
    ): AmsContact @create(model: "App\\Models\\Ams\\Contact")

    updateAmsContact(
        input: UpdateAmsContactInput! @spread
    ): AmsContact @update(model: "App\\Models\\Ams\\Contact")

    deleteAmsContact(
        id: ID! @rules(apply: ["required"])
    ): AmsContact @delete(model: "App\\Models\\Ams\\Contact")
}

type AmsAccount @node(namespace: "App\\Models\\Ams\\Account") {
    id: ID! @globalId
    name: String!
    url: String
    telephone: String!
    created_at: DateTime!
    updated_at: DateTime!
}

input CreateAmsAccountInput {
    name: String!
    url: String
    telephone: String!
}

input UpdateAmsAccountInput {
    id: ID!
    name: String!
    url: String
    telephone: String!
}

input UpsertAmsAccountInput {
    id: ID!
    name: String!
    url: String
    telephone: String!
}

type AmsContact @node(namespace: "App\\Models\\Ams\\Contact") {
    id: ID! @globalId
    forename: String!
    surname: String!
    active: Boolean
    email: String!
    amsAccount: AmsAccount! @belongsTo
    created_at: DateTime!
    updated_at: DateTime!
}

input CreateAmsContactInput {
    forename: String!
    surname: String!
    active: Boolean
    email: String!
    amsAccount: CreateAmsAccountBelongsTo
}

input UpdateAmsContactInput {
    id: ID!
    forename: String!
    surname: String!
    active: Boolean
    email: String!
    amsAccount: UpdateAmsAccountBelongsTo
}

input UpsertAmsContactInput {
    id: ID!
    forename: String!
    surname: String!
    active: Boolean
    email: String!
    amsAccount: UpsertAmsAccountBelongsTo
}

input CreateAmsAccountBelongsTo {
    connect: ID
    create: CreateAmsAccountInput
    update: UpdateAmsAccountInput
    upsert: UpsertAmsAccountInput
}

input UpdateAmsAccountBelongsTo {
    connect: ID
    create: CreateAmsAccountInput
    update: UpdateAmsAccountInput
    upsert: UpsertAmsAccountInput
    disconnect: Boolean
    delete: Boolean
}

input UpsertAmsAccountBelongsTo {
    connect: ID
    create: CreateAmsAccountInput
    update: UpdateAmsAccountInput
    upsert: UpsertAmsAccountInput
    disconnect: Boolean
    delete: Boolean
}

When I try and run the following mutation in the playground:

mutation {
  createAmsContact(
    input: {
      forename: "Jane"
      surname: "Doe"
      active: true
      email: "[email protected]"
      amsAccount: {
        connect: 1
      }
    }
  ) {
    id
    forename
    surname
    amsAccount: id
  }
}

I get an error about the following error message:

"debugMessage": "SQLSTATE[HY000]: General error: 1364 Field 'ams_account_id' doesn't have a default value (SQL: insert into `ams_contacts` (`forename`, `surname`, `active`, `email`, `updated_at`, `created_at`) values (Jane, Doe, 1, [email protected], 2020-04-12 15:31:52, 2020-04-12 15:31:52))"

I've tried a number of fixes and suggestions from around the web, and as far as I can see my schema follows the defined format from the documentation, but I cannot fathom out why it's not working.

I've also confirmed that the simple mutation for the account work fine. I also get a similar error message when I try to create a new account at the same time as the contact.

My inclination is that it's to do with the namespaces of the different models, but I can't imagine I'm the first to have this requirement, nor can I find any references to how to overcome this.

Any and all help will be greatly appreciated.

Upvotes: 0

Views: 230

Answers (1)

rabrowne
rabrowne

Reputation: 96

So I wasn't actually a million miles from the mark with my Namespace problem.

The problem lies with the Model file, specifically here:

public function amsAccount(): BelongsTo
    {
        return $this->belongsTo(\App\Models\Ams\Account::class, 'ams_account_id', 'id');
    }

The issue is that the BelongsTo had not been specified in the use clauses at the start of the model class. As such the return class type could not be detected properly.

As such it was necessary to add the following to the use clauses at the top of the file:

use Illuminate\Database\Eloquent\Relations\BelongsTo;

This applies to other relationship types.

This is what the Documentation states, but is so subtle that it didn't click.

RTFM.

Thanks for all who looked and hope this helps someone else in the future.

Upvotes: 2

Related Questions