how to load nested relationships in laravel - php

i have 3 models that i want to start from a and load the relation with b and i want to load the relation betwen b and c there in a too is that possible ?? here is what i want to do in code :
AccommodationRoomModel which is the B model:
public function accommodation(){
return $this->belongsTo(Accommodation::class);
}
public function roomPricingHistory(){
return $this->hasMany(RoomPricingHistory::class);
}
and in The accomodation model :
public function accommodationRoom()
{
return $this->Hasmany(AccommodationRoom::class);
}
and finaly in the room pricingHistory :
public function accommodationRoom(){
return $this->belongsTo(AccommodationRoom::class);
}
now in my accomodation controller i want to get All the accomodation with the room and from room i want to get the price so here is it
A = Accomodation
B = Room
C = price
and i want to call somehow like this
From A get B and The relation Of it with C and show all in A

You can use Laravels nested eager loading for this:
From the docs:
To eager load nested relationships, you may use "dot" syntax. For
example, let's eager load all of the book's authors and all of the
author's personal contacts in one Eloquent statement:
$books = App\Book::with('author.contacts')->get();
In your case:
$accomodations = Accomodation::with('accommodationRoom.roomPricingHistory')->get();

nested-eager-loading
A::with('B.C');

Related

Sort on the number of related (hasMany) documents

I have 2 collections.
Vehicles and Views.
I would like to bring back a list of vehicles sorted by the number of views.
My Vehicle class
class Vehicle extends Moloquent {
protected $dates = ['date_assigned'];
public function associated_views()
{
return $this->hasMany('App\Collections\View');
}
}
And my View class
class View extends Moloquent {
public function associated_vehicle()
{
return $this->belongsTo('App\Collections\Vehicle');
}
}
I can get the count of the views after the fact, with $vehicle->associated_views->count(), but this doesn't enable me to sort on the field before pulling back every single record. Is this possible?
Use withCount():
Vehicle::withCount('views')->latest('views_count')->get()
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
https://laravel.com/docs/5.5/eloquent-relationships#counting-related-models

Laravel 5 Eloquent relationship: can't modify/overwrite relationship table property

I am using Laravel 5's belongsToMany method to define related tables using an intermediary pivot table. My application is using the eloquent models Tour and TourCategory. In the Tour Model I have:
namespace App;
use Illuminate\Database\Eloquent\Model;
class Tour extends Model
{
public function cats(){
return $this->belongsToMany('App\TourCategory', 'tour_cat_assignments', 'tour_id', 'cat_id');
}
}
In my controller I am retrieving all the data from the tour table along with the associated category data using Laravel's with method:
$tours = Tour::with('cats')->get();
That all works fine. The problem is that I don't want the category data in its current raw form, I need to first rearrange it. However I cannot overwrite the cats property without unsetting it first:
public function serveTourData(){
$tours = Tour::with('sections', 'cats')->get();
foreach($tours as $tour){
unset($tour->cats); // If I unset first, then it respects the new value. Why do I need to do this?
$tour->cats = "SOME NEW VALUE";
}
Log::info($tours);
}
Can someone explain the logic behind this please?
To override relations on some model, you can use:
public function serveTourData(){
$tours = Tour::with('sections', 'cats')->get();
foreach($tours as $tour){
$tour->setRelation('cats', "SOME NEW VALUE");
}
Log::info($tours);
}
For laravel 5.4 - setRelation
Of course if you are using laravel >= 5.6, you can unset relations by unsetRelation

Convert Laravel query to Eager Loading

I've been leaning Laravel and trying to implement something and I did, it worked but I heard that it might be possible to do the same in an easier way using Eager Loading.
Imagine that we have 4 tables: garages, cars, securities, places.
garages is where you can find the cars, securities is the security that is keeping that car safe inside of a garage, and places is where you can find garages similar to that one.
What I want is to list garages and join the three tables that is cars, securities and places like this:
Garage 1 has 2 cars with 3 securities and has 3 more garages similar
Garage 2 has 1 cars with 1 securities and has 2 more garages similar
And here is the query:
select
g.garage_name,
count(distinct c.car_id) as count_cars,
count(distinct s.security_id) as count_securities,
count(distinct p.place_id) as count_places
from garages g
left join cars c on c.garage_id = g.garage_id
left join securities s on s.car_id = c.car_id
left join places p on p.garage_id = g.garage_id
group by g.garage_name
order by g.garage_name;
It's working, as you can see here.
I converted it to:
$garages = Garages::select('garage_name',
DB::raw('count(distinct cars.car_id) as count_cars'),
DB::raw('count(distinct securities.security_id) as count_securities'),
DB::raw('count(distinct places.place_id) as count_places'))
->leftJoin('cars', 'cars.garage_id', '=', 'garages.garage_id')
->leftJoin('securities', 'securities.car_id', '=', 'cars.car_id')
->leftJoin('places', 'places.garage_id', '=', 'garages.garage.id')
->groupBy('garages.garage_name')
->orderBy('garages.garage_name')
->get();
As I said above, it's working but I'm wondering if there's an easier way for doing this using Eager Loading and how to convert?
When I say easier I mean more readable, separated, the right way instead of that big query.
Lets say you do have the 4 Eloquent Model classes for the entities.
If that's the case, you can try eager loading through the Eloquent ORM.
$Garages = Garage::with(['cars','places', 'securities'])->get();
Then you may prepare the necessary output as you like using the returned result set which is a collection (Illuminate\Support\Collection). You can transform the items in a collection as any way you like. Here is an example.
In your case, I am assuming this would be the way to do it.
$Garages->transform(function($garage) {
return array(
'garage_name' => $garage->garage_name,
'count_cars' => $garage->cars->count(),
'count_securities' => $garage->securities->count(),
'count_places' => $garage->places->count(),
// add as many custom details you want here
);
});
return $Garages->toArray(); // see the output
Now I hope you got the idea.
Keep in mind that your model class files should have the relationships. It should be like the following.
class Garage extends Eloquent {
public function cars() {
return $this->hasMany('Car');
}
public function securities()
{
return $this->hasMany('Security');
}
public function places()
{
return $this->hasMany('Place');
}
}
class Car extends Eloquent {
// relationships here
}
class Security extends Eloquent {
// relationships here
}
class Place extends Eloquent {
// relationships here
}
This is just a simple example to help you understand.
Enjoy ! :D

Getting count from pivot table in laravel eloquent

I have a many to many relationship for orders and products.
<?php
class Order extends Eloquent {
public function user()
{
return $this->belongsTo('User');
}
public function products()
{
return $this->belongsToMany('Product');
}
}
?>
<?php
class Product extends Eloquent {
public function orders()
{
return $this->belongsToMany('Order');
}
}
?>
Need to fetch the number of times each product is ordered.In mysql,this task can be achieved by using the following query
SELECT products.id, products.description, count( products.id )
FROM products
INNER JOIN order_product ON products.id = order_product.product_id
INNER JOIN orders ON orders.id = order_product.order_id
GROUP BY product_id
LIMIT 0 , 30
Result of the above query is as follows:-
id description count(products.id)
1 Shoes 3
2 Bag 2
3 Sun glasses 2
4 Shirt 2
How this task can be achieved using laravel eloquent (without using query builder)????How can i fetch the number of times each product is ordered using laravel eloquent??
For future viewers, as of Laravel 5.2, there is native functionality for counting relationships without loading them, without involving your resource model or accessors -
In the context of the example in the approved answer, you would place in your controller:
$products = Product::withCount('orders')->get();
Now, when you iterate through $products on your view, there is a orders_count (or, generically, just a {resource}_count) column on each retrieved product record, which you can simply display as you would any other column value:
#foreach($products as $product)
{{ $product->orders_count }}
#endforeach
This method produces 2 fewer database queries than the approved method for the same result, and the only model involvement is ensuring your relationships are set up correctly. If you're using L5.2+ at this point, I would use this solution instead.
Mind that Eloquent uses Query\Builder under the hood, so there is no such thing in Laravel, like 'query eloquent without using query builder'.
And this is what you need:
// additional helper relation for the count
public function ordersCount()
{
return $this->belongsToMany('Order')
->selectRaw('count(orders.id) as aggregate')
->groupBy('pivot_product_id');
}
// accessor for easier fetching the count
public function getOrdersCountAttribute()
{
if ( ! array_key_exists('ordersCount', $this->relations)) $this->load('ordersCount');
$related = $this->getRelation('ordersCount')->first();
return ($related) ? $related->aggregate : 0;
}
This will let you take advantage of eager loading:
$products = Product::with('ordersCount')->get();
// then for each product you can call it like this
$products->first()->ordersCount; // thanks to the accessor
Read more about Eloquent accessors & mutators,
and about dynamic properties, of which behaviour the above accessor mimics.
Of course you could use simple joins to get exactly the same query like in you example.
If you already have the $products object, you can do the following:
$rolecount = $products->roles()->count();
Or if you are using eager loading:
$rolecount = $products->roles->count();
Cheers.
I am using Laravel 5.1 and i am able to accomplish that by doing this
$photo->posts->count()
And the posts method in Photo model looks like this
public function posts(){
return $this->belongsToMany('App\Models\Posts\Post', 'post_photos');
}

MySQL relation with multiple tables

I have 4 mysql tables, as the following:
Makes:
id - make_name
Models:
id - model_name - make_id
Trims:
id - trim_name - model_id
Forsale_Cars:
id - trim_id - year - price
in the Makes table I have ~700 records, so my question is, how can get a list of Makes which only have a child trim in the forsale table?
I have 20 records in the forsale table, I want to get the list of Makes for these cars.
I am using Laravel, so if anybody has achieved that previously using eloquent it will be great
Eloquent way:
// Makes that have forsale nested relation
Make::whereHas('models', function ($q) {
$q->whereHas('trims', function ($q) {
$q->has('forsales');
});
})->get(); // returns Eloquent Collection
Models with correct relations (hasMany can be replaced with hasOne if that's actual relation somewhere):
// Make model
public function models()
{
return $this->hasMany('CarModel');
}
// CarModel (as you can't use Model name)
public function trims()
{
return $this->hasMany('Trim');
}
public function make()
{
return $this->belongsTo('Make');
}
// Trim model
public function forsales()
{
return $this->hasMany('Forsale');
}
public function carModel()
{
return $this->belongsTo('CarModel');
}
// Forsale model
public function trim()
{
return $this->belongsTo('Trim');
}
You could use this query for the first question you asked. Not sure if the this completely answers your question.
SELECT * FROM Makes m left join Foresale_Cars fc on (m.id = fc.id) left join Trims t on (fc.trim_id = t.id) where t.trim_name = "child trim"
I assume you have all your models set up, e.g.:
class Makes extends Eloquent {
protected $table = 'makes';
public function models() {
return $this->hasMany('Model', 'makes_id');
}
}
and so on.. (you have to do this with all your models, of course)
Now, if you want to get all the cars for sale you'd simply chain some foreach loops:
foreach( $makes->models as $model ) {
foreach( $model->trims as $trim ) {
{{ $trim->forsale_cars }}
...
}
}
Edit: Yes you can use raw queries, of course, but using models and the power of eloquent is much more elegant and useful...
For more information on this topic: http://laravel.com/docs/eloquent#relationships

Categories