Eloquent relation with subquery - laravel

I am having a problem with simplyfing following eloquent relationship. It has a kind of nested query that uses a FieldRole model which I consider a bad pattern to follow. If I would have a raw query, then it should be evaluated as a one query using a join on field roles and it's name, but here it's doing two queries that probably are not cache'able by eloquent ORM.
class Team extends Model {
// ...
public function goalkeepers() {
return $this->belongsToMany(
SoccerPlayer::class,
'team_has_players',
'id_teams',
'id_soccer_players'
)->where(
'id_field_role',
FieldRole::where(
'name',
'Goalkeeper'
)
->first()
->getKey()
);
}
}
The second query is exectured there
FieldRole::where(
'name',
'Goalkeeper'
)
->first()
->getKey()
Is there a way to make it as a one query relationship?

You can use a JOIN:
public function goalkeepers() {
return $this->belongsToMany(
SoccerPlayer::class,
'team_has_players',
'id_teams',
'id_soccer_players'
)->join('field_roles', function($join) {
$join->on('team_has_players.id_field_role', 'field_roles.id')
->where('field_roles.name', 'Goalkeeper');
});
}

Related

I want to change my laravel database query to model relationship query for search data from database

I am learning laravel Eloquent relationship, I successfully created a relationship between models for eq city belongsTo State and belongsTo country.
public function state(){
return $this->belongsTo(State::class,'state_id', 'sr_id');
}
public function country(){
return $this->belongsTo(Country::class,'country_id', 'sr_id');
}
this is my old search code, I want to know how we search data using model relationship method, how we call each columns from their following table.
$city = city::leftjoin('master_country','master_country.sr_id','=','master_city.country_id')
->leftjoin('master_state','master_state.sr_id','=','master_city.state_id')
->where('master_city.sr_status',0)
->where('master_city.sr_id','LIKE','%'.$query.'%')
->orWhere('master_city.sr_name','LIKE','%'.$query.'%')
->orWhere('master_city.city_tel_code','LIKE','%'.$query.'%')
->orWhere('master_country.sr_name','LIKE','%'.$query.'%')
->orWhere('master_state.sr_name','LIKE','%'.$query.'%')
->orderBy('master_city.sr_id')
->paginate(3,array('master_city.*','master_country.sr_name AS c_name','master_state.sr_name As s_name'));
so it would we like..
City::with('state','country')->where etc
You can have a look at Constraining Eager Loads
$cities = City::with(
[
'state' => function ($query) {
$query->where('state_column', 'some_value');
},
'country' => function ($query) {
$query->where('country_column', 'some_value');
}
]
)->get();
If you don't want to retrieve the relation data, you can use the whereHas() method:
City::whereHas('state', fn (Builder $builder) => $builder->where('state_column', 'some_value')));
Using arrow function is not required.
More documentation on this

Laravel: sort query results based on field of nested relationship

I have two models with relations as defined below
Order
public function owner()
{
return $this->belongsTo(User::class, 'owner_id');
}
User
public function company(){
return $this->belongsTo(Company::class, 'company_id');
}
company table have 'title' field.
what I want is to get all the orders sorted/order by company title. I've tried different solution but nothing seems to work. Any help or hint would be appreciated.
Recent solution that I tried is
$query = OrderModel::whereHas('owner', function($q) use ($request){
// $q->orderBy('owner');
$q->whereHas('company',function ($q2) use ($request){
$q2->orderBy('title',$request->get('orderByDirection') ?? 'asc');
});
});
but I am not getting user and company relation in query results. also the result remains same for 'ASC' and 'DESC' order.
You could sort the query after adding join like:
return Order::join('users', 'users.id', '=', 'owner_id')
->join('companies', 'companies.id', '=', 'users.company_id')
->orderBy('companies.title')
->select('orders.*')
->get();
You can define new relations in User and Company models.
User
public function orders()
{
return $this->hasMany(Order::class);
}
Company
public function users()
{
return $this->hasMany(User::class);
}
Now you can fetch companies that are in asc order and with the use of relation, you can fetch users and orders. So the ORM like be,
$companies = Company::with('users.orders')->orderBy('title', 'ASC')->get();
So these are the company-wise orders. You can use this too.

Laravel eloquent get model property of current query

I'm trying to do where clause for fortune_code inside joindraw table, comparing with the lucky_fortune_code from product table. How can i access and do the check?
Product::where('status', StatusConstant::PT_ENDED_PUBLISHED)
->where('lucky_fortune_code', '<>', '')
->with(['joindraw' => function ($query){
$query->where('fortune_code', $this->lucky_fortune_code)
->with('user');}])->desc()->get();
Product.php
class Product extends Model
{
public function joindraw(){
return $this->hasMany('App\Models\Joindraw');
}
Joindraw.php
class Joindraw extends Model
{
public function product(){
return $this->belongsTo('App\Models\Product', 'product_id');
}
What you can do is a join:
Product::where('status', StatusConstant::PT_ENDED_PUBLISHED)
->where('lucky_fortune_code', '!=', '')
->join('joindraws', 'joindraws.fortune_code', '=', 'products.lucky_fortune_code')->get();
By the way, you can also omit the second 'product_id' parameter in the belongsTo() relation, as this column name is already assumed by convention.
Also, there is no desc() method on the query builder. Use orderBy('lucky_fortune_code', 'desc') instead.
However, whenever you have to write joins in Laravel, you should think about your relationship structure, because there's probably something wrong.

Equivalent laravel relationship query for this join

this is my query
$personnel_info = \DB::table('assigns AS a')
->join('boxes AS b','b.id','=', 'a.box_id')
->join('positions AS p','p.id','=', 'b.position_id')
->select('a.id','b.id AS box_id','p.id as position_id','p.title','a.status','a.end_date')
->where('a.personnel_id','=',$personnel_id)
->get();
and this realtionship for boxes:
class Boxes extends Model
{
public function position()
{
return $this->belongsTo('Positions');
}
public function assign()
{
return $this->hasOne('Assigns', 'box_id');
}
}
how to use eloquent query(also realtionship) for replace DB facade query?
i want select some field for tables.without define fileds in boxes model
tnx
Try
$personnel_info = Assign::with('box.position')
->where('personnel_id', $personnel_id)
->get();
Then dd($personnel_info) to see everything that was returned. If you don't like the values, then add your select() clause.

Laravel 5.3 inner join not working properly

I'm having two tables as 'jobs' and 'desired_skills'.
Table structure is as follows.
jobs table
jobs Table
desired_skills table
desired_skils table
where desired_skills.job_id refers to jobs.job id
In controller I have (I am getting $id as an argument from the url, and I can confirm the argument grabs the desired value)
$jobs = DB::table('jobs')->where(function ($query) use ($id) {
$query->Join('desired_skills', 'desired_skills.job_id', '=', 'jobs.job_id')
->where('jobs.employer_id', '=', $id);
->select('*')
})->get();
when I dump and die $jobs it only returns values from jobs table.
but when I run the query
SELECT * FROM jobs INNER JOIN desired_skills ON desired_skills.job_id = jobs.job_id it returns the desired value set.
What am I doing wrong? Any help will be greatly appreciated.
I think it has to do with wrapping your join inside of a where clause. I don't think it's giving you your desired query with that there.
$jobs = DB::table('jobs')
->join('desired_skills', 'desired_skills.job_id', '=', 'jobs.job_id')
->where('jobs.employer_id', '=', $id)
->get();
The query SELECT * FROM jobs INNER JOIN desired_skills ON desired_skills.job_id = jobs.job_id
is not the same has what you are trying to do in the function. In this query there is not
mention of 'employer_id' in the table 'jobs'.
An alternative would be to use eloquent relationships, as refered in a comment.
You need 3 classes in models:
Employer
Job
DesiredSkill
Between Employer and Job -> one-to-many relation (an employer can have multiple jobs).
Between DesiredSkill and Job -> one-to-one relation.
I'm not sure what you are trying to get from the join, but i think that if you implement
the methods that allow the relationships i believe you solve whatever.
class Job extends Model
{
public function employer()
{
return $this->hasOne('App\Job');
}
}
class Employer extends Model
{
public function jobs()
{
return $this->hasMany('App\Employer');
}
public function desiredSkill()
{
return $this->hasOne('App\DesiredSkill');
}
}
class DesiredSkill extends Model
{
public function job()
{
return $this->hasOne('App\DesiredSkill');
}
}
Try this:
$jobs = DB::table('jobs')
->join('desired_skills', 'desired_skills.job_id', '=', 'jobs.job_id')
->select('jobs.*', 'desired_skills.*')
->get();

Resources