Condition in with and whereHas laravel has been duplicate - laravel

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.

Related

Laravel mass update with different value based on condition

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

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;
});

Laravel: Merge two query builders

I have a table of courses which will be free to access or an admin will need to click something to let users see the course.
The course table looks like this:
| id | title | invite_only |
|----|----------------|-------------|
| 1 | free course | 0 |
| 2 | private course | 1 |
Separate from this I have a course_user table, where initially users request access, then admins can approve or deny access:
| id | user_id | course_id | approved | declined |
|----|---------|-----------|----------|----------|
| 1 | 3 | 2 | 1 | 0 |
| 2 | 4 | 1 | 0 | 1 |
| 3 | 4 | 2 | 0 | 0 |
I'd like to index all the courses a user has access to:
class User extends model{
public function myCourses(){
$public = $this->publicCourses;
$invited = $this->invitedCourses;
return $public->merge($invited);
}
public function publicCourses(){
return $this
->hasMany('App\Course')
->where('invite_only', false);
}
public function invitedCourses(){
return $this
->belongsToMany("\App\Course")
->using('App\CourseUser')
->wherePivot('approved', 1);
}
}
How can I make the myCourses function return the results of both publicCourses and invitedCourses by doing only one database query? I'd like to merge the two query builder instances.
According to the doc, you can use union to merge query builders. But as far as I know, it does not work with relations. So maybe you should do it from within controller instead of model. This is an example based on what I understand from your example:
$q1 = App\Course::join('course_user', 'course_user.course_id', 'courses.id')
->join('users', 'users.id', 'course_user.user_id')
->where('courses.invite_only', 0)
->select('courses.*');
$q2 = App\Course::join('course_user', 'course_user.course_id', 'courses.id')
->join('users', 'users.id', 'course_user.user_id')
->where('courses.invite_only', 1)
->where('course_user.approvoed', 1)
->select('courses.*');
$myCourses = $q1->unionAll($q2)->get();
You can also refactor the code further by creating a join scope in App\Course.
I was able to make a much simpler query, and use Laravel's orWherePivot to extract the correct courses:
public function enrolledCourses()
{
return $this
->courses()
->where('invitation_only', false)
->orWherePivot('approved', true);
}

How can I filter a query using laravel relationships

I have a table of items that contain info of the items. Plus, I have 3 other tables for some characteristics that could have more than one.
Follows an example:
table items
-----------------------
| id | price | stock |
-----------------------
| 1 | 19 | 99 |
-----------------------
table tallas
-----------------------------
| id | item_id| description |
-----------------------------
| 1 | 1 | large |
-----------------------------
table colors
-----------------------------
| id | item_id| description |
-----------------------------
| 1 | 1 | blue |
-----------------------------
table materials
-----------------------------
| id | item_id| description |
-----------------------------
| 1 | 1 | cotton |
-----------------------------
I want to know if there is a way where I can filter an item passing the item_id to the tallas relationship without using join (for example).
My model looks like this:
<?php
class Item extends Eloquent{
use Conner\Tagging\TaggableTrait;
public function tallas()
{
return $this->hasMany('Talla','item_id');
}
public function colores()
{
return $this->hasMany('Color','item_id');
}
public function materiales()
{
return $this->hasMany('Material','item_id');
}
public function imagenes()
{
return $this->hasMany('ItemImage','item_id');
}
}
And I have tried this:
$items = Item::with('imagenes')
->with(array('tallas' => function($query) use($dataFilter) {
$query->where('id','=',$dataFilter['filter-size']);
}));
But this return all the items and filter the tallas , using the example tables if i look for an item small it will return something like this.
[item]{
id:1,
price:19,
stock:99,
tallas:
[]...
colores:
[]...
materiales:
[]...
}
It should not return any item, any help?
EDITED
i forget to mention i'm using laravel 4.2
If I understand you question correctly I think you want whereHas e.g.
$items = Item::with('imagenes')
->whereHas('tallas', function ($query) use ($dataFilter) {
$query->where('id', '=', $dataFilter['filter-size']);
});
https://laravel.com/docs/4.2/eloquent#querying-relations
Hope this helps!

Cakephp 3: belongsToMany contain or matching condition?

Piece of my database looks like database part
Categories use tree behavior.
How can i get a manufacturer's (Producers) Products for current Category?
I tried contain and matching, but i received duplicated data or Producers names without related Products.
EDIT:
$query = $this->Producers->find()->matching('Products.Categories',
function ($q) {
return $q->where(['Categories.id' => 18]);
}
);
Results:
Producent: Canon
-------------------------------------------
| ID | Name | Barcode |
-------------------------------------------
| 1 | EOS 1000D | |
-------------------------------------------
| 18 | Camera | |
-------------------------------------------
| 23 | 18 | |
-------------------------------------------
First row (id = 1) it's what i need.
Now i have to remove from results:
second row (id = 18) this is Category id from table Categories,
thrid row (id = 23) - from Products_Categories table.
Done. There is working query:
$query = $this->Producers->find()
->select(['Producers.id','Producers.name', 'Products.id', 'Products.name'])
->matching(
'Products.Categories', function ($q) use ($categoryId){
return $q->where(['Categories.id' => $categoryId]);
}
);

Resources