Laravel 5 multi level category - laravel-5

i currently i have 2 tables category & subcategory and everything works just fine, but i need another level of sub category which will be like category->sub->sub for that matter i tried to find a reasonable solution and i end up with bunch of packages to handle that for me.
now the question is
do i have to delete my category tables and their relations to my
another modes before i try any package or i should just add package top of my current tables?
what is the best package for my purpose base on your experience?
thanks in advance.

You don't need to depend on packages to implement this.
You can design your categories table like following:
|----------|------------|---------------|
| id | name | category_id |
|----------|------------|---------------|
Here category_id is nullable field and foreign key referenced to id of categories table.
For category category_id field will be NULL and for sub-category category_id will be it's parent category id. For sub sub category, category_id will be parent sub category id.
In model, you can write relation like following:
Category.php
/**
* Get the sub categories for the category.
*/
public function categories()
{
return $this->hasMany(Category::class);
}
Now you can get your sub categories like $category->categories.
N.B: You don't need the subcategory table, Only one table will do the work.
Update- Show product categories
Update Category.php:
/**
* Get the parent category that owns the category.
*/
public function parent()
{
return $this->belongsTo(Category::class);
}
In Product.php:
/**
* Get the category that owns the product.
*/
public function category()
{
return $this->belongsTo(Category::class);
}
Now, you need to get product category and all of its parents. It'll be an array of categories from parents to child. Then you can show as you wish.
$category = $product->category;
$categories = [$category];
while (!is_null($category) && !is_null($category = $category->parent)) {
$categories.unshift($category);
}
// $categories = ['parent category', 'sub category', 'sub sub category' ..]
Show category title sequentially
foreach ($categories as $category) {
echo $category->title . '<br>';
}

Related

belongsToMany withPivot that belongs to another item with pivot

I have a problem I haven't been able to solve in days. Maybe there is someone here who can help me.
Basic idea
I have a model named Product. Each Product can have multiple ProductAttributes (belongsToMany with pivot).
Example:
Product: Car
ProductAttributes:
Color
HORSEPOWER
The pivot table for ProductAttribute holds the values for the respective attributes (color = blue, PS = 180).
This already works very well.
Problem
Now I want to implement product packages. A product package (ProductBundle) has many products. But these products should have their own pivot tables for their attributes. So in a product bundle I want to be able to specify that the car I created has more PS than defined in the actual product.
For this I need 2 pivot tables for the attributes.
What I've already tried
ProductBundle belongsToMany Product using a different pivot-table
ProductBundle belongsToMany ProductBundleProduct (ProductBundleProduct has a field called product_id which refers to the actual "basic product")
In both scenarios I have the problem that the pivot table for the attributes of the product belonging to a product bundle is not saved correctly:
Product
/**
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function productBundleAttributes()
{
return $this->belongsToMany(ProductAttribute::class,
product_bundle_product_attribute')
->withPivot($this->attributefields)-> withTimestamps();
}
controller
$prod = Product::findOrFail($product['id']);
$added = $productbundle->products()->save($prod, [
'custom' => $product['custom'],
'title' => $product['title'],
# 'factor' => $product['factor']
]);
/*Save attributes*/
$added->syncProductBundleAttributes($product['attributes']],
$productbundle->id);
sync method
public function syncProductBundleAttributes(
array $attributes,
int $id
) {
$this->checkProductAttributesRecursively(collect($attributes)->transform(function (
$attributes
) use (
$id
) {
$attribute['product_bundle_id'] = $id;
$attribute['product_attribute_id'] = $attribute['id'];
return $attribute;
})->toArray());
$this->productBundleAttributes()->attach($this->result);
return $this->result;
}
Unfortunately, this means that only one attribute is stored at a time.
You can have an additional column in the pivot table between Product and ProductAttributes, say product_bundle_id. So this way you can get attributes for specific bundle, or basic attributes with product_bundle_id as 0
Look at Saving Additional Data On A Pivot Table in Laravel Eloquent, Many to Many

Laravel, many-to-many relationship among multiple models

I Have multiple models that have many to many relationship
Here are the models
News Section
Categories
Sub Categories
Approved News
Pending News
Each News Section can have multiple Categories.
Each Category can have multiple Sub Categories.
Each Sub Category can has multiple Approved News and Pending News.
I want to have News with Categories, Sub Categories and Pending / Approve news
and stuff like
Categories with Sub Categories and Approve news
I tried with pivot tables but not able to get results
Models are as follow
News Section
class NewsSection extends Model
{
public function categories()
{
return $this->belongsToMany(Category::class);
}
}
Category
class Category extends Model
{
public function subcats(){
return $this->belongsToMany(SubCategory::class);
}
public function newssections(){
return $this->belongsToMany(NewsSection::class);
}
}
SubCategory
class SubCategory extends Model
{
public function category(){
return $this->belongsTo(Category::class);
}
public function approvednews(){
return $this->belongsToMany(ApprovedNews::class);
}
public function pendingnews(){
return $this->belongsToMany(PendingNews::class);
}
}
ApprovedNews
class ApprovedNews extends Model
{
public function subcategories (){
return $this->belongsToMany(SubCategory::class);
}
}
PendingdNews
class PendingdNewsextends Model
{
public function subcategories (){
return $this->belongsToMany(SubCategory::class);
}
}
Update
This what I have done so far
$news =Category::with('subcats.approvednews')->where('id',1)->get();
I got all the approved news with subcategories and categories
how can i modify this to get specific subcats and approved news per category, if i do this
$news =Category::with('subcats.approvednews')->where('subcats.id',1)->get();
I get an error like id ambiguous
Is it possible to pick and chose items from relation for instance return just 2 subcats and 3 approved news for each subcat of selected category
or
get count of approved news and pending news per subcat and category
Thanks in advance
The error "error like id ambiguous" means that you need to specify the table in your where('id', 1) like where('table.id', 1) so that MySQL knows which id column in which table you mean.
You can constrain the models returned by with like this:
Category::with(['subcats' => function(Builder $query) {
$query->where('id', '=', 1);
}]);
Also you can count relations:
$subcat = SubCategory::withCount(['approvednews']);
$subcat->approvednews_count;
Limiting eager loaded relations is not possible per the docs.
A workaround may be to go the other way round starting from ApprovedNews:
ApprovedNews::whereHas(['subcategories' => function(Builder $query) {
$query->where('id', '=', 1);
}])->limit(10);
I have a few suggestions of how you can get this to work. In your comments, you say you are getting an issue doing the following:
$items=Category::with('subcategory')->where('id',1)->get();
Where is 'subcategory' coming from? By the looks of your model, your relationship between Category and Subcategory is called subcats. So you would need to change it to:
$items=Category::with('subcats')->where('id',1)->get();
And if you dump that out, you should see that you will get the category where the ID is 1, and the subcategories loaded in. A way to test that your relationships are working would be something like this:
$category = Category::find(1);
$subCats = $category->subcats()->get();
dd($subCats);
In your relationships, instead of using SubCategory::class I would suggest trying return $this->belongsToMany('App\SubCategory'); so that the models are definitely connected.
Once you have tested that your relationships between one another work, you can get started on testing that you can go from a->b->c etc.
May be using "Nested Eager Loading" and "scope", you can do something like
$pendings = NewSection::with('categories.subCategories')->pending()->get()
$approved = NewSection::with('categories.subCategories')->approved()->get()
not tested it, but you can try, may be with some modification, you can reach to your goal.
if you want return one collection, you may like to merge it
$approved->merge($pendings);
but, you should avoid it.

One-to-Many relationship between multiple tables

I'm trying to make a relationship between Product and Brand models with an intermediate model BrandProduct, intermediate model also contains some additional info like product_model etc.
My aim is to access all the products that belong to a specific brand with additional manufacturer info and also I want to access the brand of a specific product with additional info.
I've a Model named Product with these attributes
id
name
sku
quantity
etc.....
Another Model named Brand with following attributes
id
name
logo
And an intermediate Model named BrandProduct with attributes
id
brand_id
product_id
model
etc.....
I'm registering Brands and Products separately and making a relationship between them by BrandProduct Model with additional attributes like product_model.
Brand.php model contains:
public function products(){
return $this->hasMany('App\Models\BrandProduct');
}
Product.php model contains:
public function manufacturer(){
return $this->hasOne('App\Models\BrandProduct');
}
And BrandProduct.php model contains:
public function data(){
return $this->belongsTo('App\Models\Product', 'product_id', 'id');
}
public function brand(){
return $this->belongsTo('App\Models\Brand', 'brand_id', 'id');
}
Now I can successfully retrieve Product > Manufacturer by
$p = Product::find(id)->manufacturer
But I can't get inverse relation BrandProduct > Data by trying
$p = BrandProduct::find(id)->data
Similarly I can retrieve all the Brand > Products by
$p = Brand::find(id)->products
But can't get inverse relation
$b = BrandProduct::find(id)->brand
In the end I'd like to achieve something like this:
//For Brand > Products
$p = Brand::find(id)->products;
$product_model = $p[0]->model;
$product_name = $p[0]->data->name;
//For Product > Manufacturer
$p = Product::find(id)->manufacturer;
$product_model = $p->model;
$brand_name = $p->brand->name;
Please tell me what's wrong with my approach all other relationships are working fine except this one.

Getting an ID from Pivot table Eloquent belongsToMany() - Laravel 5.2

I have two items. Shops & Categories. In my Shop model I have declared this relation :
public function categories()
{
return $this->belongsToMany('App\Models\Category');
}
and in my Category model I have this :
public function shops()
{
return $this->belongsToMany('App\Models\Shop');
}
I am able to add Shop in to category by using attach. For example this code :
$shop->categories()->attach($cat_id);
By using the above attach method, a record is automatically created in my pivot table category_shop with category_id and shop_id.
Now, I have a shop loaded to $shop. Like below :
$shop = Shop::findOrFail($id);
Obviously now I have my shop id in $shop->id. My question is how can I get back the category id(s) of this shop by using the above setup.? I am new to laravel.
Since, shop has many categories. You will get many categories id.
$shop= Shop::findOrFail($id);
foreach($shop->categories as $category)
{
print_r($category->id);
}

Laravel Get products from multiple taxonomies

I have 3 tables, products, taxonomies and product_taxonomy. The 3rd table is a pivot table that contains product_id and taxonomy_id, meaning that product and taxonomy are many to many relationship. Given a list of taxonomy ids, how can I get all the products that belong to these taxonomies? NOTE: I want to have the products result set being able to paginated or order by maybe it's price or something.
You need to create many-to-many relationship and that requires relationship methods like:
// Product Model
public function taxonomies()
{
return $this->belongsToMany('Taxonomy');
}
// Taxonomy Model
public function products()
{
return $this->belongsToMany('Product');
}
The query:
$listOfTaxonomyIds = [1,2,3];
$products = Product::whereHas('taxonomies', function($query) use ($listOfTaxonomyIds){
$query->whereIn('taxonomy_id', $listOfTaxonomyIds);
})->get();

Resources