How to pass parent column value in subquery in eloquent - php

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?

Related

Laravel/Php - Nested Model calling using Eloquent ORM

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();

Dynamic Property call giving error (Property [game] does not exist on the Eloquent builder instance.)

I have two models: Game and Game_Assignment. Game_Assignment tells whose job it is to play a game.
I am trying to count the number of Game_Assignment's that a user has their id on that also have a specific value on the Game model that it relates to. I'll just get into the Models/the code
Game Model Relationship:
public function assignments() {
return $this->hasMany('App\Models\Game_Assignment', 'game_id');
}
Game_Assignment Relationship:
public function game() {
return $this->belongsTo('App\Models\Game', 'game_id');
}
Where things are going wrong (in a queue job, if that makes a difference)
$gamesDue = Game_Assignment::where('statistician_id', $statistician->id)->game->where('stats_done', '!=', 'yes')->count();
I have also tried the following two things, neither worked:
$gamesDue = Game_Assignment::where('statistician_id', $statistician->id)->game()->where('stats_done', '!=', 'yes')->count();
and...
$gamesDue = Game_Assignment::where('statistician_id', $defaultStatistician->id)->with(['games' => function($query) {
$query->where('stats_done', '!=', 'yes');
}])->count();
None of these work, and the first one I showed threw an error:
Property [game] does not exist on the Eloquent builder instance.
Anyone have an idea of where I am going wrong? I am using this link as my reference https://laravel.com/docs/5.8/eloquent-relationships#eager-loading
When using the query builder of your Game_Assignment model, you cannot simply switch context to the query builder of Game. You can only call ->game() or ->game after you retrieved one or many model instances of Game_Assignment with first() or get().
So, in your particular case, you were looking for whereHas('game', $callback) (where $callback is a function that applies constraints on the foreign table) in order to add a constraint on the foreign table:
use Illuminate\Database\Eloquent\Builder;
$gamesDue = Game_Assignment::query()
->where('statistician_id', $statistician->id)
->whereHas('game', function (Builder $query) {
$query->where('stats_done', '!=', 'yes');
})
->count();
Side note: a column (stats_done) that seems to hold a boolean value (yes/no) should be of boolean type and not string/varchar.

Laravel 5.1 select field in with function

Can I select value from relationships with function "with" ?
So make something like this:
$test = User::where('id',1)->with(['user_detail' => function($query){
$query->select("detail_1");
}])->get();
Yes I know that I can put select in relation "user_detail" but can I select in with function?
You can select within with as you made the example given below:
$test = User::where('id',1)->with(['user_detail' => function($query){
$query->select("detail_1");
}])->get();
But it won't not work (as you commented in other answer) because you've only selected a single property but the foreign key is not available in your select statement. So, make sure that, you also select the related foreign key as well and then it'll work.
In your case, I believe that, you've to also select the user_id in your select for example:
$test = User::where('id',1)->with(['user_detail' => function($query){
$query->select(
'user_id', // This is required if this key is the foreign key
'detail_1'
);
}])->get();
So, without the foreign key that makes the relation, Eloquent won't be able to load the related models and that's why you get null in your result as you mentioned in other comment.
Yes, you can use select() inside with(). Just pass an array of columns:
$query->select(['detail_1', 'detail_2']);
Alternatively, you can create another relation and add select() to it:
public function userDatails()
{
return $this->hasMany('App\UserDetail')->select(['detail_1', 'detail_2']);
}
$result = Staff::where('live_status',2)
->with('position')->with('department')->with('gender')
->with(['partner' => function($query){
$query->where('alive',0);
}]);

Laravel - Unnecessary columns still being loaded with select statement

I am wanting to limit a controller's function's result to only pass certain columns into the view.
It is necessary because it will be used within an API, and so I need the results to be as streamlined as possible.
I have done this successfully with the following function:
public function getIndex()
{
$alerts = Criteria::select('id', 'user_id', 'coordinate_id', 'alert_name')
->with(['coordinate' => function($q){
$q->select('name', 'id');
}])
->get();
}
So it only returns id, user_id and coordinate_id from the criteria table.
However on the function below, I am using a has query (to access a relationship), and thus, using with afterwards to limit the columns, but it's still returning all:
public function getMatches()
{
$matches = Criteria::select('id')
->has('alerts')
->with(['alerts' => function ($q){
$q->select('id', 'headline', 'price_value', 'price_type');
}])
->with('alerts.user.companies')
->get();
}
But, for example, it's still returning the description column, which is in the alert's table. The with query proceeding the has query clearly isn't working (but it's presenting no errors).
Also, the ->with('alerts.user.companies') query, is returning everything within the user's table, which is also unnecessary. How can I return just the companies table data, that's related to the user, who's related to the alert?
Your help would be greatly appreciated.
Depending what you want to achieve, you could use $hidden property to hide columns you don't want to return as json or arrays.
In your Alert model you could do:
protected $hidden = ['description'];
And this way description field won't be returned.
If it's not the way for you (sometimes you want to return description) you could create extra relationships where you limit fields from database.
You could for example create the following relationship:
public function alertsSimple() {
return $this->hasMany('Alert')->select('id', 'headline', 'price_value', 'price_type', 'criteria_id');
}
Also maybe in your select the problem is that you don't use foreign key at all. You could also try with:
$q->select('id', 'headline', 'price_value', 'price_type','criteria_id');
instead of
$q->select('id', 'headline', 'price_value', 'price_type');

How to order by pivot table data in Laravel's Eloquent ORM

In my Database, I have:
tops Table
posts Table
tops_has_posts Table.
When I retrieve a top on my tops table I also retrieve the posts in relation with the top.
But what if I want to retrieve these posts in a certain order ?
So I add a range field in my pivot table tops_has_posts and I my trying to order by the result using Eloquent but it doesn't work.
I try this :
$top->articles()->whereHas('articles', function($q) {
$q->orderBy('range', 'ASC');
})->get()->toArray();
And this :
$top->articles()->orderBy('range', 'ASC')->get()->toArray();
Both were desperate attempts.
Thank you in advance.
There are 2 ways - one with specifying the table.field, other using Eloquent alias pivot_field if you use withPivot('field'):
// if you use withPivot
public function articles()
{
return $this->belongsToMany('Article', 'tops_has_posts')->withPivot('range');
}
// then: (with not whereHas)
$top = Top::with(['articles' => function ($q) {
$q->orderBy('pivot_range', 'asc');
}])->first(); // or get() or whatever
This will work, because Eloquent aliases all fields provided in withPivot as pivot_field_name.
Now, generic solution:
$top = Top::with(['articles' => function ($q) {
$q->orderBy('tops_has_posts.range', 'asc');
}])->first(); // or get() or whatever
// or:
$top = Top::first();
$articles = $top->articles()->orderBy('tops_has_posts.range', 'asc')->get();
This will order the related query.
Note: Don't make your life hard with naming things this way. posts are not necessarily articles, I would use either one or the other name, unless there is really need for this.
For Laravel 8.17.2+ you can use ::orderByPivot().
https://github.com/laravel/framework/releases/tag/v8.17.2
In Laravel 5.6+ (not sure about older versions) it's convenient to use this:
public function articles()
{
return $this->belongsToMany('Article', 'tops_has_posts')->withPivot('range')->orderBy('tops_has_posts.range');
}
In this case, whenever you will call articles, they will be sorted automaticaly by range property.
In Laravel 5.4 I have the following relation that works fine in Set model which belongsToMany of Job model:
public function jobs()
{
return $this->belongsToMany(Job::class, 'eqtype_jobs')
->withPivot(['created_at','updated_at','id'])
->orderBy('pivot_created_at','desc');
}
The above relation returns all jobs that the specified Set has been joined ordered by the pivot table's (eqtype_jobs) field created_at DESC.
The SQL printout of $set->jobs()->paginate(20) Looks like the following:
select
`jobs`.*, `eqtype_jobs`.`set_id` as `pivot_set_id`,
`eqtype_jobs`.`job_id` as `pivot_job_id`,
`eqtype_jobs`.`created_at` as `pivot_created_at`,
`eqtype_jobs`.`updated_at` as `pivot_updated_at`,
`eqtype_jobs`.`id` as `pivot_id`
from `jobs`
inner join `eqtype_jobs` on `jobs`.`id` = `eqtype_jobs`.`job_id`
where `eqtype_jobs`.`set_id` = 56
order by `pivot_created_at` desc
limit 20
offset 0
in your blade try this:
$top->articles()->orderBy('pivot_range','asc')->get();
If you print out the SQL query of belongsToMany relationship, you will find that the column names of pivot tables are using the pivot_ prefix as a new alias.
For example, created_at, updated_at in pivot table have got pivot_created_at, pivot_updated_at aliases. So the orderBy method should use these aliases instead.
Here is an example of how you can do that.
class User {
...
public function posts(): BelongsToMany {
return $this->belongsToMany(
Post::class,
'post_user',
'user_id',
'post_id')
->withTimestamps()
->latest('pivot_created_at');
}
...
}
You can use orderBy instead of using latest method if you prefer. In the above example, post_user is pivot table, and you can see that the column name for ordering is now pivot_created_at or pivot_updated_at.
you can use this:
public function keywords() {
return $this->morphToMany(\App\Models\Keyword::class, "keywordable")->withPivot('order');
}
public function getKeywordOrderAttribute() {
return $this->keywords()->first()->pivot->order;
}
and append keyword attribiute to model after geting and use sortby
$courses->get()->append('keyword_order')->sortBy('keyword_order');

Categories