Eloquent ORM - join by existing relation - mysql

I'm working with Eloquent ORM 4 - without Laravel Framework.
As I noticed, this ORM is really close to Ruby's "Active Record".
In my situation I need to have a model with a lot of relationships. Also, I need to search this objects with relations. So, I have to use joins.
But it makes doubled code. Example:
public function colors()
{
return $this->belongsToMany('Color', 'products_colors', 'product_id', 'color_id');
}
public function scopeJoinColors()
{
return $this->join('product_colors', 'products.id', '=', 'products_colors.product.id')
->join('colors', 'product_colors.color_id', '=', 'colors.id')
}
In this way there are a lot of same things.
And in the fact, I need to restrict relation and join in the same way like this:
...->where('products_colors.published', '=', 1)
So, the question
Is it possible in Eloquent make joins by existing relations?
If not, how do you manage with those tricky situations?
And one more question.
What do you do, if you need 2 or more joins to one table? I mean EAV.

I'm not sure about the Eloquent way to have relationships only where a certain condition is met, but it can be done through the query builder, just pass the join function a closure. This should answer both of your questions as well.
return $this->join('product_colors', 'products.id', '=', 'products_colors.product.id')
->join('colors', function($join)
$join->on('colors.published','=','1');
$join->on('colors.id', '=', 'products.color_id');
});

Related

laravel eloquent join where clause with sub query to use column name comparision not working

I am having issues with laravel eloquent join to compare date in where clause using sub query. below query gives me error unknown column policy_periods.statecode i am using this column in where clause of the join subquery for example App\Elrd::where("state_code",'=',DB::raw(policy_periods.statecode))->where('date',"<=",$mod_rating_eff_date)->max('date')
can you please give me an idea if any solution possible ?
i have already tried whereRaw, whereColumn but none of them is working.
DB::table('policy_periods')
->join('payrolls','payrolls.policy_period_id', '=', 'policy_periods.id')
->leftjoin('elrds', function($join) use($mod_rating_eff_date)
{
$join->on('elrds.state_code', '=', 'policy_periods.statecode');
$join->on('elrds.class_code', '=', 'payrolls.code');
$join->where('elrds.date',App\Elrd::where("state_code",'=',DB::raw(`policy_periods`.`statecode`))->where('date',"<=",$mod_rating_eff_date)->max('date'));
})
->select('payrolls.*','payrolls.elr as payrollelr','policy_periods.id as pid','policy_periods.policy_no',DB::raw('CONCAT(eff_date, "-", exp_date) as dateGroup'),'policy_periods.eff_date','policy_periods.exp_date','policy_periods.statecode as ratingeffPolicyYear','policy_periods.statecode','elrds.class_code','elrds.year','elrds.date','elrds.elr','elrds.dratio')
->where('policy_periods.mod_id',$id)
->get();
When I encounter such issue, the first thing I usually do is to determine if the generated query is what I wanted.
You should log your queries so that you can ensure it.
To do that, a possible solution is to add a log in your AppServiceProvider (in function boot):
DB::listen(function ($query) {
Log::debug('query',[
$query->sql,
$query->bindings,
$query->time
]);
});
That being said, where do these backticks come from?
DB::raw(`policy_periods`.`statecode`)
You could replace it with DB::raw('policy_periods.statecode')
Please tell me if it fixes your issue, or provide the generated SQL query if the problem is not fixed.

Laravel 5.3 Query - Left join some table

I'm trying to get the most recent record for each candidate_id from a ìnterviews` table.
This is what I want to achive:
I'm using Eloquent on laravel and have already tried this methods (with and without eloquent):
$candidates = DB::table('interviews')->select('interviews.*', 'i2.*')
->leftJoin('interviews as i2',
function ($join) {
$join->on('interviews.candidate_id', '=', 'i2.candidate_id');
$join->on('interviews.created_at', '<', 'i2.created_at');
}
)
->whereNull('i2.candidate_id')
->get();
and with eloquent I've tried this:
$candidates = Interview::leftJoin('interviews as i2',
function ($join) {
$join->on('interviews.candidate_id', '=', 'i2.candidate_id');
$join->on('interviews.created_at', '<', 'i2.created_at');
}
)->whereNull('i2.candidate_id')
->get();
If I change get() to toSql() I have exactly the same query that's shown on the above image, but running on laravel I'm getting always these results (this using the first method, with query builder):
Anyone know why I get this results? Is hard to understand that laravel is doing the same query that I do in HeidiSql but I get diferent results :(
Any tip?
Thanks in advance!
Because you are using ->select('interviews.*', 'i2.*') combined with ->whereNull('i2.candidate_id') I am assuming the second select parameter is overriding all fields on the interviews table with nulls, try reversing the order to ->select('i2.*','interviews.*') or not use the i2.* at all.
This is because the output ignores the alias and only uses the fieldname as element key in the returned collection.
Hope it works.
Perfect case scenario you pick the exact columns you want from each of the joined tables for e.g. it may go like this: table1.id,table1.column1,table1.column2,table2.column2 as smth_so_it_doesnt_override

Eloquent select statement based on a condition in another table

I have an laravel eloquent select statement which looks like this:
$test = Test::with(['a.b.companies']) .. and so on
Now, I want to return results for this query based on some company names in companies table.
I tried to write a where clause with various trial and errors but it doesn't work. I am new to laravel and mysql. Any help in the right direction will be good. thanks.
You may use where, for more reference : - Eloquent ORM
$test = Test::where('company_name1', '=', $company_name1)->orWhere('company_name2', '=', $company_name2)->get();

How to format Laravel Eloquent query with optional sections

Im new to Laravel, but am struggling with how Eloquent queries can have optional sections.
I have the following Eloquent query at the moment:
Posts::where('approved', '=', 'Y')->orderBy('created_at', 'DESC')->take($noofposts)->skip($skipno)->get();
That works fine. However I now need to add two optional sections, which i'd like to do without having to duplicate the query each time.
I need to add AND WHERE userid=$x (looped for one or more times) if $x (which is an array) is present, if its not it should ignore then ... and finally add AND (WHERE status=$y[0] OR $status=$y[1] OR $status=$y[2]) - again if the status flags are not set, then just ignore.
Basically if no flags are set I end up with the original query, but if they are we get
Posts::where('approved', '=', 'Y')->where('userid', '=', '2')->where('userid', '=', '23')->where('status', '=', 'K')->orWhere('status', '=', 'N')->orderBy('created_at', 'DESC')->take($noofposts)->skip($skipno)->get();
I can work it out perfectly in normal PHP, but cannot understand how it would work in Laravel Eloquent.
Can anyone point me in the right direction? Neither the user guide nor any website examples seem to look at this kind of scenario!
Fist, instead of chaining orWhere I would use the whereIn function of Eloquent.
whereIn('status', $y);
The problem is, if $y is empty, the request won't work. (I think it just crashes)
So if you want to avoid controls and keep your code clean you can add a query scope in you Post model.
http://laravel.com/docs/4.2/eloquent#query-scopes
scopeOptionalWhereIn($query, $field, $array){
if(!empty($array))
return $query->whereIn($field, $array);
return $query; //return unchanged query
}
Then you can use this scope in your query:
Posts::where('approved', '=', 'Y')
->optionalWhereIn('status', $y)
->orderBy('created_at', 'DESC')
->take($noofposts)
->skip($skipno)->get();
You can probably use the same scope to deal with the userid conditions.

Laravel relation query returns the related ID instead of the main

I have two tables Models and Cars related as per cars.model_id = model.id.
Car belongsTo Model and Model hasMany Cars.
When I would like to do a search based on the Model name in the cars table I do the following query:
Car::join('models', 'cars.model_id', '=', 'models.id)
->where('models.name', 'like', '%'.$term.'%')
->paginate($_ENV['ITEMS_PER_PAGE']);
My problem is that Eloquent ORM of Laravel in that case returns the model.id as the main id, so resultset will have the model.id as Id instead of the Car.id. Any solution for that?
As per the documentation of Laravel, I thought that Eloquent would assume correctly the table Id without any help:
Note: Eloquent will also assume that each table has a primary key
column named id. You may define a primaryKey property to override this
convention. Likewise, you may define a connection property to override
the name of the database connection that should be used when utilizing
the model.
Just don't SELECT id or none of the models fields if you don't need them (where clause will work anyway)
Car::join('models', 'cars.model_id', '=', 'models.id')
->where('models.name', 'like', '%'.$term.'%')
->paginate($_ENV['ITEMS_PER_PAGE'], ['cars.*','models.fieldsYouNeedOrNothing']);
side note: You can use whereHas() instead of join to do it more 'eloquent' way if you really don't need joined table's fields:
Car::whereHas('models', function ($query) use ($term) {
$query->where('models.name', 'like', '%'.$term.'%');
})->paginate($_ENV['ITEMS_PER_PAGE']);
This will run subquery on models table and leave you without any worries about the keys.