In this article, we'll explore how to handle rendering relational data in Laravel. Imagine an e-commerce scenario where products are linked to categories. When displaying product details, you'd want to show the category name alongside them in a clean, user-friendly way. Or think of a blog where each post includes its comments right below the content.
This is where Object-Relational Mapping (ORM) comes in. In this guide, I'll walk you through how to implement it seamlessly and elegantly in Laravel, using AnyGridJS.
- Project Setup: Laravel Installation
Before integrating AnyGrid, ensure Laravel is installed. Follow the latest official installation guide here:
π Laravel Installation Guide
Basic Installation (For Laravel 10+):
Ensure you have PHP (β₯ 8.1), Composer, and Node.js installed.
Create a new Laravel project:
composer create-project --prefer-dist laravel/laravel anygrid-laravel
Navigate into the project folder:
cd anygrid-laravel
Start the local development server:
php artisan serve
π¨ Your Laravel app will be accessible at http://127.0.0.1:8000
.
- Install AnyGrid JS (CDN Method)
Weβll use the CDN method to quickly integrate AnyGrid JS into our Laravel Blade views.
In your resources/views/layouts/app.blade.php
or any other Blade layout file you want to use, add the following inside the <head>
tag:
<!-- AnyGrid CSS -->
<link rel="stylesheet" href="https://unpkg.com/anygridcss@1.0.1/anyGrid.css" anygrid-style>
<!-- AnyGrid JS -->
<script src="https://cdn.jsdelivr.net/npm/anygridjs@1.0.9/anyGrid.global.min.js"></script>
π‘ Ensure you are using the latest of these packages.
π‘ 3. Set Up Table Schema and Model Relationships
In this section, we'll create the database structure for an e-commerce scenario with Product and ProductCategory models, demonstrating a One-to-Many relationship.
Create Models with Migrations
Generate ProductCategory and Product Models:
Run the following commands to create models with migrations:
php artisan make:model ProductCategory -m
php artisan make:model Product -m
Update Migrations:
- ProductCategory Migration: (
database/migrations/YYYY_MM_DD_create_product_categories_table.php
)
public function up()
{
Schema::create('product_categories', function (Blueprint $table) {
$table->id();
$table->string('name')->unique();
$table->text('description')->nullable();
$table->timestamps();
});
}
- Product Migration: (
database/migrations/YYYY_MM_DD_create_products_table.php
)
public function up()
{
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->text('description')->nullable();
$table->decimal('price', 10, 2);
$table->foreignId('product_category_id')->constrained()->onDelete('cascade');
$table->timestamps();
});
}
Model Relationships
- ProductCategory Model: (
app/Models/ProductCategory.php
)
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class ProductCategory extends Model
{
protected $fillable = ['name', 'description'];
public function products()
{
return $this->hasMany(Product::class);
}
}
- Product Model: (
app/Models/Product.php
)
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
protected $fillable = ['name', 'description', 'price', 'product_category_id'];
public function category()
{
return $this->belongsTo(ProductCategory::class, 'product_category_id');
}
}
Run Migrations
Apply the database changes:
php artisan migrate
β
This creates two tables: product_categories
and products
.
π‘ Next Step: With the database schema and relationships set up, we'll move on to the controller logic for rendering products.
π’ 4. Controller Logic for Rendering Products
With our models and database ready, we'll now build a controller to retrieve and render products efficiently while avoiding the N+1 query problem using Laravel's eager loading.
The N+1 query problem occurs when a database query retrieves a list of records, and then for each record, it runs additional queries to retrieve related data. This results in one query to fetch the main data (the "1" query) and then one additional query for each related record (the "N" queries). The N+1 query is non performant and inefficient because it leads to a large number of database queries, which increases the load on the database and slows down performance.
Create Product Controller
Generate the controller:
php artisan make:controller ProductController
Open app/Http/Controllers/ProductController.php
and add the following:
namespace App\Http\Controllers;
use App\Models\Product;
use Illuminate\Http\Request;
class ProductController extends Controller
{
// Fetch all products with categories
public function index()
{
// Eager loading to avoid N+1 problem
$products = Product::with('category')->get();
return response()->json($products);
}
}
N+1 Query Problem: Without Eager Loading
If you fetch products without eager loading:
$products = Product::all();
foreach ($products as $product) {
echo $product->category->name;
}
π΄ Generated SQL (Inefficient):
-- Fetch products (1 query)
SELECT FROM products;
-- Separate query for each product's category (N queries)
SELECT FROM product_categories WHERE id = 1;
SELECT FROM product_categories WHERE id = 2;
SELECT FROM product_categories WHERE id = 3;
π¦ Resulting Data (Without Eager Loading):
[
{
"id": 1,
"name": "Smartphone",
"description": "Latest Android smartphone.",
"price": "699.99",
"product_category_id": 1
},
{
"id": 2,
"name": "Laptop",
"description": "High-performance laptop.",
"price": "1299.99",
"product_category_id": 1
}
]
Problem: To access $product->category->name
, Laravel runs a separate query for each product, causing multiple database hits.
Solution: With Eager Loading
Update the controller to use Eager Loading:
$products = Product::with('category')->get();
Generated SQL (Efficient):
-- Single query for products
SELECT FROM products;
-- Single query for all related categories
SELECT FROM product_categories WHERE id IN (1, 2, 3);
π¦ Resulting Data (With Eager Loading):
[
{
"id": 1,
"name": "Smartphone",
"description": "Latest Android smartphone.",
"price": "699.99",
"product_category_id": 1,
"category": {
"id": 1,
"name": "Electronics",
"description": "Gadgets and devices"
}
},
{
"id": 2,
"name": "Laptop",
"description": "High-performance laptop.",
"price": "1299.99",
"product_category_id": 1,
"category": {
"id": 1,
"name": "Electronics",
"description": "Gadgets and devices"
}
}
]
π£ Performance Comparison:
Aspect | Without Eager Loading | With Eager Loading |
---|---|---|
Number of Queries | 1 + N (1 per product) | 2 (Products + Categories) |
Query Efficiency | Slow (multiple hits) | Fast (batched) |
Result Structure | Flat (No relations) | Nested (Relations loaded) |
Memory Usage | Higher | Lower |
Loading multiple relationships:
Product::with(['category', 'supplier'])->get();
π‘ Next Step: Now that products load efficiently, we'll move on to Integrating AnyGridJS to display this data in the frontend.
π’ 5. Creating the Product View with AnyGridJS
Update Controller to Pass Data to View
In ProductController.php
, update the index
method to pass products to the view:
public function index()
{
$products = Product::with('category')->get();
return view('shop.products', compact('products'));
}
Create the Blade View for Displaying Products
Create the resources/views/shop/products.blade.php
file:
@extends('layouts.app')
@section('content')
<h1>Product Listings</h1>
<!-- AnyGrid Container:: The data table will be rendered here -->
<div id="anygrid"></div>
<script type="module">
// Pass Laravel data to JavaScript
const data = @json($products);
// Define columns for AnyGrid
const columns = [
{ name: 'id', header: 'ID', sortable: true },
{ name: 'name', header: 'Product Name', sortable: true },
{ name: 'price', header: 'Price', render: (value) => `R ${value}` },
{ name: 'category_id', header: 'Category', render: (value, row) => row.category ? row.category.name : 'N/A' },
{ name: 'created_at', header: 'Created Date', sortable: true,
{
name: 'actions',
header: 'Actions',
actions: [
{
label: 'EDIT',
url: 'products/edit/{id}',
class: 'edit',
id: 'edit-{id}',
},
{
label: 'DELETE',
url: 'products/delete/{id}',
class: 'delete',
id: 'delete-{id}',
confirm: true,
},
{
label: 'VIEW',
url: 'products/view/{id}',
class: 'view',
id: 'view-{id}',
},
},
]
}
];
// AnyGrid features and settings
const features = {
initialItemsPerPage: 20,
csvExport: true,
excelExport: true,
theme: 'dark'
};
// Initialize AnyGrid
new AnyGrid(data, columns, features);
</script>
@endsection
Update Routes to Load the View
In routes/web.php
:
use App\Http\Controllers\ProductController;
Route::get('/products', [ProductController::class, 'index']);
Final Result
Conclusion:
In this tutorial, we covered the essentials of integrating AnyGridJS with a Laravel application to display relational data dynamically. We walked through:
- Setting up Laravel with the required models and migrations for an ecommerce scenario (products and categories).
- Using eager loading to efficiently fetch related data and avoid the N+1 query problem.
- Integrating AnyGridJS to display data in a user-friendly grid with features like pagination, sorting, and export options.
- Configuring action buttons for editing, deleting, and viewing products directly within the grid.
With these foundational steps, you can now build more interactive and performant data tables for your Laravel applications.
Learn More
To learn more about AnyGridJS, visit our GitHub repository.
You can also visit the AnyGridJS website for further documentation and resources.
Top comments (0)