Estou correndo Laravel 7 no PHP 7.4 com o MySQL 8.0.
Eu tenho três tabelas, User
, Company
e Department
, com seus respectivos modelos e fábricas.
Eu criei um teste onde eu estou adicionando a relação:
// 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();
Estou tendo o erro a seguir: 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))
Meu esquema de tabela é definida assim:
// 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();
});
É meu entendimento que não deve haver valores nulos no SQL-tabelas, que é por isso que estou deliberadamente tentando evitar ->nullable()
nos meus migrações. Especialmente para as chaves estrangeiras como estes.
EDITAR:
Eu tentei fazê-lo desta maneira, eu também fez uma tabela dinâmica para users_companies
. Agora eu posso anexar uma empresa, mas eu ainda estou recebendo um erro SQL quando fazer o teste desta maneira:
$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',
]);
Isso também falha com o erro indicado abaixo:
$company = factory(Company::class)->create();
$company->departments()->save(factory(Department::class)->make());
O erro é o seguinte: 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',
];
});
Fábricas
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',
];
});
Alguns problemas com a estrutura da tabela são muito claros à primeira vista.
- Parece que você está tentando adicionar uma
user_id
coluna para a suacompanies
mesa. Esta não é uma boa idéia, assumindo as suas empresas têm mais de um empregado. - Se você quiser usar
NOT NULL
colunas, é melhor definir um valor padrão para cada um deles.
Assim, podemos começar por escrever o migrações algo assim, incluindo tabelas dinâmicas para as relações empresa / usuário e do departamento / utilizador:
// 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');
});
Agora temos ligações entre tabelas. Um departamento é parte de uma empresa; um usuário pode fazer parte de vários departamentos e / ou várias empresas. Isto leva às seguintes relações :
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);
}
}
Agora código como este deve funcionar:
$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',
]);
Especificamente, o attach
método é usado para atualizar muitos-para-muitos relacionamentos, o que você não parecem ter definido, com base em seu layout tabela original.