Laravel 8 Factory Migration fails on n + 1 iteration - laravel

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

Related

updateOrInsert does not work well.when a record exist , it happens duplicate error

updateOrInsert does not work well.when a record exist , it happens duplicate error
Error says
(PDOException(code: 23000): SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '2021-08-03 00:00:00-96' for key 'records.records_date_company_id_unique'
Record::updateOrInsert(
['company_id' => (int)$request->id
,'date' => $request->delivery_date
,'amount' => (int)$request->total_amount
,'created_at' => now()
]);
return response()->json($request, '200', ['Content-Type' => 'application/json','Charset' => 'utf-8'], JSON_UNESCAPED_UNICODE);
// I would like to add another response when it happens error
Migration define
class CreateRecordsTable extends Migration
{
public function up()
{
Schema::create('records', function (Blueprint $table) {
$table->dateTime('date');
$table->integer('company_id');
$table->Integer('amount');
$table->timestamps();
$table->unique(['date', 'company_id']);
});
}
The updateOrInsert method accepts two arguments: an array of conditions by which to find the record, and an array of column and value pairs indicating the columns to be updated.
In your case, it should look something like below:
Record::updateOrInsert(
['company_id' => (int) $request->id, 'date' => $request->delivery_date],
['amount' => (int) $request->total_amount]
);

Laravel eloquent can't input data that field link have foreign key to some table

It so weird, i can input data from phpmyadmin. but i get this error when using eloquent.
Integrity constraint violation: 1452 Cannot add or update a child row:
a foreign key constraint fails (laravel.medias, CONSTRAINT
medias_typeid_foreign FOREIGN KEY (TypeID) REFERENCES
media_types (TypeID)) (SQL: insert into medias (Title,
Synopsis) values (doraemon, travel to the world))
json data
{
"type_id": "3",
"title": "doraemon",
"synopsis": "travel to the world"
}
medias table
$table->mediumIncrements('MediaID');
$table->unsignedSmallInteger('TypeID')->default('0');
$table->string('Title', 255);
$table->text('Synopsis')->nullable();
$table->foreign('TypeID')->references('TypeID')->on('media_types');
$table->charset = 'utf8mb4';
$table->collation = 'utf8mb4_unicode_ci';
media types table
Schema::create('media_types', function (Blueprint $table) {
$table->smallIncrements('TypeID');
$table->string('Typename', 100);
$table->charset = 'utf8mb4';
$table->collation = 'utf8mb4_unicode_ci';
});
DB::table('media_types')->insert([
['TypeName' => 'Movie'],
['TypeName' => 'Tv show'],
['TypeName' => 'Comic']
]);
store function
Media::create([
'TypeID' => $request->type_id,
'Title' => $request->title,
'Synopsis' => $request->synopsis
]);
If the following code
Media::create([
'TypeID' => $request->type_id,
'Title' => $request->title,
'Synopsis' => $request->synopsis
]);
produces this SQL (according to your error message):
insert into medias (Title, Synopsis) values (doraemon, travel to the world)
Then you probably don't have TypeID in your $fillable properties in the Media model
class Media extends Model
{
protected $fillable = [
'TypeID',
'Title',
'Synopsis',
];
}
Also, unless you have a media_types row with TypeID = 0, that ->default(0) in the medias migration is bound to cause more errors. If you want to make medias.TypeID optional, instead of ->default(0) use ->nullable().

Issues with Database Seeder and 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. :)

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],
]);`

ON DUPLICATE KEY UPDATE in Eloquent

I just started laravel and all I want to do is get following query working in Eloquent:
INSERT INTO geschichte (geschichte_id,
geschichte_text1,
geschichte_text2,
geschichte_text3)
VALUES (:geschichte_id,
:geschichte_text1,
:geschichte_text2,
:geschichte_text3)
ON DUPLICATE KEY
UPDATE geschichte_id = :geschichte_id,
geschichte_text1 = :geschichte_text1,
geschichte_text2 = :geschichte_text2,
geschichte_text3 = :geschichte_text3;
Controller function
public function alterGeschichte(Request $request)
{
$geschichte1 = new Geschichte;
$geschichte2 = new Geschichte;
$geschichte3 = new Geschichte;
$geschichte1 = Geschichte::updateOrCreate(
['id' => 1],
['geschichte_text1' => $request->geschichte_text1]
);
$geschichte2 = Geschichte::updateOrCreate(
['id' => 2],
['geschichte_text2' => $request->geschichte_text2]
);
$geschichte3 = Geschichte::updateOrCreate(
['id' => 3],
['geschichte_text3' => $request->geschichte_text3]
);
$geschichte1->save();
$geschichte2->save();
$geschichte3->save();
return redirect('/geschichte');
}
The problem in more detail
I cannot get the 'on duplicate key update' part to work.
There always is a new entry created for every time I update. I would like the id always to be the same for every entry and just overwrite the older entry with that id.
I would be very thankful for any kind of help. I am struggling with this from hours...
UPDATE
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateGeschichteTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('geschichte', function (Blueprint $table) {
$table->increments('id');
$table->text('geschichte_text1');
$table->text('geschichte_text2');
$table->text('geschichte_text3');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::drop('geschichte');
}
}
Looks like you don't have a unique key setup on the columns you wish to be unique. In order to use on duplicate key update, you will need that so your database server will know if it needs to insert or update.
Unfortunately, Laravel doesn't have support for on duplicate key update syntax. This would be useful because it tries to insert and if it's already in the table, then it will update the columns accordingly in one query.
Using Laravel's updateOrCreate method, it first queries the table to see if it needs to generate an insert or update statement and then proceeds accordingly. You don't get the advantage of running just one query but at the same time, it's Laravel which is handling all the extra logic so it's a slight trade-off.
Since Laravel 8 there is upsert method that mimics SQL INSERT ON DUPLICATE KEY UPDATE functionality.
This query builder example:
DB::table('flights')->upsert(
[
['departure' => 'Chicago', 'destination' => 'New York', 'price' => 150]
],
['departure', 'destination'],
['price']
);
is equal to the following SQL query:
INSERT INTO flights (departure, destination, price)
VALUES ('Chicago', 'New York', 150)
ON DUPLICATE KEY UPDATE price = 150;
The upsert method has 3 arguments.
Values to insert or update
Column(s) that uniquely identify records within the associated table
Array of columns that should be updated if a matching record already exists in the database
The columns in the second argument MUST have a "primary" or "unique" index.
Full documentation
Most of what is in your controller should not be necessary. I am also a little concerned about the database structure you are using as to why you would perform a task like shown in your controller. The following should be all you need:
public function alterGeschichte(Request $request)
{
Geschichte::updateOrCreate(
['id' => 1],
['id' => 1, 'geschichte_text1' => $request->geschichte_text1]
);
Geschichte::updateOrCreate(
['id' => 2],
['id' => 2, 'geschichte_text2' => $request->geschichte_text2]
);
Geschichte::updateOrCreate(
['id' => 3],
['id' => 3, 'geschichte_text3' => $request->geschichte_text3]
);
return redirect('/geschichte');
}
If these are creating new records it is most likely because there is no record with those ID's.
updateOrCreate not work like ON DUPLICATE UPDATE you can use upsert
$data = [
'user_id' => $id,
'profile' => $profile
];
use DB::table('tablename')->upsert($data, ['user_id'], ['profile']);
user_id: column must have unique index

Resources