Laravel Insert Seed if data doesn't already exist - laravel

Is there any way to run a laravel seed to only insert the records if they do not exist already?
My current laravel seeder looks like this
DB::table('users')->insert([
//Global Admin
[
'member_id' => 11111111,
'firstname' => 'Joe',
'lastname' => 'Bloggs'
],
[
'member_id' => 22222222,
'firstname' => 'Jim',
'lastname' => 'Bloggs'
],
]);
Pretty standard!
would I have to wrap each and every insert in a try catch? like this
try {
DB::table('users')->insert(['member_id' => 11111111, 'firstname' => 'Joe', 'lastname' => 'Bloggs']);
} catch(Exception $e){
//Die silently
}
try {
DB::table('users')->insert(['member_id' => 22222222, 'firstname' => 'Jim', 'lastname' => 'Bloggs']);
} catch(Exception $e){
//Die silently
}
Later on I might want to add extra rows to the same seeder without having to write a new one, and re run php artisan db:seed to only add my new rows.
Is this possible?

You can achieve this by Eloquent firstOrCreate.
The firstOrCreate method will attempt to locate a database record
using the given column / value pairs. If the model can not be found in
the database, a record will be inserted with the attributes from the
first parameter, along with those in the optional second parameter.
So, if you identify the user by member_id, you can do something like this:
User::firstOrCreate(
['member_id' => 11111111],
[
'firstname' => 'anakin',
'lastname' => 'skywalker',
'password' => Hash::make('4nak1n')
]
);
If you want to locate the record by 'member_id', 'firstname' and 'lastname' fields, something like this:
User::firstOrCreate(
[
'member_id' => 11111111,
'firstname' => 'anakin',
'lastname' => 'skywalker'
],
[
'password' => Hash::make('4nak1n'),
]
);

As stokoe0990 stated, it is not advisable to run seeders in production. The Laravel Seeder was written as way to generate test data. Per the documentation "Laravel includes a simple method of seeding your database with test data using seed classes".
That said, The only way you can satisfy your question is to build the logic into your seed:
$users = array(
['member_id' => 11111111,'firstname' => 'Joe','lastname' => 'Bloggs'],
['member_id' => 22222222,'firstname' => 'Jim','lastname' => 'Bloggs']
);
foreach ($users as $user) {
if (\App\User::find($user['id'])) {
DB::table('users')->insert($user);
}
}

Seeding is only supposed to be used for testing, anyways.
Why don't you just execute php artisan migrate:fresh --seed?
This will refresh your database (deletes the tables) then runs all your migrations again and finally seeds the database again.
You should never be using seeders in production.
Just add the new data to your seeder, run migrate:fresh and re-seed :)

You can achieve that by using Eloquent updateOrInsert() method:
DB::table('users')->updateOrInsert('your_data');
Read more about it here.

The best I recommend is to truncate (or delete all) the data in the table anytime you migrate. Try something like this:
//data in the table
DB::table('users')->delete(); //or use model like this: User::truncate();
DB::table('users')->insert([
//Global Admin
[
'member_id' => 11111111,
'firstname' => 'Joe',
'lastname' => 'Bloggs'
],
[
'member_id' => 22222222,
'firstname' => 'Jim',
'lastname' => 'Bloggs'
],
]);
Then there will be no duplications when you run php artisan db:seed

Related

import _geo into Meilisearch from Laravel Model

ok so lets start off with code
return [
'name' => $this->name,
'user_display_name' => $this->user_display_name,
'user_city' => $this->user_city,
'user_region' => $this->user_region,
'user_country' => $this->user_country,
'profile_photo_path' => $this->profile_photo_path,
'_geo' => [
'lat' => $this->user_latitude,
'lon' => $this->user_longitude,
],
];
So, the issue is, when I do php artisan scout:import "App\Models\User" it gets all the data except inside the _geo array. I have looked at the formatting 100 times but it looks right based on all the docs and everything. Does anyone have any idea what I am not seeing?

Laravel migrate command always tries to create a migations table

I have the following code from an Artisan command that creates a new database:
public static function run($command)
{
$command->newLine();
$command->warn('Creating main database');
DB::disconnect('mysql');
// config(['database.connections.mysql.database' => '']);
DB::reconnect('mysql');
DB::statement('CREATE DATABASE IF NOT EXISTS `db`;');
DB::disconnect('mysql');
config(['database.connections.mysql.database' => 'manager']);
DB::reconnect('mysql');
$command->info('Database created');
$command->warn('Running migrations');
$command->call('migrate', [
'--path' => 'vendor/company/database/database/manager'
]);
$command->info('All migrations have been executed');
$command->warn('Inserting default record');
DB::table('customers')
->insert([
'name' => 'Customer',
'email' => 'customer#example.com.br',
'phone' => '55999999999',
'db_password' => Crypt::encryptString('pass'),
'modules' => 'basic,banners,dispatches,conversations',
'created_at' => now(),
'updated_at' => now(),
]);
$command->info('Record successfully inserted');
$command->info('Main database sucessfully created');
$command->newLine();
}
It works perfectly, but if I uncomment the line that is commented out, it starts showing an error that the migrations table already exists. If this line is commented out, it just tells you that there are no migrations to run.
I know it's something specific to me, but I'd like to understand why this single database name change is causing this error.

Optimize ApiResource

I'm having some problems when returning ApiResources because I can't figure out how to avoid overloading relationships.
In my UserResource, for example, I have:
<?php
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'role_id' => $this->role_id,
'role' => new RoleResource($this->whenLoaded('role')),
'group_ids' => $this->groups->pluck('id')->toArray(), // here are the problems
'groups' => GroupResource::collection($this->whenLoaded('groups')),
];
When loading single relationship id, all is ok, because $this->role_id is a property that is already in the Model. When using $this->whenLoaded('role') It will load the relationship conditionally, that's great!
The Problem: When loading many-to-many relationships, I can't set this 'conditional' eager load.
When I place:
'groups' => GroupResource::collection($this->whenLoaded('groups'))
It will load the ManyToMany Relation conditionally, cool!
BUT I would like to have array of ids with:
'group_ids' => $this->groups->pluck('id')->toArray()
However, this will execute the relation load ALWAYS

Will Model::updateOrCreate() update a soft-deleted model if the criteria matches?

Let's say I have a model that was soft-deleted and have the following scenario:
// EXISTING soft-deleted Model's properties
$model = [
'id' => 50,
'app_id' => 132435,
'name' => 'Joe Original',
'deleted_at' => '2015-01-01 00:00:00'
];
// Some new properties
$properties = [
'app_id' => 132435,
'name' => 'Joe Updated',
];
Model::updateOrCreate(
['app_id' => $properties['app_id']],
$properties
);
Is Joe Original now Joe Updated?
OR is there a deleted record and a new Joe Updated record?
$variable = YourModel::withTrashed()->updateOrCreate(
['whereAttributes' => $attributes1, 'anotherWhereAttributes' => $attributes2],
[
'createAttributes' => $attributes1,
'createAttributes' => $attributes2,
'createAttributes' => $attributes3,
'deleted_at' => null,
]
);
create a new OR update an exsiting that was soft deleted AND reset the softDelete to NULL
updateOrCreate will look for model with deleted_at equal to NULL so it won't find a soft-deleted model. However, because it won't find it will try to create a new one resulting in duplicates, which is probably not what you need.
BTW, you have an error in your code. Model::updateOrCreate takes array as first argument.
RoleUser::onlyTrashed()->updateOrCreate(
[
'role_id' => $roleId,
'user_id' => $user->id
],
[
'deleted_at' => NULL,
'updated_at' => new \DateTime()
])->restore();
Like this you create a new OR update an exsiting that was soft deleted AND reset the softDelete to NULL
Model::withTrashed()->updateOrCreate([
'foo' => $foo,
'bar' => $bar
], [
'baz' => $baz,
'deleted_at' => NULL
]);
Works as expected (Laravel 5.7) - updates an existing record and "undeletes" it.
I tested the solution by #mathieu-dierckxwith Laravel 5.3 and MySql
If the model to update has no changes (i.e. you are trying to update with the same old values) the updateOrCreate method returns null and the restore() gives a Illegal offset type in isset or empty
I got it working by adding withTrashed so that it will include soft-deleted items when it tries to update or create. Make sure deleted_at is in the fillable array of your model.
$model = UserRole::withTrashed()->updateOrCreate([
'creator_id' => $creator->id,
'user_id' => $user->id,
'role_id' => $role->id,
],[
'deleted_at' => NULL
])->fresh();
try this logic..
foreach ($harga as $key => $value) {
$flight = salesprice::updateOrCreate(
['customerID' => $value['customerID'],'productID' => $value['productID'], 'productCode' => $value['productCode']],
['price' => $value['price']]
);
}
it work for me

How to seed timestamps on laravel 4.1?

Good day,
I was having an error "Object of class DateTime could not be converted to string" when Im trying to seed my database.
here is my migration code:
public function up()
{
Schema::create('tblinventory', function(Blueprint $table) {
$table->increments('id');
$table->integer('itemId');
$table->enum('status', array('active','inactive'))->default(null)->nullable();
$table->float('purchasePrice');
$table->float('sellingPrice');
$table->date('expirationDate');
$table->float('ReceivedQuantity');
$table->float('soldQuantity');
$table->timestamps();
});
}
and my seeder:
<?php
class InventoryTableSeeder extends Seeder {
public function run()
{
// Uncomment the below to wipe the table clean before populating
DB::table('tblinventory')->truncate();
$insert = [
[
'itemId' => '1',
'status' => 'inactive',
'ReceivedQuantity'=>'100',
'SoldQuantity'=>'93',
'sellingPrice'=>'4.5',
'purchasePrice'=>'3.5',
'created_at' => new DateTime,
'expirationDate'=>date('2015-02-22')
],
[
'itemId' => '1',
'status' => 'inactive',
'ReceivedQuantity'=>'300',
'SoldQuantity'=>'300',
'sellingPrice'=>'4.75',
'purchasePrice'=>'3.65',
'expirationDate'=>date('2015-02-22')
],
[
'itemId' => '2',
'status' => 'inactive',
'ReceivedQuantity'=>'100',
'SoldQuantity'=>'93',
'sellingPrice'=>'3.5',
'purchasePrice'=>'2.5',
'expirationDate'=>date('2014-07-22')
],
[
'itemId' => '3',
'status' => 'inactive',
'ReceivedQuantity'=>'100',
'SoldQuantity'=>'93',
'sellingPrice'=>'12.5',
'purchasePrice'=>'10.5',
'expirationDate'=>date('2017-01-02')
],
[
'itemId' => '3',
'status' => 'inactive',
'ReceivedQuantity'=>'100',
'SoldQuantity'=>'100',
'sellingPrice'=>'14.5',
'purchasePrice'=>'13.5',
'expirationDate'=>date('2017-07-22')
],
[
'itemId' => '4',
'status' => 'inactive',
'ReceivedQuantity'=>'100',
'SoldQuantity'=>'93',
'sellingPrice'=>'24.5',
'purchasePrice'=>'23.5',
'expirationDate'=>date('2015-07-22')
]
];
DB::table('tblinventory')->insert($insert);
// Uncomment the below to run the seeder
// DB::table('inventories')->insert($inventories);
}
}
I get the error when I put 'created_at'=> new DateTime. How can I fix this? thank you!
Try to create your dates using Carbon (Laravel uses it internally):
'expirationDate' => \Carbon\Carbon::createFromDate(2014,07,22)->toDateTimeString()
or
'created_at' => \Carbon\Carbon::now()->toDateTimeString()
I would recommend using PHP Faker if you want to randomize your seeds for mock data. Otherwise you can just use
date('Y-m-d H:i:s');
Using Faker
https://github.com/fzaninotto/Faker
Add to composer.json
"fzaninotto/faker" : "dev-master",
Include the Namespace
use Faker\Factory as Faker;
Initialize Faker
$faker = Faker::create();
Start Faking Stuff
$faker->dateTime();
I am a little late to the party here but I wanted to give another option that others may find useful.
If you have already created your models using Eloquent, then there is another option to have Eloquent fill those fields for you automatically by using the orm. Assuming your btlinventory has a model name of Inventory:
foreach($insert as $row ){
$inventory = new Inventory;
$inventory->fill($row);
$inventory->save();
}
insert is a query builder method so by itself it will not handle any Eloquent tasks, however, you can always chain query builder methods off of an Eloquent object and then it would work. If you use Inventory::create($array); and still have issues then I hear this may get fixed by explicitly stating public $timestamps = true; in your model.

Resources