What's Laravel's Eloquent search on relation lightest query? - php

In a classic Rest API approach, my application needs to check if the user is related to a certain account. We can have actions on multiple accounts, so the query is executed on every request.
Since this is our most often called query, it's lightness is important for my peace of mind.
Current query, using Eloquent
$user-> accounts()-> where($primaryKey, $id);
Somehow, these both generate errors (Eloquent bug?)
$user-> accounts()-> find ($id);
$user-> accounts()-> where ($primaryKey, $id)->count();
The error:
SQLSTATE[23000]: Integrity constraint violation: 1052 Column 'a_id' in where clause is ambiguous (SQL: select * from 'accounts' inner join 'users_accounts' on 'accounts'.'a_id' = 'users_accounts'.'a_id' where 'users_accounts'.'u_id' = 1 and 'a_id' = 1 limit 1)
Back to my question, is there a more elegant solution then the "where" function?

If this is one of your most used queries, why not cache it?
$user->accounts()->where($primaryKey, '=', $id)->remember(60)->get();
The mentioned queries fail because you have a column with the same name in both tables
where 'users_accounts'.'u_id' = 1 and 'a_id' = 1

Related

Laravel Group & Count by Eloquent Relationship

I know this question has be asked before here: Laravel Grouping by Eloquent Relationship but the answers are from 2016 and they seem not to work in 2019. Also my question is more simple, since I only need one relation level.
My Question
A
user has multiple items.
How to find how many items a user has of each item_type with one query?
This is what I tried:
A query like
User::with(['items' => function($q){
$q->groupBy('type');
});
returns this error:
Syntax error or access violation: 1055 Expression #1 of SELECT list
is not in GROUP BY clause and contains nonaggregated column 'items.id'
which is not functionally dependent on columns in GROUP BY clause;
this is incompatible with sql_mode=only_full_group_by
I tried to fix this error with the following query:
User::with(['items' => function($q){
$q->select('type', \DB::raw('count(*) as total'))
->groupBy('type');
});
However, this returns a collection of users where each user's item collection is empty.
Is it somehow possible to group by a relation in the query?
There is an error :
You are using $q as closure argument and inside you are using $query. Also sometimes I have faced issue where I had to pass the foreign key inside the relation query closure to get the results :
<?php
$userWithItems = User::with(['items' => function($q){
$q->select('type', \DB::raw('count(*) as total'), 'user_id')
->groupBy('type');
});
Try it once by removing user_id if it works then it's better. Secondly you can not select non aggregated columns in mysql when you have groupby. The option is disable only_full_group_by in mysql configurations. So mostl likely user_id will also fail unless you disable this configuration

Phalcon - How do i do a SELECT IN Subquery with Phalcon models?

I need to know how to do i do a subquery type selection with phalcon models?
for example i want to select all the users who viewed me, they are stored in the UserView table with columns 'id','user_from','user_to' (mapped by User table user_id to either user_from or user_to)
so i want to select all the users who has a user_to as with the current user, and group by user_to make sure i only get one recorded, I wrote below function to do this but there is fundamental two problems
1. Is how to do sub-query using phalcon models
2. Is my logic correctly applied on the back-end of the DB (as i cant see real executed query)
public function getUserWithViewedMe($limit=1000000){
return User::query()
->rightJoin("XYZ\Models\UsersView")
->andWhere(" XYZ\Models\UsersView.user_from IN :user_id: ",
array('user_id' => $this->user->user_id) )
->group('user_id')
->order("XYZ\Models\UsersView.id DESC ")
->limit($limit)
->execute();
}
This returns empty set...
So far it is not possible to model subqueries in Phalcon. There is also topic according to standard implementation issues.
To query params according to other table, here is an answer.
To query IN you can use queryBuilder
$this->modelsManager->createBuilder()
// ...
->inWhere('column', $array);

Id Column Overwritten by Join

Using Laravel 4.2 & MySQL.
I have an applications table with an id and a fit_score_id column, and a fit_scores table with an id column. It's a basic "belongs to" relationship.
The following code:
$query = Application::join('fit_scores', 'applications.fit_score_id', '=', 'fit_scores.id');
$collection = $query->get();
...produces a collection of Application models with the id property set to the value of the fit_score_id. What am I doing to cause this?
I should note that it is necessary to do this join rather than simply using eloquent relations, because I'm going to want to order the results by a column on the fit_scores table. I don't believe this is possible using Eloquent without an explicit join.
The best way to solve this is by chaining the join method to a select method as following:
Application::select('*', \DB::raw("applications.id as appid"))
->join('fit_scores', 'applications.fit_score_id', '=', 'fit_scores.id')
->get();
Explained: The solution simply suggest that instead of thinking to prevent the behavior of overwriting the first id with the joined id, we can hook into the primary selection query (before joining) and change the label of the id column into something else (in this case 'appid'). By doing so, we end up with both the id of the parent table being labeled 'appid' and the id of the joined table being labeled 'id' again while they lives together on the final result.
I was able to find a possible solution using this answer:
Laravel 4 - JOIN - Same column name
Basically, since Laravel does not automatically prefix column names with table_name. for joined tables, we need to manually work around it by aliasing any conflicting column names in joins. Adding this select statement to my query did it:
->select(DB::raw("applications.*, fit_scores.*, applications.id as id"))
It depends on what you need but probably you can achieve it using eager loading. In case you need to mix joins and eager loading check this out. http://www.jmilan.net/posts/eager-loading-joins-in-laravel

Why do I get the account id as the user id in Laravel's Eloquent ORM

I'm building a multi-tenant site where each account has their own set of users... In my Users controller, I have an update method that fetches the user with the following:
$user = User::whereUsername($username)
->leftJoin('accounts', 'accounts.id', '=', 'users.account_id')
->where('accounts.id', '=', DB::raw('users.account_id'))
->where('accounts.subdomain', '=', $subdomain)
->firstOrFail();
Problem is, when I do $user->id, it's giving me the accounts.id instead...
I was able to fix this by adding ->select('users.id as id') but I'd like to know why it happen and if there is a better way to avoid it. I would think that since I'm selecting from users that the columns in the users table would take priority in any naming conflicts...
There is no way for Laravel to know which id should take priority. Normally, if you would try to select it in SQL, you would get an error saying id is ambigeous. You can just select it in SQL by using its table name. To avoid confusion in Laravel, you could use aliases in your select statement. You did that, which is good. Try moving database related tasks out of your controller.

Laravel query builder, use custom select when calling count()

I have three tables, one defining a many to many relationship between the other two.
table : collections
table : collection_question
table : questions
I am running this query to return the number of questions a collection has
$results = DB::table('collections')
->select(array(
DB::raw('count(collection_question.question_id) as question_count'),
'name',
'description'
))->leftJoin(
'collection_question',
'collections.id',
'=',
'collection_question.collection_id'
)->where('question_count', '>', 1)
->orderBy('question_count', 'asc')
->get();
This works fine as the select query is not tampered with by the query builder.
When I swap out get() for count() however the query builder replaces my select clause with select count(*) as aggregate which is understandable, however I loose the binding to question_count in the process and a SQL exception is thrown.
I've looked through the source code for Illuminate\Database\Query\Builder to try and find a solution but other than manually executing the raw query with a custom count(*) alongside my other select clauses I'm at a bit of a loss.
Can anyone spot a solution to this?
Instead of calling count() on a Builder object, I've resorted to creating my own count expression in addition to any other select clauses on the query rather than replacing them.
// This is in a class that uses Illuminate\Database\Query\Expression the clone
// isn't necessary in most instances but for my case I needed to take a snapshot
// of the query in its current state and then manipulate it further.
$query = clone($query);
$query->addSelect(new Expression("count(*) as aggregate"));
$count = (int) $query->first()->aggregate;

Categories