Aggrid Php Example Updated May 2026

Create api/get-rows.php. This is the core updated AG Grid PHP example.

Key updates from old tutorials:

<?php
// api/get-rows.php
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, OPTIONS');
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') exit(0);

require_once '../config/database.php';

$input = json_decode(file_get_contents('php://input'), true);

$startRow = (int)($input['startRow'] ?? 0); $endRow = (int)($input['endRow'] ?? 20); $limit = $endRow - $startRow; $offset = $startRow;

$sortModel = $input['sortModel'] ?? []; $filterModel = $input['filterModel'] ?? [];

$pdo = getConnection();

// ---------- Build WHERE clause dynamically ---------- $whereClause = ""; $params = [];

foreach ($filterModel as $colId => $filter) $value === '') continue; if ($filterType === 'text') $type = $filter['type'] ?? 'contains'; switch ($type) case 'contains': $whereClause .= " AND `$colId` LIKE :$colId_cont"; $params[":$colId_cont"] = "%$value%"; break; case 'equals': $whereClause .= " AND `$colId` = :$colId_eq"; $params[":$colId_eq"] = $value; break; case 'startsWith': $whereClause .= " AND `$colId` LIKE :$colId_start"; $params[":$colId_start"] = "$value%"; break; case 'endsWith': $whereClause .= " AND `$colId` LIKE :$colId_end"; $params[":$colId_end"] = "%$value"; break; elseif ($filterType === 'number') $type = $filter['type'] ?? 'equals'; switch ($type) case 'equals': $whereClause .= " AND `$colId` = :$colId_eq"; $params[":$colId_eq"] = $value; break; case 'greaterThan': $whereClause .= " AND `$colId` > :$colId_gt"; $params[":$colId_gt"] = $value; break; case 'lessThan': $whereClause .= " AND `$colId` < :$colId_lt"; $params[":$colId_lt"] = $value; break; elseif ($filterType === 'date') $date = date('Y-m-d H:i:s', strtotime($value)); $whereClause .= " AND DATE(`$colId`) = :$colId_date"; $params[":$colId_date"] = $date;

// ---------- Build ORDER BY clause ---------- $orderClause = ""; if (!empty($sortModel)) $sorts = []; foreach ($sortModel as $sort) $col = $sort['colId']; $dir = strtoupper($sort['sort']) === 'ASC' ? 'ASC' : 'DESC'; $sorts[] = "$col $dir"; $orderClause = " ORDER BY " . implode(", ", $sorts);

// ---------- Get total row count (for lastRow) ---------- $countSql = "SELECT COUNT(*) FROM products WHERE 1=1 $whereClause"; $countStmt = $pdo->prepare($countSql); foreach ($params as $key => $val) $countStmt->bindValue($key, $val); $countStmt->execute(); $totalRows = (int)$countStmt->fetchColumn();

// ---------- Get paginated data ---------- $sql = "SELECT id, product_name, category, price, stock_quantity, last_updated FROM products WHERE 1=1 $whereClause $orderClause LIMIT :limit OFFSET :offset";

$stmt = $pdo->prepare($sql);

// Bind filter params foreach ($params as $key => $val) $stmt->bindValue($key, $val); $stmt->bindValue(':limit', $limit, PDO::PARAM_INT); $stmt->bindValue(':offset', $offset, PDO::PARAM_INT); $stmt->execute(); aggrid php example updated

$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

// Return AG Grid expected structure echo json_encode([ 'rows' => $rows, 'lastRow' => $totalRows ]);

database.php (config file):

<?php
// config/database.php
function getConnection() 
    $host = 'localhost';
    $db = 'your_database';
    $user = 'your_user';
    $pass = 'your_password';
    $charset = 'utf8mb4';
$dsn = "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    PDO::ATTR_EMULATE_PREPARES => false,
];
return new PDO($dsn, $user, $pass, $options);


Create an index.html file using AG Grid v31+ with the server-side row model.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>AG Grid PHP Example – Updated Server-Side</title>
    <script src="https://cdn.jsdelivr.net/npm/ag-grid-community@31.3.2/dist/ag-grid-community.min.js"></script>
    <style>
        html, body  height: 100%; margin: 0; 
        .ag-theme-alpine  height: 90vh; width: 100%; 
    </style>
</head>
<body>
    <div id="myGrid" class="ag-theme-alpine"></div>
<script>
    // Define columns
    const columnDefs = [
         field: "id", sortable: true, filter: "agNumberColumnFilter" ,
         field: "product_name", headerName: "Product Name", sortable: true, filter: "agTextColumnFilter" ,
         field: "category", sortable: true, filter: "agSetColumnFilter" ,
         field: "price", sortable: true, filter: "agNumberColumnFilter" ,
         field: "stock_quantity", headerName: "Stock", sortable: true ,
         field: "last_updated", headerName: "Last Updated", sortable: true, filter: "agDateColumnFilter" 
    ];
// Server-side datasource
    const dataSource = 
        getRows: async (params) => 
            const request = 
                startRow: params.request.startRow,
                endRow: params.request.endRow,
                sortModel: params.request.sortModel,
                filterModel: params.request.filterModel
            ;
try 
                const response = await fetch('http://localhost/aggregid-php/api/get-rows.php', 
                    method: 'POST',
                    headers:  'Content-Type': 'application/json' ,
                    body: JSON.stringify(request)
                );
                const result = await response.json();
                params.successCallback(result.rows, result.lastRow);
             catch (error) 
                console.error('AG Grid fetch failed:', error);
                params.failCallback();
;
const gridOptions = 
        columnDefs: columnDefs,
        rowModelType: 'serverSide',
        serverSideStoreType: 'partial',
        cacheBlockSize: 100,
        pagination: true,
        paginationPageSize: 100,
        animateRows: true
    ;
const gridDiv = document.querySelector('#myGrid');
    const gridApi = agGrid.createGrid(gridDiv, gridOptions);
    gridApi.setGridOption('serverSideDatasource', dataSource);
</script>

</body> </html>


  • New: Use the fetch() API to pull JSON from api.php. This decouples the frontend from the backend, allowing for AJAX refreshes.
  • AG Grid’s server-side row model requires specific request parameters. We’ll build a RESTful endpoint that handles:

    File: db.php (PDO connection)

    <?php
    header('Content-Type: application/json');
    header('Access-Control-Allow-Origin: *');
    header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE');
    header('Access-Control-Allow-Headers: Content-Type');
    

    $host = 'localhost'; $dbname = 'aggrid_demo'; $user = 'root'; $pass = '';

    try $pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8mb4", $user, $pass); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); catch(PDOException $e) die(json_encode(['error' => 'Database connection failed'])); ?>

    File: server.php (main API logic)

    <?php
    require_once 'db.php';
    

    $request_method = $_SERVER['REQUEST_METHOD']; $input = json_decode(file_get_contents('php://input'), true);

    // Handle GET request for grid data if ($request_method === 'GET' && isset($_GET['action']) && $_GET['action'] === 'getRows') // Extract AG Grid request parameters $startRow = isset($_GET['startRow']) ? (int)$_GET['startRow'] : 0; $endRow = isset($_GET['endRow']) ? (int)$_GET['endRow'] : 100; $limit = $endRow - $startRow;

    $sortModel = isset($_GET['sortModel']) ? json_decode($_GET['sortModel'], true) : [];
    $filterModel = isset($_GET['filterModel']) ? json_decode($_GET['filterModel'], true) : [];
    // Base query
    $sql = "SELECT * FROM products";
    $countSql = "SELECT COUNT(*) as total FROM products";
    $params = [];
    // Build WHERE clause from filterModel
    $whereClauses = [];
    if (!empty($filterModel)) 
        foreach ($filterModel as $field => $filter) 
            if ($filter['filterType'] === 'text') 
                $whereClauses[] = "$field LIKE :$field";
                $params[":$field"] = '%' . $filter['filter'] . '%';
             elseif ($filter['filterType'] === 'number') 
                if (isset($filter['filter'])) 
                    $whereClauses[] = "$field = :$field_eq";
                    $params[":$field_eq"] = $filter['filter'];
    if (isset($filter['filterTo'])) 
                    $whereClauses[] = "$field <= :$field_to";
                    $params[":$field_to"] = $filter['filterTo'];
    if (!empty($whereClauses)) 
        $whereStr = " WHERE " . implode(" AND ", $whereClauses);
        $sql .= $whereStr;
        $countSql .= $whereStr;
    // Build ORDER BY from sortModel
    if (!empty($sortModel)) 
        $orderBy = [];
        foreach ($sortModel as $sort) 
            $orderBy[] = "$sort['colId'] $sort['sort']";
    $sql .= " ORDER BY " . implode(", ", $orderBy);
    // Add LIMIT for pagination
    $sql .= " LIMIT $startRow, $limit";
    // Get total row count (without pagination)
    $totalStmt = $pdo->prepare($countSql);
    foreach ($params as $key => &$val) 
        $totalStmt->bindParam($key, $val);
    $totalStmt->execute();
    $totalRows = $totalStmt->fetch(PDO::FETCH_ASSOC)['total'];
    // Get paginated data
    $stmt = $pdo->prepare($sql);
    foreach ($params as $key => &$val) 
        $stmt->bindParam($key, $val);
    $stmt->execute();
    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
    // Return AG Grid expected format
    echo json_encode([
        'success' => true,
        'rows' => $rows,
        'lastRow' => $totalRows
    ]);
    exit;
    

    // Handle POST to add a new row if ($request_method === 'POST' && isset($_GET['action']) && $_GET['action'] === 'addRow') $stmt = $pdo->prepare("INSERT INTO products (name, category, price, stock) VALUES (:name, :category, :price, :stock)"); $stmt->execute([ ':name' => $input['name'], ':category' => $input['category'], ':price' => $input['price'], ':stock' => $input['stock'] ]); echo json_encode(['success' => true, 'id' => $pdo->lastInsertId()]); exit;

    // Handle PUT to update a row if ($request_method === 'PUT' && isset($_GET['action']) && $_GET['action'] === 'updateRow') $stmt = $pdo->prepare("UPDATE products SET name=:name, category=:category, price=:price, stock=:stock WHERE id=:id"); $stmt->execute([ ':id' => $input['id'], ':name' => $input['name'], ':category' => $input['category'], ':price' => $input['price'], ':stock' => $input['stock'] ]); echo json_encode(['success' => true]); exit;

    // Handle DELETE if ($request_method === 'DELETE' && isset($_GET['action']) && $_GET['action'] === 'deleteRow') $id = $_GET['id']; $stmt = $pdo->prepare("DELETE FROM products WHERE id = ?"); $stmt->execute([$id]); echo json_encode(['success' => true]); exit; ?>


    This updated AG Grid PHP example provides a fully functional, enterprise-ready data grid with server-side sorting, filtering, pagination, and CRUD operations. The backend uses modern PHP (8.1+) with PDO, and the frontend leverages AG Grid v31’s server-side row model for optimal performance even with thousands of rows.

    Next steps: Integrate AG Grid Enterprise features like Excel export, charting, or master/detail views, and enhance PHP with input validation, logging, and rate limiting for production deployment.


    File structure recap:

    aggrid-php-example/
    ├── index.html
    ├── server.php
    ├── db.php
    └── (optional) .env for credentials
    

    Run with php -S localhost:8000 and open http://localhost:8000. Your AG Grid will communicate seamlessly with the PHP backend, handling all dynamic data operations in real time.

    Integrating AG Grid with a PHP backend allows you to handle massive datasets with high-performance features like filtering, sorting, and pagination. Because AG Grid is a client-side library, the "PHP connection" is actually an API bridge where PHP serves JSON data to the grid. Building a Modern AG Grid & PHP Integration 🛠️ The Stack Frontend: AG Grid (Community or Enterprise) Backend: PHP 8.x (Vanilla or Framework) Database: MySQL / PostgreSQL Communication: Fetch API (JSON) 1. The Frontend (index.html)

    You need to define the grid container and tell AG Grid where to fetch the data.

    Use code with caution. Copied to clipboard 2. The Backend (data.php)

    Your PHP script acts as a data provider. It queries the database and returns a JSON array.

    query("SELECT id, name, email, created_at FROM users"); $results = $stmt->fetchAll(PDO::FETCH_ASSOC); // Send JSON response echo json_encode($results); catch (PDOException $e) http_response_code(500); echo json_encode(['error' => $e->getMessage()]); ?> Use code with caution. Copied to clipboard 🚀 Key Optimization Strategies 🔹 Server-Side Row Model (SSRM)

    For datasets with millions of rows, don't load everything at once.

    Client side: Requests a specific "block" of rows (e.g., rows 100-200). PHP side: Uses LIMIT and OFFSET in the SQL query. Benefit: Keeps the browser memory usage low. 🔹 Security Best Practices

    PDO Prepared Statements: Always use prepared statements to prevent SQL injection.

    CORS: If your frontend and backend are on different domains, configure Access-Control-Allow-Origin headers.

    Sanitization: Use json_encode() to ensure data types are preserved correctly. 🔹 Handling Updates (CRUD) To make the grid editable: Enable editable: true in columnDefs. Use the onCellValueChanged event in AG Grid.

    Send a POST or PUT request to a save.php script with the updated row data. 💡 Why this works in 2026

    Modular JS: Works with Vite, Webpack, or simple

    Create a MySQL database and add a table with some sample data. For this example, we'll use a simple table called "employees" with the following columns:

    | id | name | email | department | | --- | --- | --- | --- | | 1 | John Smith | john.smith@example.com | Sales | | 2 | Jane Doe | jane.doe@example.com | Marketing| | 3 | Bob Brown | bob.brown@example.com | IT |

    In this example, we've created a simple AG Grid table using PHP and MySQL. We've demonstrated how to fetch data from a database and display it in an interactive table. AG Grid offers a wide range of features and customization options, making it a powerful tool for creating dynamic and interactive tables.


    Create api/get-rows.php. This is the core updated AG Grid PHP example.

    Key updates from old tutorials:

    <?php
    // api/get-rows.php
    header('Content-Type: application/json');
    header('Access-Control-Allow-Origin: *');
    header('Access-Control-Allow-Methods: POST, OPTIONS');
    if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') exit(0);
    

    require_once '../config/database.php';

    $input = json_decode(file_get_contents('php://input'), true);

    $startRow = (int)($input['startRow'] ?? 0); $endRow = (int)($input['endRow'] ?? 20); $limit = $endRow - $startRow; $offset = $startRow;

    $sortModel = $input['sortModel'] ?? []; $filterModel = $input['filterModel'] ?? [];

    $pdo = getConnection();

    // ---------- Build WHERE clause dynamically ---------- $whereClause = ""; $params = [];

    foreach ($filterModel as $colId => $filter) $value === '') continue; if ($filterType === 'text') $type = $filter['type'] ?? 'contains'; switch ($type) case 'contains': $whereClause .= " AND `$colId` LIKE :$colId_cont"; $params[":$colId_cont"] = "%$value%"; break; case 'equals': $whereClause .= " AND `$colId` = :$colId_eq"; $params[":$colId_eq"] = $value; break; case 'startsWith': $whereClause .= " AND `$colId` LIKE :$colId_start"; $params[":$colId_start"] = "$value%"; break; case 'endsWith': $whereClause .= " AND `$colId` LIKE :$colId_end"; $params[":$colId_end"] = "%$value"; break; elseif ($filterType === 'number') $type = $filter['type'] ?? 'equals'; switch ($type) case 'equals': $whereClause .= " AND `$colId` = :$colId_eq"; $params[":$colId_eq"] = $value; break; case 'greaterThan': $whereClause .= " AND `$colId` > :$colId_gt"; $params[":$colId_gt"] = $value; break; case 'lessThan': $whereClause .= " AND `$colId` < :$colId_lt"; $params[":$colId_lt"] = $value; break; elseif ($filterType === 'date') $date = date('Y-m-d H:i:s', strtotime($value)); $whereClause .= " AND DATE(`$colId`) = :$colId_date"; $params[":$colId_date"] = $date;

    // ---------- Build ORDER BY clause ---------- $orderClause = ""; if (!empty($sortModel)) $sorts = []; foreach ($sortModel as $sort) $col = $sort['colId']; $dir = strtoupper($sort['sort']) === 'ASC' ? 'ASC' : 'DESC'; $sorts[] = "$col $dir"; $orderClause = " ORDER BY " . implode(", ", $sorts);

    // ---------- Get total row count (for lastRow) ---------- $countSql = "SELECT COUNT(*) FROM products WHERE 1=1 $whereClause"; $countStmt = $pdo->prepare($countSql); foreach ($params as $key => $val) $countStmt->bindValue($key, $val); $countStmt->execute(); $totalRows = (int)$countStmt->fetchColumn();

    // ---------- Get paginated data ---------- $sql = "SELECT id, product_name, category, price, stock_quantity, last_updated FROM products WHERE 1=1 $whereClause $orderClause LIMIT :limit OFFSET :offset";

    $stmt = $pdo->prepare($sql);

    // Bind filter params foreach ($params as $key => $val) $stmt->bindValue($key, $val); $stmt->bindValue(':limit', $limit, PDO::PARAM_INT); $stmt->bindValue(':offset', $offset, PDO::PARAM_INT); $stmt->execute();

    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

    // Return AG Grid expected structure echo json_encode([ 'rows' => $rows, 'lastRow' => $totalRows ]);

    database.php (config file):

    <?php
    // config/database.php
    function getConnection() 
        $host = 'localhost';
        $db = 'your_database';
        $user = 'your_user';
        $pass = 'your_password';
        $charset = 'utf8mb4';
    
    $dsn = "mysql:host=$host;dbname=$db;charset=$charset";
    $options = [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        PDO::ATTR_EMULATE_PREPARES => false,
    ];
    return new PDO($dsn, $user, $pass, $options);
    


    Create an index.html file using AG Grid v31+ with the server-side row model.

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>AG Grid PHP Example – Updated Server-Side</title>
        <script src="https://cdn.jsdelivr.net/npm/ag-grid-community@31.3.2/dist/ag-grid-community.min.js"></script>
        <style>
            html, body  height: 100%; margin: 0; 
            .ag-theme-alpine  height: 90vh; width: 100%; 
        </style>
    </head>
    <body>
        <div id="myGrid" class="ag-theme-alpine"></div>
    
    <script>
        // Define columns
        const columnDefs = [
             field: "id", sortable: true, filter: "agNumberColumnFilter" ,
             field: "product_name", headerName: "Product Name", sortable: true, filter: "agTextColumnFilter" ,
             field: "category", sortable: true, filter: "agSetColumnFilter" ,
             field: "price", sortable: true, filter: "agNumberColumnFilter" ,
             field: "stock_quantity", headerName: "Stock", sortable: true ,
             field: "last_updated", headerName: "Last Updated", sortable: true, filter: "agDateColumnFilter" 
        ];
    // Server-side datasource
        const dataSource = 
            getRows: async (params) => 
                const request = 
                    startRow: params.request.startRow,
                    endRow: params.request.endRow,
                    sortModel: params.request.sortModel,
                    filterModel: params.request.filterModel
                ;
    try 
                    const response = await fetch('http://localhost/aggregid-php/api/get-rows.php', 
                        method: 'POST',
                        headers:  'Content-Type': 'application/json' ,
                        body: JSON.stringify(request)
                    );
                    const result = await response.json();
                    params.successCallback(result.rows, result.lastRow);
                 catch (error) 
                    console.error('AG Grid fetch failed:', error);
                    params.failCallback();
    ;
    const gridOptions = 
            columnDefs: columnDefs,
            rowModelType: 'serverSide',
            serverSideStoreType: 'partial',
            cacheBlockSize: 100,
            pagination: true,
            paginationPageSize: 100,
            animateRows: true
        ;
    const gridDiv = document.querySelector('#myGrid');
        const gridApi = agGrid.createGrid(gridDiv, gridOptions);
        gridApi.setGridOption('serverSideDatasource', dataSource);
    </script>
    

    </body> </html>


  • New: Use the fetch() API to pull JSON from api.php. This decouples the frontend from the backend, allowing for AJAX refreshes.
  • AG Grid’s server-side row model requires specific request parameters. We’ll build a RESTful endpoint that handles:

    File: db.php (PDO connection)

    <?php
    header('Content-Type: application/json');
    header('Access-Control-Allow-Origin: *');
    header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE');
    header('Access-Control-Allow-Headers: Content-Type');
    

    $host = 'localhost'; $dbname = 'aggrid_demo'; $user = 'root'; $pass = '';

    try $pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8mb4", $user, $pass); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); catch(PDOException $e) die(json_encode(['error' => 'Database connection failed'])); ?>

    File: server.php (main API logic)

    <?php
    require_once 'db.php';
    

    $request_method = $_SERVER['REQUEST_METHOD']; $input = json_decode(file_get_contents('php://input'), true);

    // Handle GET request for grid data if ($request_method === 'GET' && isset($_GET['action']) && $_GET['action'] === 'getRows') // Extract AG Grid request parameters $startRow = isset($_GET['startRow']) ? (int)$_GET['startRow'] : 0; $endRow = isset($_GET['endRow']) ? (int)$_GET['endRow'] : 100; $limit = $endRow - $startRow;

    $sortModel = isset($_GET['sortModel']) ? json_decode($_GET['sortModel'], true) : [];
    $filterModel = isset($_GET['filterModel']) ? json_decode($_GET['filterModel'], true) : [];
    // Base query
    $sql = "SELECT * FROM products";
    $countSql = "SELECT COUNT(*) as total FROM products";
    $params = [];
    // Build WHERE clause from filterModel
    $whereClauses = [];
    if (!empty($filterModel)) 
        foreach ($filterModel as $field => $filter) 
            if ($filter['filterType'] === 'text') 
                $whereClauses[] = "$field LIKE :$field";
                $params[":$field"] = '%' . $filter['filter'] . '%';
             elseif ($filter['filterType'] === 'number') 
                if (isset($filter['filter'])) 
                    $whereClauses[] = "$field = :$field_eq";
                    $params[":$field_eq"] = $filter['filter'];
    if (isset($filter['filterTo'])) 
                    $whereClauses[] = "$field <= :$field_to";
                    $params[":$field_to"] = $filter['filterTo'];
    if (!empty($whereClauses)) 
        $whereStr = " WHERE " . implode(" AND ", $whereClauses);
        $sql .= $whereStr;
        $countSql .= $whereStr;
    // Build ORDER BY from sortModel
    if (!empty($sortModel)) 
        $orderBy = [];
        foreach ($sortModel as $sort) 
            $orderBy[] = "$sort['colId'] $sort['sort']";
    $sql .= " ORDER BY " . implode(", ", $orderBy);
    // Add LIMIT for pagination
    $sql .= " LIMIT $startRow, $limit";
    // Get total row count (without pagination)
    $totalStmt = $pdo->prepare($countSql);
    foreach ($params as $key => &$val) 
        $totalStmt->bindParam($key, $val);
    $totalStmt->execute();
    $totalRows = $totalStmt->fetch(PDO::FETCH_ASSOC)['total'];
    // Get paginated data
    $stmt = $pdo->prepare($sql);
    foreach ($params as $key => &$val) 
        $stmt->bindParam($key, $val);
    $stmt->execute();
    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
    // Return AG Grid expected format
    echo json_encode([
        'success' => true,
        'rows' => $rows,
        'lastRow' => $totalRows
    ]);
    exit;
    

    // Handle POST to add a new row if ($request_method === 'POST' && isset($_GET['action']) && $_GET['action'] === 'addRow') $stmt = $pdo->prepare("INSERT INTO products (name, category, price, stock) VALUES (:name, :category, :price, :stock)"); $stmt->execute([ ':name' => $input['name'], ':category' => $input['category'], ':price' => $input['price'], ':stock' => $input['stock'] ]); echo json_encode(['success' => true, 'id' => $pdo->lastInsertId()]); exit;

    // Handle PUT to update a row if ($request_method === 'PUT' && isset($_GET['action']) && $_GET['action'] === 'updateRow') $stmt = $pdo->prepare("UPDATE products SET name=:name, category=:category, price=:price, stock=:stock WHERE id=:id"); $stmt->execute([ ':id' => $input['id'], ':name' => $input['name'], ':category' => $input['category'], ':price' => $input['price'], ':stock' => $input['stock'] ]); echo json_encode(['success' => true]); exit;

    // Handle DELETE if ($request_method === 'DELETE' && isset($_GET['action']) && $_GET['action'] === 'deleteRow') $id = $_GET['id']; $stmt = $pdo->prepare("DELETE FROM products WHERE id = ?"); $stmt->execute([$id]); echo json_encode(['success' => true]); exit; ?>


    This updated AG Grid PHP example provides a fully functional, enterprise-ready data grid with server-side sorting, filtering, pagination, and CRUD operations. The backend uses modern PHP (8.1+) with PDO, and the frontend leverages AG Grid v31’s server-side row model for optimal performance even with thousands of rows.

    Next steps: Integrate AG Grid Enterprise features like Excel export, charting, or master/detail views, and enhance PHP with input validation, logging, and rate limiting for production deployment.


    File structure recap:

    aggrid-php-example/
    ├── index.html
    ├── server.php
    ├── db.php
    └── (optional) .env for credentials
    

    Run with php -S localhost:8000 and open http://localhost:8000. Your AG Grid will communicate seamlessly with the PHP backend, handling all dynamic data operations in real time.

    Integrating AG Grid with a PHP backend allows you to handle massive datasets with high-performance features like filtering, sorting, and pagination. Because AG Grid is a client-side library, the "PHP connection" is actually an API bridge where PHP serves JSON data to the grid. Building a Modern AG Grid & PHP Integration 🛠️ The Stack Frontend: AG Grid (Community or Enterprise) Backend: PHP 8.x (Vanilla or Framework) Database: MySQL / PostgreSQL Communication: Fetch API (JSON) 1. The Frontend (index.html)

    You need to define the grid container and tell AG Grid where to fetch the data.

    Use code with caution. Copied to clipboard 2. The Backend (data.php)

    Your PHP script acts as a data provider. It queries the database and returns a JSON array.

    query("SELECT id, name, email, created_at FROM users"); $results = $stmt->fetchAll(PDO::FETCH_ASSOC); // Send JSON response echo json_encode($results); catch (PDOException $e) http_response_code(500); echo json_encode(['error' => $e->getMessage()]); ?> Use code with caution. Copied to clipboard 🚀 Key Optimization Strategies 🔹 Server-Side Row Model (SSRM)

    For datasets with millions of rows, don't load everything at once.

    Client side: Requests a specific "block" of rows (e.g., rows 100-200). PHP side: Uses LIMIT and OFFSET in the SQL query. Benefit: Keeps the browser memory usage low. 🔹 Security Best Practices

    PDO Prepared Statements: Always use prepared statements to prevent SQL injection.

    CORS: If your frontend and backend are on different domains, configure Access-Control-Allow-Origin headers.

    Sanitization: Use json_encode() to ensure data types are preserved correctly. 🔹 Handling Updates (CRUD) To make the grid editable: Enable editable: true in columnDefs. Use the onCellValueChanged event in AG Grid.

    Send a POST or PUT request to a save.php script with the updated row data. 💡 Why this works in 2026

    Modular JS: Works with Vite, Webpack, or simple

    Create a MySQL database and add a table with some sample data. For this example, we'll use a simple table called "employees" with the following columns:

    | id | name | email | department | | --- | --- | --- | --- | | 1 | John Smith | john.smith@example.com | Sales | | 2 | Jane Doe | jane.doe@example.com | Marketing| | 3 | Bob Brown | bob.brown@example.com | IT |

    In this example, we've created a simple AG Grid table using PHP and MySQL. We've demonstrated how to fetch data from a database and display it in an interactive table. AG Grid offers a wide range of features and customization options, making it a powerful tool for creating dynamic and interactive tables.