Laravel get a collection of relationship items - php

I'm stuck on this what seems like a simple task.
I have a User that has many Shops that have many Products..
I'm trying to get all the Products for a certain User.
This is working, and is returning the Shops with their Products
\Auth::user()->shops()->with('products')->get();
But I need a Collection of only the Products. I tried the following but it's messing up the Query
\Auth::user()->shops()->with('products')->select('products.*')->get();
Any idea what I'm doing wrong? Thank you!

You can use this :
\Auth::user()->shops()->with('products')->get()->pluck('products')->flatten();
if you don't want replicate, you can use ->unique()
If you want to directly work on a query (for performances):
Product::whereHas('shops', function($query){
$query->where('user_id', auth()->user()->id);
})->get();

what you need is a relationship between the User Model and the Product Model ..
this is possible using hasManyThrough relationship ..
USER MODEL
public function products()
{
return $this->hasManyThrough('App\Product', 'App\Shop')
}
now, you can access all the user's products with
Auth::user()->products;

In this case you can use lazy eager loading:
auth()->user()->load('shops.products');
To iterate over products:
#foreach (auth()->user()->shops as $shop)
#foreach ($shop->products as $product)
{{ $product->name }}
#endforeach
#endforeach
If you need just products:
Product::whereHas('shop', function ($q) {
$q->where('user_id', auth()->id());
})->get();
In both cases, you'll have the same number of queries to DB, so I'd use the first example in a real app.

Assuming that your product model has the shop id stored, try the following:
Products::whereIn('id_shop',
Shops::select('id')
->where('id_owner_user',Auth::user()->id)
->get())
->get()
It will retrieve a collection of products that belong to the list of shops which belong to the authenticated user

Related

Laravel - order collection by relatioship

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.

Eloquent Eager Loading "Property [service_code] does not exist on this collection instance"

I cannot figure out how eager loading works with the following example. This is my current DB with two tables, Quotes: where the general information is stored, and QuotesDetails: where details of each Quote in Quotes is stored.
In models I have the following structures:
Quotes.php
class Quotes extends Model
{
public function quotesdetails()
{
return $this->hasMany('App\QuotesDetails', 'quoteid', 'id');
}
}
and QuotesDetails.php with the following model:
class QuotesDetails extends Model
{
public function quotes()
{
return $this->belongsTo('App\Quotes', 'id', 'quoteid');
}
}
I used hasMany (in Quotes) because each quote can have/display 3-4 quotesdetails.
In my controller im using the following query:
$returnquotes = Quotes::with('quotesdetails')->where('id', '=', $quoteid)->get();
In my view im using the following structure:
#foreach ($returnquotes as $quotes)
{{$quotes->shipcity }}
{{$quotes->quotesdetails->service_code }}
#endforeach
shipcity displays the information with no problems, but service_code is not displayed and gives error.
Honestly I believe it has to work with this schema but I cannot figure out why is not working. My thoughts:
in controller: using "with('quotesdetails')" in controller it must establish the relation that appears in Quotes.php with name -> quotedetails that hasmany registries associated in QuotesDetails.php
in view: using "$quotes->quotesdetails->service_code" must retrieve the service_code associated to the quotes table (i'm using foreach because quotesdetails can have multiple registries per quote)
in model: Im using "hasMany" for Quotes.php because the dependent table is QuotesDetails and "belongsTo" in QuotesDetails.php for the inverse reason.
any help to understand the logic of eager loading with eloquent and laravel appreciated.
$quotes->quotesdetails is a collection itself (has many) so you need to iterate over it using another foreach:
#foreach ($returnquotes as $quotes)
{{$quotes->shipcity }}
#foreach ($quotes->quotesdetails as $quotedetail)
{{$quotedetail->service_code }}
#endforeach
#endforeach

How to get data from two related tables in laravel 5

I have 3 tables with foreign keys installed.
customers {customer_id, customer_name}
products {product_id, product_name}
customer_products {id, customer_id (foreignkey), product_id (foreignkey)}
My Controller code:
$CustomerProducts = ModelName::where('customer_id', 'somevalue')
->Join('customer_products', 'product_id', '=', 'customer_id')
->get();
My Model code:
class ModelName extends Model {
protected $table = 'hd_products';
public $primaryKey = 'id'; }
What is wrong in my code, since I'm getting wrong results. I want to show customer information and its related products.
This is where Laravel makes life easy. By adding the relationships on the model, you can then simply call the relationship via an eager load. You don't need the join, you can just pull the relationship. So
On your Customer model, set up the product relationship (you look like you have the right database structure for a many-to-many):
public function products(){
return $this->belongsToMany("\App\Product");
}
And then in your Controller, when you go to load your customers, you can grab the products at the same time:
$customer = Customer::with("products")->first();
I'm just taking the first customer as an example - you could get all of them and loop on customer as well as products if you wish.
And finally when you want to call the data like in a blade view, you can access it by chaining on the $customer model. :
{{ $customer->products->first()->name }}
If you want to loop through the products on the customer in a blade view:
#foreach($customer->products as $product){}
And, you still have the primary data for the $customer:
$customer->name // Etc.
HTH
If you want to show customer information and its related products, you gotta select the data from the tables.
In your code, in the controller, to get all data from all tables
you add:
->select(['customers.*' ,'products.*' ,'customer_products.*'])->get();
and edit the join statement so the controller will be like:
$CustomerProducts= DB::table('customer_products')
->join('customers','customers.customer_id','customer_products.customer_id')
->join('products','products.product_id','customer_products.product_id')
->select(['customers.*' ,'products.*' ,'customer_products.*'])
->get();
do not forget to add (if not added)
use DB;
At the beginning of your file (in the namespace area or the imports area), so it is like:
namespace App\Http\Controllers;
use DB;
use App\ //"your_file";
use Illuminate\Http\Request;
Hope this is helpful :)

Eliminating Duplicate Queries with Eager Loading

I am trying to eliminate unnecessary queries on my site but am struggling to wrap my head around Eager Loading and Lazy Loading. All users on my site have listings, and listings have multiple users. They are connected through the table listing_users. Every listing then has one "order" associated with it. Here is the user model:
User Model:
public function listings(){
return $this->belongsToMany(Listing::class)->withPivot('role_id');
}
Listing Model:
public function order(){
return $this->hasOne(Order::class)->first();
}
My current dashboard is loaded by calling this viewListings in the UserController:
public function viewListings(){
$user = Auth::user();
$listings = $user->listings()->orderBy('created_at','desc')->get();
return view('user.listings', compact('listings'));
}
The problem occurs in my blade view user.listings where I have a foreach loop for every listing and then call each order as well. I need a way to pass the listings to the page, with their related orders.
#foreach($listings as $listing)
#if($listing->order()->status == 'completed')
{{-- Display the listing details here --}}
#endif
#endforeach
Any advice into the above situation would be greatly appreciated! I'm sure there is a simple Laravel solution for this that I'm overlooking.
Try this:
Listing Model:
public function order(){
return $this->hasOne(Order::class); //without first()
}
UserController:
Here we use method with('order') for eager loading Order model for each Listing models retrieved by query. So now in your blade will not be unnecessary queries.
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.
public function viewListings(){
$user = Auth::user();
$listings = $user->listings()->orderBy('created_at','desc')
->with('order')->get();//added with('order')
return view('user.listings', compact('listings'));
}
user.listings:
You should use order without () if you need retrieve your model. So if you want modify order then use it with () as query builder, and add further constraints like where, orderBy etc. and in the end add first().
Here you can understand why we removed first() from hasOne above.
#foreach($listings as $listing)
#if($listing->order->status == 'completed')
{{-- order instead of order() --}}
{{-- Display the listing details here --}}
#endif
#endforeach

Laravel4, eager loading is not working

I'm trying to get "reviews" from my database where the user is from MX (for example) so, I have this:
Review::with(array('user' => function($query)
{
$query->where('country_code', '=', 'MX');
}))
->with('user.country','pictures')
->where('shop_id',Input::get('id'))
->orderBy('id','DESC'))
->get()
->toArray()
But seems to be like where('country_code', '=', 'MX') is not taken into account because retrieve all reviews and I just want the reviews written by the user from MX.
user and picture inside with are functions within my User model
country_code is a field from users table
The goal is: Just get the reviews written by a user from the country specified, and was testing something like this:
Review::where('shop_id',Input::get('id'))
->with('user.country','pictures')
->where('users.country_code','MX')
->orderBy('id','DESC'))
->get()
->toArray()
But is not working as well because says: Unknow column users.country_code in where....
You want to do a query that filters based on the relation. Eager loading constraints only constrain the records that come with your main result set.
Review::whereHas('users', function ($q)
{
$q->where('country_code', 'MX');
})
->with('user','pictures')
->where('shop_id',Input::get('id'))
->orderBy('id','DESC'))
->get();
The whereHas is saying ... give me only Reviews that have a User with country_code=MX.
Reference
Laravel Docs - Eloquent - Querying Relations
If you want reviews written by a certain user or group of users you don't want to use eager loading. Try this:
Review::with(array('user', 'pictures'))
->join('users', 'reviews.user_id', '=', 'users.id)
->where('users.country_code', '=', 'MX')
->where('reviews.shop_id', Input::get('id'))
->orderBy('reviews.id', 'DESC'))
->get();

Categories