Issues with Database Seeder and Laravel - laravel

I have 3 tables, the schema is below.
Employees,
EmployeeDB.php
$factory->define(Employee::class, function (Faker $faker) {
return [
'emp_id' => $faker->randomDigit,
'name' => $faker->name,
'dept_id' => $faker->numberBetween($min = 1, $max = 15),
'salary_id' => $faker->randomDigit(),
'gender' => $faker->randomElement(['M', 'F', 'T']),
/*if I remove these next 2 statements, I receive an error for
SQLSTATE[42S22]: Column not found: 1054 Unknown column
'employee_id' in 'field list' (SQL: insert into `employees` (`emp_id`, `name`, `dept_id`, `salary_id`, `gender`, `date_of_joining`,
`date_of_birth`, `employee_id`, `updated_at`, `created_at`) values (2, Jamaal Beer, 3, 9, T, 2018-05-05 18:59:40, 2005-07-05 13:17:23, ?,
2019-12-11 11:15:42, 2019-12-11 11:15:42))
*/
'employee_id' => $faker->randomDigit,
//Same for this one as well
'department_id' => $faker->randomDigit,
'date_of_joining' => $faker->dateTimeBetween($startDate = '-5 years', $endDate = 'now', $timezone = null),
'date_of_birth' => $faker->dateTimeBetween($startDate = '-20 years', $endDate = 'now', $timezone = null),
Employee Migration table:
Schema::create('employees', function (Blueprint $table) {
$table->bigIncrements('emp_id');
$table->integer('dept_id')->unsigned();
$table->foreign('dept_id')->references('id')->on('departments');
$table->integer('salary_id')->unsigned();
$table->foreign('salary_id')->references('id')->on('salaries');
$table->string('name');
$table->string('gender');
$table->timestamp('date_of_joining');
$table->dateTime('date_of_birth');
/*SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'employee_id' cannot be null
(SQL: insert into `employees` (`emp_id`, `name`, `dept_id`, `salary_id`, `gender`, `employee_id`, `department_id`,
`date_of_joining`, `date_of_birth`, `updated_at`, `created_at`)
values (6, Martina Wuckert, 7, 3, F, ?, 3, 2018-09-14 05:59:15, 20
*/
$table->string('employee_id')->nullable();
$table->string('department_id')->nullable();
$table->timestamps();
//added to prevent errors
Departments
DepartmentsDB.php
factory->define(Department::class, function (Faker $faker) {
return [
//Yes, this is a hack. Use Multidimensional Arrays the next time.
'id' => $faker->numberBetween($min = 1, $max = 15),
'dept_name' => $faker->randomElement(['Production', 'Purchase & Quality', 'Operations', 'Sales', 'Customer Serice', 'Business Development', 'Maketing', 'Tech Support', 'Finance', 'Human Resources', 'Research & Development', 'IT', 'Legal']),
];
DepartmentsMigration
Schema::create('departments', function (Blueprint $table) {
$table->increments('id');
$table->string('dept_name');
$table->timestamps();
});
Salaries
SalaryDb.php
$factory->define(Salary::class, function (Faker $faker) {
return [
'id' => $faker->randomDigit(),
'monthlySalary' => $faker->randomNumber($nbDigits = 3),
//Yes this is a hack. Use MultiDimensional Arrays the next time.
];
});
SalariesMigration
$factory->define(Salary::class, function (Faker $faker) {
return [
'id' => $faker->randomDigit(),
'monthlySalary' => $faker->randomNumber($nbDigits = 3),
//Yes this is a hack. Use MultiDimensional Arrays the next time.
];
});
App\Department
class Department extends Model
{
//
public function employee()
{
return $this->hasMany(Employee::class);
}
public function salary()
{
return $this->hasMany(Salary::class);
}
}
App\Employee
class Employee extends Model
{ //belongsto block
public function department()
{
return $this->hasOne(Department::class);
}
public function Salary()
{
return $this->hasOne(Salary::class);
}
}
App\Salary
class Salary extends Model
{
//
public function employee()
{
return $this->belongsTo(Employee::class);
}
}
DatabaseSeeder.php
factory(App\Employee::class, 25)->create()->each(function ($employee) {
$department = factory(App\Department::class)->make();
$employee->department()->save($department);
$salary = factory(App\Salary::class)->make();
$employee->salary()->save($salary);
});
When I run the below command, this is the error I get
php artisan db:seed
Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (`volga_2`.`employees`, CONSTRAINT `employees_dept_id_foreign` FOREIGN KEY (`dept_id`) REFERENCES `departments` (`id`))")
What I am trying to do is: for every Employee Record, One Department and One Salary is assigned.
This is for later: Departments cannot be duplicated within its own table. Ie. Once a department id with name is created, it cannot be recreated again.
I know I am going wrong, else there wouldn't be errors. When I run the dbSeeder for departments and salaries, it works fine. But whenever there is a duplicate department entry it throws up an error. But the data stored in the db is fine.
I would like a solution or a few pointers, because I'm still learning Laravel.
Thanks!
EDIT
When I execute
php artisan migrate
Order of Migrations
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_000000_create_users_table (0.44 seconds)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated: 2014_10_12_100000_create_password_resets_table (1.18 seconds)
Migrating: 2019_12_09_105432_create_departments_table
Migrated: 2019_12_09_105432_create_departments_table (0.29 seconds)
Migrating: 2019_12_09_105743_create_salaries_table
Migrated: 2019_12_09_105743_create_salaries_table (0.21 seconds)
Migrating: 2019_12_10_104739_create_employees_table
Migrated: 2019_12_10_104739_create_employees_table (2.04 seconds)

Just change the order of the migrations. I am guessing you are trying to make relation before the table created. Make the first migration of the parent table

Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (`volga_2`.`employees`, CONSTRAINT `employees_dept_id_foreign` FOREIGN KEY (`dept_id`) REFERENCES `departments` (`id`))")
this error says that it cant generate relation with departments table for some reason. as you write, you try to generate Employee with relation to 2 other table before generate them, by changing name of your migration, you can reorder migration. that means you can change order of running your migration with changing time in migration name so that you migrate with this order:
1) DepartmentsMigration
2) SalariesMigration
3) Employee Migration

Thank you all for providing solutions and ideas to help me solve this issue. But after tinkering with it, I worked out a solution that works fine.
I will just be posting the changes i made to keep it easier to keep a track of and it may be of help to someone else.
Here it is.
EmployeeDB.php
$factory->define(Employee::class, function (Faker $faker) {
return [
'emp_id' => $faker->unique()->randomNumber($nbDigits = 3, $strict = true),
'name' => $faker->name,
//dept_id and salary_id pulls the data from their respective tables and then //randomly assigns an id from the id column
'dept_id' => App\Department::all()->random()->id,
'salary_id' => App\Salary::all()->random()->id,
'gender' => $faker->randomElement(['M', 'F', 'T']),
'date_of_joining' => $faker->dateTimeBetween($startDate = '-5 years', $endDate = 'now', $timezone = null),
'date_of_birth' => $faker->dateTimeBetween($startDate = '-20 years', $endDate = 'now', $timezone = null),
];
});
EmployeeMigration
public function up()
{
Schema::create('employees', function (Blueprint $table) {
$table->unsignedInteger('emp_id', true);
$table->unsignedInteger('dept_id');
$table->foreign('dept_id')->references('id')->on('departments');
$table->unsignedInteger('salary_id');
$table->foreign('salary_id')->references('id')->on('salaries');
$table->string('name');
$table->string('gender');
$table->timestamp('date_of_joining');
$table->dateTime('date_of_birth');
$table->timestamps();
//added to prevent errors
});
Departmentdb
$factory->define(Department::class, function (Faker $faker) {
return [
//Yes, this is a hack. Use Multidimensional Arrays the next time.
'id' => $faker->unique()->numberBetween(1, 12),
'dept_name' => $faker->randomElement(['Production', 'Purchase & Quality', 'Operations', 'Sales', 'Customer Serice', 'Business Development', 'Maketing', 'Tech Support', 'Finance', 'Human Resources', 'Research & Development', 'IT', 'Legal']),
];
});
DepartmentMigration
protected $dept_id = 'id';
public function up()
{
Schema::create('departments', function (Blueprint $table) {
$table->unsignedInteger('id', true);
$table->string('dept_name');
$table->timestamps();
});
}
SalaryDB
$factory->define(Salary::class, function (Faker $faker) {
return [
'id' => $faker->unique()->randomNumber($nbDigits = 5, $strict = true),
'monthlySalary' => $faker->randomNumber($nbDigits = 3),
//Yes this is a hack. Use MultiDimensional Arrays the next time.
];
});
Salary Migration
public function up()
{
Schema::create('salaries', function (Blueprint $table) {
$table->unsignedInteger('id', true);
$table->string('monthlySalary');
$table->timestamps();
});
}
DatabaseSeeder.php
public function run()
{
$this->call([UsersTableSeeder::class]);//this is not important
factory('App\Department', 12)->create();
factory('App\Salary', 50)->create();
factory('App\Employee', 55)->create();
}
}
What is happening is that, I first seeded the Departments table, with the department ID's and their respective names. Then, I seeded, the salaries table, with just the salary id's and the salary amounts.
Then the employees seeder ran, which referenced the departments, and salaries table through the foreign key relations and inserted the data from the id columns in those respective tables into the dept_id and salary_id columns in my employees table.
Another important aspect to this, to set up the Primary Key is using
protected $id = 'Primary Key Table name'
See DepartmentMigration code block above. This needs to be defined outside the scope of the up() function, and must be present wherever a primary key needs to be defined.
I have done this over 3 hours, working out each individual DB Seeder, so I am happy this works. There is possibly a better more elegant solution out there, but I will get on to that tomorrow. :)
Thank you for reading, and I hope that this has help you.
I will answer any questions regarding this to the best of my ability. :)

Related

Laravel 8 Factory Migration fails on n + 1 iteration

Not sure what is happening here. I have a table called news as follows:
Schema::create('news', function(Blueprint $table) {
$table->increments('id');
$table->unsignedBigInteger('customer_id');
$table->string('title');
$table->string('featured_img');
$table->text('body');
$table->enum('audience', ['all', 'users', 'customers', 'residents', 'plumbers'])->default('users');
$table->timestamp('published_at')->nullable();
$table->timestamp('featured_from')->nullable();
$table->timestamp('featured_to')->nullable();
$table->timestamps();
$table->foreign('customer_id')
->references('id')
->on('customers');
});
I have a factory for news:
class NewsFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* #var string
*/
protected $model = News::class;
/**
* Define the model's default state.
*
* #return array
*/
public function definition()
{
$audience = [
'all', 'users', 'customers', 'residents', 'plumbers',
];
$img = [
'https://images.unsplash.com/photo-1492724441997-5dc865305da7?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1679&q=80',
'https://images.unsplash.com/photo-1547586696-ea22b4d4235d?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1679&q=80',
'https://images.unsplash.com/photo-1496128858413-b36217c2ce36?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1679&q=80',
];
$date = now()->subMonths(rand(1, 20));
$randAudience = $audience[array_rand($audience)];
$randImg = $img[array_rand($img)];
$customer = Customer::where('parent_id', 1)->orWhereNull('parent_id')->inRandomOrder()->first();
echo "c => " . $randAudience . "\n";
return [
'customer_id' => $customer->id,
'audience' => $randAudience,
'private_customer_id' => null,
'title' => $this->faker->title,
'body' => $this->faker->realText,
'featured_img' => $randImg,
'published_at' => $date,
'featured_from' => $date,
'featured_to' => $date->addDays(14),
];
}
}
I then call it in a seeder like this:
public function run()
{
News::factory()
->count(3)
->create();
}
It shows me it runs the 3 times, then seems to fail on the fourth iteration (?) with the following error:
c => residents
c => all
c => users
Illuminate\Database\QueryException with message 'SQLSTATE[23514]: Check violation: 7 ERROR: new row for relation "news" violates check constraint "news_audience_check"
Any help is appreciated
UPDATE: From the Postgresql console, the \d news output per #IGP suggestion. The check is the ENUM validation.
Check constraints:
"news_audience_check" CHECK (audience::text = ANY (ARRAY['all'::character varying, 'users'::character varying, 'customers'::character varying, 'residents'::character varying, 'plumbers'::character varying]::text[]))
Additionally, if I comment out the random assignment of an audience value (and allow the default to be set) then it works.
// 'audience' => $randAudience,
Using a random value it triggers the check on n+1 iteration (why a 4th iteration when I asked for 3 instances?). Also, the error dump (below) shows the audience column of n+1 record to contain residents which is a valid entry.
SQLSTATE[23514]: Check violation: 7 ERROR: new row for relation "news" violates check constraint "news_audience_check"
DETAIL: Failing row contains (1, b9f00221-49f8-4d92-9813-026206e6aa76, 2, Mr., https://images.unsplash.com/photo-1547586696-ea22b4d4235d?ixlib=..., Mouse. '--I proceed. "Edwin and Morcar, the earls of Mercia and ..., "residents", null, 2020-10-23 23:35:58, 2020-10-23 23:35:58, 2020-10-23 23:35:58, 1, 1, null, 2021-01-09 23:35:59, 2021-01-09 23:35:59). (SQL: insert into "news" ("customer_id", "audience", "private_customer_id", "title", "body", "featured_img", "published_at", "featured_from", "featured_to", "uuid", "creator_id", "updater_id", "updated_at", "created_at") values (2, "residents", ?, Mr., Mouse. '--I proceed. "Edwin and Morcar, the earls of Mercia and Northumbria, declared for him: and even Stigand, the patriotic archbishop of Canterbury, found it advisable--"' 'Found WHAT?' said the., https://images.unsplash.com/photo-1547586696-ea22b4d4235d?ixlib=rb-.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1679&q=80, 2020-10-23 23:35:58, 2020-10-23 23:35:58, 2020-10-23 23:35:58, b9f00221-49f8-4d92-9813-026206e6aa76, 1, 1, 2021-01-09 23:35:59, 2021-01-09 23:35:59) returning "id")

Not able to reference foreign key other than primary in Laravel

I have two models: Departments and DepartmentTypes. In DepartmentTypes i have a column "depth" serving as foreign key to departments (field: hirarchy_lvl). That said, i am trying to get the DepartmentType label to be displayed in a view but keep on failing...
Here's what i have:
Department.php:
public function departmentType()
{
return $this->belongsTo(DepartmentType::class, 'depth', 'hirarchy_lvl');
}
DepartmentType.php
public function department()
{
return $this->hasMany(Department::class, 'hirarchy_lvl', 'depth');
}
db schema create:
public function up()
{
Schema::create('department_types', function (Blueprint $table) {
$table->bigIncrements('id');
$table->timestamps();
$table->bigInteger('depth')->unsigned()->unique();
$table->string('hirarchy_lvl_name');
});
DB::table('department_types')->insert(array('created_at' => now(), 'depth' => '10', 'hirarchy_lvl_name' => 'company'));
DB::table('department_types')->insert(array('created_at' => now(), 'depth' => '20', 'hirarchy_lvl_name' => 'department'));
DB::table('department_types')->insert(array('created_at' => now(), 'depth' => '30', 'hirarchy_lvl_name' => 'team'));
DB::table('department_types')->insert(array('created_at' => now(), 'depth' => '40', 'hirarchy_lvl_name' => 'individual'));
Schema::table('departments', function (Blueprint $table) {
$table->foreign('hirarchy_lvl')->references('depth')->on('department_types')->onDelete('NO ACTION')->change(); //shall reference other objective
});
}
In the controller:
public function listDepartmentsByHirarchy($hirLvl = 0){
if($hirLvl){
$departments = Department::where('hirarchy_lvl', $hirLvl)->get();
}
else
{
$departments = Department::get();
}
$department_types = DepartmentType::get();
return view('configcenter/listDepartments', compact('departments', 'department_types'));
}
Testing in the view:
<td>
{{$department}}
</td>
gives me:
{"id":2,"created_at":"2019-12-18 13:04:11","updated_at":null,"description":"dep1","parent_dep":{"id":1,"created_at":"2019-12-18 13:04:11","updated_at":null,"description":"company","parent_dep":1,"hirarchy_lvl":10},"hirarchy_lvl":20}
Leading me to the conclusion that the foreign key does not get resolved properly (as it's just the value rather than the object i would need over here).
Now, after studying the documentation (https://laravel.com/docs/5.4/eloquent-relationships#has-many-through) and several posts, i cannot help myself but ask the noobish question: how do i reference the foreign key properly so i can work on the object in the view?
appreciate any hint...
Your Department::departmentType() relationship is incorrect, I believe. The fields should be switched.
When defining the belongsTo side of the relationship, the second parameter is the field on the defining model (departments.hirarchy_lvl) and the third parameter is the field on the related model (department_types.depth).
When defining the hasOne/hasMany side of the relationship, it is the opposite. The second parameter is the field on the related model, and the third parameter is the field on the defining model.
Department.php:
public function departmentType()
{
return $this->belongsTo(DepartmentType::class, 'hirarchy_lvl', 'depth');
}
DepartmentType.php
public function department()
{
return $this->hasMany(Department::class, 'hirarchy_lvl', 'depth');
}

Laravel FirstOrCreate Method returned Column not found

I am trying to check if data exist in a table before saving. I can save to the table
public function read($book_id)
{
MyBook::create([
'book_id' => $id,
'user_id' => Auth::id()
]);
$readBooks = Book::where('id', $book_id)->get()->first();
return view('read')->with('data', $readBooks);
}
But when I tried to use firstOrCreate to check before saving it returned this error
SQLSTATE[42S22]: Column not found: 1054 Unknown column '1' in 'where
clause' (SQL: select * from my_books where (1 is null and 1 is
null) limit 1)
This is the method I am using
public function read($book_id)
{
$mybook = MyBook::firstOrCreate([
['book_id' => $book_id],
['user_id' => Auth::id()]
]);
$readBooks = Book::where('id', $book_id)->get()->first();
return view('read')->with('data', $readBooks);
}
My my_book table schema
public function up()
{
Schema::create('my_books', function (Blueprint $table) {
$table->bigIncrements('id');
$table->integer('book_id')->unsigned();
$table->integer('user_id')->unsigned();
$table->timestamps();
});
}
I don't know what I am doing wrong, I hope I can find help here.Thanks.
You are passing array inside it also 2 array. But that is not correct. You must be use like this
$mybook = MyBook::firstOrCreate(
['book_id' => $book_id],
['user_id' => Auth::id()]
);
You are passing a single array of arrays as the first argument to firstOrCreate. It takes associative arrays for the arguments.
$mybook = MyBook::firstOrCreate(
['book_id' => $book_id],
['user_id' => Auth::id()]
);
Argument 1 and 2 are associative arrays.
Laravel 6.x Docs - Eloquent - Other Creation Methods firstOrCreate

Laravel seed's with uuid

I have problem with my seeds. Here is my structure of my tables:
1.Complaints:
Schema::create('complaints', function (Blueprint $table) {
$table->uuid('id');
$table->unsignedInteger('origin_id');
$table->timestamps();
$table->primary('id');
});
2.Complaint_bill
Schema::create('complaint_bills', function (Blueprint $table) {
$table->uuid('complaint_id');
$table->string('symbol')->nullable();
$table->string('item_name');
$table->timestamps();
$table->primary('complaint_id');
$table->foreign('complaint_id')->references('id')-
>on('complaints');
Now I have seeds :
factory(Complaint::class, 10)->create()->each(function ($c) {
$product = Product::inRandomOrder()->first();
factory(ComplaintBill::class, 10)->create([
'complaint_id' => $c->id,
'item_id' => $product->id,
'item_name' => $product->name,
'item_numeric_index' => $product->numeric_index,
'item_gross_price' => $product->sell_price_gross,
]);
})'
I have problem/error like this:
SQLSTATE[23505]: Unique violation: 7 ERROR: duplicate key value
violates u
nique constraint "complaint_bills_pkey"
DETAIL: Key (complaint_id)=(ea302ab8-67dc-3bed-afc8-4215a99f1f68)
already exists.
When I comment primary in Complaint_bill ( column - complaint_id )then everything is ok. It looks like the problem is that I have primary key on uuid on Complaint_bill, which is foregin on Complaint->id. Why it is doing like this? I can't have primary on foregin when I have relation with two primary's?
The reason you are seeing this issue is that a UUID is not an auto-incrementing value in the traditional sense that an INTEGER AUTO_INCREMENT is when defining a primary key. When using factory methods to insert a set of data, the value doesn't "go up by one" automatically on each insert.
You would need to initialise each model generated with a boot function to generate the UUID for yourself prior to storage. From the below article:
protected static function boot()
{
parent::boot();
static::creating(function ($model) {
$model->{$model->getKeyName()} = Uuid::generate()->string;
});
}
The below article explains it in a bit more detail.
https://medium.com/#steveazz/setting-up-uuids-in-laravel-5-552412db2088
Try this: \Ramsey\Uuid\Uuid::uuid4()->toString()
`DB::table('customers')->insert([
['id' => \Ramsey\Uuid\Uuid::uuid4()->toString(), 'name' => 'Test', 'color' => '#aaa', 'created_at' => $now],
]);`

Having problems making a query in one to many Relationship in Laravel 4

This is pretty weird and I have no idea what i'm doing wrong.
I have 2 models:
class Project extends Eloquent {
public function status()
{
return $this->belongsTo('ProjectStatus','status_id');
}
}
and
class ProjectStatus extends Eloquent {
protected $table = 'PStatus';
public function projects()
{
return $this->hasMany('Project');
}
}
The table "projects" has the proper foreign keys:
Schema::create('PStatus', function($table) {
$table->increments('id');
$table->string('name', 64);
$table->unique('name');
});
Schema::create('Projects', function($table) {
$table->increments('id');
$table->string('name', 100);
$table->integer('status_id')->unsigned();
$table->foreign('status_id')->references('id')
->on('PStatus')
->onDelete('cascade');
});
In the database (for example) I have only 1 project: "Project_1" (id = 1) with status_id = 1 (Lets say status name = "Open"). If I execute the following query:
$projects = Project::with(array('status' => function($query){
$query->where('name', '<>', 'Open');
}))->get();
I'm still getting the project in the results!!. This is the sql log:
array (size=3)
'query' => string 'select * from `PStatus` where `PStatus`.`id` in (?) and `name` <> ?' (length=67)
'bindings' =>
array (size=2)
0 => int 1
1 => string 'Open' (length=4)
'time' => float 0.36
if I print:
var_dump($projects->count());
I still get 1 project! How come?
I can easily solve my problem by changing the query to something like:
$projects = Project::where('status_id', '<>', 1)->get(); //assuming that status_id=1 => "Open"
but i prefer not to use ids directly when I guess the method with should work. What am I doing wrong here???
Actually this is the answer:
Eloquent Nested Relation with Some Constraint
A huge confusion with the with method. I should have used a join.

Resources