Laravel Eloquent using "with" with conditions - php

I have two tables, say Products and Biddings where one product can be bid by many users. Naturally I have two models:
class Product extends Model
{
public function biddings()
{
return $this->hasMany('App\Bidding');
}
}
class Bidding extends Model
{
public function product()
{
return $this->belongsTo('App\Product');
}
}
So, say I want to get all products along with the highest priced bidding I did something like this.
$productBidding = DB::table('biddings')
->select('*', DB::raw('max(price) as price'))
->join('products', 'products.id', '=', 'biddings.product_id')
->groupBy('product_id')
->get();
That works well BUT I kinda want to do it Eloquent way. So how do I convert Query Builder way to Eloquent? I am currently on this but do not know how to put the "max" condition in.
$productBidding = Products::with('biddings')
->get();

$productbinding=Bidding:with('product')
->get();
foreach($productbinding as $productbind)
{
echo $productbind->product->name; // example
}

I would extract the highest bid to a separate function on the Product model, like so:
public function highestBid() {
return $this->biddings()->max('price');
}
Then fetch the products and get the highest bid:
$products = Product::get();
foreach ($products AS $product) {
echo $product->highestBid();
}

Related

Laravel Eloquent query recursive relationship model with pagination

I am building a store, where I have to display to the user all products in a given category and all other products that are contained in the subsequent subcategories of the currently accessed one. The categories have the N+1 problem since there can be infinite subcategories. I want to be able to filter trough these products and also to be able to paginate them.
This is my categories model:
class CatalogCategory extends Model
{
public function parent()
{
return $this->belongsTo('App/CatalogCategory','parent_id');
}
public function children()
{
return $this->hasMany($this,'parent_id')
->orderBy('order_place','ASC')
->with('children');
}
/*
* Return products, that belong just to the parent category.
*/
public function products()
{
return $this->hasMany('App\CatalogProduct','parent_id')
->where('is_active', 1)
->whereDate('active_from', '<=', Carbon::now('Europe/Sofia'))
->orderBy('created_at','DESC');
}
/*
* Return all products contained in the parent category and its children categories.
*/
public function all_products()
{
$products = $this->products;
foreach ($this->children as $child) {
$products = $products->merge($child->all_products());
}
return $products;
}
}
The all_products() method returns all of the products, that I want, but since it's a collection i'm unable to paginate or filter through it. My question is if there is a better way to retrieve the products and how to retrieve them so, that i can query them for filtering and paginate them?
You could use nested set technique to store categories.
Nested set technique allows to retrieve all descendants or ancestors for a certain node in hierarchical structures in one query.
You could try this package: https://github.com/lazychaser/laravel-nestedset. Imho it's the best implentation of nested set in laravel.
Installation and configuring will cost you 10 min.
After that you could retrieve your products something like this:
public function products($slug)
{
//first query: retrieving current category
$category = CatalogCategory
::where('slug', $slug)
->first();
//second query: retrieving all category descendants and self ids.
$categoryIds = $category
->descendants
->pluck('id')
->push($category->id);
//third query: retrieving all products.
$products = CatalogProduct
::whereIn('parent_id', $categoryIds)
->where('is_active', 1)
->whereDate('active_from', '<=', Carbon::now('Europe/Sofia'))
->orderBy('created_at', 'desc');
->paginate(50);
return view('path_to_view', compact('products', 'category'));
}

Eloquent Laravel Fetch Data from multiple tables

I've spent two days trying to solve this but I can't figure how.
I have five tables
Product
Category
Category_Product
Order
Order_Product
From the view,clicking on a category button I have to fetch all his ordered products with the relative category.
I have the current models:
Product Model
class Product extends Model
{
public function categories() {
return $this->belongsToMany('App\Category');
}
public function orders() {
return $this->belongsTo('App\Order');
}
}
Category Model
public function products() {
return $this->belongsToMany('App\Product');
}
Order Model
public function products() {
return $this->belongsToMany('App\Product');
}
Now the problem is that I can't figure how to fetch the data from the current tables.When I press a button I'm able to fetch the category from the Product Table,but I want to fetch from the Ordered_Products. I really can't figure how.
With this I'm able to fetch all the categories from Product
if (request()->category) {
$products = Product::with('categories')->whereHas('categories', function ($query) {
$query->where('slug', request()->category);
})->get();
}
With this instead,I'm able to fetch the ordered products.
$products = DB::table('order_product')
->join('products', 'order_product.product_id','=', 'products.id')
->where('order_product.user_id','=',$user_id)
->get();
For the latter, there's a better way to do it, that's for sure. I'm sorry if it's a dumb question but I'm rather new with this framework. I am using Laravel 7.2.
Basically Eloquent Model doesn't encourage joining tables to retrieve data. It should be joined only for filtering results (So you need to drop field of other table using ->select('original_table.*'))
In this case, you should simply retrieve categories at first. Then retrieve related data using relation property accessing.
e.g.
$categories = Category::query()
->with('products')
->where('slug', request('category'))
->get();
$products = $categories->flatMap->products;
$pivots = $products->map->pivot;
Solved using whereHas two times:
$products = Product::with('categories')->whereHas('categories',function($query){
$query->where('slug',request()->category);
})->whereHas('orders',function($query){
$query->where('orders.user_id',Auth::id());
})->get();

How to retrieve data from one table based on the calculation of another two table?

Suppose I have Three model named as Customer ,Invoice and Payment.
Invoice and Payment model looks like
id , customer_id, amount
I want to get only those customer whose
Invoice.sum(amount)>Payment.sum(amount) with these amount difference
I am currently retrieve like
$customers=Customer::get();
foreach($customers as $customer)
{
$pay=Payment::where('customer_id',$customer->id)->sum('amount');
$due=Invoice::where('customer_id',$customer->id)->sum('amount');
if($due>$pay){
// showing this customers
}
}
Is there any better way with eloquent join?
How Can I get In laravel eloquent ?
Have you set any relationship in the Model? A better eloquent query will look like this. You might need to adjust a bit
Customer::join('payment','customer.id','=','payment.customer_id')
->join('invoice','customer.id','=','invoice.customer_id')
->select(array('customer.*'),DB::raw("SUM(payment.amount) as payment_sum,SUM(invoice.amount) as invoice_sum"))
//->where('customer_id',$customer->id)
->groupBy('customer.id') //replace with anything that make sense for you.
->havingRaw('invoice_sum > payment_sum')
->get();
Try this
First, define the relationship in your Customer Model
public function payments()
{
return $this->hasMany(Payment::class); //based on your logic or structure
}
public function invoices()
{
return $this->hasMany(Invoice::class); //based on your logic or structure
}
Customer::with(['payments' => function($query) {
$query->sum('amount');
}])
->get();
or
$customers=Customer::with('payments','invoices')->get();
foreach($customers as $customer)
{
$pay = $customer->payments()->sum('amount');
$due = $customer->invoices()->sum('amount');
//other logic
}

Laravel paginate pivot tables

I have collections which contains custom products I need to paginate those collections products but I receive error.
data
With this query I can get my collection and it's products but I'm not able to paginate products.
$collection = Collection::where('slug', $slug)->where('status',
'active')->with('products')->first();
With this query I receive error
$collection = Product::with('collections')->whereHas('collections',
function($query) use($slug) { $query->where('slug',
$slug)->where('status', 'active')->first(); });
Error
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'shopping.product_id' doesn't exist (SQL: select * from `collection_products` inner join `product_id` on `collection_products`.`id` = `product_id`.`collection_product_id` where `products`.`id` = `product_id`.`product_id` and `slug` = test and `status` = active limit 1)
Code
Product model
public function collections()
{
return $this->belongsToMany(CollectionProduct::class, 'product_id');
}
Collection model
public function collection(){
return $this->belongsToMany(CollectionProduct::class);
}
public function products(){
return $this->belongsToMany(Product::class, 'collection_products', 'collection_id', 'product_id');
}
CollectionProduct model
public function collection()
{
return $this->belongsTo(Collection::class, 'collection_id','id');
}
Controller default query
public function single($slug){
$collection = Collection::where('slug', $slug)->where('status', 'active')->with('products')->first();
return view('front.collections.single', compact('collection'));
}
Question
How can I get my collection products with pagination ability?
Couple things:
You are trying to call the first() method inside of a relationship query in this line:
$collection = Product::with('collections')->whereHas('collections', function($query) use($slug) { $query->where('slug', $slug)->where('status', 'active')->first(); });
The methods first() and get() are used to execute the query, so you should keep them at the end of the chain of eloquent methods:
$collection = Product::with('collections')
->whereHas('collections', function($query) use($slug) {
$query->where('slug', $slug)->where('status', 'active');
})
->get();
https://laravel.com/docs/5.7/eloquent#retrieving-models
However, if you want to paginate the list of products, then what you really want is the paginate() method:
$collection = Product::with('collections')
->whereHas('collections', function($query) use($slug) {
$query->where('slug', $slug)->where('status', 'active');
})
->paginate(20); // will return 20 products per page
https://laravel.com/docs/5.7/pagination
Also, the collections() method on your Product model has product_id listed as the join table, and is joining to the CollectionProduct model instead of the Collection model.
Try this instead for your Product model:
public function collections()
{
return $this->belongsToMany(Collection::class, 'collection_products', 'product_id', 'collection_id');
}
i use following code and working very well
the first step get product
$product = Product()->find($product_id);
than i get collections and pagination
$collections = Product::find($product_id)->collections->paginate(20);
for example i used above code in this site.

How to use eloquent to retrieve a column from another table

I have two tables
product: id|category ...
category: id|name ...
product.category is a foreign key linked to category.id . I am building a basic CRUD and I would like to display all Products in the the product table as well as the name of the category they belong to rather than their category ID. TO do this, while searching the laravel documentation I came across the query builder and I achieved my goal.
public function index()
{
$products = \DB::table('products')
->join('categories', 'products.category', '=', 'categories.id')
->select('*')
->get();
return view('product' ,compact('products'));
}
Under my models for product and category I have created the appropriate relationships.
product.php :
public function category()
{
return $this->belongsTo('App\Category');
}
category.php :
public function products()
{
return $this->hasMany('App\Product');
}
I keep hearing about the power of Eloquent and was wondering how I could achieve a similar result with eloquent and if eloquent is designed for such operations or if the query builder is the right way to go.
Every tutorial online seems to only use the post and comments scenario of getting all comments belonging to a post.
You can use this code
public function index()
{
$products = Product::with('category')->get();
return view('product' ,compact('products'));
}
In blade
#foreach($products as $product)
{{$product->name}}
{{$product->category->name ?? ''}}
//or
#if ($product->category)
$product->category->name
#endif
#endforeach
Also if in project table foreign key is not equal category_id. In your case
public function category()
{
return $this->belongsTo('App\Category', 'category');
}

Categories