Using Parent-Child Recursive Relationships in Laravel
Recursive relationships, especially in the context of data like category hierarchies, are quite common in many applications. Laravel, a popular PHP framework, offers robust support for managing these kinds of relationships. This article will explore how to utilize a parent-child recursive relationship in Laravel using the example of category data.

What is a Parent-Child Recursive Relationship?
In databases, a parent-child recursive relationship occurs when a record in a table is related to another record in the same table. In the context of categories, this means a category can have a parent that’s also a category. This creates a hierarchical structure, making it possible to have categories, sub-categories, sub-sub-categories, and so on.
Setting Up The Database
To demonstrate, we’ll create a `categories` table:
Schema::create('categories', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('parent_id')->nullable();
$table->string('name');
$table->timestamps();
$table->foreign('parent_id')
->references('id')
->on('categories')
->onDelete('cascade');
});Here, the parent_id column refers to the parent category. If it’s null, the category is a top-level category.
Defining The Eloquent Relationship
Next, we’ll define the Eloquent relationships in our Category model:
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Category extends Model
{
use HasFactory;
protected $fillable = [
'name',
'parent_id'
];
public function parent()
{
return $this->belongsTo(Category::class, 'parent_id');
}
public function children()
{
return $this->hasMany(Category::class, 'parent_id');
}
public function descendants()
{
return $this->children()->with('descendants');
}
}- parent() relationship gives the immediate parent category.
- children() provides the direct subcategories.
- descendants() gives all the child categories recursively.
Retrieving Data
To retrieve the immediate children:
$category = Category::find(1);
$children = $category->children;
To retrieve all descendants recursively:
$descendants = $category->descendants;
Using The Relationship in a Real-World Scenario
Imagine you are building an online store and you need to categorise products. The categories can be something like:
- Electronics - Mobile Phones - Smartphones - Feature Phones - Laptops - Gaming - Business - Clothing - Men - Women
Each category can have products. Using the parent-child recursive relationship, you can efficiently navigate and manage these product categories.
Testing
Testing is a critical aspect of the development process, especially in a framework like Laravel that encourages a test-driven development approach.
Feature Test: Category Management
Feature tests in Laravel are meant to test a chunk of your application to ensure it works as expected. Here, we’ll test if we can create, read, and relate categories:
1. Set up the test environment:
First, ensure you’ve set up your Laravel testing environment properly. You should be able to use the `RefreshDatabase` trait, which will allow your tests to interact with a fresh database every time.
2. Create a factory:
<?php
namespace Database\Factories;
use App\Models\Category;
use Illuminate\Database\Eloquent\Factories\Factory;
class CategoryFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'name' => fake()->name,
'parent_id' => null
];
}
}3. Create a test:
Run: php artisan make:test CategoryTest
4. Implement the test:
In tests/Feature/CategoryTest.php:
namespace Tests\Feature;
use Tests\TestCase;
use App\Models\Category;
use Illuminate\Foundation\Testing\RefreshDatabase;
class CategoryTest extends TestCase
{
use RefreshDatabase;
public function test_a_category_can_be_created()
{
$category = Category::factory()->create([
'name' => 'Electronics'
]);
$this->assertDatabaseHas('categories', ['name' => 'Electronics']);
}
public function test_a_category_can_have_a_parent_category()
{
$parent = Category::factory()->create([
'name' => 'Electronics'
]);
$child = Category::factory()->create([
'name' => 'Mobile Phones',
'parent_id' => $parent->id
]);
$this->assertEquals($parent->id, $child->parent->id);
}
}Unit Test: Eloquent Relationships
Unit tests in Laravel generally test the smallest parts of your application in isolation (e.g., methods). Here, we will test the parent-child relationship.
1. Create the test:
Run: php artisan make:test CategoryRelationshipTest --unit
2. Implement the test:
In tests/Unit/CategoryRelationshipTest.php:
namespace Tests\Unit;
use Tests\TestCase;
use App\Models\Category;
use Illuminate\Foundation\Testing\RefreshDatabase;
class CategoryRelationshipTest extends TestCase
{
use RefreshDatabase;
public function test_a_category_has_children()
{
$category = Category::factory()->create([
'name' => 'Electronics'
]);
$child1 = Category::factory()->create([
'name' => 'Mobile Phones',
'parent_id' => $category->id
]);
$child2 = Category::factory()->create([
'name' => 'Laptops',
'parent_id' => $category->id
]);
$this->assertCount(2, $category->children);
}
public function test_a_category_has_a_parent()
{
$parent = Category::factory()->create([
'name' => 'Electronics'
]);
$child = Category::factory()->create([
'name' => 'Mobile Phones',
'parent_id' => $parent->id
]);
$this->assertEquals($parent->id, $child->parent->id);
}
}Finally, to run your tests: php artisan test
You should see the following (note I am using sail so my commend to run the tests is sail artisan test ).

These tests cover basic CRUD operations and relationships. In a real-world scenario, you’d also want to test other parts of your application, such as validation, authorisation, and error handling.
Conclusion
Laravel’s Eloquent ORM provides an elegant way of managing parent-child recursive relationships. While our example focused on categories, the concept can be applied to any scenario that requires a hierarchical data structure, like organisational charts or file systems. With a strong understanding of this relationship, developers can efficiently structure and navigate hierarchical data in Laravel applications.
Resources
Feel free to check out my code here: https://github.com/shaunthornburgh/parent-child-recursive-demo





