Check if related model have entry in another related model - laravel

i have following models and there relation
Models
User
Product
Orders
OrderProduct
ProductReviews
Relations
User hasMany Orders
Orders hasMany OrderProdut
User hasMany ProductReview
Product hasMany ProductReview
ProductReview belongsTo User
Inside the product page all the reviews for that product is listed with the user details who made the review.
I want to check if review in list has made a purchase of the reviewed product.
So far i have done this with php loops and eager loading. Is there a better way to do this
$product = Product::with([
'reviews'=>function($q) {
$q->where('status', 1);
},
'reviews.user'
])->first();
if($product){
$product->load(['reviews.user.orders.orderProducts' => function($q) use($product){
$q->where('product_id',$product->id);
}]);
}
foreach ($product->reviews as $review){
$review->purchased_from_microless = false;
foreach ($review->user->orders as $order) {
if($order->orderProducts->count()){
$review->purchased_from_microless = true;
break;
}
}
}

It is possible to inline the query to check if reviewed product was purchased:
$product = Product::with([
'reviews' => function($q) {
$q->where('status', 1);
},
'reviews.user.orders'
])->first();
$reviews = $product
->reviews
->map(function ($value, $key) {
$wasPurchased = $review->user->orders->orderProduct()->where('product_id', $product->id)->count() > 0;
$review['purchased_from_microless'] = $wasPurchased;
return $review;
})->all();
Where, the reviews collection is looped through with a check to see if the review's user's orders has an product which has the id of the product in question.
Return true if the count is greater than one.

Related

How many products do I have Laravel

I have a table whit some products and I try to do that if I add more products than I have in my inventory it has to show me an alert.
I did it but its not working, In products i have the sku and reference.In inventories i have all the products that i have.
Controller:
foreach ($request->dataTable as $dataTable) {
$productId = DB::table('products')
->where('sku', $dataTable[2])
->whereNull('deleted_at')
->select('id', 'reference')
->first();
$inventoryId = DB::table('inventories')
->where('product_id', $productId->id)
->whereNull('deleted_at')
->whereNull('sale_id')
->select('id')
->first();
if ($inventoryId == null) {
return response()->json([
'type' => 'error',
'msg' => 'No existe suficiente stock para realizar la venta del producto con referencia: ' . $productId->reference . '. Solo hay ' . $productId->references->count(),
]);
}
}
Product Model:
public function references()
{
return $this->hasOne('App\Models\Inventory');
}
Products
id
reference
sku
1
BCR2214
0108888893016580
2
BCR2219
0108888893016580
Inventories
ID
product_id
1
1
2
1
3
2
If I add a product_id = 1 three times it has to show me the alert and says "The product 1 only has 2 in stock in inventory"
But I add two products and says: "error", msg: "Undefined property: stdClass::$references"}
You're not able to access eloquent relationships on the DB facade.
If you wanted to access the references relationship you'd need to change
$productId = DB::table('products')
->where('sku', $dataTable[2])
->whereNull('deleted_at')
->select('id', 'reference')
->first();
into an eloquent model lookup, something like
$productId = App\Models\Products::where('sku', $dataTable[2])
->whereNull('deleted_at')
->select('id', 'reference')
->first();
As a separate note and to help with performance, I'd also suggest eager loading the inventories relationship. Without knowing much about your DB design, something like the below should get you started
// Product model
public function inventories()
{
return $this->belongsTo('App\Models\Inventory');
}
Then you could adjust the top eloquent query to be something like
$productId = App\Models\Products::where('sku', $dataTable[2])
->whereNull('deleted_at')
->doesntHave('inventories')
->select('id', 'reference')
->first();
Be sure to checkout Laravel eloquent relationships for more information specific to your situation

Laravel 9 hasMany relationship not working

I have a model with a relationship with another model, when calling the relationship in the controller it gives me
Exception: Property [products] does not exist on the Eloquent builder instance.
model:
public function products(): HasMany
{
return $this->hasMany(CartProduct::class,'cart_id','id');
}
controller
public function showCartOfAuth()
{
$id =auth()->guard('customers')->user()->id;
$cart = Cart::where('customer_id',$id)->get();
$products = Cart::where('customer_id',$id)->products->get();
$response = [
'cart' => $cart,
'items' => $products
];
return response($response,200);
}
Can you try this, please: Cart::with('products')->where('customer_id',$id)->get();
The way you have implemented requires atleast two database queries.
One: $cart = Cart::where('customer_id',$id)->get();
Two: Cart::where('customer_id',$id)->first()->products; //maybe a third query to fetch the products
For better performance you should
public function showCartOfAuth()
{
$id =auth()->guard('customers')->user()->id;
// Either Option 1
// Possibly 2 database queries, one to fetch the Cart and another to fetch Products for the Cart
$cart = Cart::where('customer_id',$id)->first();
$products = $cart->products;
$response = [
'cart' => $cart,
'items' => $products
];
// OR Option 2
// One database query using eager loading
$cart = Cart::with('products')->where('customer_id', $id)->first();
$response = ['cart' => $cart];
return response($response,200);
}
If you use option 2 from above then instead of $items you can use $cart->products

Eloquent GroupBy with hasManyThrough relationship

I have three models - Company, Driver that belongs to a company, and Order that belongs to a driver. I have stated the relationship between companies and booking like:
public function orders() {
return $this->hasManyThrough(Order::class, Driver::class);
}
But! If I want to get all orders grouped by companies how do I do that?
you can make Accessor in your order model to get company_id
public function getCompanyIdAttriubte(){
return $this->driver->company->id;
}
then to get orders group by company_id
$orders = Order::all();
$orders = $orders->groupBy('company_id');
you can view these links to understand what id did exactly
https://laravel.com/docs/5.7/collections#method-groupby
https://laravel.com/docs/5.7/eloquent-mutators#defining-an-accessor
You have two ways:
First:
$companies = Company::with(['orders', 'orders.user'])
->whereHas('orders')
->get();
foreach($companies as $company){
echo $company->id;
foreach($company->orders as $order){
echo $order->driver->id;
echo $order->id;
}
}
Second:
$orders = Orders::with(['driver', 'driver.company'])->get();
$orders = $orders->groupBy(function($order){
return $order->driver->company_id;
});
foreach($orders as $companyId => $group){
foreach($group as $order){
echo $order->id;
echo $oder->driver->id;
echo $order->driver->company->id;
}
}

Laravel filtering one to many relations

I have Product and Category models tied one to many.How to filter Products by category? In template i have
{{ $category->name }}
How to write function to filter Products with Category?
public function productsByCategory($category_id){
$products = Product:: ????
return view("layouts._productsByCategory", compact("products"));
Answer is
$products = Product::where('category_id', $category_id)->get();
You can use:
$products = Product::where('category_id', $category_id)->get();
or
$products = Product::whereHas('category', function($q) use ($category_id) {
$q->where('id', $category_id);
});
assuming you set category relationship in Product model.
You might find it easier to go via the category:
public function productsByCategory($category_id){
return view("layouts._productsByCategory", [
'products' => Category::with('products')->find($category_id)->products
]);
}

Laravel Eloquent nested query

I was working with Laravel and got stuck in a situation. I have following models:
Category
Product
CategoryProduct
CategoryProduct holds the information about which product belongs to which category (a product may belong to multiple categories).
Now, when I want to load all products belonging to a particular category, I need to run query on Product and CategoryProduct which is where I'm stuck.
I gave it the following try but was unsuccessful:
$products = Product::where('status', '=', 'active')
->where('category_id', '=', $category_id)
->take($count)
->skip($skip)
->get();
Obviously, it will say that category_id is not a column.
Here is my DB & Model structure:
categories table
id,
name,
etc.
products table
id,
name,
sku,
etc.
category_products table
id,
product_id, ( Foreign key to Product.id )
category_id, ( Foreign key to Category.id )
etc.
Product model
class Product extends Eloquent {
protected $table = 'products';
protected $hidden = array();
public static $rules = array('name' => 'required|min:3');
}
Category model
class Category extends Eloquent {
protected $table = 'categories';
public static $rules = array('name' => 'required|min:3');
}
CategoryProduct model
<?php
class CategoryProduct extends Eloquent {
protected $table = 'category_products';
public function product()
{
return $this->belongsTo('Product');
}
public function category()
{
return $this->belongsTo('Category');
}
}
Update
A new question on this
I'm trying to display products. If category is not passed (value is -1), then I will show all products, otherwise I will show products from the passed category.
Now, when I show all products, those products may already exist in a category. I want to display ticked checkbox for products that are already in a category. I'm doing something like this:
if($category_id==-1)
$products = Product::where('status', '=', 'active')->take($count)->skip($skip)->get();
else{
$products = Product::whereHas('categories', function($q) use ($category_id)
{
$q->where('category_id', $category_id);
})->where('status', 'active')
->take($count)
->skip($skip)
->get();
}
The table category_products have product_id, category_id as columns.
Now, the query:
$products = Product::where('status', '=', 'active')->take($count)->skip($skip)->get();
will pick products only from products table. If I check each product for its existence in category_products, then there will be too many database queries for large number of products.
Any idea, how to achieve this. I hope I was able to clear my situation. Thanks
The CategoryProduct model should not be necessary unless you have additional fields besides product_id and category_id which point to other relationships.
What is necessary are the methods for setting up the relationship on the Category and Product models.
In Category, add the relationship function...
public function products()
{
return $this->belongsToMany('Product', 'category_products');
}
In your Product model, do the same for categories.
public function categories()
{
return $this->belongsToMany('Category', 'category_products');
}
Then you can query for your active products that belong to that category using your relationship method and whereHas()
$products = Product::whereHas('categories', function($q) use ($category_id)
{
$q->where('id', $category_id);
})->where('status', 'active')
->take($count)
->skip($skip)
->get();
You don't need a model for a pivot table in Many-to-Many relationships. Look at this section of the Eloquent documentation for further explanation.
You still need to create a migration to set up the pivot table (or do it manually if you don't use migrations), but not a model. Instead, create a function for Category to designate the relationship:
public function products()
{
return $this->belongsToMany('App\Product', 'category_products');
// - You might need to adjust the namespace of App\Product
// - category_products refers to the pivot table name
}
Likewise, Product needs a similar public function.
Then you're able to do it the other way around, by finding the category and then listing all its related products:
$products = Category::find($category_id)
->products()
->where('status', 'active')
->take($count)
->skip($skip)
->get();
This question could also be relevant to yours.

Resources