Laravel/Php - Nested Model calling using Eloquent ORM - php

Assume the following, I have a:
Course Model:
belongsToMany User Model
belongsToMany Exam Model
HasMany Question Model
HasMany Answers Model
Course Exam Results Table
id
user_id
course_id
exam_id
question_id
answer_id
Is there a better way to get the results without having to directly query the table?
I am currently doing the following:
public function displayResult($user, $course, $exam) {
$course_exam_results = \App\CourseExamResult::where([
'user_id' => $user->id,
'course_id' => $course->id,
'exam_id' => $exam->id
])
->get();
dd($course_exam_results);
}
Would love to do something better like auth()->user()->course(??)->exam(??)->results. I thought about using laravel hasManyThrough but I don't think I can make it work in this case as it only accepts 2 Models while I might have to call 3-5 models at a time.
Thank you in advance.

If you want to use multiple wheres in Laravel Eloquent, you must do like example below:
You must give to where() an array.
Every search has two arguments so it is an array in an array.
$course_exam_results = \App\CourseExamResult::where([
['user_id', auth()->user()->id],
['course_id', $course->id],
['exam_id', $exam->id]
])
->firstOrFail();

Related

Laravel eloquent update column using relationship column

How can achieve this query?
Sale::with(['catalog'])
->whereIn('id', $ids)
->update(['price' => DB::raw('catalog.price')]);
This is not working, it shows undefined table... I tried to put the name of the table but it's the same.
On the internet I always found the easy query:
Sale::with(['catalog'])
->whereIn('id', $ids)
->update(['price' => 5]);
Okay! When I want to update all rows with the same value is easy, in addition is easy when you want to update with a column of the same table like:
Sale::with(['catalog'])
->whereIn('id', $ids)
->update(['price' => DB::raw('price_alternative')]);
But how about using a column of another table with a relationship? I haven't found the solution.
I know this can be solved using entire raw query, but I wanted to know if it can be achieved by the eloquent way
You probably need to join in the catalog table on your querybuilder. This is different than using with(). Something along the lines of
Sale::whereIn('id', $ids)
->join('catalog', 'sales.catalog_id', '=', 'catalog.id')
->update(['price' => DB::raw('catalog.price')]);
This is not better than answer of #Qirel, but it is Eloquent way, i like this because that's more clearly.
$Sales = Sale::whereIn('sales.id', $ids)
->with('Cat')->get();
$Sales->map(function($q){
$q->price = $q->Cat->price;
$q->save();
});
Assume you have this relation code in Sale model:
public function Cat()
{
return $this->hasOne(CatModel::class, 'id', 'catalog_id');
}

Get first element in relation with Eloquent Builder

I'm trying to retrieve a user's answer. I have this query :
$data = Category::where('id', $id)->with(
[
'questions' => function ($query) {
$query->with(['answers' => function ($query) {
$query->where( 'user_id', auth()->user()->id )->first();
} ])->orderBy('position', 'ASC');
}
]
)->first();
I would like to collect only the answer (a user can only have one answer to a question)
But laravel returns an array "answers"
How to have only the answer, without this array? Thank you !
You can define another relationship called answer and make it a hasOne. This way you only get 1 result, however note that without an order (order by) the result can change from request to request.

How to get pivot data inside eloquent withCount function callback?

I'm trying to use a pivot variable of a parent relationship inside the eloquent withCount() method.
Background:
There is a application with a ManyToMany relationship between Users and Clusters. Users can send messages within a cluster. To keep track of the unread message count for a user in a specific cluster i keep track of the last read message id in the join table, like so:
table: cluster_user
cluster_id | user_id | last_read_message_id
-------------------------------------------
1 | 59 | 3
2 | 62 | 8
The User() model has a belongsToMany() relation with the Cluster() model
The Cluster() model has a belongsToMany() relation with the User() model
The Cluster() model has a hasMany() relation with the Messages() model
The Message() model has a belongsTo() relation with the Cluster() model
Now I would like to list all the clusters of the authenticated user including a unread message count.
Currently I'm stuck on this:
$clusters = Auth::user()->clusters()->withCount(['messages' => function ($query) {
$query->where('messages.id', '>', '???');
}])->get();
I've already tried:
$clusters = Auth::user()->clusters()->withCount(['messages' => function ($query) {
$query->where('messages.id', '>', 'cluster_user.last_read_message_id');
}])->get();
But this gives me a total count of all the messages in stead of the ones with an id higher than x.
If I hardcode an id, like this:
$clusters = Auth::user()->clusters()->withCount(['messages' => function ($query) {
$query->where('messages.id', '>', '3');
}])->get();
Then I get the correct unread message count.
So can somebody tell me how to use the pivot variable 'last_read_message_id' of the user()->cluster() relationship inside the withCount() callback function with the following in mind:
I'ts crucial to use as little queries as possible.
The unread message count must be a part of the cluster() collection because I'm returning a ClusterResource later on, like so:
return ClusterResource::collection($clusters);
which includes the unread message count.
class ClusterResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'name' => $this->name,
'unread_messages_count' => $this->whenPivotLoaded('cluster_user', $this->messages_count)
];
}
}
Thnx!
Found the answer due to a comment of #cbaconnier.
Placing DB::raw('cluster_user.last_read_message_id') on the spot is working. I't not neat, but it works.
Full example:
$clusters = Auth::user()
->clusters()
->withCount(['messages' => function ($query) {
$query->where('messages.id', '>', DB::raw('cluster_user.last_read_message_id'));
}])
->get();
Good question! I think you should be able to do use the withPivot method on your relationship and then use the pivot attribute in your query.
For example, in your Cluster model where you define the cluster_user relationship, do:
function cluster_user() {
return $this->belongsToMany('App\User')
->withPivot('last_read_message_id');
}
And then in your query you could use whereColumn to compare the columns. Something like this:
$clusters = Auth::user()
->clusters()
->withCount(['messages' => function ($query) {
$query->whereColumn('messages.id', '>',
'pivot.last_read_message_id');
}])
->get();
Search for "Retrieving Intermediate Table Columns" on the Eloquent relationships documentation for more information.

How to pass parent column value in subquery in eloquent

How to pass _user_id column value inside with in $query. this is hasMany relationship. I am not able to figure out how to user user_id of RFX into the $query where condition.
public function response_pricings(){
return $this->hasMany('App\Models\Website\RFXRequestPricingResponse', ['rfx_request_id'=>'_rfx_request_id', 'user_id'=>'_user_id'])->selectRaw("*");
}
return RFXRequestSupplierResponded::select(
'id as _id',
'rfx_request_id as _rfx_request_id',
'user_id as _user_id',
'status',
'is_qualified',
DB::RAW('(SELECT name FROM users WHERE id=user_id) as user_name'),
DB::RAW('(SELECT note FROM rfx_request_response_notes WHERE rfx_request_id='.$rfx_request_id.' AND user_id=rfx_request_suppliers_responded.user_id LIMIT 1) as note')
)
->with(
[
'response_pricings' => function ($query) {
/*$query->where('user_id', $_user_id);*/
}
]
)
->where('rfx_request_id',$rfx_request_id)
->get();
When you have defined a relationship on a model, Laravel would automatically link the models using either the dynamically determined foreign keys or foreign keys that you have specified. Therefore you don't need to pass in the user_id to the query. Laravel would automatically use the the user_id of the RFXRequestSupplierResponded instance.
However, it looks like you are linking the RFXRequestSupplierResponded to the RFXRequestPricingResponse model using multiple foreign keys. Eloquent doesn't have built-in support for that. Take a look at the answers to this question for examples on how to add support for multiple foreign keys.
You pass local scope parameters to closure functions using use
... ->with(
['response_pricings' => function ($query) use ($_user_id) {
$query->where('user_id', $_user_id);
}
]
)
->where(....
More info in this Q&A: In PHP 5.3.0, what is the function "use" identifier?

get count in relation table in yii2 Activerecord

I have two table for post and user. I want to show post count of user in users list gridview. In yii 1 I use this in model to define a relation for this purpose:
'postCount' => array(self::STAT, 'Post', 'author',
'condition' => 'status = ' . Post::ACTIVE),
...
User:find...().with('postCount').....
But i don't know how to implement this in Yii2 to get count of post in User:find():with('...') to show in gridview.
Anyone try this in yii2?
Here is an example of what I did and it seems to work fine so far. It was used to get a count of comments on a post. I simply used a standard active record count and created the relation with the where statement using $this->id and the primary key of the entry its getting a count for.
public function getComment_count()
{
return Comment::find()->where(['post' => $this->id])->count();
}
Just a passing it along...
You can try the code below:
User::find()->joinWith('posts',true,'RIGHT JOIN')->where(['user.id'=>'posts.id'])->count();
Or if you want to specify a user count:
//user id 2 for example
User::find()->joinWith('posts',true,'RIGHT JOIN')->where(['user.id'=>'posts.id','user.id'=>2])->count();
Please note that, posts is a relation defined in your User model like below:
public function getPosts()
{
return $this->hasMany(Post::className(), ['user_id' => 'id']);
}
Well still I think for those who it may concern, if you JUST want the count a select and not the data it will be better use this instead imho:
$count = (new \yii\db\Query())
->select('count(*)')
->from('table')
->where(['condition'=>'value'])
->scalar();
echo $count;

Categories