How to order by a second relationship in Laravel query? - laravel

I have a bit of an issue that I've been spinning my wheels on for a little while. I have a pretty complex (and maybe not correct - or rather most efficient) data structure that I'm trying to work my way through the logic for.
I am wondering how I perform a query (orderBy) on a second relationship function on a single query. I'm not even sure if that is the right way to describe it, so here's my code:
$players = $team->players()
->wherePivot('year_id', $year->id)
->with(['stats' => function ($query) {
$query->orderBy('score', 'desc');
}])
->get();
It seems like it should be pretty straight forward, but the orderBy clause is not working. The query otherwise works (returns players and their stats). Here is how my relationships are set up:
Team -> belongsToMany Player (and vice versa)
Player -> belongsToMany Stat (and vice versa)
Each of my pivot tables that have consequence in this situation have an extra key besides the two specified tables, they also have a year_id. This is because the relationship is only relevant for a particular year, and I think this may be what is making this more complicated. So for example, my team_player table looks like this:
team_id
player_id
year_id
And my player_stat table looks like this:
player_id
stat_id
year_id
I'm not sure if this is the most efficient or best way to accomplish what I want, but it made sense at the time of build. So how can I work through this architecture to make this query work? Also, any feedback on a data structure that may work better would be appreciated too.

If I understand you correctly you want to sort all players (not stats), you will need to join the tables:
$players = $team->players()
->wherePivot('year_id', $year->id)
->with(['stats'])
->join('stats', 'stats.player_id', '=', 'players.id')
->orderBy('stats.score', 'desc')
->get();

$players = $team->players()->wherePivot('year_id', $year->id)->with('stats')->orderBy('stats.score', 'desc')->get();

Related

Laravel: Ordering posts by category in numerical order

In my application I want to order my posts by category in numerical order.
So I guess I need a mapping table like this:
category_id | post_id | sort_order
then setting up a kind of "Order model" with specific methods that handle it all and use this model as a relationship with the Post model.
But before I start on this project I'd like to know if there is not a Laravel package out there that does the job or another solution that I might have missed.
I'm not 100% sure, if I understand your need correctly, but I guess your posts are stored in a DB.
Then you can simply directly get the posts from DB ordered:
$posts = DB::table('posts')
->orderBy('category_id ', 'asc')
->orderBy('post_id', 'asc')
->get();
See the documentation:
https://laravel.com/docs/8.x/queries#ordering-grouping-limit-and-offset
I don't think, you need a Order model or something similar to realize this.

hasManyThrough relations ships with join?

I have 4 tables (Cars, Vignettes, Inspections and Insurances). Vignettes, Inspections and Insurances are "belongsTo" to Cars.
I need to combine results of these three tables into one query and sorting by 'created_at' and take first 3-4 of them. How can I achieve that?
All of the three tables have same column names and types
You should be able to chain things together, something like
$car->with('Vignettes')
->orderBy('created_at', 'desc)
->take(4)
->get();
As with everything laravel, the docs are your friend.

How to use Query Builder to make a relation in an array? Laravel

I would like to make a relation with query builder... I have three tables, and I would like to join the tables for work with the function.. I'm working in a model.. not in a controller
This is my function
public function map($contactabilidad): array
{
$relation = DB::table('tbl_lista_contactabilidad')
->join('tbl_equipo_postventaatcs', 'tbl_equipo_postventaatcs.id', '=', 'tbl_lista_contactabilidad.postventaatc_id')
->join('users', 'users.id', '=', 'tbl_equipo_postventaatcs.asesor_id')
->get();
return [
$contactabilidad->$relation->name,
$contactabilidad->postventaatc_id,
$contactabilidad->rif,
$contactabilidad->razon_social,
$contactabilidad->fecha_contacto,
$contactabilidad->persona_contacto,
$contactabilidad->correo_contacto,
$contactabilidad->numero_contacto,
$contactabilidad->celular_contacto,
$contactabilidad->comentarios,
$contactabilidad->contactado,
$contactabilidad->respuesta->respuesta
];
}
Query\Builder is best thought of as the primary tool used by Eloquent, but is, nontheless, a completely different package. Query\Builder's purpose is to decouple SQL syntax from the logic that feeds into it, whereas Eloquent's purpose is to decouple that logic from table structures and relationships. So only Eloquent supports Model and Relation classes, Query\Builder does not. And what you're asking for has to do with Relations, so in short, you're kind of barking up the wrong tree.
By the way, I'm differentiating 'Query\Builder' here because Eloquent also has its own wrapper for that class called Eloquent\Builder that shares most of the same syntax. For better or for worse, Eloquent attempts to allow the developer to interact with it in a way that's familiar; not having to track a new set of method names even if you've been seamlessly dropped out of Eloquent and into a Query\Builder object via a magic __call method. It also does something similar regarding Eloquent\Collections vs. Support\Collections. But that can make things very confusing at first, because you have to just kind of know what package you're talking to.
So, to answer your question...
Build a Model class for each of your three tables
Apply relationship methods to each one to pre-configure the model with an awareness of your foreign keys
Call on them using lazy or eager-loading
Something else to note is that with() does not ask Eloquent to perform a JOIN. All it does is run the parent query, extract the key values from the result, run the child query using them in an IN() statement, and marrying the results together afterwards. That's what results in nested results. Speaking from experience, it's kind of a mess generating true JOIN statements off Model Relations and keeping the table aliases unique, so it makes sense this package just skips trying to do that (except with pivot tables on many-to-many relations). This also has the added benefit though, that your related tables don't need to live in the same database. A Query\Builder join() on the other hand, as you have there, would present all fields for all tables at the top-level.

Laravel Eloquent: Order results by eaged loaded model.property, and paginate the result

Lets say I have three models: Post, Category and Tag.
The Post belongsTo Category and Category hasMany Post.
Theres manyToMany relation between Tag and Category.
I want to list my posts by Category name and paginate the results.
Post::with('category','category.tags')
->orderBy('category.name') //this is the bogus line
->paginate(10);
But this syntax doesn't work.
What I tried is this as:
Post::select('categories.*')
->join('categories','posts.category_id','=','categories.id')
->orderBy('categories.name)
->paginate(10);
But then I lose the eager loaded data.
If I drop the select() clause then I get rubbish data as categories.id overwrites posts.id. See here.
Is there any elegant way to solve this issue? After spending hours on this I'm one step away from iterating through paginated posts and 'manually' loading the relations as:
foreach($posts as $post) {
$post->load('category','category.tags');
}
Not even sure if there's downside to this but it doesn't seem right. Please correct me if I'm wrong.
UPDATE on last step: Eager loading on paginated results won't work so if I go that road I'll need to implement even uglier fix.
You should be able to use both join and with.
Post::select('posts.*') // select the posts table fields here, not categories
->with('category','category.tags')
->join('categories','posts.category_id','=','categories.id')
->orderBy('categories.name)
->paginate(10);
remember, the with clause does not alter your query. Only after the query is executed, it will collect the n+1 relations.
Your workaround indeed loses the eager loading benefits. But you can call load(..) on a collection/paginator (query result) as well, so calling ->paginate(10)->load('category','category.tags') is equivalent to the query above.

LINQ to SQL Lookup table? Join perhaps? I'm lost... ;/

Not sure if this is called a lookup table... Here's a screenshot of the schema: http://apoads.com/db-schema.png
What I want to do is join the Ad and MilBase table where the MilBase.BaseID is a given value. In the result, I'd like to be able to access the data like so: ad.MilBase.BaseName, etc...
I just can't seem to wrap my mind around this right now... ;/
Your problem is that your schema has the semantics of a many-to-many relationship between Ad and MilBase and as such the really desired way you would be wanting to do this in LINQ would be ad.Milbases which would then hold a Collection of Milbases.
The problem is not made any better by the fact that LINQ To SQL does not support many-to-many relationships directly, and in reality (assuming that your lookup table only defines a one-to-one or one-to-many relationship) you'd have to do something like
ad.Adbases.Single().MilBase
Of course assuming there will always be one - if that's not the case, then you've got some more complicated things ahead of you.
Of course a join is also always possible too - either way - if this is not the information you were looking for, could you clarify the relationship between Ad and Milbase? (many-to-many, etc?). Also - if this is not a many-to-many and you are able to do so, I would really change this to be a foreign key in either Milbase or the Ad table.
EDIT: In response to your comment,
You could do something like
var query = from a in db.ad
select new {
a.property1,
a.property2,
...
Milbases = a.Adbases.Select(s => s.Milbase)
};
Obviously that code won't compile, but it should give you a rough idea - also I'm certain it can be done in better ways, but something like that should work.

Resources