ArtiGrid
v2.0

Menu Action tree

This demo shows how to build a hierarchical menu using ArtiGrid's Tree View. Items can be organized into parent and child relationships, reordered using drag and drop, and managed with custom edit and delete actions. The tree data and ordering are handled through separate PHP endpoints.


<?php
$grid = new ArtiGrid();
$grid->table('nav_items')
    ->formFields([
        'parent_id',
        'slug',
        'title',
        'icon',
        'active',
        'order'
    ])
    ->required(false)
    ->combobox('active', [
        '1' => 'Active',
        '2' => 'Disabled'
    ])
    ->fieldAttr('icon', [
        'data-iconpicker' => '1',
        'class' => 'icon-picker-input'
    ])
    ->validation_required([
        'slug',
        'title',
        'icon',
        'active'
    ])
    ->fieldType('order', 'hidden')
    ->colRename('parent_id', 'Padre')
    ->colRename('slug', 'Slug')
    ->combobox('parent_id', 'nav_items', 'id', 'title')
    ->orderby('order', 'asc')
    ->setActionCondition('delete', [
        ['id', '!=', 8],
        ['id', '!=', 12],
        ['id', '!=', 28],
        ['id', '!=', 29],
        ['id', '!=', 1],
        ['id', '!=', 6],
        ['id', '!=', 2],
        ['id', '!=', 3],
    ])
    ->fieldCondition(
        'title',
        'slug',
        'in',
        ['dashboard', 'table-builder', 'builder', 'menu'],
        'hide'
    )
    ->fieldCondition(
        'slug',
        'slug',
        'in',
        ['dashboard', 'table-builder', 'builder', 'menu'],
        'hide'
    )
    ->perPage(5)
    ->columnHide(['id'])
    ->unset('clone', false)
    ->unset('view', false)
    ->treeView([
        'treeUrl' => 'tree.php?action=tree',
        'orderUrl' => 'save_order.php',
        'noDeleteIds' => [8, 12, 28, 29, 1, 6, 2, 3],
        'title' => 'Menú',
    ]);
echo $grid->render('tree');
?>

// tree.php

<?php

require __DIR__ . '/../../artigrid/ArtiGrid.php';
header('Content-Type: application/json');
$action = $_GET['action'] ?? '';

try {

    if ($action === 'tree') {
        $db = DB::connect();
        $rows = $db->query(
            "SELECT
                id,
                parent_id,
                slug,
                title,
                icon,
                `order`,
                active
            FROM nav_items
            ORDER BY ISNULL(parent_id) DESC,
                     parent_id ASC,
                     `order` ASC"
        )->fetchAll(PDO::FETCH_ASSOC);
        if (function_exists('user_can')) {
            $rows = array_values(
                array_filter($rows, function ($row) {

                    $slug = $row['slug'] ?? '';

                    return $slug === ''
                        ? true
                        : user_can($slug);
                })
            );
        }

        foreach ($rows as &$row) {
            $row['actions'] = [
                [
                    'action' => 'edit',
                    'id' => $row['id']
                ],
                [
                    'action' => 'delete',
                    'id' => $row['id']
                ]
            ];
        }

        unset($row);
        echo json_encode($rows);
        exit;
    }

    if ($action === 'save-order') {
        $body = json_decode(
            file_get_contents('php://input'),
            true
        );
        $items = $body['order'] ?? [];
        if (empty($items)) {
            echo json_encode([
                'success' => false,
                'message' => 'No data received'
            ]);
            exit;
        }

        $pdo = DB::connect();
        $stmt = $pdo->prepare(
            "UPDATE nav_items
             SET parent_id = :pid,
                 `order` = :ord
             WHERE id = :id"
        );
        $pdo->beginTransaction();
        foreach ($items as $item) {
            $stmt->execute([
                ':pid' => !empty($item['parent_id'])
                    ? (int) $item['parent_id']
                    : null,
                ':ord' => (int) ($item['order'] ?? 0),
                ':id' => (int) ($item['id'] ?? 0)
            ]);
        }
        $pdo->commit();
        echo json_encode([
            'success' => true
        ]);
        exit;
    }

    http_response_code(400);
    echo json_encode([
        'success' => false,
        'message' => 'Invalid action'
    ]);

} catch (\Throwable $e) {
    if (isset($pdo) && $pdo instanceof PDO && $pdo->inTransaction()) {
        $pdo->rollBack();
    }
    http_response_code(500);
    echo json_encode([
        'success' => false,
        'message' => $e->getMessage()
    ]);
}
?>

// save_order.php

<?php

require __DIR__ . '/../../artigrid/ArtiGrid.php';
header('Content-Type: application/json');
$pdo = null;

try {
    $body = json_decode(
        file_get_contents('php://input'),
        true
    );
    $items = $body['order'] ?? [];
    if (empty($items)) {
        echo json_encode([
            'success' => false,
            'message' => 'No data received'
        ]);
        exit;
    }

    $pdo = DB::connect();
    $stmt = $pdo->prepare(
        "UPDATE nav_items
         SET parent_id = :pid,
             `order` = :ord
         WHERE id = :id"
    );
    $pdo->beginTransaction();
    foreach ($items as $item) {
        $stmt->execute([
            ':pid' => !empty($item['parent_id'])
                ? (int) $item['parent_id']
                : null,
            ':ord' => (int) ($item['order'] ?? 0),
            ':id' => (int) ($item['id'] ?? 0)
        ]);
    }

    $pdo->commit();
    echo json_encode([
        'success' => true,
        'message' => 'Order saved successfully'
    ]);
} catch (\Throwable $e) {
    if ($pdo && $pdo->inTransaction()) {
        $pdo->rollBack();
    }
    http_response_code(500);
    echo json_encode([
        'success' => false,
        'message' => $e->getMessage()
    ]);
}

exit;
?>