Laravel eloquent how to get relationships relationship? - php

I have Users table,Jobs table and JobStatus table. I get user jobs using relationship
return User::find($dto->getUserId())
->jobs()
This will return all user jobs as collection, not as relationship inside user model.
Now I need to get jobs statuses, but when I try this
User::find($dto->getUserId())
->jobs()
->statuses()
I get error Call to undefined method. I need to get statuses as collection so I can use where,sort by etc while getting them from db. Is there any way to do so?
I need to do something like this , but with statuses
return User::find($dto->getUserId())
->jobs()
->has('status')
->where('role','responsible')
->where('jobs.created_at','>=',Carbon::now()->subDays($dto->getPeriod())->toDateString())
->get();

To retrieve the jobs by filtering on the statuses relationship, you can do it like this:
$user->jobs()->whereHas('statuses', function ($query) {
// Perform filter on the query for jobs->statuses
})->get();
For more information about querying relationship existence: https://laravel.com/docs/7.x/eloquent-relationships#querying-relationship-existence
If you want to retrieve the statuses instances, you can do it like this:
$statuses = JobStatus::where('created_at', '>=', now())
->whereHas('jobs', function ($query) use ($user) {
$query->whereUserId($user->id);
})
->get();

Just use jobs without parenthesises to get the model itself :
User::find($dto->getUserId())
->jobs
->statuses;

define your statuses() function first with the relations in your model with the jobs
like many to many or one to many relationship
public function statuses(){
return $this->belongsToMany('App\Job')->withTimestamps();
}

Related

How to check relation data of a collection before sending it to view

I have a Controller method like this:
public function awaiting()
{
$producers = Producer::where('producer_process',4)->get();
$producers_list = [];
foreach($producers as $producer){
if($producer->brand->brand_rejected == 0){
array_push($producers_list, $producer);
}
}
return view('admin.brands.awaiting', compact('producers_list'));
}
So basically there's One To One relationship between Producer model & Brand model.
In order to get the collection of brands table records that has producer_process of 4 and ALSO the brand_rejected field of related brands table record must be set to 0, I added an array_push and check the condition.
Now this works fine and properly shows the correct data but I wanted to know, what is the shorthand method of doing this with Eloquent relationships?
I mean is there any concise and useful method written in Eloquent relationships that can do this without using array_push or another foreach loop?
You can use whereHas to constrain the result set based on the existence of a relationship. Here we are saying we only want producers that have the field 'produce_process' set to 4 and have a brand with a field of 'brand_rejected' set to 0:
$producers = Producer::where('producer_process', 4)
->whereHas('brand', function ($q) { $q->where('brand_rejected', 0); })
->get();
If you want these producers to have their brand relationship loaded to use you should eager load that. Before the get call you can tell it to load the relationship:
$producers = Producer::where(...)->whereHas(...)->with('brand')->get();
Laravel 5.8 Docs - Eloquent - Relationships - Querying Relationship Existence whereHas
Laravel 5.8 Docs - Eloquent - Relationships - Eager Loading with
You can try this:
public function awaiting()
{
$producers = Producer::where('producer_process',4)
->with('brand', function($q) {
$q->where('brand_rejected', 0);
})->get();
// dd($producers);
dd($producers->pluck(brand));
Sure you can use the method with() also with the where() clause to can apply some conditions to the relationship
Example
$yourQuery->with('brand', function($query){
$query->where('brand_rejected', 0);
});
check this for more info
https://laravel.com/docs/9.x/eloquent-relationships#constraining-eager-loads
I hope it's helpful

Laravel 5.5: Convert query builder to eloquent

Is there a way to transform this query builder to eloquent?
$transactions = DB::table('transactions')
->join('accounts', 'transactions.account_id', '=', 'accounts.id')
->select('transactions.*', 'accounts.name as account_name')
->paginate(5);
I tried it with One To Many. But it need the find() function so it give me one account's transactions but I need to select all transactions with accounts.name
In comments, you've said you have one to many relationship defined (I assume it's Accounts has many Transactions) and you need to get all transactions with account name, so do this:
Transaction::with('account')->paginate(5);
Where account is relationship:
public function account()
{
return $this->belongsTo(Account::class);
}
Then you'll be able to display the data like this:
#foreach ($transactions as $transaction)
{{ $transaction->id }}
{{ $transaction->account->name }}
#endforeach
You will need to use models to do that.
Transaction model and Account model.
Define their relationships
Create a method in the transaction model to retrieve the related account names.
function accoutNames() { //blablabla }
Do the query in controller, something like that:
Transaction->accountNames();

laravel 5.2 eloquent order by on relationship result count

I have two tables website_link and website_links_type. website_link is related website_links_type with hasmany relationship.
$this->website_links->where('id',1)->Paginate(10);
and relationship
public function broken()
{
return $this->hasMany('App\Website_links_type')->where('status_code','!=',"200");
}
Now I want to get result from website_link table but Orderby that result on count of broken relationship result.
There are many ways to solve this problem. In my answer I'll use two I know.
You can eagerload your relationship and use the function sortBy(). However I don't think you can use the paginate() functionality with this solution.
Example:
$results = Website_link::with('website_links_type')->get()->sortBy(function ($website_link) {
return $website_link->website_links_type->count();
});
See this answer
You can also use raw queries to solve this problem. With this solution you can still use the pagination functionality (I think).
Example:
$results = Website_link
::select([
'*',
'(
SELECT COUNT(*)
FROM website_links_type
WHERE
website_links_type.website_link_id = website_link.id
AND
status_code <> 200
) as broken_count'
])
->orderBy('broken_count')
->paginate(10);
You may have to change the column names to match your database.
You can not put WHERE condition in model file.
You just give relationship hasMany in model file.
And use where condition in controller side.
Refer this document.
Try this
Model file:
public function broken()
{
return $this->hasMany('App\Website_links_type');
}
Controller file:
$model_name->website_links->where('id',1)
->where('status_code','!=',"200")
->orderBy('name', 'desc')
->Paginate(10);

Laravel. Eloquent query

I got this query:
$users = DB::table('users')->whereExists(function($query)
{
$query->select(DB::raw(1))
->from('galleries')
->whereRaw('galleries.user_id = users.id');
})->get();
This query selects all users who have gallery. Problem is that I can't use eloquent releationships now. Whenever i try to loop like this:
#foreach ($user->gallery as $gallery)
{{$gallery->name}}
#endforeach
I get error:
Undefined property: stdClass::$gallery
It happens with all other tables. What am I doing wrong here? My realationships are defined and they work just fine, i got problems only in this query. Thanks.
EDIT
Since it's not eloquent query, could you show me example how to write query, into few tables with eloquent. For example, I need all users who have their status approved in example table
First, determine a relationship in the User class, like this:
class User {
// Determine relation to Example table
public function examples() {
return $this->hasMany('Example', 'user_id', 'id'); // second parameter is the foreign key
}
}
Then the query:
User::whereHas('examples', function( $query ) {
$query->where('status','approved');
})->get();

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