Laravel - order collection by relatioship - php

I have collection that I need to order like this:
So if my Model has that relationship it needs to be first on a list
If my Model does not have that relationship it needs to list after the Model that has relatioship
Here is my relationship function:
public function adMegaPremiumAdObjects()
{
return $this->hasMany(MegaPremiumAdObject::class, 'ad_id', 'id');
}
And with this I can only order my collection with fields inside that table:
public function index()
{
$collection = AdObject::orderBy('some_field', 'DESC')->get();
}
So I need to order this collection by my relationship adMegaPremiumAdObjects - if some Model have that relationship it needs to show first in a list.
If I try with whereHas it only shows me collection with that relationship and that doesn't help me.
How can I do that?

You can use withCount() to add a relation count column, which you can order by:
$collection = AdObject::withCount('adMegaPremiumAdObjects')->orderBy('adMegaPremiumAdObjects_count', 'DESC')->get();
From Laravel docs:
If you want to count the number of results from a relationship without
actually loading them you may use the withCount method, which will
place a {relation}_count column on your resulting models.

Related

How should attributes be eager loaded?

I have a set of attributes/accessors on my model that are working out the number of flights for a specific aircraft type. Is there a way to only return these when I want them? If I add them to appends[] I get an N+1 problem when loading all the resources.
/**
* Gets the number of flights flown by the aircraft
*/
public function getTotalFlightsAttribute () {
return Flight::whereHas('aircraft', function($query) {
$query->where('aircraft_type_id', '=', $this->id);
})
->count();
}
I'd like to be able to call $aircraftType->load('total_flights') when I'm serializing this model to send to Vue to like I can with relationships. Am I missing something here? I've tried to call the getAttributes method on the instance, which gets the value, but only the value. I want to simply include it like I can with relationships.
<total-flights :data-aircraft-type="{{ $aircraftType->getAttribute('total_flights') }}"></total-flights>
Ideally, I'm looking for a withAttributes method.
If I get your problem, I guess this should do the job :
$aircraft_flights = $aircraft->total_flights;
Supposing $aircraft is an instance of Aircraft
If you have a relationship from Flight to Aircraft, you should have the inverse from Aircraft to Flight. With that relationship setup you can get the relationship count:
$aircrafts = Aircraft::withCount('flights')->......->get();
Then you can remove that accessor from appends as these Aircraft models retrieved will have a flights_count attribute.

Laravel polymorphic relationship with pivot

Assume I have posts and videos that can be seen by multiple users.
- users
- id
- posts
- id
- videos
- id
- user_accessables (pivot)
- id
- user_id
- accessable_id
- accessable_type
In an example like that, I have set my User relationship like so but something feels wrong
class User extends Model {
public function posts() {
return $this->morphedByMany(
Post::class,
'accessable',
'user_accessables'
);
}
public function videos() {
return $this->morphedByMany(
Video::class,
'accessable',
'user_accessables'
);
}
public function allowedEntities() {
return ($this->posts)->merge($this->videos);
}
}
With the allowedEntities() I can get a collection of both models joined together.
However, I think the use of polymorphic relationship is returning a collection of entities through relationship rather than needing a combiner relationship, right?
I am having problems with understanding polymorphic with pivot table (the tag example in documentation doesn't seem like same scenario).
Because now I can't do:
$collection = collect(); // multiple models of Video & Post
$user->allowedEntities()->sync($collection);
As #Jonas Staudenmeir said is not possible to have a relationship that returns all related model, BUT you can define a method on the model that returns a query builder object with all entities you need (search with on the docs).

Laravel eloquent multiple table join with filter

There are theree tables in my system.
Students
Articles
categories
Student can write many articles and a article is belong to just one student. And A Article can have only one category.
Controller
public function all_articles_by_student_by_category(Request $request){
$students_id = $request->students_id;
$categories_id = $request->categories_id;
$article_list = Students::find($students_id)->articles->all();
//This return Something like, Select All Articles Written by Damith
}
Model
class Students extends Model
{
protected $fillable = ['id','first_name', 'last_name', 'age', 'created_at', 'updated_at'];
public function articles()
{
return $this->hasMany('App\Articles');
}
}
What I am try to get
Something like, Select All Articles Written by Damith for Technology Category (Category Name should be there)
What I able to do so far
Something like, Select All Articles Written by Damith using $article_list = Students::find($students_id)->articles->all(); (You can find this code from controller)
What I want from you
How do I modify $article_list = Students::find($students_id)->articles->all(); to get, something like, Select All Articles Written by Damith for Technology Category. (Category name must be there in result and it is on category table, and for where condtion you can use the category_id which is i the article table )
First off with what you have done so far the ->all() method is not needed when getting the records for a relation on a model, this would return all of the articles linked to that student:
Students::find($students_id)->articles
Go through Articles Model
You could do something like:
Article::where('student_id', $students_id)
->where('category_id', $category_id)->get();
Which would acheive the result you are after.
Go through Students Model
If you want to go through Students Model you can constrain the relation using the with method.
$student = Students::with(['articles' => function($query) use ($category_id) {
$query->where('category_id', $category_id);
}])->find($student_id);
$filteredArticles = $student->articles
Useful Links
Laravel Docs 5.5 for Eager Loading : https://laravel.com/docs/5.5/eloquent-relationships#eager-loading
When accessing Eloquent relationships as properties, the relationship data is "lazy loaded". This means the relationship data is not actually loaded until you first access the property. However, Eloquent can "eager load" relationships at the time you query the parent model.
Laravel Docs 5.5 for Constraining Eager Loads: https://laravel.com/docs/5.5/eloquent-relationships#constraining-eager-loads
Sometimes you may wish to eager load a relationship, but also specify additional query constraints for the eager loading query.
Something like this should work:
$technologyArticles = Articles::where('student_id', '=', $students_id)->where('category_id', '=', $categories_id)->get();

Eager load ONE from Many to Many relationship

I have a many to many relationship for users and roles. A user can have multiple roles, but I only want with to grab the FIRST role.
Consider the following code:
User::with('roles')->get()
Works great for all roles, but I only want the first role.
I've set this up in my model but doesn't work:
public function role()
{
return $this->roles()->first();
}
How do I load with for only the first result?
You should be able to call first directly on the eager loaded relationship like this:
User::with(['roles' => function ($query) {
$query->first();
})->get();
first() actually executes the query and returns the results as a collection. Relationships must return a query builder, which can then be chained or executed, so using first() in a relationship won't work.
UPDATE
I realised you want to use role in with, so you need to create a relationship to do that. Create a new relationship on your User model (you can use any limit described in the docs, not just oldest()):
public function role()
{
return $this->hasOne('App\Role')->oldest();
}
And then you can use it in with:
$users = User::with('role')->get();

Laravel eager loading, loaded table fields

how can I choose which fields I want to get from the with ORM eloquent. For example
$tourTeams = Tournament::with('teams')->where('id', $tourId)->first();
From the teams relation I want only to get the name (without the id and timestamps).
I didn't it in the documentation. For the Tournament eloquent I can do it via the get function while passing it an array of fields names, like this: get(array('name', 'id')). But how do I do this on the Team eloquent?
Note: here is how Team related to Tournament, this code taken from the Tournament eloquent file:
public function teams()
{
return $this->belongsToMany('Team', 'Tournament_Team');
}
You can get specific columns from the relation like this:
$tourTeams = Tournament::with(['teams'=>function($q){
$q->select('id','name');
}])->where('id', $tourId)->first();

Categories