laravel withCount of linear nested relations in laravel - php

I have tables,
ec_products : id | brand_id (id from ec_brands) | store_id (id from mp_stores)
ec_brands: id
mp_stores: id
I am calculating total products belong to each brand and store using relations and withCount of Laravel, Like,
Brand model,
public function products()
{
return $this->hasMany(Product::class, 'brand_id')->where('is_variation', 0);
}
Stores model
public function products()
{
return $this->hasMany(Product::class, 'store_id')->where('is_variation', 0);
}
And in each model,
$data = $data->withCount(‘products’);
Now I introduced categories, One product belongs to multiple categories.
So used separate table to link category and product.
ec_product_category_product: id | category_id (from category table) | product_id (from our products table)
So final question I have, How to join all,
I want to list each of these (brand,store, category) and count products of each based on request parameters.
Like
if any brands selected, then count store and category related to that brand.
if stores selected, then count brands and category related to that store.
if any category selected, then count store and brand related to that category.
structure of UI
Suggestion or solution.
Thanks

You should always start you Eloquent query with the Object you want to manipulate, here, it's Product and THEN constraint your query,
so in your controller :
$product_count = Product::when($request->category_id, function($query) use ($request){
$query->where('category_id', $request->category_id);
})
[...] //do that for each of your constraints
->count();
if you want to do a search on the name of the category for example, use a whereHas :
$product_count = Product::when($request->search, function($query) use ($request){
$query->whereHas('category', function($query) use ($request){
$query->where('name', 'like', "%$request->search%");
});
})
[...] //do that for each of your constraints
->count();

Related

Select rows with same values in relationship Eloquent

I have two models in my project
Product and Product attributes
The relationship between them is one to many, so i can get product with relative attributes. Now I need to get a list of products and also get common product attributes between selected products. As in ex:
Product
id
Product
1
Apple
2
Pear
3
Ananas
Product attributes
id
attribute
product_id
1
fruit
1
2
fruit
2
3
fruit
3
4
green
1
5
yellow
2
6
brown
3
Now when I'm extracting all 3 products, i want to get their common attributes, in this case the common attribute is "fruit". How can I do this with laravel eloquent?
As per your current schema (which doesn't look good) you can get common attributes as
$products = Product::take(3)->get();
From above products collect product ids
$productIds = $products->pluck('id');
And then query product_attributes matching previously collected $productIds, this query will involve aggregation to satisfy your condition for common attributes.
$attributes = DB::table('product_attributes')
->select(['name',DB::raw('count(distinct product_id) total_count')])
->whereIn('product_id', $productIds)
->groupBy('name')
->havingRaw('total_count = ?', [count($productIds)])
->get();
Above query will return common attributes by checking the result of count() expression with count of $productIds if they match and this means that every products has this attributed attached to it.
At first, as said above, add many-to-many relationships, one-to-many is not suitable for that case. Then I can propose such a variant with two queries:
// select here product ids you need
$products = Product::all(['id']);
//select common attributes
$attibuteBuilder = ProductAttribute::query();
foreach($products as $product){
$attibuteBuilder->whereHas('products', function (Builder $builder) use ($product) {
$builder->where('product_id', $product->id);
})
}
$atributes = $attibuteBuilder->get();
The second variant: get the collection of products with attributes and check the general/

Laravel joining table relationship

I'm wanting to do a joining table but I'm not sure how to set up the relationship in laravel.
I have 3 tables, products, categories, and product_categories. product_categories will consist of the product ID and the category ID. How would I join theses up in a relationship in laravel so I can just call $product->categories() like a normal relationship?
Your relationship will be belongsToMany.
Your products table would be
id | name
Your categories table would be
id | name
your product_categories table would be
id | product_id | category_id
As per relationship in Laravel
Product Model
class Product extends Model
{
protected $table = 'products';
public function categories()
{
return $this->belongsToMany('App\Category','product_categories','product_id','category_id');
}
}
Category Model
class Category extends Model
{
protected $table = 'categories';
public function products()
{
return $this->belongsToMany('App\Product','product_categories','category_id','product_id');
}
}
In controller now
Product::with('categories')->get();
This is a many-to-many relationship.
You can use belongsToMany on both products and categories and it would work. However you also should rename product_categories to follow the rule
use singular table names in alphabetical order
this would be category_product

How to get ID from table only if it contains multiple values needed, from different rows

I have a channel_members table containing channel_id, user_id. I need to get the channel_id if there are multiple rows using that same channel_id which contains multiple user_id that I will provide.
Example:
If there are 5 rows in the table
CHANNEL_ID | USER_ID
2 | 2
2 | 3
2 | 4
3 | 2
3 | 4
I need to get the channel_id which are being used by user_id 2, 3, and 4. So based in the table above that I provided. I should get channel_id 2.
I assume you have already defined your relations uisng models, As per your provided description, channel has many to many relation with user
class Channel extends Model
{
public function members()
{
return $this->belongsToMany('App\User', 'channel_members', 'channel_id', 'user_id');
}
}
Using eloquent realtions you can check existance of related models as
$channels = App\Channel::whereHas('members', function ($query) {
$query->where('id', '=', 2);
})->whereHas('members', function ($query) {
$query->where('id', '=', 3);
})->whereHas('members', function ($query) {
$query->where('id', '=', 4);
})->get();
Above will return you only channels who have associations with all 3 users based on supplied id
Querying Relationship Existence
SELECT CHANNEL_ID
FROM channel_members
WHERE USER_ID in (2,3,4)
GROUP BY CHANNEL_ID
HAVING COUNT(CHANNEL_ID) > 0

Laravel query builder with pivot table

I have two tables with a pivot table
Table tours
id | name | country_id | featured
Table countries
id | name
Pivot Table country_tour
id | country_id | tour_id
I want to to find the tour that has featured column of tours table set to 1 and country_id of country_tour table set to 1.
UPDATED:
You can do it like this using Laravel's query Builder method - whereHas():
Your models should look like this (Many to Many Relationships):
Tour Model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Tour extends Model
{
public function countries() {
return $this->belongsToMany('App\Country');
}
}
and Country Model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Country extends Model
{
public function tours() {
return $this->belongsToMany('App\Tour');
}
}
and now you can fetch the desired results by using the below query:
Tour::where('featured', 1)
->whereHas('countries', function($q) {
$q->where('id', 1);
})
->get();
This will get you the collection of tours with featured = 1 and having country with id = 1.
Hope this helps!
With reference to Saumya Rastogi answer, change the id to countries.id to avoid "column 'id' in where clause is ambiguous" error.
i.e.
From:
Tour::where('featured', 1)
->whereHas('countries', function($q) {
$q->where('id', 1);
})->get()
To:
Tour::where('featured', 1)
->whereHas('countries', function($q) {
$q->where('countries.id', 1);
})->get();

Laravel leftJoin only last record of right table

I am new to laravel.
I have two tables.
1) products
2) prices
-----------------------------
- products -
-----------------------------
- id_product | int (p_key) -
- name | varchar -
-----------------------------
-------------------------
- prices -
-----------------------------
- id_price | int (p_key) -
- id_product | int -
- price | int -
-----------------------------
the products table holds data about products like id, name,...
the price changes are stored in prices table where the last record is the newest price that should be displayed to users.
now I want to search through products and get the last price of each product from prices table. this is my query:
$result = DB::table('products')->leftJoin('prices', function($join) {
$join->on('products.id_product', '=', 'prices.id_product');
})->whereRaw(MY_SEARCH_FILTERS);
the above code is wrong because if a product has 4 records in prices table, then it will be repeated 4 times in $result, but only 1 record with the last price should be displayed.
Here we have 2 tables users and answers where users is left table and answers is right table which has user answers.
We wanted to left join users with answers but the join should be with the latest record or answers table.
$query = Users::select('users.id', 'users.user_name','answers.created_at as last_activity_date')
->leftJoin('answers', function($query)
{
$query->on('users.id','=','answers.user_id')
->whereRaw('answers.id IN (select MAX(a2.id) from answers as a2 join users as u2 on u2.id = a2.user_id group by u2.id)');
})
->where('users.role_type_id', Users::STUDENT_ROLE_TYPE)->get();
you can make make it easy by using Laravel Elquent:
class Product extends Model
{
public function lastPrice()
{
// optional: change id_price to created_at by add created_at to prices table
return $this->hasOne(Price::class)->orderBy('id_price', 'DESC');
}
}
now in
public function getProducts(){
$MY_SEARCH_FILTERS=....;
// get all products with last price
$products=Product::with('lastPrice')->whereRaw(MY_SEARCH_FILTERS)->get()
return $products
}
Here we have 2 tables 'articles' and 'comments' where articles is left table and comments is right table which has article's comments.
We wanted to left join articles with comments but the join should be with the latest record from comments table.
$query = Article::select('articles.*', 'comments.comment as article_comment')
->leftJoin('comments', function($query) {
$query->on('comments.article_id','=','articles.id')
->whereRaw('comments.id IN (select MAX(a2.id) from comments as a2 join articles as u2 on u2.id = a2.article_id group by u2.id)');
})
->get();
i found this solution from here https://laravelcode.com/post/how-to-get-last-record-from-leftjoin-table-in-laravel
You need to add two things in here,
1) orderBy descending on prices table.
2) first clause in the DB::table function (It will fetch only 1
record, that will be the latest price).
The solution :
$result = DB::table('products')
->leftJoin('prices',function($join)
{
$join->on('products.id_product', '=', 'prices.id_product')
})->whereRaw(MY_SEARCH_FILTERS)
->orderBy('prices.id_price','desc')
->first();
You can also use (Laravel 5.1) :
$result = DB::table('products')
->leftJoin('products.id','=','prices.id_product')
->whereRaw(MY_SEARCH_FILTERS)
->orderBy('prices.id_price','desc')
->first();

Categories