Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

motinska94's avatar

Get all subcategories' id as array

I know there's a ton of posts on subcategories subject here but I've checked most of them and none seem to answer my question.

I have "categories" table with records are either a root category or related to a root category by parent_id like this;

ID  |	NAME          |	PARENT_ID
------------------------------------
1	|	root          |	null
2	|	child 1       |	1
3	|	child 1.1     |	2
4	|	child 1.1.1   |	3

What I want to achieve;

subCategoriesArray(1); 		// [2, 3, 4]

Reason : I'm making a category edit page and I want users to be able to change a category's parent. In order to prevent a category from being a child of its own child, I need to eliminate its subcategories in the category selection box.

My code so far :

function subCategoriesArray($id){
    $data=[];
    $children = Category::whereParentId($id)->get();
    foreach ($children as $child){
        $data[] = $child->id;
        $data = array_merge($data, subCategoriesList($child->id));
    }
    return $data;
}

it only returns category's direct children for some reason.

Category Model :

class Category extends Model
{
    use HasFactory;
    use SoftDeletes;

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

    public function parent(){
        return $this->hasOne(Category::class, 'id', 'parent_id');
    }

}
0 likes
7 replies
kokoshneta's avatar

They already did – that is a recursive method in the question.

1 like
kokoshneta's avatar
Level 27

Honestly, I’m not sure why your function doesn’t work. It seems to me like it ought to.

Playing around with a test table and a slightly differently laid-out function, the version pasted below seems to work for me, even though it does pretty much exactly the same thing as yours. The only fundamental differences are that I made it a method on the model rather than a standalone helper function, and that I used a collection instead of a plain array for easier sorting (it works with a plain array as well, using array_merge like you did).

// Model
class Category extends Model {
	use HasFactory;
	use SoftDeletes;

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

	public function parent() {
		return $this->hasOne(self::class, 'id', 'parent_id');
	}

	public function getNestedChildren() {
		$data = \collect();
		$children = self::whereParentId($this->id)->get();
		
		foreach ($children as $child) {
			$data->push($child->id);
			$data = $data->merge($child->getNestedChildren());
		}
	
		return $data;
	}
}
// Controller
$category = Category::find(1);
$nestedChildren = $category->getNestedChildren();
2 likes
kokoshneta's avatar

@motinska94 Then you’re doing something else wrong somewhere, because that exact code worked for me and got all levels of children.

Did you also take into account that I removed the with() call on the children() relationship? I don’t really see why always eager-loading children should break it, but who knows…

1 like
motinska94's avatar

@kokoshneta I have no idea why it didn't work before, but I just did a migrate:fresh and recreate the category tree, it works perfectly now. Thank you so much!

Please or to participate in this conversation.