Laravel eloquent distinct select with possibility to call relation? - laravel

I want to select distinct results from one table, the structure is following:
id, user_id, category_id, last_answered_question_id
I have relation for last_answered_question_id called: lastAnsweredQuestion()
My query is:
Answer::select('category_id')->where(['user_id' => $userId])->distinct()->get();
This query works as expected but in this case I can't call relation $answer = lastAnsweredQuestion.
Is there any known way to get distinct items and call relation lastAnsweredQuestion() anyway?

Related

Laravel. Join three tables from three way pivot where one column (jsonb) contains an array of id's

I have a table called user_business_survey which has a user_uuid, business_uuid and a third containing one or more survey_uuid's IN A JSONB column. I need to query the DB to bring back the following two scenarios: USER1 belongs to BUSINESS1 AND HAS SURVEY A, SURVEY B, SURVEY C then also USER1 belongs to BUSINESS3 AND HAS SURVEY B, SURVEY D, SURVEY E
This is the query I am trying but to no avail. It return empty.
DB::table('user_business_survey')
->select(['*'])
->leftJoin('businesses', 'businesses.uuid', '=', 'user_business_survey.business_uuid')
->leftJoin('surveys', 'surveys.uuid', '=', 'user_business_survey.survey_uuid')
->whereJsonContains('survey_uuid', 'surveys.uuid')
->where('user_business_survey.user_uuid', '=', $id)
->get();
DB table is as follows:
user_uuid
business_uuid
surveys_uuids is an column with one or more survey id's as jsonb
I need to itrate through all id's and join them from the jsonb column
I thank you in advance for any help to build this query

Laravel where clause of current and related table

how to compare current table column to related table column
example: A.quantity < B.criticalQuantity
similarly like this AModel::where('quantity', "<", "b.criticalQuantity")->get()
the relations is
B HasMany A
A BelongsTo B
you can use whereColumn
it is specialist in comparing columns not a column with value.
anyway you can't directly compare two columns from two table, you have to join them first by anyway of join types
something like:
$values = ModelA::join('model_b_table_name', 'model_b_table_name.id', 'model_a_table_name.model_b_id')
->whereColumn('model_b_table_name.column.quantity', 'model_b_table_name.quantity')
->get();
you must be specific in joining the table, you should join by the columns that consist the relation between the two tables.
->whereRaw('table_1.name = table_2.name')

Why Laravel does not make lots of queries when using exists on relations?

I have such entities as:
Company
Person
Company hasMany Persons. So in the persons table there is company_id column.
I return company list, which I pass to CompanyResource. There I return has_persons => $this->persons()->exists() value.
Then I checked the result of DB::getQueryLog() and I found out that there is only one SQL query, which does not have count or anything like that.
In order to count how many persons a company has, Laravel should make one query per company, shouldn't it? Like select count (*) from persons where company_id = 5 for example
try this
$this->persons->count()

In Laravel Eloquent, how do I reference primary query in subquery

I have a model User that has many Orders. Orders have many products, with the pivot table order-product. I don't want to preload and iterate through the orders if at all possible.
I need to return users where
signed_date === true on User
order_date on Order is after signed_date on User
order-product shows product hasn't been paid
I am failing on number 2.
In the following code, the first query within whereHas is wrong. I don't know how to reference the signed date of the user from within the where has. If I was iterating through users in a collection I could do something like ($query) use $user, but how do I do this without preloading all the users?
return User::whereNotNull('signed_date')
->whereHas('orders', function ($query) {
$query->where('order_date', '<=', 'user.signed_date');
$query->whereHas('products', function ($q) {
$q->where('paid', false);
});
})
->get(['id','fname','lname', 'title', 'signed_date']);
I would like to use eloquent if possible. If that is not possible, I would be happy for tips in solving this problem using the query builder/sql.
The Eloquent query builder has a special function called whereColumn('a', '<=', 'b') to compare columns instead of a column against a value. Using this function instead of a normal where() is necessary because of the way the query builder builds the actual query. You need to let the query builder know that you are going to pass a column name instead of a value for proper escaping and formatting of the query string.
Anyway, it seems you can also pass column names prefixed with a table name to the function, allowing you to compare columns across tables:
$query->whereColumn('orders.order_date', '<=', 'users.signed_date')
This works because you are using whereHas() in your query. Your query basically gets translated to:
SELECT id, fname, lname, title, signed_date
FROM users
WHERE signed_date NOT NULL
AND EXISTS (
SELECT 1
FROM orders
WHERE orders.order_date <= users.signed_date
AND EXISTS (
SELECT 1
FROM products
WHERE paid = 0
)
)
It might actually be not necessary at all to use the table name together with the column name in whereColumn(). But in case you'll ever add a column named the same on another table, the query might break - so IMHO it is good practice to use the table name in custom queries.
By the way, the reason this will not work together with with('relationship') is that this function results in an additional query and you obviously cannot compare columns across queries. Imagine the following:
Order::with('user')->take(5)->get();
It will be translated into the following:
SELECT *
FROM orders
LIMIT 5
SELECT *
FROM users
WHERE id IN (?, ?, ?, ?, ?)
where the five ? will be the user_ids of the orders. If the first query returns multiple rows with the same user_id, the amount of rows fetched from the users table gets reduced of course.
Note: All the queries are only examples. Might be that the query builder builds different queries based on the database type and/or escapes them differently (i.e. column names in backticks).

Laravel Eloquent select function cause empty relation

Following is my query
$user = User::select(['uuid','name','about'])->with(['education','work'])->first();
this returns empty data for relationship education and work,
but if I remove select function from query I am getting data in relationship and it also returns all columns of user table which I don't want.
how can solve this problem
The problem is that relationships (with(...)) execute an additional query to get the related results. Let's say you have one to many relationship where users have many works. User::with('work')->find(1) will then execute these 2 queries:
select user where id = 1 and select works where user_id = 1.
So basically in order to be able to execute the second query (fetch relationship data) you need to include id (or whichever column you're referencing) in you select statement.
Fix:
$user = User::select(['uuid','name','about', 'id'])->with(['education','work'])->first();
Same principle in different forms applies to all relationships. For example in the inverse of hasMany which is belongsTo you would need to select the foreign key (for example user_id).

Resources