Laravel Eloquent 3 queries into one - php

What I'm trying to achieve is doing 3 queries in one go, to limit the n1+ problem :
given we have 3 models :
trips
id => int
price => float
city_id => uint
........
cities
id => int
name => varchar
........
ratings:
id => int
ratable_id => int
rate => small-int
......
pseudocode:
select from tours where price >= 100
-then from the result
select from cities where id in result.city_id as cities
select count from ratings where ratable_id in result.id as rates groupBy rate
so the result is
[
trips => list of the trips where price more than or equal 100
cities=> list of the cities those trips belongs to
rates => list of rating with it's count so like [1 => 5, 2 => 100] assuming that '1 and 2' are the actual rating , and '5,100' is the trips count
]
how would I achieve that?

Two ways to go, Use eloquent methods which is preferred approach or use joins a single query to get your desired results
Moving forward with eloquent way i assume you have defined your models and their mappings based on their type of relationship (1:m, m:m)
$trips= Trips::with('city')
->withCount('ratings')
->where('price', '>=', 100)
->get();
Moving forward with join
$trips = DB::table('trips as t')
->select('t.id', 't.price','c.name',DB::raw('count(*) as rating_count'))
->join('cities as c' ,'t.city_id', '=' , 'c.id')
->join('ratings as r' ,'t.ratable_id', '=' , 'r.id')
->where('t.price', '>=', 100)
->groupBy('t.id', 't.price','c.name')
->get();

Trip Model Relationship
public function city(){
return $this->belongsTo(City::class);
}
public function ratings(){
return $this->hasMany(Rating::class, 'ratable_id'); //assuming ratable_id is an id of trips table
}
Fetch Data
$trips= Trip::with('city', 'ratings')
->where('price', '>=', 100)
->get();
Print Data
foreach($trips as $trip){
$trip->city->name." - ". $trip->price." - ". $trip->ratings()->avg('rate');
}

Related

sort by sum of multiple columns laravel

I want to ask how to orderBy some of multiple columns.
In the items table, I have 3 columns 'is_recommended', 'is_popular', 'is_new' (tags)
I want to sort by the total number of tags for each row. For examples
id=1 has is_recommended=1, is_popular=1, is_new=1 => attributes = 3
id=2 has is_recommended=0, is_popular=0, is_new=1 => attributes = 1
id=3 has is_recommended=1, is_popular=0, is_new=1 => attributes = 2
Therefore, the order will be id2, id3, id1.
My code:
$items = Item::addSelect(['attributes' => Item::selectRaw('`is_recommended` + `is_popular` + `is_new` as attributes')])
->sortable()
->orderBy('id', 'DESC')->with('item_category', 'restaurant')->paginate(20);

How to count table column categoryname in Laravel 5.6?

working with Laravel 5.6 and mysql. I have following table name as vehicles
id name number type
1 def 123 Car
2 lki 589 Van
3 loi 256 Truck
4 oiu 569 Car
5 hyy 589 Van
Now I need count number of each type columns values I do not wanna loop count. just I need count each Type and show on blade file. like this,
Car 2
Van 2
Truck 1
etc.. how can do this?
You need to simply Group By on the type column, and use Count(*) to count the number of rows:
SELECT type, COUNT(*) AS count
FROM vehicles
GROUP BY type
This should work
$vehiclesInfo = DB::table('vehicles')
->select('type', DB::raw('count(*) as total'))
->groupBy('type')
->get();
For more info on group by and count you can follow this tutorial.
Larvel queries
Just use DB facade to get count with groupby
$vehicles= DB::table('vehicles')
->select('type', DB::raw('count(*) as total'))
->groupBy('type')
->get();

Average of a calculated second average in Laravel Query Builder

I have 3 tables : Appointments, Reviews, Review_ratings
Each review_ratings has a rating value. Each review has many review_ratings and finally, eeach appointment has many reviews.
What I am trying to do is to calculated the average of review_ratings per review, and then calculate the average of reviews total ratings and see if it is higher than a specific int.
Exemple:
Apointment id : 1, has 2 Reviews, each review has review_rating one of 4 and 3 and review rating number 2 of 5 and 5.
AVG(review_ratings) should return the average between average(5,5) and average(4,3) which is 3.5 then AVG(the_first_avg) should return average (3.5,5) which is 4.25.
My last tested query is :
if($minRating > 0){
$search->join('appointments','appointments.doctor_id','users.id')
->join('reviews','reviews.appointment_id','appointments.id')
->join('review_ratings','review_ratings.review_id','reviews.id')
->addSelect(array('review_ratings.id',
DB::raw('AVG(review_ratings.rating) as review_ratings_count')
))
->addSelect(
DB::raw('AVG(review_ratings_count) as ratings_avg')
)
->where('ratings_avg','>=',$minRating)
;
}
The error I'm getting is
Column not found: 1054 Unknown column 'review_ratings_count' in 'field
list'
I found the solution to be much simpler that my code, and using AVG(name_of_column) rather than using the 'AS' value
->selectRaw('AVG(review_ratings.rating) as overall_rating')
->when($minRating > 0, function ($query) use($minRating)
{
$query->havingRaw('AVG(review_ratings.rating) >= ?', [$minRating]);
})

Adding column that displays count of record under same group gridview yii2

I am trying to count the record with the same group in a table. I know this is attainable easily with a query and yes I already done it but I am thinking of the proper way to place the count on main models and search models.
The database looks like this:
Device
----------------------------
id name group_id
1 Phone Sony 2
2 Computer 1
3 Printer 1
4 Phone LG 1
Group
----------------------------
id name
1 Home
2 Office
OUTPUT TABLE:
Group Name Device Count
Home 1
Office 3
I have done this by doing a query on the controller.
SELECT COUNT( * )
FROM `hosts`
WHERE group_id = *~DESIRED NUMBER HERE~*
I am looking for a better approach. I am new to SQL and I think joining tables is the proper way to do this but I am still not very sure.
Under GroupSearch model I will add this:
$dataProvider->setSort(['attributes' => [
'devicecount' => [
'asc' => [ 'devicecount' => SORT_ASC ],
'desc' => [ 'devicecount' => SORT_DESC ],
],
]
]);
Since devicecount is not available in the tables it will show an error. Where should I put the counting of the same store_id? Under Device model or under Group model? How will I be able to sort them also?
There is an example of handling this kind of situations in official guide.
Example of model:
class Group extends \yii\db\ActiveRecord
{
public $devicesCount;
// ...
public function getDevices()
{
return $this->hasMany(Device::className(), ['group_id' => 'id']);
}
}
Example of query:
$customers = Group::find()
->select([
'{{group}}.*', // select all customer fields
'COUNT({{device}}.id) AS ordersCount' // calculate orders count
])
->joinWith('devices') // ensure table junction
->groupBy('{{group}}.id') // group the result to ensure aggregation function works
->all();
where {{group}} and {{device}} - table names for groups and devices accordingly, or you can use Group::tableName() and Device::tableName() for better code maintaining.
Then because you added count as model property, you will be able to sort results by this column.

Getting average from database in laravel 4

I have two tables: Workplans and Progress. Here is the Workplan table.
Workplan Table:
id
division_name
division_chief
Progress Table:
id
score
workplan_id foreign_key
Here workplan_id is foreign key of workplan table.
Now I want to get average from progress table grouping by the division_name. But I am really messed up with my query. What is I have done:
$report = DB::table('workplans')
->join('progress','workplans.id','=','progress.workplan_id')
->avg('progress.score')
->groupBy('workplans.division_name')
->get();
dd($report);
The SQL you want is something like this...
SELECT workplans.division_name, AVG(progress.score) as average_score
FROM `workplans`
LEFT JOIN `progress` ON progress.workplan_id = workplans.id
GROUP BY workplans.id
With the laravel query builder...
$report = DB::table ('workplans')
->join ('progress', 'progress.workplan_id', '=', 'workplans.id')
->select ('workplans.division_name', DB::raw ('AVG(progress.score) as average_score'))
->groupBy ('workplans.id')
->get ();
So for division 1 with scores 5 and 15, division 2 with scores 15 and 25 this returns...
Array
(
[0] => stdClass Object
(
[division_name] => Division 1
[average_score] => 10
)
[1] => stdClass Object
(
[division_name] => Division 2
[average_score] => 20
)
)
The problem with your query is that the avg aggregate function returns the average value for all columns on the table, not a reference back to the query so that you can continue to chain additional functions. The solution to this is to manually specify the average using the MySQL AVG function using the DB::raw method.
Try to do it via raw select:
$report = DB::table('workplans')
->join('progress','workplans.id','=','progress.activity_id')
->select(DB::raw('avg(progress.score) as avg_progress_score'))
->groupBy('workplans.division_name')
->get();
I'm not sure how to write it using Laravel's query builder, but a static MySQL query might be easier:
$report = DB::select("SELECT AVG(progress.score) AS 'avg' FROM workplans JOIN progress ON workplans.id=progress.activity_id GROUP BY workplans.division_name;");

Categories