Laravel, why do I have so many queries? - laravel

Controller
$attendees = Attendee::with('User')->get();
return View::make('admin.attendees.index', compact('attendees'));
Attendee model
public function user() | if( !( $user->hasRole('admin') || $user->hasRole('programmer') ))
{ | return Redirect::to('/');
return $this->belongsTo('User'); |
}
View
#foreach($attendees as $attendee)
<td>{{link_to_route('admin.users.show', $attendee->user->username, $attendee->user->id)}}</td>
#endforeach
223 queries
select * from `users` where `users`.`id` = '4' limit 1600μs
select `roles`.*, `assigned_roles`.`user_id` as `pivot_user_id`, `assigned_roles`.`role_id` as `pivot_role_id` from `roles` inner join `assigned_roles` on `roles`.`id` = `assigned_roles`.`role_id` where `assigned_roles`.`user_id` = '4'630μs
select * from `attendees`1.24ms
select * from `users` where `users`.`id` in ('5', '1', '3', '8', '9', '10')780μs
select * from `users` where `users`.`id` = '5' limit 1680μs
select * from `users` where `users`.`id` = '5' limit 1650μs
select * from `users` where `users`.`id` = '5' limit 1680μs
select * from `users` where `users`.`id` = '5' limit 1590μs
select * from `users` where `users`.`id` = '1' limit 1
<continues like so for each user id>
I am using phpdebugbar to show the queries.
Migration
Schema::table('attendees', function(Blueprint $table) {
$table->foreign('user_id')->references('id')->on('users')
->onDelete('cascade')
->onUpdate('no action');
Am I doing something wrong that is causing the query to be run over and over again?

The eager load should be the name of the relationship function, not the relationship's model, and it's apparently case sensitive:
$attendees = Attendee::with('user')->get();

Related

laravel query to make pagination

$users = SELECT users.id,
users.name,
cd.title,
cd.updated_at,
profiles.avatar
FROM `users`
JOIN (SELECT Max(id) max_id,
user_id
FROM book
GROUP BY user_id) c_max
ON ( c_max.user_id = users.id )
JOIN book cd
ON ( cd.id = c_max.user_id )
JOIN profiles
ON ( profiles.user_id = users.id )
ORDER BY cd.updated_at DESC
i am using this to get data in laravel query
$users = DB::select(DB::raw('SELECT users.id, users.name, cd.title, cd.updated_at, profiles.avatar FROM users JOIN ( SELECT MAX(id) max_id, user_id FROM book GROUP BY user_id )
c_max ON (c_max.user_id = users.id) JOIN book cd ON (cd.id = c_max.user_id) JOIN profiles ON (profiles.user_id = users.id) ORDER by cd.updated_at DESC limit 24 offset 0'));

Multiple whereHas calls are not being aggregated together

I've got multiple calls to whereHas() on an instance of \Illuminate\Database\Query\Builder ($cars):
$cars->whereHas("finance", function (Eloquent\Builder $query) {
$query->where('term'...)
}
$cars->whereHas("finance", function (Eloquent\Builder $query) {
$query->where('payment'...)
}
Is there some way to aggregate the where(s) together without needing to do all the where calls within the containing whereHas?
The SQL query being executed:
SELECT id
FROM `cars`
WHERE EXISTS
(SELECT `finance`.`payment` as payment
FROM `finance`
INNER JOIN `car_finance` ON `finance`.`id` = `car_finance`.`finance_id`
WHERE `car_finance`.`car_id` = `cars`.`id`
AND `payment` >= 50)
AND EXISTS
(SELECT *
FROM `finance`
INNER JOIN `car_finance` ON `finance`.`id` = `car_finance`.`finance_id`
WHERE `car_finance`.`car_id` = `cars`.`id`
AND `payment` <= 200)
AND EXISTS
(SELECT *
FROM `finance`
INNER JOIN `car_finance` ON `finance`.`id` = `car_finance`.`finance_id`
WHERE `car_finance`.`car_id` = `cars`.`id`
AND `term` = 48)
AND EXISTS
(SELECT *
FROM `finance`
INNER JOIN `car_finance` ON `finance`.`id` = `car_finance`.`finance_id`
WHERE `car_finance`.`car_id` = `cars`.`id`
AND `deposit` = 1000)
AND `active` = 1
The SQL query that I would like to be executed:
SELECT *
FROM cars
WHERE EXISTS
(SELECT *
FROM `finance`
INNER JOIN `car_finance` ON `finance`.`id` = `car_finance`.`finance_id`
WHERE `car_finance`.`car_id` = `cars`.`id`
AND deposit = 1000
AND term = 48
AND payment >= 50
AND payment <= 200)
AND active = 1
Your question it's not clear but I think you want this:
$cars->whereHas("finance", function ($query) {
$query->where('deposit', 1000)->where('term', 48)
->whereBetween('payment', 50, 200);
})->where('active', 1)->get();

Laravel Eloquent - Where In All

In Laravel 4.2, I am trying to achieve a query that returns all users, that have all of certain activities. As of now, I have a query that returns all users that have one of many activities:
//$selectedActivities being an array
$userByActivities = User::with('activities')
->whereHas('activities', function($query) use($selectedActivities){
$query->whereIn('id', $selectedActivities);
})->get();
To be more clear: given activities a,b,c. I am looking for all users that have activity a AND b AND c. My query returns all users that have activity a OR b OR c.
Thank you for your help.
EDIT:
The solution offered by lukasgeiter results in following query:
select * from `users` where
(select count(*) from `activities` inner join `activity_user` on `activities`.`id` = `activity_user`.`activity_id` where `activity_user`.`user_id` = `users`.`id` and `id` = '7') >= 1
and (select count(*) from `activities` inner join `activity_user` on `activities`.`id` = `activity_user`.`activity_id` where `activity_user`.`user_id` = `users`.`id` and `id` = '3') >= 1
and (select count(*) from `activities` inner join `activity_user` on `activities`.`id` = `activity_user`.`activity_id` where `activity_user`.`user_id` = `users`.`id` and `id` = '1') >= 1
and (select count(*) from `activities` inner join `activity_user` on `activities`.`id` = `activity_user`.`activity_id` where `activity_user`.`user_id` = `users`.`id` and `id` = '2') >= 1
Whereas the solution offered by Jarek Tkaczyk:
$userByActivities = User::with('activities')
->whereHas('activities', function($query) use($selectedActivities) {
$query->selectRaw('count(distinct id)')->whereIn('id', $selectedActivities);
}, '=', count($selectedActivities))->get();
for a similar request, results in following query:
select * from `users` where (select count(distinct id) from `activities`
inner join `activity_user` on `activities`.`id` = `activity_user`.`activity_id`
where `activity_user`.`user_id` = `users`.`id` and `id` in ('7', '3', '1', '2')) = 4
You'll have to add multiple whereHas for that:
$query = User::with('activities');
foreach($selectedActivities as $activityId){
$query->whereHas('activities', function($q) use ($activityId){
$q->where('id', $activityId);
});
}
$userByActivities = $query->get();
If you are getting Cardinality violation: 1241 Operand should contain 2 column(s) the problem is the nested selectCount adds to the normal select count(*) instead of overriding the existing select, so changing to $query->distinct()->whereIn('id', $selectedActivities); did the trick for me, or changing to $query->select(DB::raw(count(distinct id)))

Select single column from a row in Laravel?

How can I fetch a single column from a single row in Laravel 4?
Here's what I'm trying:
return DB::selectOne("
SELECT EXISTS(
SELECT *
FROM permissions p
JOIN role_permissions rp ON rp.permission_id=p.id
JOIN user_roles ur ON ur.role_id=rp.role_id
WHERE ur.user_id=? AND p.code=?
)
",[Auth::user()->id,$perm_code]);
selectOne returns this ugly object though:
object(stdClass)#297 (1) {
["EXISTS(
SELECT *
FROM permissions p
JOIN role_permissions rp ON rp.permission_id=p.id
JOIN user_roles ur ON ur.role_id=rp.role_id
WHERE ur.user_id=? AND p.code=?
"]=>
string(1) "0"
}
Is there something better?
Using PDO is ugly too:
$stmt = DB::getPdo()->prepare("
SELECT EXISTS(
SELECT *
FROM permissions p
JOIN role_permissions rp ON rp.permission_id=p.id
JOIN user_roles ur ON ur.role_id=rp.role_id
WHERE ur.user_id=? AND p.code=?
)
");
$stmt->execute([Auth::user()->id,$perm_code]);
return (bool)$stmt->fetchColumn();
Use this:
DB::table('users')->pluck('columname');
From the inline documentation:
/**
* Pluck a single column's value from the first result of a query.
*
* #param string $column
* #return mixed
*/
Is this what you are looking for?
DB::table('users')->select('columnName')->join('whatever',...,...,...)->take(1)->get();
Another example
DB::table('users')
->join('contacts', 'users.id', '=', 'contacts.user_id')
->join('orders', 'users.id', '=', 'orders.user_id')
->select('columnName')
->take(1)
->get();
Raw didn't worked for you?:
return DB::select(DB::raw("
SELECT EXISTS(
SELECT *
FROM permissions p
JOIN role_permissions rp ON rp.permission_id=p.id
JOIN user_roles ur ON ur.role_id=rp.role_id
WHERE ur.user_id=? AND p.code=?
))
",[Auth::user()->id,$perm_code]);
And you can process that object to get what you want:
$result = DB::select(DB::raw("
SELECT EXISTS(
SELECT *
FROM permissions p
JOIN role_permissions rp ON rp.permission_id=p.id
JOIN user_roles ur ON ur.role_id=rp.role_id
WHERE ur.user_id=? AND p.code=?
) as exists)
",[Auth::user()->id,$perm_code]);
$result[0]->exists ? '1' : '0'
This is a test I just did here:
Route::any('test', ['as' => 'test', function()
{
$result = DB::select(DB::raw('select exists(select * from users) as exists;'));
dd($result[0]->exists ? '1' : '0');
}]);
Worked like a charm.
To fetch a single value from the first result, you can use \Illuminate\Database\Query\Builder::value. e.g.
$lastInvoice = DB::table('booking_groups')
->where('company_id','=',$booking->company_id)
->orderBy('invoice_number','desc')
->value('invoice_number');

orWhereHas - parameter grouping on Eloquent query - how do I do this in Laravel?

In an eloquent query I am building, I am placing a constraint on a has relationship using Laravel 4.1's whereHas and orWhereHas methods.
In the example soccer application, I wish to place a constraint on the homeClub and awayClub relationships so that I can the result set will select where the homeClub = Arsenal OR awayClub = Arsenal.
This issue has evolved from an earlier question It seems that when using the orWhereHas method - the resulting sql doesn't group the or constraint.
The query (relevant excerpt that is placing the constraints):
$ret
->where( function( $subquery ) use ( $ret ){
$ret->whereHas('homeClub', function ( $query ){
$query->where('name','Arsenal' );
})->orWhereHas('awayClub',function ( $query ){
$query->where('name','Arsenal' );
});
})
->where( function ( $subquery ) use ( $ret, $parameterValues ){
$ret->whereHas('season', function ($query) use ( $parameterValues ){
$query->where('name', $parameterValues['season_names'] );
});
} )
->whereHas('territory',function( $query ) use ( $parameterValues ){
$query->where('region','Australia');
})->get()->toArray();
This produces the sql:
SELECT * FROM `broadcasts` WHERE
(SELECT count(*) FROM `uploads` WHERE `broadcasts`.`upload_id` = `uploads`.`id` and `type` = 'international-audience') >= '1'
and
(SELECT count(*) FROM `clubs` WHERE `clubs`.`id` = `broadcasts`.`home_club_id` and `name` = 'Arsenal') >= '1'
or
(SELECT count(*) FROM `clubs` WHERE `clubs`.`id` = `broadcasts`.`away_club_id` and `name` = 'Arsenal') >= '1'
and
(SELECT count(*) FROM `seasons` WHERE `broadcasts`.`season_id` = `seasons`.`id` and `name` = '2012/13') >= '1'
and
(SELECT count(*) FROM `territories` WHERE `broadcasts`.`territory_id` = `territories`.`id` and `region` = 'Australia') >= '1'
But, this isn't what I want, because referring to the eloquent statement, the club queries are grouped and the query above either selects the homeClub constraints OR, the awayClub, season name, territory region. What I'm intending is the following SQL:
SELECT * FROM `broadcasts` WHERE
(SELECT count(*) FROM `uploads` WHERE `broadcasts`.`upload_id` = `uploads`.`id` and `type` = 'international-audience') >= '1'
and
((SELECT count(*) FROM `clubs` WHERE `clubs`.`id` = `broadcasts`.`home_club_id` and `name` = 'Arsenal') >= '1'
or
(SELECT count(*) FROM `clubs` WHERE `clubs`.`id` = `broadcasts`.`away_club_id` and `name` = 'Arsenal') >= '1' )
and
(SELECT count(*) FROM `seasons` WHERE `broadcasts`.`season_id` = `seasons`.`id` and `name` = '2012/13') >= '1'
and
(SELECT count(*) FROM `territories` WHERE `broadcasts`.`territory_id` = `territories`.`id` and `region` = 'Australia') >= '1'
Note.. the parentheses on the club subquery.
Does anyone know how I would write this as the eloquent query? I really don't want to have to revert to fluent / joins.
You need to reference the query passed through to the where closure. Otherwise you are adding the grouped where clauses to the main query bypassing any groupings:
$ret
->where( function( $query ){
$query->whereHas('homeClub', function ( $subquery ){
$subquery->where('name','Arsenal' );
})
->orWhereHas('awayClub',function ( $subquery ){
$subquery->where('name','Arsenal' );
});
})
->where( function ( $query ) use ( $parameterValues ){
$query->whereHas('season', function ($subquery) use ( $parameterValues ){
$subquery->where('name', $parameterValues['season_names'] );
});
})
->whereHas('territory',function( $query ) use ( $parameterValues ){
$query->where('region','Australia');
})
->get();

Resources