Laravel 7, SQLSTATE[23000]: Integrity constraint violation: 19 NOT NULL constraint failed when trying to add a relationship

NicT :

I'm running Laravel 7 on PHP 7.4 with MySQL 8.0.

I have three tables, User, Company and Department, with their respective models and factories.

I created a test where I'm adding the relationship:

// MyTest.php
$user = factory(User::class)->create();

$company = factory(Company::class)->make();
$company->user()->associate($user);
$company->create(); // it fails here because of NOT NULL constraint, companies.user_id

$department = factory(Department::class)->make();
$department->company()->associate($company);
$department->create();

I get the following error: Integrity constraint violation: 19 NOT NULL constraint failed: companies.user_id (SQL: insert into "companies" ("updated_at", "created_at") values (2020-03-10 07:27:51, 2020-03-10 07:27:51))

My table schema is defined like this:

// users
Schema::create('users', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->string('email')->unique();
    $table->timestamp('email_verified_at')->nullable();
    $table->string('phone');
    $table->integer('user_type');
    $table->string('password');
    $table->rememberToken();
    $table->timestamps();
});

// companies
Schema::create('companies', function (Blueprint $table) {
    $table->id();
    $table->foreignId('user_id')->constrained()->onDelete('cascade');
    $table->string('name');
    $table->string('contact_email');
    $table->string('contact_phone');
    $table->timestamps();
});

// departments
Schema::create('departments', function (Blueprint $table) {
    $table->id();
    $table->foreignId('company_id')->constrained()->onDelete('cascade');
    $table->string('name');
    $table->string('contact_email');
    $table->string('contact_phone');
    $table->timestamps();
});

It is my understanding that there should be no NULL-values in SQL-tables, which is why I am deliberately trying to avoid ->nullable() in my migrations. Especially for foreign keys like these.

EDIT:

I tried doing it this way, I also made a pivot table for users_companies. Now I can attach a company, but I'm still getting an SQL-error when doing the test this way:

$user = factory(User::class)->create();
$company = factory(Company::class)->create();

$user->companies()->attach($company);
$company->departments()->create([
    'name' => 'Department 1',
    'contact_email' => '[email protected]',
    'contact_phone' => '123456789',
]);

This also fails with the error stated below:

$company = factory(Company::class)->create();
$company->departments()->save(factory(Department::class)->make());

The error is this: Integrity constraint violation: 19 NOT NULL constraint failed: departments.company_id (SQL: insert into "departments" ("name", "contact_email", "contact_phone", "company_id", "updated_at", "created_at") values (Department 1, [email protected], '123456789', ?, 2020-03-11 07:59:31, 2020-03-11 07:59:31)).

CompanyFactory.php

<?php

/** @var \Illuminate\Database\Eloquent\Factory $factory */

use App\Company;
use Faker\Generator as Faker;

$factory->define(Company::class, function (Faker $faker) {
    return [
        'name' => 'Company 1',
        'contact_email' => '[email protected]',
        'contact_phone' => '123456789',
    ];
});

Factories

DepartmentFactory.php

<?php

/** @var \Illuminate\Database\Eloquent\Factory $factory */

use App\Department;
use Faker\Generator as Faker;

$factory->define(Department::class, function (Faker $faker) {
    return [
        'name' => 'Department 1',
        'contact_email' => '[email protected]',
        'contact_phone' => '123456789',
    ];
});
miken32 :

Some problems with your table structure are very clear at first glance.

  • It appears you're trying to add a user_id column to your companies table. This is not a good idea, assuming your companies have more than one employee.
  • If you want to use NOT NULL columns, you'd better define a default value for each of them.

So we can start by writing the migrations something like this, including pivot tables for the company/user and department/user relationships:

// companies
Schema::create('companies', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->string('contact_email')->default('');
    $table->string('contact_phone')->default('');
    $table->timestamps();
});

// departments
Schema::create('departments', function (Blueprint $table) {
    $table->id();
    $table->foreignId('company_id')->constrained()->onDelete('cascade');
    $table->string('name');
    $table->string('contact_email')->default('');
    $table->string('contact_phone')->default('');
    $table->timestamps();
});

// users
Schema::create('users', function (Blueprint $table) {
    $table->id();
    $table->string('email')->unique();
    $table->timestamp('email_verified_at')->nullable();
    $table->string('name')->default('');
    $table->string('phone')->default('');
    $table->integer('user_type')->default(0);
    $table->string('password');
    $table->rememberToken();
    $table->timestamps();
});

Schema::create('company_user', function (Blueprint $table) {
    $table->id();
    $table->foreignId('user_id')->constrained()->onDelete('cascade');
    $table->foreignId('company_id')->constrained()->onDelete('cascade');
});

Schema::create('department_user', function (Blueprint $table) {
    $table->id();
    $table->foreignId('user_id')->constrained()->onDelete('cascade');
    $table->foreignId('department_id')->constrained()->onDelete('cascade');
});

Now we have links between tables. A department is part of a company; a user can be part of multiple departments and/or multiple companies. This leads to the following relationships:

class User extends Model {
    // many-to-many
    public function companies() {
        return $this->belongsToMany(App\Company::class);
    }
    // many-to-many
    public function departments() {
        return $this->belongsToMany(App\Department::class);
    }
}

class Company extends Model {
    public function departments() {
        // one-to-many
        return $this->hasMany(App\Department::class);
    }
    public function users() {
        // many-to-many
        return $this->belongsToMany(App\User::class);
    }
}

class Department extends Model {
    public function company() {
        // one-to-many (inverse)
        return $this->belongsTo(App\Company::class);
    }
    public function users() {
        // many-to-many
        return $this->belongsToMany(App\User::class);
    }
}

Now code like this should work:

$user = factory(User::class)->create();
$company = factory(Company::class)->create();

$user->companies()->attach($company);
$company->departments()->create([
    'name' => 'Department 1',
    'contact_email' => '[email protected]',
    'contact_phone' => '123456789',
]);

Specifically, the attach method is used for updating many-to-many relationships, which you did not appear to have defined, based on your original table layout.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=237954&siteId=1