I am trying to make a scheduled job that will take data from a table in one database and insert it into a table on another database. The job will run every minute, so I need to check for duplicates and insert only the new data.
This is what I have in my Job:
$db_maintenance_old = DB::connection('maintenance_old');
$getMaintenanceMachines = $db_maintenance_old->table('machines')->get();
foreach ($getMaintenanceMachines as $key => $value) {
MaintenanceMachine::updateOrCreate([
'machine_name' => $value->Code,
'external_id' => $value->MachnineID,
],[
'machine_name' => $value->Code,
'external_id' => $value->MachnineID,
'machine_status' => 1,
'created_at' => date('Y-m-d H:i:s'),
]);
}
With php artisan schedule:run I get this error:
BadMethodCallException : Call to undefined method Illuminate\Database\Query\Builder::updateOrCreate()
I think I am using the updateOrCreate Method right, or maybe not. I have no clue what I am doing wrong.
Related
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.
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
I have a code:-
$tokenUpdated = AppToken::updateOrCreate(
array(
'user_id' => $user_id, 'token' => $token),
array('expiry'=>$expiryTime,
'created_date'=>$created_at,
'modified_date'=>$created_at)
);
Though new rows are being inserted, the expiry, created_date fields values aren't getting saved. They records show NULL value which are default values.
What am I doing wrong?
First you need to check if expiry and created_date are returning values, if so then you could use the updateOrCreate function like this:
$tokenUpdated = AppToken::updateOrCreate(
['user_id' => $user_id, 'token' => $token]
['expiry'=>$expiryTime,
'created_date'=>$created_at,
'modified_date'=>$created_at
]
);
check laravel eloquent documentation: https://laravel.com/docs/5.6/eloquent
I have a model factory like this
$factory->define(App\Sale::class, function (Faker\Generator $faker) {
return [
'unit' => $faker->randomDigit,
'street_no' => $faker->randomDigit,
'street_name' => $faker->streetName,
'street_type' => $faker->streetSuffix,
'suburb' => $faker->randomElements(['Melton South','Melton West','Rye']),
'postcode' => $faker->numberBetween($min=1000, $max=4000),
'sale_date' => $faker->dateTimeThisYear,
];
});
Database seeder runs it
factory(App\Sale::class, 5)->create();
The problem is when i run it php artisan db:seed I'm getting the error
[Illuminate\Database\QueryException]
Array to string conversion (SQL: insert into `sales` (`unit`, `street_no`,
`street_name`, `street_type`, `suburb`, `postcode`, `sale_date`,
`updated_at`, `created_at`)
values (7, 1, Labadie Centers, Bridge, Rye, 3758, 2016-08-12 1
5:02:07, 2017-05-23 13:16:56, 2017-05-23 13:16:56))
The error sql doesnt show any arrays that i can see.
When i pasted that sql into my db app and ran it, in order to make it work i had to quote all the strings, but laravel docs dont say anything about that when using faker ?
Am I missing something in the model factory ?
exi
Try to change
'suburb' => $faker->randomElements(['Melton South','Melton West','Rye']),
to
'suburb' => $faker->randomElement(['Melton South','Melton West','Rye']),
Note 's' on Elements
I could not figure out how can i use both update and limit methods in laravel eloquent orm.
$affectedRows = Promo::where('used','=',0)
->update(array('user_id' => Auth::user()->id))
->limit(1); // Call to a member function limit() on a non-object
//->take(1); // Call to a member function take() on a non-object
I tried both limit and take methods.
I want to do only one result will be update.
But i think, i can not use limit or take methods on update.
Is there any way to update only one row via eloquent?
Add :
Eloquent ORM
$affectedRows = Promo::where('user_id','=',DB::raw('null'))->take(1)
->update(
array(
'user_id' => Auth::user()->id,
'created_ip' =>Request::getClientIp(),
'created_at' => new DateTime,
'updated_at' => new DateTime
)
);
Query Builder
$affectedRows = DB::table('promos')->whereNull('user_id')
->take(1)
->update(array(
'user_id' => Auth::user()->id,
'created_ip' =>Request::getClientIp(),
'created_at' => new DateTime,
'updated_at' => new DateTime
));
These two codes did not add limit param to the query
Output:
update `promos` set `user_id` = '1', `created_ip` = '127.0.0.1', `created_at` = '2013-06-04 14:09:53', `updated_at` = '2013-06-04 14:09:53' where `user_id` = null
Talking about laravel 5 (not sure about L4), depends on db engine.
MySQL supports limit for update so it works, here is the laravel code that do that:
https://github.com/laravel/framework/blob/5.4/src/Illuminate/Database/Query/Grammars/MySqlGrammar.php#L129
so, first ->limit(1) and then ->update([fields]);
DB::table('table')
->where('field', 'value')
->limit(1)
->update(['field', 'new value']);
I used raw query. There is no method limit/take for update and delete queries on both eloquent and query builder. Use
DB::update(DB::raw("UPDATE query"));
like this.
I have not tried it but the Laravel 4 logic makes me think this syntax would work :
$affectedRows = Promo::where('used','=',0)
->limit(1)
->update(array('user_id' => Auth::user()->id));