Hi i have on table with categories with foreign key to herself where parent_id is the same that id in this table. I want to get two arrays of objects. First with categories where
parent_id=0
and second with subcategories. But I dont know how can I catch this subcategories. I have this:
$category= Category::where('parent_id', '=', 0)->get();
dd($category[0]['id']);
$subcategory= Category::where('parent_id', '=', (($category[0]['id']??)));
First $category shouild return me array of categories and second array with subcategories i need adjust id of each object of array $category to each subcategory array. Is it possible or there are other ways?
If you define your model relations correctly you can get categories and their subcategories in a much nicer way.
First define the relations:
class Category extends Model {
public function parent() {
return $this->belongsTo(Category::class);
}
public function subcategories() {
} return $this->hasMany(Category::class, 'parent_id');
}
You can now get all parent categories with their subcategories the following way:
$parents = Category::whereParentId(0)->with('subcategories')->get();
This will give you list of all parent categories, each of them will have subcategories property that will store all subcategories. You can traverse them in the following way:
foreach ($parents as $parent) {
printf("Parent category %s\n", $parent->name);
foreach ($parent->subcategories as $subcategory) {
printf("Subcategory %s\n", $subcategory->name);
}
}
Small suggestion: make your parent_id nullable and store NULL for parent categories instead of 0, as 0 is not a correct category ID.
Related
I'm trying to get only the category names, of all articles on the index() function on Laravel. So all articles and the related categories of each article. It's a many-to-many relationship.
What i did so far:
foreach($articles as $article) {
if (isset($article)) {
$article['categories'] = $article->category;
}
}
This gets me all articles with the related categories as a collection, it works, but not quite what I want. As I said, I want only the category names.
I tried $article->category->name; obviously did not work
also: $article->category->get('name');
furthermore what is interesting, If I rename $article['categories'] to $article['cat'] I get 2 collections the category with all related category objects and another exact same on just names cat.
I also tried:
...
$article['categories'] = $this->getcat($articles);
return response()->json($articles, 200);
}
public function getcat($query) {
if (isset($query) && is_countable($query)) {
foreach ($query as &$entity) {
if (!isset($entity)) { continue; }
if (isset($entity->categories)) {
$originObjects = $entity->categories;
$competition_all = [];
foreach($originObjects as $object) {
array_push($competition_all, $object->name);
}
return $competition_all;
}
}
}
The function returns what I need, but it does not return it as json, on the frontend.
From what i see.. your response is return response()->json($names, 200); , but your categories names array is saved into the $article['categories'] array. So.. if you return the response()->json($article, 200);, your categories names should be returned as json. The reason that $article->category->name; does not works is because this is array with objects because the type of your relation, but here you call only property.
The better way is to add to the query, the with function and pass the relation as first parameter, and then in second to add anonym function with the query and select only the names.
Eg.
$articles = Article::with('category', function($q) { ... })->get();
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'));
}
I am working with Laravel data querying and I need a query that is going to group all the children of a parent when I take the categories.
the categories table has a name and a parent_id, the routes of the categories have the parent_id set as null, the query should return every category grouped by parent id and the parent should be the first node of every group.
If you only want to display the categories as parent child somewhere, you do not need to collect them like that, you can make a relationship within the model like
class Category {
public function children()
{
return $this->hasMany(self::class, 'parent_id');
}
public function parent()
{
return $this->hasMany(self::class, 'id', 'parent_id');
}
}
may be it will be one-to-many relationship instead of many-to-many depending on your requirement.
Now you can just get all the parents like
Category::whereNull('parent_id')->get();
or using a scope
Category::parent()->get(); and define the scope in the model
and loop through the parent category like
#foreach ( $categories as $category )
{{ $category->name }}
#foreach ( $category->children as $subCategory )
{{ $subCategory->name }}
#endforeach
#endofreach
and to retrieve parent with children you can use
Category::whereNull('parent_id')->with('children')->get();
or
Category::parent()->with('children')->get();
I have not tested the code, but roughly it will be like this.
contoller
$locations = OurLocation::groupBy('country_id')->with('children')->get();
model
public function children()
{
return $this->hasMany(OurLocation::class, 'country_id','country_id');
}
blade
#foreach($locations as $index=>$location)
#foreach($location->children as $children)
{{ $children->company_name }} <br>
#endforeach
#endforeach
When you get the returned collection from the query, you are able to use the ->groupBy() method where you can specify the field which the results should be grouped by.
Assuming your categories model is Category:
$categories = Category::all()->groupBy('parent_id')->toArray();
I have 3 model:
Company, Category, Product.
HasMany relationship between Company-Category and Product-Category
How can I get all Product from company?
My code:
$company = Company::findOrFail($id);
if (isset($company->categories)){
$categories = $company->categories;
if (isset($categories->first()->products)){
$products = $categories->first()->products;
}
}
However my code returns only first category's products.
There are couple of things wrong here.
First, you need to eager Load your relationships to avoid the N+1 query problem.
Second, if you want to access all the products belonging to all the categories, you cannot call first() on the categories relationship. Instead, you need to loop over it.
The following should work.
$company = Company::with('categories.products')
->findOrFail($id);
if ($company)) {
foreach ($company->categories as $category) {
foreach ($category->products as $product) {
// Access your Product model here
}
}
}
Maybe you can do this
$company = Company::findOrFail($id);
$categories_ids = $company->category->pluck('id');
$products = Product::whereIn('category_id',$categories_ids')->get();
You maybe can use hasThrouthMany
class Category extends Model
{
public function posts()
{
return $this->hasManyThrough(
'App\Product',
'App\Company',
'category_id', // Foreign key on company table...
'company_id', // Foreign key on product table...
'id', // Local key on category table...
'id' // Local key on company table...
);
}
}
You can read more info in https://laravel.com/docs/5.7/eloquent-relationships#has-many-through
please try this and let me know how it works :)
Let say my category level is like:
Parent
- child one
- child two
- child two (one)
Now I am visiting Parent page. but i want to get all products from all levels and not only parent.
Question
How can I have access to products in parent child one child
two & child two(one)?
How to show them by pagination in parent page?
Codes
this is what i have currently and it only shows products under parent
public function totalcategoriessubs($catslug) {
$categories = Category::where('slug','=',$catslug)->with('childs')->paginate(12);
$products = Product::whereHas('category', function($q) use($catslug){
$q->where('slug',$catslug);
})->paginate(12);
return view('front.categoriessubs', compact('products', 'categories'));
}
UPDATE
I have changed my function and added $category to it so now is like:
public function totalcategoriessubs($catslug) {
//changed
$category = Category::where('slug','=',$catslug)->with('childs')->first();
//testing this
$products = Product::whereHas('category', function($q) use ($catslug,$category)
{
$q->where(function($q) use ($catslug,$category) {
$q->where('slug',$catslug)->orWhere('category_id',$category->id);
});
})->orderBy('created_at', 'DESC')->paginate(10);
//end testing
return view('front.categoriessubs', compact('products', 'category'));
}
with this i can get product list of
Parent
- child one
- child two
but still can't get products of child two (one)
any idea?
Category::with(['childsOne', 'childsTwo' => function($query){
$query->with('ChildsTwoChilds');
])->where('slug', $catSlug)->paginate(12)