mzgs/fastcrud

Fast and simple CRUD operations library for PHP

Maintainers

Details

github.com/mzgs/fastcrud

Source

Issues

Installs: 95

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/mzgs/fastcrud

v1.0.0 2025-09-18 11:19 UTC

README

🚀 FastCRUD

A fast and simple CRUD operations library for PHP with built-in pagination and AJAX support

PHP Version License Packagist Bootstrap

✨ Zero-config setup • 🔄 AJAX-powered • 📊 Built-in pagination • 🎨 Bootstrap 5 ready

📚 Table of Contents

🎆 Features

Zero-config CRUD with automatic pagination, search, and column sorting
🔄 AJAX-powered forms, inline editing, bulk updates, and real-time validation feedback
🔗 Nested tables, relations, and subselect support for modelling complex data
🪝 Lifecycle callbacks, custom columns, and field modifiers for fine-grained control
📊 Built-in CSV/Excel export, soft-delete helpers, and configurable action buttons
🔍 Visual query builder to compose filters & sorts with reusable saved views
🎨 Global styling hooks and upload helpers so you can align the UI with your project

📦 Installation

# Install via Composer
composer require mzgs/fastcrud

📋 Requirements

PHP 8.0 or higher
PDO extension
Database - MySQL, PostgreSQL, SQLite, etc.
Bootstrap 5 for styling
jQuery for AJAX functionality

🚀 Quick Start

<?php
require __DIR__ . '/vendor/autoload.php';

use FastCrud\Crud;

// 🔌 Initialize database connection
// Supports: driver, host, port, database, username, password, options
Crud::init([
    'driver' => 'mysql',        // mysql, pgsql, sqlite
    'host' => '127.0.0.1',
    'database' => 'your_database',
    'username' => 'your_username',
    'password' => 'your_password',
]);

// ✨ Create and render a CRUD table - that's it!
echo new Crud('users')->render();

🎉 That's it! FastCRUD automatically generates a complete CRUD interface with pagination, search, and AJAX functionality.

🖼️ What You Get Out of the Box

Example of a fully functional CRUD table generated with just 2 lines of code

📊 Data Table with sorting, searching, and pagination
✏️ Inline Editing for quick updates
📝 Forms for create/edit operations
📋 Export to CSV/Excel
🗑️ Bulk Actions for mass operations

🌍 Complete HTML Example

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>FastCRUD Demo</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
    <link href="https://mzgs.github.io/fa7/css/all.css" rel="stylesheet"  >
    <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>

</head>
<body>
    <div class="container py-5">
        <div class="card">
            <div class="card-header"><h1>FastCRUD Demo</h1></div>
            <div class="card-body">
                <?php
                require __DIR__ . '/vendor/autoload.php';
                use FastCrud\Crud;
                
                Crud::init(['database' => 'your_db', 'username' => 'user', 'password' => 'pass']);
                echo new Crud('users')->render();
                ?>
            </div>
        </div>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

🔧 Configuration

📋 Rendering Multiple Tables

// Each Crud instance is independent - reuse the same connection
$users = new Crud('users');
$orders = (new Crud('orders'))->setPerPage(10)->order_by('created_at', 'desc');

echo $users->render();
echo $orders->render();

🗃️ Database Editor

FastCRUD includes a visual Database Editor that provides a web-based interface for managing your database schema. Create, modify, and organize tables and columns directly from your browser without writing SQL.

🎯 Features

  • Visual Schema Management - Create, rename, and delete tables through an intuitive interface
  • Column Management - Add, rename, and modify column types with live feedback
  • Drag & Drop Reordering - Reorder columns visually (MySQL only)
  • Multi-Database Support - Works with MySQL, PostgreSQL, and SQLite
  • Database Export - Download complete database dumps as SQL files
  • Real-time Updates - AJAX-powered interface with instant feedback
  • Type-safe Operations - Built-in validation prevents invalid schema changes

🚀 Quick Start

<?php
require __DIR__ . '/vendor/autoload.php';

use FastCrud\DatabaseEditor;

// Initialize with an existing PDO instance
$pdo = new PDO('mysql:host=127.0.0.1;dbname=your_database', 'your_username', 'your_password');
DatabaseEditor::init($pdo);

// Render the database editor interface
echo DatabaseEditor::render();

// Uncomment to include the CRUD records table for the selected table beneath the editor
// echo DatabaseEditor::render(true, true);
?>

Tip: If you have already called Crud::init([...]) (or otherwise configured CrudConfig) earlier in your bootstrap, you can simply call DatabaseEditor::init() — it will reuse the existing DB config/connection.

use FastCrud\Crud;
use FastCrud\DatabaseEditor;

Crud::init(['database' => 'app', 'username' => 'user', 'password' => 'secret']);
DatabaseEditor::init(); // reuses previously set config
echo DatabaseEditor::render();

🛠️ API Reference

Database Editor Class

Initialization
  • DatabaseEditor::init(?PDO $pdo = null): void – Initialize the database editor with an optional PDO instance
    $pdo = new PDO('mysql:host=localhost;dbname=my_app', 'db_user', 'db_password');
    DatabaseEditor::init($pdo);
    • If Crud::init([...]) (or CrudConfig::setDbConfig([...])) has already been called, DatabaseEditor::init() can be invoked without arguments to reuse the existing configuration and connection.
    • Supplying a PDO instance lets you share an existing connection without reconfiguring CrudConfig.
Rendering
  • DatabaseEditor::render(bool $showHeader = true, bool $showRecordsTable = false): string – Generate and return the complete database editor HTML interface. Pass false to omit the hero header block and set $showRecordsTable to true to render a live CRUD table for the currently selected database table beneath the editor. When enabled, switching tables in the sidebar automatically refreshes the CRUD view without reloading the page.
    // Hero header hidden, records view enabled
    echo DatabaseEditor::render(false, true);

⚡ Supported Operations

Table Management

  • Create Tables - Add new tables with auto-generated primary key
  • Rename Tables - Click table names to rename them inline
  • Delete Tables - Remove tables with confirmation prompts
  • Table Overview - View table structure and column count at a glance

Column Management

  • Add Columns - Create new columns with customizable data types
  • Rename Columns - Click column names for inline editing
  • Change Types - Modify column data types using dropdown selectors
  • Reorder Columns - Drag and drop to reorder (MySQL only)

Data Types by Database

MySQL Types: BIGINT, BINARY(255), BIT, BOOLEAN, CHAR(36), DATE, DATETIME, DECIMAL(10,2), DOUBLE, FLOAT, INT, JSON, LONGTEXT, MEDIUMTEXT, SMALLINT, TEXT, TIME, TIMESTAMP, TINYINT, TINYINT(1), VARCHAR(255)

PostgreSQL Types: BIGINT, BIGSERIAL, BOOLEAN, DATE, DECIMAL(10,2), DOUBLE PRECISION, INTEGER, JSON, JSONB, NUMERIC(10,2), SERIAL, SMALLINT, TEXT, TIMESTAMP, UUID, VARCHAR(255)

SQLite Types: INTEGER, REAL, TEXT, BLOB, NUMERIC

💾 Database Export

  • One-click Export - Complete SQL dumps with data
  • Timestamped Files - Auto-generated filenames
  • Multi-database Support - Works with MySQL, PostgreSQL, SQLite

🔒 Security Features

  • SQL Injection Protection - Input validation and identifier quoting
  • Type Safety - Column type validation
  • Error Handling - Graceful error recovery

💡 Usage Tips

  • Backup First - Always backup before structural changes
  • Test on Dev - Use development databases before production
  • Column Reordering - MySQL-only feature
  • Keyboard Navigation - Tab/Enter for quick navigation

📜 API Reference & Customization

All customization options are available through the main FastCrud\Crud class methods:

🛠️ FastCrud\Crud - Main CRUD Class

🚀 Setup & Bootstrap

  • Crud::init(PDO|array|null $dbConfig = null): void – Configure the connection defaults (keys like driver, host, port, database, username, password, options) or inject an existing PDO instance. When called without arguments, it reuses the configuration stored in CrudConfig.
    Crud::init([
        'database' => 'app',
        'username' => 'root',
        'password' => 'secret',
    ]);
  • Crud::fromAjax(string $table, ?string $id, array|string|null $configPayload, ?PDO $connection = null): self – Rehydrate an instance from data posted by the FastCRUD frontend.
    $crud = Crud::fromAjax('users', $_GET['fastcrud_id'] ?? null, $_POST['config'] ?? null);
  • __construct(string $table, ?PDO $connection = null) – Build a CRUD controller for the given table.
    $crud = new Crud('users', $customPdo);
  • getTable(): string – Return the raw table identifier.
    $tableName = $crud->getTable();
  • primary_key(string $column): self – Override the primary key column that FastCRUD uses.
    $crud->primary_key('user_id');
  • setPerPage(int $perPage): self – Set the default rows per page (use any positive integer).
    $crud->setPerPage(25);
  • limit(int $limit): self – Alias of setPerPage() for fluent configuration.
    $crud->limit(10);
  • limit_list(string|array $limits): self – Define the pagination dropdown values; mix integers with the special 'all' keyword.
    $crud->limit_list([5, 10, 25, 'all']);
  • setPanelWidth(string $width): self – Adjust the edit panel width with any CSS length ('640px', '30%', '40rem').
    $crud->setPanelWidth('640px');
    $crud->setPanelWidth('30%');

📋 Table Display

  • inline_edit(string|array $fields): self – Enable inline edits for selected columns (pass an array or a comma-separated string).
    $crud->inline_edit(['status', 'priority']);
  • columns(string|array $columns, bool $reverse = false): self – Control which columns appear and in what order (pass an array or a comma-separated string).
    $crud->columns(['id', 'name', 'email']);
  • set_column_labels(array|string $labels, ?string $label = null): self – Change table heading labels (accepts associative arrays or single column/label pairs).
    $crud->set_column_labels(['created_at' => 'Created']);
  • set_field_labels(array|string $labels, ?string $label = null): self – Rename form fields without renaming columns; the same shapes as set_column_labels() apply.
    $crud->set_field_labels('phone', 'Contact Number');

📊 Column Presentation

  • column_pattern(string|array $columns, string $pattern): self – Render column values with template tokens like {value}, {formatted}, {raw}, {column}, {label}, and any column name from the row.
    $crud->column_pattern('email', '<a href="mailto:{raw}">{value}</a>');
    $crud->column_pattern('name', '<strong>{first_name} {last_name}</strong> ({id})');
    Use {formatted} to reference the truncated display produced by helpers like column_truncate(), while {value} preserves the original string.
  • column_callback(string|array $columns, string|array $callback): self – Pass values through a formatter callback (use a named function 'function_name', 'Class::method', or [ClassName::class, 'method']).
    // Using a named function (function must accept 4 params: $value, $row, $column, $display)
    // $value: current cell value, $row: full row data, $column: column name, $display: formatted value
    function format_total_with_tax($value, $row, $column, $display) {
        $tax_rate = $row['tax_rate'] ?? 0;
        $total_with_tax = $value * (1 + $tax_rate / 100);
        return '$' . number_format($total_with_tax, 2);
    }
    $crud->column_callback('total', 'format_total_with_tax');
  • column_callbacks(array $definitions): self – Apply several column_callback() rules at once by passing an associative array where each key is a comma-separated column list and each value is the callable.
    $crud->column_callbacks([
        'status' => 'map_status',
        'total,subtotal' => [InvoicePresenter::class, 'formatCurrency'],
    ]);
  • custom_column(string $column, string|array $callback): self – Add computed virtual columns to the grid; callback forms mirror column_callback().
    // Using a named function (function must accept 1 param: $row)
    // $row: full row data array
    function compute_full_name($row) {
        return trim(($row['first_name'] ?? '') . ' ' . ($row['last_name'] ?? ''));
    }
    $crud->custom_column('full_name', 'compute_full_name');
  • custom_columns(array $definitions): self – Batch syntax for custom_column(). Provide associative maps where each key can target multiple custom columns via comma-separated names.
    $crud->custom_columns([
        'full_name' => 'compute_full_name',
        'total_with_tax,total_without_tax' => [InvoicePresenter::class, 'formatTotals'],
    ]);
  • column_class(string|array $columns, string|array $classes): self – Append custom CSS classes to specific cells (pass space-separated strings or arrays).
    $crud->column_class('status', 'text-uppercase text-success');
  • column_width(string|array $columns, string $width): self – Set fixed widths for columns using any CSS length ('160px', '20%', '12rem').
    $crud->column_width('name', '220px');
  • column_truncate(string|array $columns, int $length, string $suffix = '…'): self – Truncate long text for clean tables; customise the suffix (e.g. '...'). FastCRUD now trims every column to 300 chars by default via CrudConfig::$default_column_truncate; override globally by changing that flag, per Crud instance with default_column_truncate(), or per column with column_truncate().
    $crud->column_truncate('description', 80);
  • default_column_truncate(int|array|null $value): self – Override the per-instance fallback that applies to every column (before column_truncate() rules). Pass 80 to trim to 80 chars with the default suffix, ['length' => 120, 'suffix' => '...'] for custom suffixes, or null to disable automatic truncation for that Crud instance only.
    $crud->default_column_truncate(['length' => 120, 'suffix' => '...']);
  • highlight(string|array $columns, string $operator, mixed $value = null, string $class = 'text-warning'): self – Highlight cells that match conditions using operators such as equals, not_equals, contains, not_contains, gt, gte, lt, lte, in, not_in, empty, not_empty (symbol aliases like =, !=, >=, < are also accepted).
    $crud->highlight('status', '=', 'pending', 'text-danger');
    $crud->highlight(['status', 'priority'], 'empty', null, 'text-muted');
    $crud->highlight('notes', 'not_contains', 'internal', 'text-danger');
  • highlight_row(string|array $columns, string $operator, mixed $value = null, string $class = 'table-warning'): self – Highlight entire rows based on the same operator options used by highlight().
    $crud->highlight_row('balance', 'lt', 0, 'table-danger');
  • column_summary(string|array $columns, string $type = 'sum', ?string $label = null, ?int $precision = null): self – Display aggregated totals in the footer with summary types sum, avg, min, max, or count.
    $crud->column_summary('total', 'sum', 'Grand Total', 2);

📋 Field & Form Customization

  • custom_field(string $field, string|array $callback): self – Inject additional, non-database fields into the form; callbacks accept the same shapes as other behaviour hooks.
    // Using a named function (function must accept 4 params: $field, $value, $row, $mode)
    // $field: field name, $value: current value, $row: full row data, $mode: 'create'|'edit'|'view'
    function add_confirmation_checkbox($field, $value, $row, $mode) {
        return <<<HTML
            <label class="form-check-label">
                <input 
                    type="checkbox" 
                    class="form-check-input"
                    name="{$field}"
                    data-fastcrud-field="{$field}" 
                    value="1"
                > 
                I confirm this action
            </label>
        HTML;
    }
    $crud->custom_field('confirmation', 'add_confirmation_checkbox');
  • field_callback(string|array $fields, string|array $callback): self – Create custom HTML input fields by returning raw HTML markup instead of the default input. Required: Include data-fastcrud-field="{field}" attribute for AJAX form submission.
    // Using a named function (function must accept 4 params: $field, $value, $row, $mode)
    // $field: field name, $value: current value, $row: full row data, $mode: 'create'|'edit'|'view'
    function create_color_picker($field, $value, $row, $mode) {
        $value = htmlspecialchars($value ?? '#000000');
        return <<<HTML
            <input
                type="color"
                class="form-control form-control-color"
                name="{$field}"
                data-fastcrud-field="{$field}"
                value="{$value}"
                title="Choose your color"
            >
        HTML;
    }
    $crud->field_callback('color', 'create_color_picker');
  • fields_callbacks(array $definitions): self – Batch syntax for field_callback(). Use associative maps (['color' => 'create_color_picker', 'status,priority' => 'decorate_badges']). Alias: field_callbacks().
    $crud->fields_callbacks([
        'color' => 'create_color_picker',
        'status,priority' => 'decorate_badges',
    ]);
  • fields(string|array $fields, bool $reverse = false, string|false $tab = false, string|array|false $mode = false): self – Arrange form fields into sections and tabs; target specific modes using 'create', 'edit', 'view', or 'all' (or pass false to apply everywhere).
    $crud->fields(['name', 'email', 'phone'], false, 'Details');
  • form_section(string $identifier, array $definition, string|array|false $mode = false): self – Group fields into labeled form blocks with optional icons, descriptive copy, and collapsible behaviour. Define the fields inside the section with fields, set a human-friendly title, optionally include a description, icon, custom wrapper class, dedicated title_class for the heading area, or collapsible/collapsed flags, and scope to specific modes using either the third argument or a 'mode' key in the definition.
    $crud
        ->fields('name,email,timezone,locale')
        ->form_section('profile', [
            'title'       => 'Profile',
            'description' => 'Primary contact details',
            'icon'        => 'fas fa-id-card',
            'class'       => 'card shadow-sm',
            'title_class' => 'px-3 pt-3',
            'fields'      => ['name', 'email'],
        ])
        ->form_section('preferences', [
            'title'        => 'Preferences',
            'fields'       => ['timezone', 'locale'],
            'collapsible'  => true,
            'start_collapsed' => true,
        ], ['edit', 'view']);
  • default_tab(string $tabName, string|array|false $mode = false): self – Choose the default tab for each form mode ('create', 'edit', 'view', or 'all').
    $crud->default_tab('Details', ['create', 'edit']);
  • change_type(string|array $fields, string $type, mixed $default = '', array $params = []): self – Swap the input widget or field type. Supported types include text-based ('text', 'textarea', 'rich_editor', 'json', 'password'), selection ('select', 'multiselect', 'radio', 'multicheckbox'), boolean ('bool', 'checkbox', 'switch'), date/time ('date', 'datetime', 'time'), numeric ('number', 'int', 'float'), file upload ('file', 'files', 'image', 'images'), and special ('email', 'color', 'hidden').
    // Text input (default)
    $crud->change_type('username', 'text');
    
    // Textarea for longer content
    $crud->change_type('description', 'textarea', '', ['rows' => 5]);
    
    // Select dropdown with options
    $crud->change_type('status', 'select', 'active', ['options' => ['active' => 'Active', 'inactive' => 'Inactive']]);
    
    // Rich text editor (WYSIWYG with TinyMCE)
    $crud->change_type('content', 'rich_editor', '', ['height' => 400]);
    
    // Image upload (basic)
    $crud->change_type('avatar', 'image', '', ['path' => 'avatars']);
    
    // Image upload with all options
    $crud->change_type('product_image', 'image', '', [
        'path' => 'products',
        'width' => 1200,
        'height' => 800,
        'crop' => false,
        'previewHeight' => 200,
        'aspectRatio' => '3:2',
        'thumbs' => [
            [
                'width' => 300,
                'height' => 200,
                'crop' => true,
                'marker' => '_medium',
                'folder' => 'medium'
            ],
            [
                'width' => 150,
                'height' => 150,
                'crop' => true,
                'marker' => '_thumb',
                'folder' => 'thumbs'
            ]
        ],
        'placeholder' => 'Upload product image',
        'class' => 'product-image-uploader'
    ]);
    
    // Switch/toggle button
    $crud->change_type('is_featured', 'switch', false);
    
    // Number input with constraints
    $crud->change_type('price', 'number', 0, ['step' => 'any', 'min' => 0, 'max' => 1000]);
    
    // Date picker
    $crud->change_type('birth_date', 'date');
    
    // DateTime picker
    $crud->change_type('created_at', 'datetime');
    
    // Color picker
    $crud->change_type('theme_color', 'color', '#000000');
    
    // JSON editor with syntax highlighting
    $crud->change_type('metadata', 'json', '{}');
    
    // Password input; hashes with password_hash (defaults to PASSWORD_BCRYPT)
    $crud->change_type('password', 'password', '', [
        'placeholder' => 'Set a password',
        'algorithm' => PASSWORD_BCRYPT, // optional, accepts PASSWORD_* constants or aliases
        'cost' => 12,                    // optional password_hash option shortcut
    ]);
  • Password inputs render blank when editing existing rows; entering a new value hashes it automatically before persistence, leaving the field empty keeps the stored hash untouched.
  • getChangeTypeDefinition(string $field): ?array – Inspect previously configured type overrides.
    $definition = $crud->getChangeTypeDefinition('avatar');
  • pass_var(string|array $fields, mixed $value, string|array $mode = 'all'): self – Inject runtime values each time the form renders; target the usual modes ('create', 'edit', 'view', 'all').
    $crud->pass_var('updated_by', Auth::id());
  • pass_default(string|array $fields, mixed $value, string|array $mode = 'all'): self – Supply fallback values when inputs are empty using the same mode flags ('create', 'edit', 'view', 'all').
    $crud->pass_default('status', 'pending', 'create');
  • readonly(string|array $fields, string|array $mode = 'all'): self – Mark fields as read-only per mode ('create', 'edit', 'view', 'all').
    $crud->readonly(['email'], ['edit', 'view']);
  • disabled(string|array $fields, string|array $mode = 'all'): self – Disable inputs entirely for certain modes ('create', 'edit', 'view', 'all').
    $crud->disabled('type', 'create');

✅ Validation Helpers

  • validation_required(string|array $fields, int $minLength = 1, string|array $mode = 'all'): self – Enforce required fields and minimum length (modes 'create', 'edit', 'view', 'all').
    $crud->validation_required('name', 1);
  • validation_pattern(string|array $fields, string $pattern, string|array $mode = 'all'): self – Apply regex-based validation rules using any valid PCRE pattern string (e.g. '/^\+?[0-9]{7,15}$/').
    $crud->validation_pattern('phone', '/^\+?[0-9]{7,15}$/');
  • unique(string|array $fields, string|array $mode = 'all'): self – Ensure values remain unique when saving; scope to modes 'create', 'edit', 'view', or 'all'.
    $crud->unique('email', ['create', 'edit']);

🪝 Lifecycle Hooks

Lifecycle hook methods accept only serializable callbacks: named functions ('function_name'), static method strings ('Class::method'), or class/method arrays ([ClassName::class, 'method']). Closures are not supported because the configuration is serialized for AJAX.

  • before_insert(string|array $callback): self – Run logic right before an insert occurs.
    // Using a named function (function must accept 3 params: $payload, $context, $crud)
    // $payload: array of field values to be inserted
    // $context: ['operation'=>'insert', 'stage'=>'before', 'table'=>'...', 'mode'=>'create', 'primary_key'=>'...', 'fields'=>[...], 'current_state'=>[...]]
    // $crud: Crud instance
    function add_timestamps($payload, $context, $crud) {
        $payload['created_at'] = date('Y-m-d H:i:s');
        $payload['created_by'] = $_SESSION['user_id'] ?? null;
        return $payload; // Return modified payload or false to cancel
    }
    $crud->before_insert('add_timestamps');
  • after_insert(string|array $callback): self – React immediately after a record is inserted.
    // Using a named function (function must accept 3 params: $payload, $context, $crud)
    // $payload: the inserted record data array
    // $context: ['operation'=>'insert', 'stage'=>'after', 'table'=>'...', 'mode'=>'create', 'primary_key'=>'...', 'primary_value'=>123, 'fields'=>[...], 'row'=>[...]]
    // $crud: Crud instance
    function log_new_user($payload, $context, $crud) {
        $primaryValue = $context['primary_value'] ?? 'unknown';
        error_log("New user created: ID {$primaryValue}, Email: " . ($payload['email'] ?? 'N/A'));
        return $payload; // Return payload (modifications allowed) or null
    }
    $crud->after_insert('log_new_user');
  • before_create(string|array $callback): self – Intercept create form submissions before validation.
    // Using a named function (function must accept 3 params: $payload, $context, $crud)
    // $payload: array of field values to be inserted
    // $context: ['operation'=>'insert', 'stage'=>'before', 'table'=>'...', 'mode'=>'create', 'primary_key'=>'...', 'fields'=>[...], 'current_state'=>[...]]
    // $crud: Crud instance
    function prepare_form_data($payload, $context, $crud) {
        $payload['created_by'] = $_SESSION['user_id'] ?? null;
        $payload['ip_address'] = $_SERVER['REMOTE_ADDR'] ?? null;
        return $payload; // Return modified payload or false to cancel
    }
    $crud->before_create('prepare_form_data');
  • after_create(string|array $callback): self – React once the create form has finished.
    // Using a named function (function must accept 3 params: $payload, $context, $crud)
    // $payload: the inserted record data array
    // $context: ['operation'=>'insert', 'stage'=>'after', 'table'=>'...', 'mode'=>'create', 'primary_key'=>'...', 'primary_value'=>123, 'fields'=>[...], 'row'=>[...]]
    // $crud: Crud instance
    function send_welcome_email($payload, $context, $crud) {
        if (!empty($payload['email'])) {
            mail($payload['email'], 'Welcome!', 'Thank you for registering.');
        }
        return $payload; // Return payload (modifications allowed) or null
    }
    $crud->after_create('send_welcome_email');
  • before_update(string|array $callback): self – Run logic prior to updating a record.
    // Using a named function (function must accept 3 params: $payload, $context, $crud)
    // $payload: array of field values to be updated
    // $context: ['operation'=>'update', 'stage'=>'before', 'table'=>'...', 'primary_key'=>'...', 'primary_value'=>123, 'mode'=>'edit', 'current_row'=>[...], 'fields'=>[...]]
    // $crud: Crud instance
    function update_timestamp($payload, $context, $crud) {
        $payload['updated_at'] = date('Y-m-d H:i:s');
        $payload['updated_by'] = $_SESSION['user_id'] ?? null;
        return $payload; // Return modified payload or false to cancel
    }
    $crud->before_update('update_timestamp');
  • after_update(string|array $callback): self – React to successful updates.
    // Using a named function (function must accept 3 params: $payload, $context, $crud)
    // $payload: the updated record data array
    // $context: ['operation'=>'update', 'stage'=>'after', 'table'=>'...', 'primary_key'=>'...', 'primary_value'=>123, 'mode'=>'edit', 'changes'=>[...], 'previous_row'=>[...], 'row'=>[...]]
    // $crud: Crud instance
    function notify_status_change($payload, $context, $crud) {
        $changes = $context['changes'] ?? [];
        $primaryValue = $context['primary_value'] ?? 'unknown';
        if (isset($changes['status'])) {
            error_log("Status changed for record {$primaryValue}: " . ($payload['status'] ?? 'N/A'));
        }
        return $payload; // Return payload (modifications allowed) or null
    }
    $crud->after_update('notify_status_change');
  • before_delete(string|array $callback): self – Perform checks before deletions execute.
    // Using a named function (function must accept 3 params: $payload, $context, $crud)
    // $payload: current record data to be deleted
    // $context: ['operation'=>'delete', 'stage'=>'before', 'table'=>'...', 'primary_key'=>'...', 'primary_value'=>123, 'mode'=>'hard'|'soft']
    // $crud: Crud instance
    function check_delete_permission($payload, $context, $crud) {
        if (($payload['created_by'] ?? null) !== ($_SESSION['user_id'] ?? null)) {
            return false; // Return false to cancel deletion
        }
        return $payload; // Return payload to continue
    }
    $crud->before_delete('check_delete_permission');
  • after_delete(string|array $callback): self – Handle clean-up after deletions.
    // Using a named function (function must accept 3 params: $payload, $context, $crud)
    // $payload: the deleted record data
    // $context: ['operation'=>'delete', 'stage'=>'after', 'table'=>'...', 'primary_key'=>'...', 'primary_value'=>123, 'deleted'=>true, 'mode'=>'hard'|'soft', 'row'=>[...]]
    // $crud: Crud instance
    function cleanup_files($payload, $context, $crud) {
        if ($context['deleted'] && !empty($payload['avatar_path'])) {
            @unlink($payload['avatar_path']); // Clean up associated files
        }
        return null; // Return value ignored for after_delete
    }
    $crud->after_delete('cleanup_files');
  • before_fetch(string|array $callback): self – Adjust pagination payloads before data loads.
    // Using a named function (function must accept 3 params: $payload, $context, $crud)
    // $payload: ['page'=>1, 'per_page'=>20, 'search_term'=>'...', 'search_column'=>'...']
    // $context: ['operation'=>'fetch', 'stage'=>'before', 'table'=>'...', 'id'=>'...', 'resolved'=>[...]]
    // $crud: Crud instance
    function apply_user_filter($payload, $context, $crud) {
        // Add user-specific filtering to the fetch operation
        $payload['user_filter'] = $_SESSION['user_id'] ?? 0;
        return $payload; // Return modified payload
    }
    $crud->before_fetch('apply_user_filter');
  • after_fetch(string|array $callback): self – Transform row collections after loading.
    // Using a named function (function must accept 3 params: $payload, $context, $crud)
    // $payload: ['rows'=>[...], 'columns'=>[...], 'pagination'=>[...], 'meta'=>[...]]
    // $context: ['operation'=>'fetch', 'stage'=>'after', 'table'=>'...', 'id'=>'...', 'resolved'=>[...]]
    // $crud: Crud instance
    function add_computed_fields($payload, $context, $crud) {
        if (isset($payload['rows'])) {
            foreach ($payload['rows'] as &$row) {
                $row['full_name'] = trim(($row['first_name'] ?? '') . ' ' . ($row['last_name'] ?? ''));
            }
        }
        return $payload; // Return modified payload
    }
    $crud->after_fetch('add_computed_fields');
  • before_read(string|array $callback): self – Inspect requests before a single record load.
    // Using a named function (function must accept 3 params: $payload, $context, $crud)
    // $payload: ['primary_key_column'=>'id', 'primary_key_value'=>123]
    // $context: ['operation'=>'read', 'stage'=>'before', 'table'=>'...', 'id'=>'...']
    // $crud: Crud instance
    function log_record_access($payload, $context, $crud) {
        $primaryValue = $payload['primary_key_value'] ?? 'unknown';
        error_log("User " . ($_SESSION['user_id'] ?? 'anonymous') . " accessing record {$primaryValue}");
        return $payload; // Return payload or false to cancel read
    }
    $crud->before_read('log_record_access');
  • after_read(string|array $callback): self – React after a single record is retrieved.
    // Using a named function (function must accept 3 params: $payload, $context, $crud)
    // $payload: ['row'=>[...], 'primary_key_column'=>'id', 'primary_key_value'=>123]
    // $context: ['operation'=>'read', 'stage'=>'after', 'table'=>'...', 'id'=>'...', 'found'=>true]
    // $crud: Crud instance
    function add_permissions($payload, $context, $crud) {
        if (isset($payload['row']) && $context['found']) {
            $payload['row']['can_edit'] = ($payload['row']['created_by'] ?? null) === ($_SESSION['user_id'] ?? null);
        }
        return $payload; // Return modified payload
    }
    $crud->after_read('add_permissions');

⚙️ Actions & Toolbar

  • table_title(string $title): self – Set the headline shown above the table.
    $crud->table_title('Customer Accounts');
  • hide_table_title(bool $hidden = true): self – Hide or show the table title row.
    $crud->hide_table_title();
  • table_tooltip(string $tooltip): self – Provide a tooltip for the table header.
    $crud->table_tooltip('Live customer data');
  • table_icon(string $iconClass): self – Add an icon before the table title.
    $crud->table_icon('fas fa-users');
  • enable_add(bool $enabled = true): self – Toggle the add-record button.
    $crud->enable_add(false);
  • enable_view(bool $enabled = true, string|false $field = false, string|false $operand = false, mixed $value = false): self – Control per-row view permissions using the condition operators equals, not_equals, contains, gt, gte, lt, lte, in, not_in, empty, not_empty.
    $crud->enable_view(true, 'status', 'equals', 'active');
  • enable_edit(bool $enabled = true, string|false $field = false, string|false $operand = false, mixed $value = false): self – Decide which rows are editable with the same operator list as enable_view().
    $crud->enable_edit(true, 'locked', 'equals', 0);
  • enable_delete(bool $enabled = true, string|false $field = false, string|false $operand = false, mixed $value = false): self – Restrict delete access using the standard condition operators (equals, not_equals, etc.).
    $crud->enable_delete(true, 'status', 'not_equals', 'archived');
  • enable_duplicate(bool $enabled = true, string|false $field = false, string|false $operand = false, mixed $value = false): self – Enable duplication for qualifying rows using the same operator set as above.
    $crud->enable_duplicate(true, 'type', 'equals', 'template');
  • enable_batch_delete(bool $enabled = true): self – Show or hide batch deletion controls.
    $crud->enable_batch_delete();
  • add_bulk_action(string $name, string $label, array $options = []): self – Register a custom bulk update. Supply a 'fields' => ['column' => value] map and optionally a 'confirm' message before applying.
    $crud->add_bulk_action('flag', 'Flag Selected', [
        'fields'  => ['flagged' => 1],
        'confirm' => 'Flag all chosen records?',
    ]);
  • set_bulk_actions(array $actions): self – Replace the entire bulk action list. Each entry mirrors add_bulk_action() with name, label, and a 'fields' array.
    $crud->set_bulk_actions([
        ['name' => 'close', 'label' => 'Close', 'fields' => ['open' => 0]],
    ]);
  • enable_soft_delete(string $column, array $options = []): self – Configure soft-delete behaviour ('mode' accepts 'timestamp', 'literal', or 'expression'; provide 'value' for non-timestamp modes and optional 'additional' assignments).
    $crud->enable_soft_delete('deleted_at', ['mode' => 'timestamp']);
    $crud->enable_soft_delete('is_deleted', ['mode' => 'literal', 'value' => 1]);
  • set_soft_delete_assignments(array $assignments): self – Provide advanced soft-delete assignments; each entry should specify a column and 'mode' ('timestamp', 'literal', 'expression') plus an optional 'value'.
    $crud->set_soft_delete_assignments([
        ['column' => 'deleted_at', 'mode' => 'timestamp'],
        'deleted_by' => ['mode' => 'literal', 'value' => Auth::id()],
    ]);
  • disable_soft_delete(): self – Remove soft-delete configuration.
    $crud->disable_soft_delete();
  • enable_delete_confirm(bool $enabled = true): self – Toggle confirmation prompts before deletion.
    $crud->enable_delete_confirm(false);
  • enable_export_csv(bool $enabled = true): self – Show or hide the CSV export button.
    $crud->enable_export_csv();
  • enable_export_excel(bool $enabled = true): self – Show or hide the Excel export button.
    $crud->enable_export_excel();
  • add_link_button(string|array $urlOrConfig, ?string $iconClass = null, ?string $label = null, ?string $buttonClass = null, array $options = []): self – Append a custom toolbar button; call it multiple times to stack more buttons. You can keep the legacy positional arguments or pass a full array payload (keys such as 'url', 'icon', 'label', 'button_class', 'options') for consistency with other configuration helpers. The $options array lets you set arbitrary HTML attributes like ['target' => '_blank'].
    // Array payload for better readability
    $crud->add_link_button([
        'url' => '/reports',
        'icon' => 'fas fa-file-alt',
        'label' => 'Reports',
        'button_class' => 'btn btn-sm btn-outline-info',
        'options' => ['target' => '_blank']
    ]);
    
    // Legacy positional signature remains supported
    $crud->add_link_button('/reports/export', 'fas fa-download', 'Export', 'btn btn-sm btn-secondary');
  • add_toolbar_action(string|array $urlOrConfig, ?string $iconClass = null, ?string $label = null, ?string $buttonClass = null, array $options = []): self – Inject a static action button into the top toolbar (rendered to the left of bulk/delete/add controls). Accepts the same payload formats as add_link_button(), so you can mix and match array payloads or the legacy positional signature plus an $options array for custom attributes.
    $crud->add_toolbar_action([
        'url' => route('reports.index'),
        'icon' => 'fas fa-chart-line',
        'label' => 'Reports',
        'button_class' => 'btn btn-sm btn-outline-primary',
        'options' => ['target' => '_blank']
    ]);
    
    // Legacy signature stays compatible as well
    $crud->add_toolbar_action('/reports/new', 'fas fa-plus-circle', 'New Report');
  • add_multi_link_button(array $mainButton = [], array $items = []): self – Append a dropdown button that expands into multiple links. Supply at least one actionable entry in $items, each with 'url' and 'label' plus optional 'icon' and 'options' for per-link attributes (placeholders like {id} are resolved per-row). To insert a divider between links, either pass an empty array or ['type' => 'divider'] as an item, and optionally add 'title' => 'Section Name' to print a small caption under the separator. You can also push built-in destructive utilities into the dropdown via entries such as ['type' => 'duplicate', 'label' => 'Clone'] or ['type' => 'delete', 'label' => 'Remove']; these menu items stay visible even when enable_duplicate(false) / enable_delete(false) or action conditions hide the stock row buttons, while still reusing the existing handlers (rows that fail their condition will be rejected when clicked). Need a quick input before redirecting? Use ['type' => 'input', 'input_name' => 'query'] (falls back to 'exampleinput') to pop up a prompt, append ?query=value to the resolved URL, and follow it (add 'prompt' => 'Search email' to customize the question). $mainButton configures the trigger with keys such as 'icon', 'label', 'button_class', 'menu_class', 'container_class', and 'options'; omit any key to fall back to sensible defaults. Invoke this repeatedly to add more dropdown clusters.
    $crud->add_multi_link_button([
        'icon' => 'fas fa-ellipsis-h',
        'label' => 'More Actions',
        'options' => ['data-bs-auto-close' => 'outside'],
        'container_class' => 'btn-group dropstart'
    ], [
        ['url' => '/customers/{id}', 'label' => 'Profile', 'icon' => 'fas fa-user'],
        ['type' => 'input', 'url' => '/customers/search', 'label' => 'Email lookup', 'input_name' => 'email', 'icon' => 'fas fa-search', 'prompt' => 'Enter email to search'],
        ['url' => '/customers/{id}/orders', 'label' => 'Orders', 'icon' => 'fas fa-receipt', 'options' => ['target' => '_blank']],
        ['type' => 'divider', 'title' => 'Danger zone'],
        ['type' => 'duplicate', 'label' => 'Clone'],
        ['type' => 'delete', 'label' => 'Remove']
    ]);
  • enable_select2(bool $enabled = true): self – Enable or disable Select2 widgets for dropdown fields on this CRUD instance, overriding the global CrudConfig::$enable_select2 setting.
    $crud->enable_select2(true); // Use Select2 for this table
    $crud->enable_select2(false); // Disable Select2 for this table

🔍 Sorting, Filtering & Relationships

  • order_by(string|array $fields, string $direction = 'asc'): self – Define default ordering for query results; direction must be 'asc' or 'desc' (case-insensitive).
    $crud->order_by(['status' => 'asc', 'created_at' => 'desc']);
  • enable_filters(bool $enabled = true): self – Show or hide the Query Builder filters UI in the toolbar.
    $crud->enable_filters(); // enable filters UI
  • disable_sort(string|array $columns): self – Prevent UI sorting on columns.
    $crud->disable_sort(['notes']);
  • search_columns(string|array $columns, string|false $default = false): self – Control which columns participate in quick search; set $default to a column name or false to leave the selector blank.
    $crud->search_columns(['name', 'email'], 'name');

    💡 Using MySQL? FastCRUD will automatically apply JSON_SEARCH() for any JSON columns so you can search the nested document structure from the quick search box and the Query Builder without extra configuration.

  • no_quotes(string|array $fields): self – Treat specified expressions as raw SQL by passing column names or raw expressions (array or comma-separated string).
    $crud->no_quotes('JSON_EXTRACT(meta, "$.flag")');
  • where(string $condition): self – Add an AND-joined raw SQL condition. Provide the complete SQL snippet you want to append.
    $crud->where('status = "active"');
  • or_where(string $condition): self – Add an OR-joined raw SQL condition.
    $crud->or_where('role = "admin"');
  • join(string|array $fields, string $joinTable, string $joinField, string|array|false $alias = false, bool $notInsert = false): self – Join related tables for display; $alias may be false, a single alias, or an array of aliases matching each field.
    $crud->join('role_id', 'roles', 'id', 'r');
  • relation(string|array $fields, string $relatedTable, string $relatedField, string|array $relName, array $relWhere = [], string|false $orderBy = false, bool $multi = false): self – Populate select fields from related tables; $relName can be a column string or an array of columns to concatenate, $orderBy accepts a SQL fragment or false, and $multi = true produces multi-select options.
    $crud->relation('country_id', 'countries', 'id', 'name', ['active' => 1]);

📊 Query Extensions

  • query(string $query): self – Replace the default select statement with your own SQL (must select the base table columns required by FastCRUD).
    $crud->query('SELECT * FROM view_users');
  • subselect(string $columnName, string $sql): self – Add a derived column via subquery.
    $crud->subselect('orders_count', 'SELECT COUNT(*) FROM orders o WHERE o.user_id = users.id');

🔗 Nested Data

  • nested_table(string $instanceName, string $parentColumn, string $innerTable, string $innerTableField, ?callable $configurator = null): self – Attach expandable child tables to each row; the method returns the child Crud instance so you can continue configuring it.
    $crud->nested_table('orders', 'id', 'orders', 'user_id', function (Crud $child) {
        $child->columns(['id', 'total'])->limit(5);
    });

🎨 Rendering & Data Access

  • render(?string $mode = null, mixed $primaryKeyValue = null): string – Output the full FastCRUD widget; $mode can be null, 'create', 'edit', or 'view' and $primaryKeyValue targets a specific row for non-create modes.
    echo $crud->render();
  • getId(): string – Retrieve the generated component ID for DOM targeting.
    $componentId = $crud->getId();
  • getTableData(int $page = 1, ?int $perPage = null, ?string $searchTerm = null, ?string $searchColumn = null): array – Fetch paginated data for AJAX responses; pass $perPage = null (or 'all' via AJAX) to load everything, and $searchColumn = null to search all configured columns.
    $payload = $crud->getTableData(1, 10, 'sam', 'name');

📄 Record Operations

  • createRecord(array $fields): ?array – Insert a new record with behaviour support; pass a column => value array and receive the inserted row or null if cancelled.
    $user = $crud->createRecord(['name' => 'Sam', 'email' => 'sam@example.com']);
  • updateRecord(string $primaryKeyColumn, mixed $primaryKeyValue, array $fields, string $mode = 'edit'): ?array – Update a record and return the latest data; $mode is usually 'edit' but also accepts 'create' or 'view' to align with form behaviours.
    $updated = $crud->updateRecord('id', 5, ['status' => 'active']);
  • deleteRecord(string $primaryKeyColumn, mixed $primaryKeyValue): bool – Delete or soft-delete a single record; returns false if the action is disabled or rejected by callbacks.
    $crud->deleteRecord('id', 9);
  • deleteRecords(string $primaryKeyColumn, array $primaryKeyValues): array – Delete multiple records at once; the values array should contain scalar primary key values.
    $result = $crud->deleteRecords('id', [1, 2, 3]);
  • updateRecords(string $primaryKeyColumn, array $primaryKeyValues, array $fields, string $mode = 'edit'): array – Apply bulk updates to several records; $fields is a column => value map and $mode follows the 'create'|'edit'|'view'|'all' pattern.
    $crud->updateRecords('id', [2, 3], ['status' => 'archived']);
  • duplicateRecord(string $primaryKeyColumn, mixed $primaryKeyValue): ?array – Clone an existing record including allowed columns, or null when duplication is disabled or blocked.
    $copy = $crud->duplicateRecord('id', 7);
  • getRecord(string $primaryKeyColumn, mixed $primaryKeyValue): ?array – Retrieve a single record with presentation rules applied.
    $record = $crud->getRecord('id', 7);

🔄 FastCrud\CrudAjax - AJAX Request Handler

  • CrudAjax::handle(): void – Process the current FastCRUD AJAX request (fastcrud_ajax=1) and emit JSON/CSV/Excel responses as needed.
    if (CrudAjax::isAjaxRequest()) {
        CrudAjax::handle();
    }
  • CrudAjax::isAjaxRequest(): bool – Detect whether a request targets FastCRUD by checking for fastcrud_ajax=1 in $_GET or $_POST.
    if (CrudAjax::isAjaxRequest()) {
        // delegate to FastCRUD handlers
    }
  • CrudAjax::autoHandle(): void – Automatically handle the request when wired into Crud::init().
    CrudAjax::autoHandle();

⚙️ FastCrud\CrudConfig - Configuration Manager

Database Configuration

  • CrudConfig::setDbConfig(array $configuration): void – Store PDO connection settings (driver may be 'mysql', 'pgsql', or 'sqlite', with optional 'host', 'port', 'database', 'username', 'password', and PDO 'options').
    CrudConfig::setDbConfig([
        'driver' => 'pgsql',
        'host' => 'db',
        'database' => 'app',
        'username' => 'user',
        'password' => 'secret',
    ]);
  • CrudConfig::getDbConfig(): array – Retrieve the stored configuration array.
    $config = CrudConfig::getDbConfig();

Global Settings

  • CrudConfig::getUploadPath(): string – Resolve the base directory for uploads (defaults to 'public/uploads' when unset).
    $path = CrudConfig::getUploadPath();
  • CrudConfig::$upload_path – Set the default filesystem destination for uploads (default: 'public/uploads').
    CrudConfig::$upload_path = 'assets/uploads';
  • CrudConfig::$upload_serve_path – Override the public URL/base path that browsers should use when referencing uploaded files (defaults to CrudConfig::$upload_path).
    CrudConfig::$upload_path = '/var/uploads/files';
    CrudConfig::$upload_serve_path = '/uploads';
  • CrudConfig::$images_in_grid – Enable/disable image thumbnails in grid view (default: true).
    CrudConfig::$images_in_grid = false; // Hide images in grid
  • CrudConfig::$images_in_grid_height – Set thumbnail height in pixels for grid images (default: 55).
    CrudConfig::$images_in_grid_height = 80; // Larger thumbnails
  • CrudConfig::$bools_in_grid – Display boolean fields as toggle switches in grid cells (default: true).
    CrudConfig::$bools_in_grid = false; // Show as text instead
  • CrudConfig::$enable_select2 – Enable Select2 widgets globally for all dropdowns (default: false).
    CrudConfig::$enable_select2 = true; // Use Select2 by default
  • CrudConfig::$enable_filters – Show query builder filter controls in toolbar by default (default: false).
    CrudConfig::$enable_filters = true; // Enable filters globally
  • CrudConfig::$hide_table_title – Hide the table header block (title, icon, tooltip) by default (default: false).
    CrudConfig::$hide_table_title = true; // Remove table titles globally
  • CrudConfig::$default_column_truncate – Automatically truncate every column unless overridden per column (defaults to 300). Accepts either an integer length (suffix defaults to '…'), an array like ['length' => 60, 'suffix' => '...'], or null to disable globally. When you only need to change a single Crud instance, call $crud->default_column_truncate(...) instead of touching the global flag.
    use FastCrud\CrudConfig;
    
    CrudConfig::$default_column_truncate = ['length' => 60, 'suffix' => '...'];
    // or
    CrudConfig::$default_column_truncate = 80;
    // disable the global truncation entirely
    CrudConfig::$default_column_truncate = null;

🎨 FastCrud\CrudStyle - Global Styling Configuration

Customize default CSS classes for buttons, rows, and components throughout FastCRUD by modifying these public static properties. All properties use Bootstrap 5 classes by default but can be overridden with any CSS framework.

🔘 Toolbar & Action Buttons

  • CrudStyle::$add_button_class – Add new record button (default: 'btn btn-sm btn-success')
  • CrudStyle::$toolbar_action_button_global_class – Apply the same classes to all toolbar action buttons (default: '', values override individual toolbar buttons unless explicitly customised)
  • CrudStyle::$link_button_class – Custom link buttons added via add_link_button() (default: 'btn btn-sm btn-outline-secondary')
  • CrudStyle::$search_button_class – Search form submit button (default: 'btn btn-outline-primary')
  • CrudStyle::$search_clear_button_class – Search form clear button (default: 'btn btn-outline-secondary')
  • CrudStyle::$filters_button_class – Query builder filters toggle button (default: 'btn btn-sm btn-outline-secondary')
  • CrudStyle::$batch_delete_button_class – Bulk delete button (default: 'btn btn-sm btn-danger')
  • CrudStyle::$bulk_apply_button_class – Bulk actions apply button (default: 'btn btn-sm btn-outline-primary')
  • CrudStyle::$export_csv_button_class – CSV export button (default: 'btn btn-sm btn-outline-secondary')
  • CrudStyle::$export_excel_button_class – Excel export button (default: 'btn btn-sm btn-outline-secondary')

🎯 Row Action Buttons

  • CrudStyle::$action_button_global_class – Apply the same classes to all row action buttons (default: '', values override individual buttons unless explicitly customised)
  • CrudStyle::$view_action_button_class – View/read record button (default: 'btn btn-sm btn-secondary')
  • CrudStyle::$edit_action_button_class – Edit record button (default: 'btn btn-sm btn-primary')
  • CrudStyle::$delete_action_button_class – Delete record button (default: 'btn btn-sm btn-danger')
  • CrudStyle::$duplicate_action_button_class – Duplicate record button (default: 'btn btn-sm btn-info')

Tip: Use CrudStyle::$action_button_global_class for row action buttons and CrudStyle::$toolbar_action_button_global_class for top toolbar actions while still allowing per-button overrides. Setting a toolbar button property to any non-empty string (even the documented default) keeps the global toolbar class from applying to that button.

🗂️ Panel & Form Buttons

  • CrudStyle::$panel_save_button_class – Save button in edit/create panels (default: 'btn btn-primary')
  • CrudStyle::$panel_cancel_button_class – Cancel button in edit/create panels (default: 'btn btn-outline-secondary')

🌲 Nested Tables & Grid

  • CrudStyle::$nested_toggle_button_classes – Expand/collapse buttons for nested tables (default: 'btn btn-link p-0')
  • CrudStyle::$edit_view_row_highlight_class – Table row highlight while editing/viewing (default: 'table-active')
  • CrudStyle::$bools_in_grid_color – Color variant for boolean switches in grid cells (default: 'primary')

🎯 Action Button Icons

  • CrudStyle::$view_action_icon – Icon class for view/read action buttons (default: 'fas fa-eye')
  • CrudStyle::$edit_action_icon – Icon class for edit action buttons (default: 'fas fa-edit')
  • CrudStyle::$delete_action_icon – Icon class for delete action buttons (default: 'fas fa-trash')
  • CrudStyle::$duplicate_action_icon – Icon class for duplicate action buttons (default: 'far fa-copy')
  • CrudStyle::$expand_action_icon – Icon class for expanding nested records (default: 'fas fa-chevron-down')
  • CrudStyle::$collapse_action_icon – Icon class for collapsing nested records (default: 'fas fa-chevron-up')
  • CrudStyle::$x_icon_class – Icon class for dismiss/remove buttons (default: 'fas fa-xmark')
  • CrudStyle::$action_icon_size – Font size for action button icons (default: '1.05rem')

💡 Usage Examples

use FastCrud\CrudStyle;

// 🎨 Customize for dark theme
CrudStyle::$add_button_class = 'btn btn-sm btn-outline-success';
CrudStyle::$edit_action_button_class = 'btn btn-sm btn-outline-warning';
CrudStyle::$delete_action_button_class = 'btn btn-sm btn-outline-danger';
CrudStyle::$edit_view_row_highlight_class = 'table-dark';

// 🌈 Customize for colorful theme
CrudStyle::$search_button_class = 'btn btn-info';
CrudStyle::$export_csv_button_class = 'btn btn-success';
CrudStyle::$export_excel_button_class = 'btn btn-warning';
CrudStyle::$bools_in_grid_color = 'success';

// 📱 Customize for mobile/compact theme
CrudStyle::$add_button_class = 'btn btn-xs btn-success';
CrudStyle::$view_action_button_class = 'btn btn-xs btn-outline-secondary';
CrudStyle::$edit_action_button_class = 'btn btn-xs btn-outline-primary';
CrudStyle::$delete_action_button_class = 'btn btn-xs btn-outline-danger';

// 🎯 Customize panel buttons
CrudStyle::$panel_save_button_class = 'btn btn-lg btn-success';
CrudStyle::$panel_cancel_button_class = 'btn btn-lg btn-secondary';

// 🌲 Customize nested table styling
CrudStyle::$nested_toggle_button_classes = 'btn btn-outline-primary btn-sm';

// 🎯 Customize action icons
CrudStyle::$view_action_icon = 'fas fa-search';
CrudStyle::$edit_action_icon = 'fas fa-pencil';
CrudStyle::$delete_action_icon = 'fas fa-times';
CrudStyle::$duplicate_action_icon = 'fas fa-clone';
CrudStyle::$expand_action_icon = 'fas fa-plus';
CrudStyle::$collapse_action_icon = 'fas fa-minus';
CrudStyle::$action_icon_size = '1.2rem';

// 🌐 Using different icon libraries (Bootstrap Icons)
CrudStyle::$view_action_icon = 'bi bi-eye';
CrudStyle::$edit_action_icon = 'bi bi-pencil';
CrudStyle::$delete_action_icon = 'bi bi-trash';
CrudStyle::$duplicate_action_icon = 'bi bi-files';

// 💼 Using custom CSS framework (Tailwind CSS example)
CrudStyle::$add_button_class = 'bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded';
CrudStyle::$edit_action_button_class = 'bg-blue-500 hover:bg-blue-700 text-white font-bold py-1 px-2 rounded text-sm';

💾 FastCrud\DB - Database Connection

  • DB::connection(): PDO – Access the shared PDO instance used by FastCRUD; connection settings come from CrudConfig::setDbConfig().
    $pdo = DB::connection();
  • DB::setConnection(PDO $connection): void – Inject your own PDO instance (for example, in tests).
    DB::setConnection($testPdo);
  • DB::disconnect(): void – Clear the cached PDO connection.
    DB::disconnect();

⚠️ FastCrud\ValidationException - Validation Errors

  • __construct(string $message, array $errors = [], int $code = 0, ?Throwable $previous = null) – Create a validation exception with field errors supplied as ['field' => 'message'].
    throw new ValidationException('Invalid data', ['email' => 'Taken']);
  • getErrors(): array – Retrieve the error messages that were supplied.
    $errors = $exception->getErrors();

📝 License

This project is licensed under the MIT License - see the LICENSE file for details.

Made with ❤️ by the FastCRUD Team

🐛 Found a bug? Report it here
⭐ Like this project? Give it a star!
💬 Questions? Start a discussion