revaycolizer/data-display

The DataDisplay class is a dynamic PHP data table renderer built with Sweet Alert and Bootstrap. It supports adding, editing, deleting, searching, joining tables, and pagination โ€” all from a fluent interface.

v1.0.0 2025-04-25 20:07 UTC

This package is auto-updated.

Last update: 2025-04-25 20:21:58 UTC


README

๐Ÿ“Š DataDisplay Component

The DataDisplay class is a dynamic PHP data table renderer built with Sweet Alert and Bootstrap. It supports adding, editing, deleting, searching, joining tables, and pagination โ€” all from a fluent interface.

Required Packages For The Magic To Work

  1. Sweet Alert 2
  2. DataTable
  3. BootStrap 5
  4. Doctrine (optional)

โœจ Features

๐Ÿ—ƒ Dynamic table rendering with Bootstrap

๐Ÿ” Searchable columns with support for input/select types

๐Ÿ” CRUD Operations With Permissions

๐Ÿ”„ Editable records with modals

โž• Add new records using modals

โŒ Delete functionality with confirmation

๐Ÿ”— Supports JOINs

๐Ÿ“‘ Pagination support

๐Ÿ” CSRF protection

To get Started Run This on Terminal

composer require revaycolizer/data-display

Instantiate the Component Using Doctrine For Data Fetching

use Revaycolizer\Crud\DataDisplay;

$dataTable = DataDisplay::create($entityManager, User::class);

Instantiate the Component Using Classes For Data fetching

$dataDisplay = DataDisplay::create(null, Category::class,DataSourceType::CLASSES);

Provide The Method Which Will be Used in Fetching Data When Using Classes

$dataDisplay
   ->setClassFetchDataFunction("all")

Provide The Method With It's Parameters Which Will be Used in Fetching Data When Using Classes

$dataDisplay->setClassFetchDataFunction("all:param1,param2")

DataDisplay Modes

Mode 1: Default

By Default the mode is Default There is no Need to Set Mode

 ->setMode("default")

Mode 2: Report

If mode is set to report it will remove the CRUD Buttons(Add,Edit & Delete)

 ->setMode("report")

๐Ÿ”ง Configuration

Set Columns to Add

$dataTable->columnToBeAdded([
    'username' => ['type' => 'input', 'input_type' => 'text', 'label' => 'Username'],
    'role' => ['type' => 'select', 'label' => 'Role', 'options' => $roles, 'value_field' => 'id', 'label_field' => 'name']
]);

Set Columns to Edit

$dataTable->columnsToBeEdited([
    'username' => ['type' => 'input', 'input_type' => 'text'],
    'role' => ['type' => 'select', 'options' => $roles, 'value_field' => 'id', 'label_field' => 'name']
]);

Set Add Permission

It takes a boolean value

$dataTable->setAddPermission(true);

set Datatable Buttons

$dataTable->setDataTableButtons([
    'copy' => true,
    'csv' => ['title' => 'Exported CSV'],
    'excel' => ['title' => 'Excel Export'],
    'pdf' => [
        'title' => 'PDF Export',
        'orientation' => 'landscape',
        'pageSize' => 'A4',
    ],
    'print' => ['title' => 'Printable View'],
    'colvis' => ['text' => 'Toggle Columns'],
]);

Select Fields and JOINs

$dataTable->valuesToSelect([
    'e.id',
    'e.username',
    'r.name AS role_name'
])->tablesToJoin([
    ['table' => 'e.role', 'alias' => 'r', 'on' => 'e.role = r.id']
]);

Values to Render

If there are no Values to Render, it uses the Values to Select as a Fallback to render the items

 ->valuesToRender([
        "name",
        "price",
        "category_name"
    ])

Edit Button Conditions

You can decide to not specify the group operator which by default it will use AND operator as a fallback

    ->setEditButtonConditions([
        'conditions' => [
            ['field' => 'name', 'operator' => '=', 'value' => 'rrrooo'],
            ['field' => 'price', 'operator' => '=', 'value' => '566'],
        ],
    ])

Specifying Group Operator

    ->setEditButtonConditions([
        'conditions' => [
            ['field' => 'name', 'operator' => '=', 'value' => 'rrrooo'],
            ['field' => 'price', 'operator' => '=', 'value' => '566'],
        ],
        'group_operator' => 'OR',
    ])

How to Set Conditions:

Case 1: Simple OR condition (Conditions connected by OR):

->setEditButtonConditions([
'conditions' => [
['field' => 'name', 'operator' => '=', 'value' => 'rrrooo'],
['field' => 'price', 'operator' => '=', 'value' => '566'],
],
'group_operator' => 'OR',
])

This will return true if either name is equal to 'rrrooo' OR price is equal to 566.

Case 2: Simple AND condition (Conditions connected by AND):

->setEditButtonConditions([
'conditions' => [
['field' => 'name', 'operator' => '=', 'value' => 'rrrooo'],
['field' => 'price', 'operator' => '=', 'value' => '566'],
],
'group_operator' => 'AND',
])

This will return true if both name is equal to 'rrrooo' AND price is equal to 566.

Case 3: Mixed AND and OR (More complex logic with groups):

->setEditButtonConditions([
'groups' => [
[
'conditions' => [
['field' => 'name', 'operator' => '=', 'value' => 'rrrooo'],
['field' => 'price', 'operator' => '=', 'value' => '566'],
],
'group_operator' => 'AND', // Conditions within this group are ANDed
],
[
'conditions' => [
['field' => 'stock', 'operator' => '>', 'value' => '100'],
['field' => 'category', 'operator' => '=', 'value' => 'Electronics'],
],
'group_operator' => 'OR', // Conditions within this group are ORed
],
],
'group_operator' => 'AND', // Groups themselves are ANDed together
])

This will evaluate like:

(name = 'rrrooo' AND price = '566')

OR

(stock > 100 OR category = 'Electronics')

The overall result will be true if either the AND group (name and price) is true AND the OR group (stock or category) is true.

Edit Callback Function

Instead of using setEditButtonCondition You can opt to use setEditButtonConditionCallback to provide your own callback

   ->setEditButtonConditionCallback(function ($row) {
        return $row['price'] > 500;
    })

Custom Add Form

->setCustomAddFormRenderer(function () {
        echo '<div class="mb-3"><label>Custom Field</label><input name="name" class="form-control" /></div>';
    })

Custom Add Form Header/Title

->setCustomAddFormHeader("Add New Category")

Custom Edit Form

THe id Of Inputs Should have the datatable id passed eg dataTable_editModalprice

Where dataTable is the id of datatable

->setCustomEditFormRenderer(function () {
    echo '<div class="mb-3"><label>Custom Field</label><input id="dataTable_editModalprice" name="name" class="form-control" /></div>';
})

Custom Edit Form With Select

->setCustomEditFormRenderer(function () use ($categories) {
    echo '<div class="mb-3">
        <label for="dataTable_editModalname">Product Name</label>
        <input id="dataTable_editModalname" name="name" class="form-control" />
      </div>';

    echo '<div class="mb-3">
        <label for="dataTable_editModalprice">Price ($)</label>
        <input id="dataTable_editModalprice" name="price"  class="form-control" />
      </div>';

    echo '<div class="mb-3">
        <label for="dataTable_editModalcategory_id">Category</label>
        <select id="dataTable_editModalcategory_id" name="category_id" class="form-control">
            <option value="">-- Select Category --</option>';
    foreach ($categories as $cat) {
        echo '<option value="' . htmlspecialchars($cat['id']) . '">' . htmlspecialchars($cat['name']) . '</option>';
    }
    echo '  </select>
      </div>';
})

Custom Edit Form Header/Title

->setCustomEditFormHeader("Edit Category")

Dialog Size

Add Dialog Size

->setAddDialogSize("modal-fullscreen")

Edit Dialog Size

->setEditDialogSize("modal-fullscreen")

Enable Search

$dataTable->searchable([
    'e.username' => ['type' => 'input', 'label' => 'Username', 'column' => 'username'],
    'r.id' => ['type' => 'select', 'label' => 'Role', 'column' => 'id', 'options' => $roles, 'value_field' => 'id', 'label_field' => 'name']
]);

Custom Columns Before Actions Columns

 ->addColumnBeforeActions('Status Icon', fn($row) => $row['active'] ? '๐ŸŸข' : '๐Ÿ”ด')

Custom Columns Before Actions Columns Without Being Escaped

->addColumnBeforeActions('Role', fn($row) => "<b>{$row['role']}</b>", raw: true)

Custom Columns Before Actions Columns With Callback Visibility

->addColumnBeforeActions(
    'Secret',
    fn($row) => '๐Ÿ”’ Secret!',
    raw: true,
    visibleWhen: fn($row) => $row['price'] > 1 && $row['name']==='RRR'
)

Custom Columns Before Actions Columns With Boolean Visibility

->addColumnAfterActions(
    'Secret Info',
    fn($row) => '๐Ÿ”’ Secret!',
    raw: true,
    visibleWhen: fn($row) => true
)

Custom Columns After Actions Columns

 ->addColumnAfterActions('Status Icon', fn($row) => $row['active'] ? '๐ŸŸข' : '๐Ÿ”ด')

Custom Columns After Actions Columns Without Being Escaped

->addColumnAfterActions('Role', fn($row) => "<b>{$row['role']}</b>", raw: true)

Custom Columns After Actions Columns With Callback Visibility

->addColumnAfterActions(
    'Secret',
    fn($row) => '๐Ÿ”’ Secret!',
    raw: true,
    visibleWhen: fn($row) => $row['price'] > 1 && $row['name']==='RRR'
)

Custom Columns After Actions Columns With Boolean Visibility

->addColumnAfterActions(
    'Secret Info',
    fn($row) => '๐Ÿ”’ Secret!',
    raw: true,
    visibleWhen: fn($row) => true
)

Data Transformation

->setRowDataTransformer(function($row) {
    if (!empty($row['id'])) {
        $row['name'] = strtoupper($row['name']);
        $row['role'] = strtoupper($row['name']);
    }

    if($row['price']>600){
        $row['active'] =true;
    }
    $row['total_price'] = $row['price'] +1;
    return $row;
})

Enable Pagination

$dataTable->enablePagination(10, ['username', 'role']);

๐Ÿ–ฅ Rendering the Table

$dataTable->renderDataTable($_GET['page'] ?? 1);

๐Ÿงผ Handling Form Submissions

    $dataTable->handleRequest($_POST ?? []);

โœ… Example a Simple Workflow Without Joins

Columns to add and Columns to Edit support using both associative and indexed array

$dataDisplay = DataDisplay::create($entityManager, User::class);

$dataDisplay
    ->valuesToSelect(["e.id", "e.name"])
    ->columnToBeAdded([
     "name"
    ])
    ->columnsToBeEdited([
        "name" => [
            "type" => "input",
            "input_type" => "text",
            "label" => "Name",
        ],
    ]);

$dataDisplay->handleRequest($_POST ?? []);
$dataDisplay->renderDataTable();

โœ… Example a Simple Workflow With Add Permission

$dataDisplay = DataDisplay::create($entityManager, User::class);

$dataDisplay
    ->valuesToSelect(["e.id", "e.name"])
    ->columnToBeAdded([
     "name"
    ])
    ->columnsToBeEdited([
        "name" => [
            "type" => "input",
            "input_type" => "text",
            "label" => "Name",
        ],
    ])
    ->setAddPermission(true)
    ->setDataTableButtons([
        'copy' => true,
        'csv' => true,
        'excel' => ['title' => 'Excel Export'],
        'pdf' => [
            'title' => 'PDF Export',
            'orientation' => 'landscape',
            'pageSize' => 'A4',
        ],
        'print' => ['title' => 'Printable View'],
        'colvis' => ['text' => 'Toggle Columns'],
    ]);

โœ… Example a Simple Workflow With Datatable Buttons

$dataDisplay = DataDisplay::create($entityManager, User::class);

$dataDisplay
    ->valuesToSelect(["e.id", "e.name"])
    ->columnToBeAdded([
     "name"
    ])
    ->columnsToBeEdited([
        "name" => [
            "type" => "input",
            "input_type" => "text",
            "label" => "Name",
        ],
    ])
    ->setDataTableButtons([
        'copy' => true,
        'csv' => true,
        'excel' => ['title' => 'Excel Export'],
        'pdf' => [
            'title' => 'PDF Export',
            'orientation' => 'landscape',
            'pageSize' => 'A4',
        ],
        'print' => ['title' => 'Printable View'],
        'colvis' => ['text' => 'Toggle Columns'],
    ]);

โœ… Example a Simple Workflow With Custom Add and Edit Form

$dataDisplay = DataDisplay::create($entityManager, User::class);

$dataDisplay
    ->valuesToSelect(["e.id", "e.name"])
    ->columnToBeAdded([
     "name"
    ])
    ->columnsToBeEdited([
        "name" => [
            "type" => "input",
            "input_type" => "text",
            "label" => "Name",
        ],
    ])
    ->setAddPermission(true)
    ->setCustomAddFormRenderer(function () {
        echo '<div class="mb-3"><label>Custom Field</label><input name="name" class="form-control" /></div>';
    })
    ->setCustomEditFormRenderer(function () {
        echo '<div class="mb-3"><label>Custom Field</label><input id="name" name="name" class="form-control" /></div>';
    })
    ->setDataTableButtons([
        'copy' => true,
        'csv' => true,
        'excel' => ['title' => 'Excel Export'],
        'pdf' => [
            'title' => 'PDF Export',
            'orientation' => 'landscape',
            'pageSize' => 'A4',
        ],
        'print' => ['title' => 'Printable View'],
        'colvis' => ['text' => 'Toggle Columns'],
    ]);

$dataDisplay->handleRequest($_POST ?? []);
$dataDisplay->renderDataTable();

โœ… Example a Simple Workflow With Joins and Search Functionality

$categories = Category::getAllCategories($entityManager) ?? [];

$dataDisplay = DataDisplay::create($entityManager, Product::class);

$dataDisplay
    ->valuesToSelect(["e.id", "e.name", "e.price", "c.name AS category_name"])
    ->columnToBeAdded([
        "name" => [
            "type" => "input",
            "input_type" => "text",
            "label" => "Product Name",
        ],
        "price" => [
            "type" => "input",
            "input_type" => "number",
            "label" => 'Price ($)',
        ],
        "category_id" => [
            "type" => "select",
            "label" => "Category",
            "options" => $categories,
            "value_field" => "id",
            "label_field" => "name",
        ],
    ])
    ->columnsToBeEdited([
        "name" => [
            "type" => "input",
            "input_type" => "text",
            "label" => "Name",
        ],
        "price" => [
            "type" => "input",
            "input_type" => "number",
            "label" => "Price",
        ],
        "category_id" => [
            "type" => "select",
            "label" => "Category",
            "options" => $categories,
            "value_field" => "id",
            "label_field" => "name",
        ],
    ])
    ->enablePagination(10, ["e.name", "e.id"])
    ->searchable([
        "category_id" => [
            "type" => "select",
            "label" => "Category",
            "operator" => "=",
            "table" => "e",
            "column" => "category_id",
            "options" => $categories,
            "value_field" => "id",
            "label_field" => "name",
        ],
    ])

    ->setAddButtonLabel("Create New Product")
    ->setTableId("dataTable")
    ->tablesToJoin([
        [
            "table" => "Category::class",
            "alias" => "c",
            "on" => "e.category_id = c.id",
        ],
    ]);

$dataDisplay->handleRequest($_POST ?? []);
$dataDisplay->renderDataTable();

โœ… Example a Simple Workflow With Custom Add and Edit Form

$categories = Category::getAllCategories($entityManager) ?? [];

$dataDisplay = DataDisplay::create($entityManager, Product::class);

$dataDisplay
    ->valuesToSelect(["e.id", "e.name", "e.price","e.category_id", "c.name AS category_name"])
    ->columnToBeAdded([
        "name" => [
            "type" => "input",
            "input_type" => "text",
            "label" => "Product Name",
        ],
        "price" => [
            "type" => "input",
            "input_type" => "number",
            "label" => 'Price ($)',
        ],
        "category_id" => [
            "type" => "select",
            "label" => "Category",
            "options" => $categories,
            "value_field" => "id",
            "label_field" => "name",
        ],
    ])
    ->columnsToBeEdited([
        "name" => [
            "type" => "input",
            "input_type" => "text",
            "label" => "Name",
        ],
        "price" => [
            "type" => "input",
            "input_type" => "number",
            "label" => "Price",
        ],
        "category_id" => [
            "type" => "select",
            "label" => "Category 1",
            "options" => $categories,
            "value_field" => "id",
            "label_field" => "name",
        ],
    ])
    ->valuesToRender([
        "id",
        "name",
        "price",
        "category_name"
    ])
    ->setCustomAddFormHeader("Add New Category")
    ->setCustomEditFormHeader("Edit Category")
    ->setCustomEditFormRenderer(function () use ($categories) {
        echo '<div class="mb-3">
            <label for="dataTable_editModalname">Product Name</label>
            <input id="dataTable_editModalname" name="name" class="form-control" />
          </div>';

        echo '<div class="mb-3">
            <label for="dataTable_editModalprice">Price ($)</label>
            <input id="dataTable_editModalprice" name="price"  class="form-control" />
          </div>';

        echo '<div class="mb-3">
            <label for="dataTable_editModalcategory_id">Category</label>
            <select id="dataTable_editModalcategory_id" name="category_id" class="form-control">
                <option value="">-- Select Category --</option>';
        foreach ($categories as $cat) {
            echo '<option value="' . htmlspecialchars($cat['id']) . '">' . htmlspecialchars($cat['name']) . '</option>';
        }
        echo '  </select>
          </div>';
    })

    ->setEditButtonConditionCallback(function ($row) {
        return $row['price'] > 100;
    })

    ->searchable([
        "category" => [
            "type" => "select",
            "label" => "Category",
            "operator" => "=",
            "table" => "e",
            "column" => "category_id",
            "options" => $categories,
            "value_field" => "id",
            "label_field" => "name",
        ],
    ])

    ->setAddButtonLabel("Create New Product")
    ->setTableId("dataTable")
    ->tablesToJoin([
        [
            "table" => Category::class,
            "alias" => "c",
            "on" => "e.category_id = c.id",
        ],
    ])
    ->setDataTableButtons([
        'copy' => true,
        'csv' => ['title' => 'Exported CSV'],
        'excel' => ['title' => 'Excel Export'],
        'pdf' => [
            'title' => 'PDF Export',
            'orientation' => 'landscape',
            'pageSize' => 'A4',
        ],
        'print' => ['title' => 'Printable View'],
        'colvis' => ['text' => 'Toggle Columns'],
    ]);

$dataDisplay->handleRequest($_POST ?? []);
$dataDisplay->renderDataTable();

โœ… Example a Simple Workflow With Data Transformation,Joins and Custom Columns

$categories = Category::getAllCategories($entityManager) ?? [];

$dataDisplay = DataDisplay::create($entityManager, Product::class);

$dataDisplay
    ->valuesToSelect(["e.id", "e.name", "e.price","e.category_id", "c.name AS category_name"])
    ->columnToBeAdded([
        "name" => [
            "type" => "input",
            "input_type" => "text",
            "label" => "Product Name",
        ],
        "price" => [
            "type" => "input",
            "input_type" => "number",
            "label" => 'Price ($)',
        ],
        "category_id" => [
            "type" => "select",
            "label" => "Category",
            "options" => $categories,
            "value_field" => "id",
            "label_field" => "name",
        ],
    ])
    ->columnsToBeEdited([
        "name" => [
            "type" => "input",
            "input_type" => "text",
            "label" => "Name",
        ],
        "price" => [
            "type" => "input",
            "input_type" => "number",
            "label" => "Price",
        ],
        "category_id" => [
            "type" => "select",
            "label" => "Category 1",
            "options" => $categories,
            "value_field" => "id",
            "label_field" => "name",
        ],
    ])
    ->valuesToRender([
        "id",
        "name",
        "price",
        "category_name"
    ])
    ->setRowDataTransformer(function($row) {
        if (!empty($row['id'])) {
            $row['name'] = strtoupper($row['name']);
            $row['role'] = strtoupper($row['name']);
        }

        if($row['price']>600){
            $row['active'] =true;
        }
        $row['total_price'] = $row['price'] +1;
        return $row;
    })
    ->addColumnAfterActions('Status Icon', fn($row) => $row['active'] ? '๐ŸŸข' : '๐Ÿ”ด')
    ->addColumnBeforeActions('Role', fn($row) => "<b>{$row['role']}</b>", raw: true)
    ->addColumnAfterActions(
        'Secret Info',
        fn($row) => '๐Ÿ”’ Secret!',
        raw: true,
        visibleWhen: fn($row) => true
    )
    ->addColumnAfterActions(
        'Secret',
        fn($row) => '๐Ÿ”’ Secret!',
        raw: true,
        visibleWhen: fn($row) => $row['price'] > 1 && $row['name']==='RRR'
    )
    ->tablesToJoin([
        [
            "table" => Category::class,
            "alias" => "c",
            "on" => "e.category_id = c.id",
        ],
    ]);

โœ… Example a Simple Workflow With Classes For Data Fetching

$dataDisplay = DataDisplay::create(null, Category::class,DataSourceType::CLASSES);

$dataDisplay
   ->setClassFetchDataFunction("all")
    ->columnToBeAdded([
        "name" => [
            "type" => "input",
            "input_type" => "text",
            "label" => "Name",
        ],
    ])
    ->columnsToBeEdited([
        "name" => [
            "type" => "input",
            "input_type" => "text",
            "label" => "Name",
        ],
    ])
    ->valuesToRender(["id", "name"])
    ->searchable([
        "name" => [
            "type" => "input",
            "input_type" => "text",
            "label" => "Name",
        ],
    ])
    ;

$dataDisplay->handleRequest($_POST ?? []);
$dataDisplay->renderDataTable();

โœ… Example a Simple Workflow With Report Mode

$dataDisplay = DataDisplay::create(null, Category::class,DataSourceType::CLASSES);

$dataDisplay
   ->setClassFetchDataFunction("all")
   ->setMode("report")
    ->columnToBeAdded([
        "name" => [
            "type" => "input",
            "input_type" => "text",
            "label" => "Name",
        ],
    ])
    ->columnsToBeEdited([
        "name" => [
            "type" => "input",
            "input_type" => "text",
            "label" => "Name",
        ],
    ])
    ->valuesToRender(["id", "name"])
    ->searchable([
        "name" => [
            "type" => "input",
            "input_type" => "text",
            "label" => "Name",
        ],
    ])
    ->setCustomAddAction("/test")
    ->setCustomEditAction("/admin")
    ;

$dataDisplay->handleRequest($_POST ?? []);
$dataDisplay->renderDataTable();

โœ… Example a Simple Workflow With Dialog Size

$dataDisplay = DataDisplay::create(null, Category::class,DataSourceType::CLASSES);

$dataDisplay
   ->setClassFetchDataFunction("all")
   ->setAddDialogSize("modal-fullscreen")
   ->setEditDialogSize("modal-fullscreen")
    ->columnToBeAdded([
        "name" => [
            "type" => "input",
            "input_type" => "text",
            "label" => "Name",
        ],
    ])
    ->columnsToBeEdited([
        "name" => [
            "type" => "input",
            "input_type" => "text",
            "label" => "Name",
        ],
    ])
    ->valuesToRender(["id", "name"])
    ->searchable([
        "name" => [
            "type" => "input",
            "input_type" => "text",
            "label" => "Name",
        ],
    ])
    ->setCustomAddAction("/test")
    ->setCustomEditAction("/admin")
    ;

$dataDisplay->handleRequest($_POST ?? []);
$dataDisplay->renderDataTable();

UPDATING DATABASE schema

php bin/doctrine orm:schema-tool:update --force --dump-sql