dav
dav

Reputation: 31

(Laravel Eloquent) Get posts of category and all nested subcategories

I would like to retrieve all posts of chosen category and all nested subcategories. I mean sth like this:

[id= 1]-Category 1
[id= 7]--Category 1.1
[id=12]---Category 1.1.1
[id=13]---Category 1.1.2
[id= 9]--Category 1.2
[id= 3]-Category 2

At first I tried to get recursively all IDs of chosen category and all nested subcategories and then retrieve posts with those categories IDs.

I wrote method getSubcategoriesIds($category) in controller which actually give me what I want but it returns array with duplicated ids. For instans, category 1 I get:

array:10 [
    0 => 1
    1 => 7
    2 => 1
    3 => 7
    4 => 12
    5 => 1
    6 => 7
    7 => 12
    8 => 13
    9 => 9
]

How can I improve the method to return IDs without duplicates? Or maybe there is better way to revive those posts?

Here are my files:

Model Category.php:

class Category extends Model {

    public function parent() {
        return $this->belongsTo('App\Category', 'parent_id');
    }

    public function children() {
        return $this->hasMany('App\Category', 'parent_id');
    }

    public function subcategories() {
        return $this->children()->with('subcategories');
    }

    public function parents() {
        return $this->parent()->with('parents');
    }

    public function posts(){
        return $this->hasMany('App\Post');
    }
}

PostController.php:

public function show($id)
{
    $ids =$this->getSubcategoriesIds(Category::with('subcategories')->where('id', $id)->first()));
    $posts = Post::whereIn('category_id', $ids)->get();
    return view('post.index', compact('posts'));
}

public function getSubcategoriesIds($category) {
    $array = [$category->id];
    if(count($category->subcategories) == 0) {
        return $array;
    } 
    else  {
        foreach ($category->subcategories as $subcategory) {
            array_push($array, $subcategory->id);
            if(count($subcategory->subcategories)){
                $array = array_merge($array, $this->getChildren($subcategory->subcategories, $array));
            }
        }
        return $array;
    }
}

public function getChildren($subcategories, $array) {
    foreach ($subcategories as $subcategory)  {
        array_push($array, $subcategory->id);
        if(count($subcategory->subcategories)){
            return $array = array_merge($array, $this->getChildren($subcategory->subcategories, $array));
        }
        return $array;
    }
}

Upvotes: 2

Views: 2532

Answers (3)

I have infinite category db like "id", "name", "parent_id". I don't have any product which is relation with my parent categories so i have to get deep categories.

I write a relation and a recursive function:

public function children(){
    return $this->hasMany(self::class, 'parent_id');
}

public function getChildrenIds(&$arr){
    $children = $this->children;
    foreach ($children as $child) {
        if (count($child->children) > 0)
            $child->getChildrenIds($arr);
        else
            array_push($arr, $child->id);
    }
}

and then in controller i can get my sub category products like:

$category = Categories::where('slug', $slug)->first();
$category_sub_cat_ids = [];
$category->getChildrenIds($category_sub_cat_ids);
$products = Products::whereIn('category_id', $category_sub_cat_ids)->paginate(30);

(I know the model names are inappropriate. this project's owner has a junior "developer" and they asked me to help.)

Upvotes: 0

dav
dav

Reputation: 31

I found problem in my recursive method. I shouldn't have passed $array to the recursive method getChildrenIds().

Here is my solution (with some refactoring):

public function getCategoriesIds($category)
{
    if(!empty($category))
    {
        $array = array($category->id);
        if(count($category->subcategories) == 0) return $array;
        else return array_merge($array, $this->getChildrenIds($category->subcategories));
    } 
    else return null;
}

public function getChildrenIds($subcategories)
{
    $array = array();
    foreach ($subcategories as $subcategory)
    {
        array_push($array, $subcategory->id);
        if(count($subcategory->subcategories))
            $array = array_merge($array, $this->getChildrenIds($subcategory->subcategories));
    }
    return $array;
}

and now $this->getCategoriesIds(Category::with('subcategories')->where('id', 1)->first()); returns:

array:10 [
0 => 1
1 => 7
2 => 12
3 => 13
4 => 9
]

Upvotes: 1

Digvijay
Digvijay

Reputation: 8927

Your question: How can I improve the method to return IDs without duplicates? Or maybe there is better way to revive those posts?

$subcategoriesIds = array:10 [
    0 => 1
    1 => 7
    2 => 1
    3 => 7
    4 => 12
    5 => 1
    6 => 7
    7 => 12
    8 => 13
    9 => 9
]

My answer: collect($subcategoriesIds)->unique()->all()

Check out Laravel Collection Unique Docs

Upvotes: 0

Related Questions