Creating a Nested Category API in Laravel 11
We’ll walk through the steps to create a nested category API in Laravel 11. We’ll build a system that allows categories to have parent-child relationships, enabling you to organize your data hierarchically. This is particularly useful for e-commerce platforms, content management systems, and other applications that require nested categories.
Step 1: Set Up Laravel Project
First, let’s create a new Laravel project:
composer create-project --prefer-dist laravel/laravel laravel-nested-categories
cd laravel-nested-categories
php artisan serve
Step 2: Create the Category
Model and Migration
Next, we’ll generate a Category
model along with a migration file:
php artisan make:model Category -m
Now, define the categories
table structure in the migration file:
Schema::create('categories', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->unsignedBigInteger('parent_id')->nullable();
$table->foreign('parent_id')->references('id')->on('categories')->onDelete('cascade');
$table->timestamps();
});
Run the migration to create the table:
php artisan migrate
Step 3: Define Relationships in the Category
Model
In the Category
model, we’ll define the relationships to handle the parent-child hierarchy:
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
use HasFactory;
protected $fillable = ['name', 'parent_id'];
// Relationship to get the parent category
public function parent()
{
return $this->belongsTo(Category::class, 'parent_id');
}
// Relationship to get the child categories
public function children()
{
return $this->hasMany(Category::class, 'parent_id');
}
}
Step 4: Create the CategoryController
Next, create a controller to handle API requests:
php artisan make:controller CategoryController
In the CategoryController
, add methods to handle category listing and creation:
namespace App\Http\Controllers;
use App\Models\Category;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class CategoryController extends Controller
{
public function index()
{
$categories = Category::with('children')
->whereNull('parent_id')
->get();
return response()->json($categories);
}
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'name' => 'required|string',
'children' => 'array',
'children.*.name' => 'required|string',
]);
if ($validator->fails()) {
return response()->json([
'code' => 422,
'status' => 'error',
'message' => 'Validation error',
'errors' => $validator->errors(),
], 422);
}
$category = $this->createCategory($request->all());
return response()->json(['category' => $category], 201);
}
private function createCategory($data, $parentId = null)
{
$category = Category::create([
'name' => $data['name'],
'parent_id' => $parentId,
]);
if (!empty($data['children'])) {
foreach ($data['children'] as $child) {
$this->createCategory($child, $category->id);
}
}
return $category->load('children');
}
}
Step 5: Set Up API Routes
Enable API routing using the install:api:
php artisan install:api
Next, we’ll define the routes in routes/api.php
:
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\CategoryController;
Route::get('/categories', [CategoryController::class, 'index']);
Route::post('/categories', [CategoryController::class, 'store']);
Step 6: Testing the API
You can test the API using curl
or any API testing tool like Postman.
Insert Nested Categories:
curl -X POST http://localhost:8000/api/categories \
-H "Content-Type: application/json" \
-d '{
"name": "Electronics",
"children": [
{
"name": "Mobile Phones",
"children": [
{
"name": "Smartphones"
},
{
"name": "Feature Phones"
}
]
},
{
"name": "Laptops"
}
]
}'
Fetch Categories:
curl -X GET http://localhost:8000/api/categories
Conclusion
In this post, we’ve created a nested category API using Laravel 11. We set up the model, migration, controller, and routes to handle nested category creation and retrieval. This API can be extended further to include updating and deleting categories, as well as more complex querying based on your application’s needs.