Getting bulk update to work in laravel 5.6 - php

My app is being made in laravel 5.6
Situation:
I have a table called "members" with a column called "membershipstatus_id".
options for status are 4, 5 and 1
4 = Active, 5 = pending and 1= expired
Target:
I want to update all active(4) members to pending(5) and all pending ones to expire(1).
Solutions I have tried:
So far, below is what i have tried with no result.
// get all active and pending members
$members = Member::where('membershipstatus_id', 5)
->orWhere('membershipstatus_id', 4)
->get();
// bulk update with chunk of 200, if this is possible
$members->chunk(200, function($members)
{
foreach($members as $member)
{
// if status is pending, update to expire
if($member->membershipstatus_id == 5)
{
$member->update(['membershipstatus_id' => 1]);
}
// if status is active, update to pending, i updated a small mistake here.
if($member->membershipstatus_id == 5)
{
$member->update(['membershipstatus_id' => 4]);
}
}
}
);
return "update confirm";
Now, If anyone has a cleaner and swift way to do this, Please let me know, also, Im sure i have made some stupid mistake up there. Please point me to the right direction.
Ashish

Use the query builder like:
// update pending to expired
DB::table('members')
->where('membershipstatus_id', 5)
->update(['membershipstatus_id' => 1]);
// update active to pending
DB::table('members')
->where('membershipstatus_id', 4)
->update(['membershipstatus_id' => 5]);

Related

Laravel - can't find() attached object in many-to-many relationship

in my POST form users are able to add other users to a room.
I put a unique constraint on the link (no duplicate entry in the link between users and rooms).
However when I refresh my page (f5) after submitting the form, Laravel complains about duplicate entries, although I do check if the objects are attached before.
Here's the code:
$roomUsers = Room::find($request->room_id)->users();
if ($request->add != null) {
foreach ($request->add as $uId)
// if null, user hasnt been attach yet
if (!$roomUsers->find($uId)) {
Log::debug($roomUsers->find($uId) == null ? 'null' : 'not null');
// then we can attach him
$roomUsers->attach($uId);
}
}
The line !$roomUsers->find($uId) returns true yet the object has been attached in the previous iteration. How is that possible ? Thanks
The reason you're above code isn't working is because you're not creating a new instance of BelongsToMany for each check. This means that every time you call find you're not actually creating a new query you're just adding to the existing one e.g.
say you the ids to add are [1, 2, 3] by the last check your query would effectively be:
SELECT * FROM users WHERE id = 1 AND id = 2 AND id = 3
To keep with the above logic you could do:
$room = Room::find($request->room_id);
if ($request->add != null) {
foreach ($request->add as $uId)
// if null, user hasnt been attach yet
if (!$room->users()->find($uId)) {
// then we can attach him
$room->users()->attach($uId);
}
}
Or a much simpler way to go about this would be to syncWithoutDetaching.
Your code could then look something like:
$roomUsers = Room::find($request->room_id);
if ($request->has('add')) {
$roomUsers->users()->syncWithoutDetaching($request->add);
}
Hope this helps!

Updates in mysql transaction - isolation

I have following model
Inventory [product_name, quantity, reserved_quantity]
with data
[Shirt, 1, 0]
[Shorts, 10, 0]
What happens if following code is executed in multiple threads at the same time?
$changes = [
['name' => 'Shirt', 'qty' => 1],
['name' => 'Shorts', 'qty' => 1],
];
$db->startTransaction();
foreach($changes as $change){
$rowsUpdated = $db->exec("UPDATE inventory
SET reserved_quantity = reserved_quantity + $change['qty']
WHERE product_name = $change['name']
and quantity >= reserved_quantity + $change['qty']");
if($rowsUpdated !== 1)
$db->rollback();
exit;
}
$db->commit();
Is it possible that the result will be?
[Shirt, 1, 2]
[Shorts, 10, 2]
It's not.
Lets see what would be happening in the following scenario:
The first transaction starts
UPDATE Shirt => an exclusive row lock will be set on the record
The second transaction starts
The second transaction tries to UPDATE Shirt. As it would need to obtain a record lock it would wait as this record has already been locked by the first transaction
The first transaction commits, the second one would resume execution and see the updated record
Of course it's only relevant to InnoDb and similar mysql engines.
Please note that you're lucky enough that traversing the records in the same order. If it were not the case you might run into a deadlock

Affected Rows not working - Codeigniter

I have this delete function in my model. When you press the delete button, it will subtract the number of total notes from the total notes column in my database. For eg. if you had 200 notes, and you deleted one of them, it will be 199 notes AND the note will be deleted from the database.
My code:
public function entry_delete($pid) {
$uid=$this->session->userdata('uid');
$whereConditions = array('pid' => $pid, 'uid' => $uid);
$this->db->where($whereConditions);
$this->db->delete('dayone_entries');
if ($this->db->affected_rows() == 0) {
$sql_entry = "UPDATE users set total_entry = total_entry - 1 WHERE uid = $uid";
$this->db->query($sql_entry);
return true;
} else {
return false;
}
}
But this doesn't work. I don't know why but when I press the delete button from my view, it will delete it from the database AND subtract -1 from my total_entry table. However, if I comment out the
$this->db->delete('dayone_entries');
It will still subtract 1 from the total_entry.
How do I fix this? Thanks.
I don't know if you have fixed this yet, but I think what Ghost was saying is your
if ($this->db->affected_rows() == 0) {}
is doing the wrong check. If you only want it to trigger if the rows where successfully deleted you would want to check
if ($this->db->affected_rows() > 0) {}
This is saying if the affected rows is more than 0 then do this. In CodeIgniter is says that the affected_rows() method is for "doing 'write' type queries (insert, update, etc.)".
It then goes on to say "Note: In MySQL "DELETE FROM TABLE" returns 0 affected rows. The database class has a small hack that allows it to return the correct number of affected rows. By default this hack is enabled but it can be turned off in the database driver file." So if your if ($this->db->affected_rows() == 0) {} is triggering in both situations then this hack could be disabled because your asking "if the affected rows equals zero do this" and it should't trigger if your delete was successful.

Laravel ManyToMany to same Model

Okay guys, i am trying to rephrase my question. Lets see how try 2 goes :)
I have a artisan command to calculate the total earnings for every user.
Every day a User gets earnings assigned. But sometime, how should I describe it, he made the earnings with stuff from another user.
And thats the assert_user_id column. If a user (user 2) made earnings with stuff from another user (user 1), these earnings should be added to the earnings of user 1 in this scenario.
id, user_id, earnings, asset_user_id
1, 1, 15, null
2, 1, 43, null
3, 1, 49, null
4, 2, 32, 1
5, 2, 25, 1
6, 2, 12, null
(In this case, user 2 is making almost all of his money with content from user 1... not very nice of him, thats why the money)
The Artisan commands gets all users. and goes through them with a foreach.
inside the foreach we call a method on the user model
return $this->sum('earnings');
remember, I am inside the collection of user 1.
And now I want to sum all earnings where asset_user_id = 1. The problem, the collection persist only the rows of user 1.
Now the question:
How can i access all other rows from inside a collection of a user. Yeha i could just do User:all() within my function in my model. But i don't think this would be SOLID code. So how do i do it the right way?
What you will probably want to do is iterate over the collection with foreach and get the sums yourself.
$results = Result::all();
$sum1 = 0;
$sum2 = 0;
foreach($results as $result) {
if($result->creator_id == 1 && $result->status = 'r') {
$sum1 += (int)$result->earnings;
}
if($result->asset_creator_id == 1 && $result->status = 'r') {
$sum2 += (int)$result->earnings;
}
}
There is also a sum method in the Collection class which might be a bit easier. Just send it a callback which returns the value you are looking for. We would have to do it twice though, once for each sum you are looking for.
$sum1 = $results->sum(function($result)
{
if($result->creator_id == '1' && $result->status = 'r') {
return $result->earnings;
}
});
$sum2 = $results->sum(function($result)
{
if($result->asset_creator_id == '1' && $result->status = 'r') {
return $result->earnings;
}
});
echo "Creator Earnings: ".$sum1;
echo "Asset Creator Earnings: ".$sum2;
Sorting, counting, summing over collection would be always worse solution than doing it with db query.
Anyway your question is messy, you ask for status=r, write total for status=f, so just a guess, to show you how you can accomplish that with Eloquent features:
public function getEarningsForAssetCreator($id)
{
return $this->where('asset_creator_id', $id)
->where('status', 'f')
->sum('earnings');
}

Update only one record with Code Igniter

Firstly, I will show what I want to do.
I want check if user has more than one record:
WHERE 'from_user_id' == '$id'
When query return > 0, I want update only ONE record where 'from_user_id' == '$id',
I dont care about order, It can be last or first record, but I have to update only one record.
Here is part of my model:
public function get_bonuses() {
if ($this->db->where('from_user_id', $this->user_id())->from($this->reff_table)->count_all_results()>0 ) {
$this->db->where('from_user_id', $this->user_id());
$this->db->update($this->reff_table, array('used' => '1'));
return true;
} else
return false;
}
It works fine but I want update only one record :<
I hope anyone will help me.
Regards
----- EDIT
SOLVED - I'm so stupid but I needed to look from the other side to my code :)
Here is working code:
if ($this->db->where(array('from_user_id' =>$this->user_id(), 'used' => '0' ))->from($this->reff_table)->count_all_results()>0 ) { $this->db->where(array('from_user_id' =>$this->user_id(), 'used' => '0' ))->limit(1); $this->db->update($this->reff_table, array('used' => '1')); return true;
If you want to update any one record from result, simply limit the query where you check count to 1 & grab its unique id (primary key) and update that row.

Categories