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/
Related
How to get only one value in the filament table for with hasMany relationship?
I have two DB tables:
products
id
sku
1
SKU_1
2
SKU_2
product_descriptions
id
product_id
translation_id
name
1
1
1
Opel
2
1
2
Vauxhall
In my Product model I have hasMany relationship
public function productDescriptions(): HasMany
{
return $this->hasMany(ProductDescription::class);
}
When I do Tables\Columns\TextColumn::make('productDescriptions.name') it return all values separated by comma. In my example "Opel, Vauxhall"
Is there any way to manipulate/mutate return value using callback? Let say, return only first value "Opel"?
You can use calculated states.
Tables\Columns\TextColumn::make('productDescriptions.name')
->getStateUsing( function (Model $record){
return $record->productDescriptions()->first()?->name;
});
please ignore syntax errors
im working on a system with these tables
products : id , title
1 p1
custom_field : id , title
10 color
11 something all products have
custom_field_attach : custom_field_id , product_id
10 1
11 0
i want to get product custom fields
i can have this relation in Product model
function CustomFields(){
return $this->belongsToMany(CustomField::class , 'custom_field_attach');
}
and have this query
$product = Product::with('CustomFields')->get();
but the thing is some custom_fields are for all the products so in the custom_field_attach table
product_id is 0for those custom_fields
sine my relation is using product_id as foreign key i wont get those rows with product_id = 0 in the result
basically i want
custom_field_attach where product_id = product.id OR product_id = 0
to make thisng simpler i can have this relation in Product model
function CustomFieldAttach(){
return $this->hasMany(CustomFieldAttach::class);
}
no i want something like
$product = Product::with(['CustomFieldAttach' => function($CustomFieldAttach){
// where forieng key works product_id=product.id orWhere product_id =0
}])->get();
i know i can do this with query builder but i want to use relation if possible
please note someone else has written this code and im just adding some features to the system ... changing the db structure is not an option
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();
CONTEXT
I am managing products. This is a shoe store. I would like to offer a view of the other variants.
The database is shaped like this:
For example you can have a leather shoe (id 1), and there is 3 variants of this shoe: a black (id 1), a brown (id 2), and a grey (id 3).
What I try to do is to construct a Laravel relationship to be able, from one variant, to get its siblings. Here is what it looks like in the database according to the example I mentioned.
SHOE
id
====
1
SHOE_VARIANT
id shoeId colorId
===================
1 1 1
2 1 2
3 1 3
...
8 2 5
9 3 2
10 3 4
In this case, if the user is viewing the black variant (id 1), I whish I could show him the 2 others variants (brown, id 2, and grey, id 3).
QUESTION
How can I construct a Laravel relationship in order to retrieve siblings from a parent id, and make sure the current record itself is not included?
EXPERIMENTS
I already tried to construct a relationship like below, but the current record itself is included and I can't figure out how to exclude the record itself because I did not find how to get the current id of the record.
// app/ShoeVariant.php
namespace App;
use Illuminate\Database\Eloquent\Model;
class ShoeVariant extends Model {
public function siblings() {
return $this->hasMany("App\ShoeVariant", "shoeId", "shoeId");
}
}
This means:
For the current shoe variant, get the shoe variants that matches knowing that you should match the foreign column named "shoeId" and the local column named "shoeId"
So if 2 shoe variants share the same column "shoeId", this works. Stuck in excluding the current record from these results.
This should do what you want:
public function siblings() {
return $this->hasMany('App\ShoeVariant', 'shoeId', 'shoeId')
->where('id', '!=', $this->id);
}
Just filter out the current variant by id and get all the others.
Alternatively, you can just make a new property:
public function getVariantsAttribute() {
return $this->siblings->reject(function($elem) {
return $elem->id == $this->id;
});
}
And then use it in code like:
$variants = $model->variants; // all except this one
I'v staked with a one thing in laravel. I have huge database with those kinds of tables: products, categories and regions.
Products
id
category_id
region_id
Categories
id
parent_id = category.id or 0 if it is root
Region
id
parent_id = region.id or 0 if it is root
And I have to get all root regions who connected to specific category. The only solution I see it's to do it like this
$products = Category::products->all();
$rootCategories = [];
foreach($products as $product){
$rootCategories[] = $product->region->ultimateParent(); //Region::ultimateParent();
}
What do you think is there some kind of more eloquent way to solve it?
you have to use join like:
$query=DB::table('products')
->join('categorie','products.catagory_id','=','catagories.id')
->join('region','products.region_id','=','region.id')
->select('products.*','catagories.*',region.*'
->get();
This query will give you all the fields from all 3 tables.