Combine two relationships in one query in Laravel - laravel

One of my models contains the following:
public function from()
{
return $this->belongsTo(Station::class, 'from_station_id');
}
public function to()
{
return $this->belongsTo(Station::class, 'to_station_id');
}
In order to use this I'm using the with('to', 'from') method. Which results in the following:
select * from "stations" where "stations"."id" in ('1')
select * from "stations" where "stations"."id" in ('2')
Two cached queries one for "to's" and one for "from's". At the moment with 1 record they are "useful". But in the future they will have a lot of duplicate IDs..
Does Laravel offer an option to combine these?

Assuming you'll need to access them from the model by the relation, like $model->from->first() or $model->to->count(), your best option would be to stick with 2 queries. A query with where in clause is not that heavy and you can additionally cache them to speed up.

Related

laravel eloquent chunk ->with method

It seems that eloquent uses a single query for "with" regardless of how many ids there are
Book::with('author')->get();
This would trigger those two queries:
SELECT * FROM books;
SELECT * FROM authors WHERE id IN (...);
The second query may have thousands of author ids in the where clause which might cause problems with performance.
Is there some way so it would chunk that when using with?
I am aware that it is generally not a good idea to query such big result sets.
Yes there are, and its clear in the documentation, you can do something like this:
use App\Models\Flight;
Flight::chunk(200, function ($flights) {
foreach ($flights as $flight) {
//
}
});
Or you can also do this:
use App\Models\Flight;
foreach (Flight::lazy() as $flight) {
//
}
You can get the details in the documentation to learn more:
https://laravel.com/docs/9.x/eloquent#chunking-results
if you need only Books and count authors you can use withCount method
Book::withCount('author')->get();

How to access one relation inside another relation in laravel?

I have a query in which I have eagar loaded two models using with function like this:
ModelA::with(['relationB', 'relationC.relationC.A'])->where(condition)->get();
So, ModelA has two relations like this:
public function B(){ return $this->blongsTo(B::class);}
public function C(){ return $this->blongsTo(C::class);}
Now, my requirement is that I want to add a condition in B() function like this:
public function C() {
if($this->B->status) {
return $this->blongsTo(C::class)->withTrashed();
}
return $this->blongsTo(C::class);
}
But it return null on line this statement:
if($this->B->status)
Here is the error message
Trying to get property 'status' of non-object
My ultimate requirement is that using one relation function I want to fetch deleted records and non deleted based on the condition, but somehow it is not working.
My laravel application version is 7.30.4.
A relational function (such as your public function C()) works a bit of magic under the hood. This is because really it is designed to be called in a query way like you show already with the ::with(['relationB', ...]).
However, because of this, if you were to eager load C, then $this is not yet loaded as the full model, and therefore B is not defined (this is assuming that modelA always has a B relation). If you were to dd($this) while performing your query, you'd see that the result would be a model without any attributes.
Getting this to work from within a relational function (with the goal of eager loading) is very difficult. You're probably better off doing the logic elsewhere, with a second query for example. This is because within the relational function, there is no way to know who or what the potential target is. However, if you only use it after modelA is loaded, then it works without issues.
You can do some things with a whereHas, but then you'd still have to do 2 queries, or you can try and see if you can get it done with an SQL IF statement, but that will not result in a relation.

Maats Laraval Excel: Can you export with multiple queries?

I have gone through the docs and also Googled. I see little mention of returning multiple queries on the same sheet from Maat's Laravel Excel. I presume therefore it is 1 query for 1 downloaded spreadsheet. I also presume that if you do have multiple queries that you will need to place each query on an additional sheet.
Have got this right ?
Many thanks
In a perfect world, every query would get its own sheet. But in reality, it will export whatever you give it so long as it receives a single array or collection for the output, depending on your configuration. It would be up to you to determine how to combine your queries into a format that could be interpreted as rows and columns.
Basic example with two queries:
class ExportSample implements FromCollection
{
// ...
public function collection()
{
// query 1
$a = User::where('id',2)->get();
// query 2
$b = User::where('id',4)->get();
// merge collections
return $a->merge($b);
}
}
Of course, if your queries result in different column structures, there may be additional obstacles.

How to combine three many to many relationship results in a single collection in Laravel?

I have a many to many relationship between the following:
actor <-> theater_play, actor <-> musical, actor <-> ballet_play
How can I make a single array to display all the results from these relationships in Laravel?
I have now:
$actor->theaterPlays, $actor->musicals, $actor->balletPlays
And I need to have something like this:
$actor->allPerformances
EDIT:
And I have to order theym by name, or by date of the performance etc.
Untested, however you should be able to create a new accessor within your Actor model which is responsible for merging all types together:
public function getAllPerformancesAttribute()
{
return $this->theaterPlays()
->get()
->merge($this->musicals()->get())
->merge($this->balletPlays()->get())
->all();
}

How do I define relation between users and points in Eloquent ORM

I have 3 tables:
Users - for storing users
User_point - for associacion between users and points(has only user_id and point_id)
Points for description of points(id, amount, description)
How do I define a relation between these? I tried
public function points(){
return $this->belongsToMany('\App\Point', 'user_point');
}
but when I do
return $user->points()->sum('amount');
it returns just one
Edit:
At first I tried making it like this as it makes more sense:
public function points(){
return $this->hasMany('\App\Point');
}
But it wouldn't work
SUM is an aggregate function and so it should only return one row.
$user->points will be a collection of points attached to that user.
$user->points() is a query that you can do additional work against (i.e. $user->points()->whereSomething(true)->get()).
As user ceejayoz pointed out, using user->points() is going to return a builder which you can do additional work on. I believe using sum() on that will look at the first row returned which is what you indicated is actually happening.
Likely, what you really want to do is $user->points->sum('amount');
That will get the sum of that column for the entire collection.

Resources