Laravel mass update with different value based on condition - laravel

I have a table structure like:
id //primary_key
value
changed_value //on update value column should not be updated instead changed value should be updated
status // 0 - default, 1- updated
am_id //foreign_key
Now, to mass update I am doing as follows:
Model::where('am_id',$request->am_id)
->where('value',$request->value)
->update([
'changed_value' => '$request->value',
'status' => 1
]);
However, we should not set status as 1 blindly, as there is one condition. It's if the value of value column is equal to the $request->value the status should be 0 instead.
How this scenario could appear?
Initially, after inserting the first row looks like
+-----+-------+---------------+--------+-------+
| id | value | changed_value | status | am_id |
+-----+-------+---------------+--------+-------+
| 1 | 20 | null | 0 | 1 |
+-----+-------+---------------+--------+-------+
After 1st update
+-----+-------+---------------+--------+-------+
| id | value | changed_value | status | am_id |
+-----+-------+---------------+--------+-------+
| 1 | 20 | 40 | 1 | 1 | // changed_value - 40 , status 1
+-----+-------+---------------+--------+-------+
After 2nd Update (let's say the value is updated to 20), in this case as the value === changed_value the status should be updated as 0 not 1
+-----+-------+---------------+--------+-------+
| id | value | changed_value | status | am_id |
+-----+-------+---------------+--------+-------+
| 1 | 20 | 20 | 0 | 1 | // changed_value - 20 , status 0
+-----+-------+---------------+--------+-------+
This means, during the model update in below-mentioned code. I want to insert a condition, if( value == $request->value) status = 0 else status = 1
Model::where('am_id',$request->am_id)
->where('value',$request->value)
->update([
'changed_value' => '$request->value',
'status' => 1
]);

Alternatively you could always use raw sql if performance was an issue.
DB::unprepared('
update model
set status = case when (changed_value = "'.$request->value.'") then 0 else 1 end
and changed_value = "'.$request->value.'"
where value = "'.$request->value.'";
')
Edit: You can do it in Eloquent with 2 queries with a subquery to filter the ids of the already updated rows.
// get ids of all matching status 1 but don't execute the query
$matches = Model::select('id')->where('am_id',$request->am_id)->where('value',$request->value)
->where('changed_value', $request->value);
// change matching status 1
Model::whereIn('id', $matches)
->update([
'changed_value' => '$request->value',
'status' => 1
]);
// matching not in status 1
Model::where('am_id',$request->am_id)
->where('value',$request->value)
->where('changed_value', '!=', $request->value)
->whereNotIn('id', $matches)
->update([
'changed_value' => '$request->value',
'status' => 0
]);

Try this one.
$value = Model::where('am_id',$request->am_id)->first()->value;
if($request->value == $value){
$status = 1;
}else{
$status = 0;
}
Model::where('am_id',$request->am_id)
->update([
'changed_value' => '$request->value',
'status' => $status
]);

Unfortunately, Eloquent was not designed to handle this kind of mass update (where you want different conditions) with a single query. You would have to step through the models, or resort to the query builder.
$models = Model::where('am_id',$request->am_id)->get();
$models->map(function ($model) use ($request) {
// status
$status = $model->value === $request->value ? 0 : 1;
// update model
$model->update([
'changed_value' => $request->value,
'status' => $status
]);
return $model;
});
EDIT: There are some query builder ideas on this page that might allow you to make something reusable, instead of just writing raw SQL any time you need to do this.
https://github.com/laravel/ideas/issues/575#issuecomment-300731748

Related

How to add array values to another array

I have two sets of array A and B, array A and B on submission is supposed to enter a database table.
A(result)
array:2 [
0 => 7
1 => 8
]
B
array:3 [
"student_test_id" => 8
"question_id" => 4
"test_id" => 3
]
EXPECTED RESULT
On submit, i want the values of Array A and B to enter this table like this
id| student_test_id | test_id | question_id |result
1 | 8 | 3 | 4 | 7
2 | 8 | 3 | 4 | 8
WHAT I HAVE TRIED
$result = $request->result;
$array_conv = array(
"student_test_id"=>$request->student_test_id,
"question_id"=>$request->question_id,
"test_id"=>$request->test_id,
);
foreach($request->result as $result){
$test_log = new StudentTestLog();
$test_log->student_test_id = $array_conv["student_test_id"];
$test_log->question_id = $array_conv["question_id"];
$test_log->test_id = $array_conv["test_id"];
$test_log->result = $result['id'];
$test_log->save();
if($test_log->save()){
return response()->json('Submited', 200);
}else{
return response()->json('Error Submitting', 400);
}
}
Please assist Thank you
When returning response from foreach except first all other iterations of loop are omitted as return ends method execution. You should move return statements outside the loop. Try something like this:
$array_conv = array(
"student_test_id"=>$request->student_test_id,
"question_id"=>$request->question_id,
"test_id"=>$request->test_id,
);
$data_to_persist = [];
foreach($request->result as $result){
array_push( $data_to_persist, [
'student_test_id' => $array_conv["student_test_id"],
'question_id' => $array_conv["question_id"],
'test_id' => $array_conv["test_id"],
'result' => $result['id']
]);
}
$saved = StudentTestLog::insert( $data_to_persist );
if($saved){
return response()->json('Submited', 200);
}else{
return response()->json('Error Submitting', 400);
}

Condition in with and whereHas laravel has been duplicate

I have two table
1. news_categories
+----+---------------------+
| id | created_at |
+----+---------------------+
| 1 | 2020-10-10 10:10:10 |
+----+---------------------+
2. news_category_translations
+----+--------+---------------+---------+
| id | map_id | language_code | name |
+----+--------+---------------+---------+
| 1 | 1 | vi | name vi |
| 2 | 1 | en | name en |
+----+--------+---------------+---------+
How do I fix the looping condition in this code
$locale = \App::getLocale()
$category = \App\Models\NewsCategory::with(['translation' => function($q){
$q->where('language_code', 'en'); // duplicate (1)
}])->whereHas('translation', function($q){
$q->where('language_code', 'en'); // duplicate (2)
})->find(1);
Model NewCategory
public function translation()
{
return $this->hasOne('App\Models\NewsCategoryTranslation', 'map_id', 'id');
}
I don't want to push conditional on the model.
Update 1:
When i remove condition (1) output:
newsCategory ['id' => 1, 'translation' => ['id' => 1]] false because language_code = en
When i remove condition (2) and delete translation with id = 2 ouput:
newsCategory ['id' => 1, 'translation' => null] => false because record with language_code = en does not exist
When I use both conditions, the result is as expected. Why?
Sorry my english is quite bad and i don't have much experience with laravel. Thanks.
Update 2:
I have referenced the following article
https://medium.com/#amritms/combining-wherehas-and-with-in-laravel-eloquent-c91391bd3c02
And i see author use
$callback = function($query) {
$query->where('something', '=', 'something');
}
$submissions = Post::whereHas('submissions', $callback)->with(['submissions' => $callback])->get();
If don't use it callback function it will be the same for me, duplicate condition from with and whereHas.

How to loop number and saved into table laravel

I have input and this input is will generate number sequentially like this
5.01
5.02
5.03
......
5.10
5.11
i get number 5 on first number is from my another data . u can see at my code .so how i can do this if this number start from '01' not '1' and if more than 10 this '0' in first number is deleted.
in my controller i saved in $data and looping this number from 01
So in my input controller is like that :
public function store(Request $request)
{
$data = $request['kode_induk'] + // i dont know what to do here for loop this input ,
$induk = IndukBelanja::create([
'user_id' => Auth::user()->id,
'kode_induk' => $request['kode_induk'],
'kode_rekening' => $data,
'uraian' => $request['uraian'],
'anggaran' => $request['anggaran'],
]);
if ($induk) {
return ['redirect' => route('rekening_induk')];
} else {
return response()->json([
'success' => false,
'message' => ' Gagal update!',
], 500);
}
}
how i can loop this number ? can someone help ?
Update its my database what i need
+----+------------+------------+------------+
| id |kode_induk |kode_rekening| uraian
+----+------------+------------+------------+
| 1 | 5 | 5.01 | some data
+----+------------+------------+------------+
| 2 | 5 | 5.02 | some data
+----+------------+------------+------------+
| 3 | 5 | 5.03 | some data
+----+------------+------------+------------+
| 4 | 5 | 5.04 | some data
+----+------------+------------+------------+
You can cast $data to int and then increment by 0.01 in a for loop.
$data = (int) $request['kode_induk'];
$numberOfSequencesToGenerate = 10;
for($i = 0; $i < $numberOfSequencesToGenerate; $++){
$induk = IndukBelanja::create([
'user_id' => Auth::user()->id,
'kode_induk' => $request['kode_induk'],
'kode_rekening' => $data + 0.01,
'uraian' => $request['uraian'],
'anggaran' => $request['anggaran'],
]);
}
The loop basically reads, run the the block of code while $i is less than $numberOfSequencesToGenerate. On each iteration increment $i by one.
I am assuming the DB field kode_rekening is of type double but if is not you can format it in PHP so is always has two decimal places with number_format(). Ex: number_format($data + 0.01, 2)

Is it possible to groupBy (month) and sum (each column) in table

+---------+--------+---------+---------+
| date | type_a | type_b | type_zzz|
+---------+--------+---------+---------+
|01-01-18 | 12 | 10 | 1 |
|02-01-18 | 2 | 5 | 1 |
|03-01-18 | 7 | 2 | 2 |
|01-02-18 | 13 | 6 | 55 |
|02-02-18 | 22 | 33 | 5 |
+---------+--------+---------+---------+
Hi,
In above example, I would like to know if it's possible to groupBy month and sum each column when getting results in Laravel (tables are dynamic so there are no models for them and also some tables don't have column 'type_a' other don't have 'type_zzz' etc...).
What I'm looking to get from above table is something like this:
"01" =>
'type_a' : '21',
'type_b' : '17',
'type_zzz': '4'
"02" =>
'type_a' : '35',
'type_b' : '39',
'type_zzz': '60'
I'm using following code to group it by month but I'm not able to find solution to return sum by each column:
DB::table($id)->get()->groupBy(function($date) {
return Carbon::parse($date->repdate)->format('m');;
});
If I understand your question correctly, you can either group and sum the values using an SQL query:
$grouped = DB::table('table_name')
->selectRaw('
SUM(type_a) AS type_a,
SUM(type_b) AS type_b,
SUM(type_z) AS type_z
')
->groupByRaw('MONTH(date)')
->get();
Or if you don't want to have to specify the column names in each query, you can use groupBy, array_column, and array_sum on your collection:
$grouped = DB::table('table_name')
->get()
->groupBy(function ($item) {
return Carbon::parse($item->date)->format('m');
})
->map(function ($group) {
$group = $group->toArray();
$summed = [];
$columns = array_keys($group[0]);
array_shift($columns);
foreach ($columns as $column) {
$summed[$column] = array_sum(array_column($group, $column));
}
return $summed;
});

ManyToMany relation - how update attribute in pivot table

I am now learning to work with pivot tables: https://laravel.com/docs/4.2/eloquent#working-with-pivot-tables
I have WeeklyRoutine model. Each routine has several Activities. The assigned activities are attached in a pivot table activity_routine.
Relation defined in the WeeklyRoutine model:
return $this->belongsToMany('App\Models\Activity', 'activity_routine', 'routine_id', 'activity_id')->withPivot('done_at')->withTimestamps();
}
it looks like this:
// activity_routine pivot table (relevant columns only)
| id | activity_id | routine_id | done_at |
| 34 | 1 | 4 | 2016-04-23 09:27:27 | // *1
| 35 | 2 | 4 | null | // *2
*1 this activity is marked as done with the code below
*2 this activity is not yet done
what I have:
I can update the done_at field in the pivot table, thus making it marked as DONE for the given week (a weeklyroutine_id = 4 in the above code
public function make_an_activity_complete($routineid, $activityid) {
$date = new \DateTime;
$object = Routine::find($routineid)->activities()->updateExistingPivot($activityid, array('done_at' => $date));
return 'done!';
}
what I need
I want to UN-DO an activity. When it is already done, that is when the done_at is not null buc contains a date, make it null.
In other words I need to do the below switch of value, but the proper way:
$pivot = DB::table('activity_routine')->where('routine_id, $routineid)->where('activity_id, $activityid)->first();
if($pivot->done_at != null) {
$new_val = new \DateTime;
} else {
$new_val = null;
}
$object = Routine::find($routineid)->activities()->updateExistingPivot($activityid, array('done_at' => $new_val));
How to do it? I have no clue!
Thx.
Your approach seems fine to me. I would probably do it like this.
$routine = Routine::find($routineid);
$activity = $routine->activities()->find($activityid);
$done_at = is_null($activity->pivot->done_at) ? new \DateTime : null;
$routine->activities()->updateExistingPivot($activityid, compact('done_at'));

Resources