PHP code example of adesin-fr / inertiajs-tables-laravel-query-builder

1. Go to this page and download the library: Download adesin-fr/inertiajs-tables-laravel-query-builder library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

adesin-fr / inertiajs-tables-laravel-query-builder example snippets


use AdesinFr\LaravelQueryBuilderInertiaJs\InertiaTable;
use Spatie\QueryBuilder\AllowedFilter;
use Spatie\QueryBuilder\QueryBuilder;

// Method 1: Direct QueryBuilder with callback
return InertiaTable::make()
    ->withQueryBuilder(
        QueryBuilder::for(User::class)
            ->allowedFilters([
                AllowedFilter::partial('name'),
                AllowedFilter::exact('status'),
                NumberFilter::getQueryBuilderFilter('age'),
            ])
            ->allowedSorts(['name', 'email', 'created_at'])
            ->defaultSort('name')
    )
    ->column('name', 'Name', sortable: true, searchable: true)
    ->column('email', 'Email', sortable: true, searchable: true)
    ->column('status', 'Status')
    ->column('age', 'Age', sortable: true)
    ->withGlobalSearch()
    ->selectFilter('status', [
        'active' => 'Active',
        'inactive' => 'Inactive',
    ])
    ->numberFilter('age', 'Age')
    ->render('Users/Index');

// Method 2: QueryBuilder callback (useful for multi-table setups)
return InertiaTable::make()
    ->withQueryBuilderCallback(function () {
        return QueryBuilder::for(User::class)
            ->allowedFilters([
                AllowedFilter::partial('name'),
                AllowedFilter::exact('status'),
                NumberFilter::getQueryBuilderFilter('age'),
            ])
            ->allowedSorts(['name', 'email', 'created_at'])
            ->defaultSort('name');
    })
    ->column('name', 'Name', sortable: true, searchable: true)
    ->column('email', 'Email', sortable: true, searchable: true)
    ->withResource(\App\Http\Resources\UserResource::class) // Optional resource transformation
    ->render('Users/Index');

use AdesinFr\LaravelQueryBuilderInertiaJs\InertiaTable;

return InertiaTable::view('Dashboard/Index')
    ->table('users', function (InertiaTable $table) {
        $table->withQueryBuilderCallback(function () {
            // Configure query parameters for this table
            InertiaTable::updateQueryBuilderParameters('users');

            return QueryBuilder::for(User::class)
                ->allowedFilters([AllowedFilter::partial('name')])
                ->allowedSorts(['name', 'email'])
                ->defaultSort('name');
        })
        ->column('name', 'Name', sortable: true, searchable: true)
        ->column('email', 'Email', sortable: true)
        ->withGlobalSearch()
        ->paginateMethod('paginate');
    })
    ->table('products', function (InertiaTable $table) {
        $table->withQueryBuilderCallback(function () {
            // Configure query parameters for this table
            InertiaTable::updateQueryBuilderParameters('products');

            return QueryBuilder::for(Product::class)
                ->allowedFilters([
                    AllowedFilter::partial('title'),
                    NumberFilter::getQueryBuilderFilter('price')
                ])
                ->allowedSorts(['title', 'price'])
                ->defaultSort('title');
        })
        ->column('title', 'Title', sortable: true, searchable: true)
        ->column('price', 'Price', sortable: true)
        ->numberFilter('price', 'Price')
        ->paginateMethod('simplePaginate');
    })
    ->with(['customData' => 'Additional data for the view'])
    ->render();

return InertiaTable::make()
    ->name('custom-table')              // Table name for multi-table setups
    ->pageName('customPage')            // Custom pagination parameter name
    ->perPageOptions([10, 25, 50])     // Available per-page options
    ->defaultSort('created_at')         // Default sorting column
    ->handleExport(true)                // Enable/disable CSV export (default: true)
    ->paginateMethod('simplePaginate')  // Pagination method
    ->withResource(\App\Http\Resources\UserResource::class) // Resource transformation
    ->with(['additional' => 'data'])    // Additional data for the view
    ->render('Users/Index');

return Inertia::render('Users/Index')->table(function (InertiaTable $table) {
    $table->searchInput('name');
    $table->selectFilter('status', ['active' => 'Active']);
});

$users = QueryBuilder::for(User::class)->paginate($request->perPage ?: 10);

return Inertia::render('Users/Index', [
    'users' => $users
]);

use AdesinFr\LaravelQueryBuilderInertiaJs\InertiaTable;

Inertia::render('Page/Index')->table(function (InertiaTable $table) {
	$table->searchInput('name');

	$table->searchInput(
		key: 'framework',
		label: 'Find your framework',
		defaultValue: 'Laravel'
	);
});

Inertia::render('Page/Index')->table(function (InertiaTable $table) {
	$table->selectFilter('language_code', [
		'en' => 'Engels',
		'nl' => 'Nederlands',
	]);
});

Inertia::render('Page/Index')->table(function (InertiaTable $table) {
	$table->selectFilter(
		key: 'language_code',
		options: $languages,
		label: 'Language',
		defaultValue: 'nl',
		noFilterOption: true,
		noFilterOptionLabel: 'All languages'
	);
});

Inertia::render('Page/Index')->table(function (InertiaTable $table) {
	$table->toggleFilter('is_verified');
});

Inertia::render('Page/Index')->table(function (InertiaTable $table) {
	$table->toggleFilter(
		key: 'is_verified',
		label: 'Is email verified',
		defaultValue: true,
	);
});

Inertia::render('Page/Index')->table(function (InertiaTable $table) {
	$table->numberRangeFilter('invoice_recall_count', 5);
});

Inertia::render('Page/Index')->table(function (InertiaTable $table) {
	$table->numberRangeFilter(
		key: 'invoice_recall_count',
		max: 5,
		min: 0,
		prefix: '',
		suffix: '',
		step: 1,
		label: 'Invoice recall count',
		defaultValue: [1,4],
	);
});

$users = QueryBuilder::for(/*...*/)
			->allowedFilters([NumberRangeFilter::getQueryBuilderFilter('invoice_recall_count')]);

Inertia::render('Page/Index')->table(function (InertiaTable $table) {
	$table->numberFilter('age');
});

Inertia::render('Page/Index')->table(function (InertiaTable $table) {
	$table->numberFilter(
		key: 'age',
		label: 'Filter by age',
		defaultOperation: 'greater_than',
		defaultValue: 18,
		column_key: 'age' // Associate with a specific column
	);
});

use AdesinFr\LaravelQueryBuilderInertiaJs\Filters\NumberFilter;

$users = QueryBuilder::for(User::class)
		->allowedFilters([
			NumberFilter::getQueryBuilderFilter('age')
		]);

$users = QueryBuilder::for(/*...*/)
			->allowedFilters([NumberRangeFilter::getQueryBuilderFilter('invoice_recall_count')]);

Inertia::render('Page/Index')->table(function (InertiaTable $table) {
	$table->column('name', 'Name')
		  ->column('status', 'Status')
		  ->column('email', 'Email')
		  ->column('created_at', 'Created');

	// Associate a select filter with the 'status' column
	$table->selectFilter(
		key: 'status',
		options: [
			'active' => 'Active',
			'inactive' => 'Inactive',
			'pending' => 'Pending'
		],
		label: 'Status',
		column_key: 'status' // 🎯 Associates the filter with the status column
	);

	// Associate a toggle filter with the 'email' column
	$table->toggleFilter(
		key: 'email_verified',
		label: 'Email Verified',
		column_key: 'email'
	);

	// Associate a number range filter with the 'created_at' column
	$table->numberRangeFilter(
		key: 'days_since_creation',
		max: 365,
		min: 0,
		label: 'Days Since Creation',
		column_key: 'created_at'
	);
});

Inertia::render('Page/Index')->table(function (InertiaTable $table) {
	$table->column('name', 'User Name');

	$table->column(
		key: 'name',
		label: 'User Name',
		canBeHidden: true,
		hidden: false,
		sortable: true,
		searchable: true
		headerClass: 'hidden md:table-cell', // This cell will be hidden on small screens
		bodyClass: 'hidden md:table-cell', // This cell will be hidden on small screens
	);
});

Inertia::render('Page/Index')->table(function (InertiaTable $table) {
	$table->withGlobalSearch();

	$table->withGlobalSearch('Search through the data...');
});

InertiaTable::defaultGlobalSearch();
InertiaTable::defaultGlobalSearch('Default custom placeholder');
InertiaTable::defaultGlobalSearch(false); // disable



namespace App\Http\Controllers;

use App\Models\User;
use App\Http\Resources\UserResource;
use AdesinFr\LaravelQueryBuilderInertiaJs\InertiaTable;
use AdesinFr\LaravelQueryBuilderInertiaJs\Filters\NumberFilter;
use Spatie\QueryBuilder\AllowedFilter;
use Spatie\QueryBuilder\QueryBuilder;

class UserIndexController
{
    public function __invoke()
    {
        $globalSearch = AllowedFilter::callback('global', function ($query, $value) {
            $query->where(function ($query) use ($value) {
                Collection::wrap($value)->each(function ($value) use ($query) {
                    $query
                        ->orWhere('name', 'LIKE', "%{$value}%")
                        ->orWhere('email', 'LIKE', "%{$value}%");
                });
            });
        });

        return InertiaTable::make()
            ->withQueryBuilder(
                QueryBuilder::for(User::class)
                    ->defaultSort('name')
                    ->allowedSorts(['name', 'email', 'created_at'])
                    ->allowedFilters([
                        'name',
                        'email',
                        'status',
                        NumberFilter::getQueryBuilderFilter('age'),
                        $globalSearch
                    ])
            )
            ->withGlobalSearch()
            ->defaultSort('name')
            ->column('name', 'User Name', canBeHidden: false, sortable: true, searchable: true)
            ->column('email', 'Email Address', sortable: true, searchable: true)
            ->column('status', 'Status')
            ->column('age', 'Age', sortable: true)
            ->column('created_at', 'Created', sortable: true)
            ->column('actions', 'Actions', canBeHidden: false, sortable: false)
            ->selectFilter('status', [
                'active' => 'Active',
                'inactive' => 'Inactive',
                'pending' => 'Pending'
            ])
            ->numberFilter('age', 'Filter by Age')
            ->withResource(UserResource::class)
            ->handleExport(true)
            ->render('Users/Index');
    }
}



namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Support\Collection;
use Inertia\Inertia;
use AdesinFr\LaravelQueryBuilderInertiaJs\InertiaTable;
use Spatie\QueryBuilder\AllowedFilter;
use Spatie\QueryBuilder\QueryBuilder;

class UserIndexController
{
	public function __invoke()
	{
		$globalSearch = AllowedFilter::callback('global', function ($query, $value) {
			$query->where(function ($query) use ($value) {
				Collection::wrap($value)->each(function ($value) use ($query) {
					$query
						->orWhere('name', 'LIKE', "%{$value}%")
						->orWhere('email', 'LIKE', "%{$value}%");
				});
			});
		});

		$users = QueryBuilder::for(User::class)
			->defaultSort('name')
			->allowedSorts(['name', 'email', 'language_code'])
			->allowedFilters(['name', 'email', 'language_code', $globalSearch])
			->paginate()
			->withQueryString();

		return Inertia::render('Users/Index', [
			'users' => $users,
		])->table(function (InertiaTable $table) {
			$table
			  ->withGlobalSearch()
			  ->defaultSort('name')
			  ->column(key: 'name', searchable: true, sortable: true, canBeHidden: false)
			  ->column(key: 'email', searchable: true, sortable: true)
			  ->column(key: 'language_code', label: 'Language')
			  ->column(label: 'Actions')
			  ->selectFilter(key: 'language_code', label: 'Language', options: [
				  'en' => 'English',
				  'nl' => 'Dutch',
			  ]);
		});
	}
}

// With the Fluent API
return InertiaTable::make()
    ->name('users-table') // Table name is ue)
    ->column('status', 'Status')
    ->render('Users/Index');

// With the Traditional API
return Inertia::render('Users/Index')->table(function (InertiaTable $table) {
    $table->column('name', 'User Name', sortable: true);
    $table->column('email', 'Email', sortable: true);
    $table->column('status', 'Status');
});

// With the Fluent API
return InertiaTable::make()
    ->column('id', 'ID', canBeHidden: false) // This column is pinned and cannot be hidden
    ->column('name', 'User Name', canBeHidden: true, sortable: true)
    ->column('email', 'Email', canBeHidden: true, sortable: true)
    ->column('actions', 'Actions', canBeHidden: false) // Actions column is also pinned
    ->render('Users/Index');

// With the Traditional API
return Inertia::render('Users/Index')->table(function (InertiaTable $table) {
    $table->column(key: 'id', label: 'ID', canBeHidden: false);
    $table->column(key: 'name', label: 'User Name', canBeHidden: true, sortable: true);
    $table->column(key: 'email', label: 'Email', canBeHidden: true, sortable: true);
    $table->column(key: 'actions', label: 'Actions', canBeHidden: false);
});

use AdesinFr\LaravelQueryBuilderInertiaJs\InertiaTable;
use Spatie\QueryBuilder\AllowedFilter;
use Spatie\QueryBuilder\QueryBuilder;

return InertiaTable::view('TwoTables')
    ->table('companies', function (InertiaTable $table) {
        $table->withQueryBuilderCallback(function () {
            // Update query parameters for this table
            InertiaTable::updateQueryBuilderParameters('companies');

            return QueryBuilder::for(Company::class)
                ->defaultSort('name')
                ->allowedSorts(['name', 'email'])
                ->allowedFilters(['name', 'email']);
        })
        ->pageName('companiesPage')
        ->column('name', 'Company Name', searchable: true, sortable: true)
        ->column('email', 'Contact Email', searchable: true, sortable: true)
        ->column('address', 'Address', searchable: true)
        ->withGlobalSearch()
        ->defaultSort('name');
    })
    ->table('users', function (InertiaTable $table) {
        $table->withQueryBuilderCallback(function () {
            // Update query parameters for this table
            InertiaTable::updateQueryBuilderParameters('users');

            return QueryBuilder::for(User::class)
                ->defaultSort('name')
                ->allowedSorts(['name', 'email'])
                ->allowedFilters(['name', 'email']);
        })
        ->pageName('usersPage')
        ->column('name', 'User Name', searchable: true, sortable: true)
        ->column('email', 'User Email', searchable: true, sortable: true)
        ->withGlobalSearch()
        ->defaultSort('name');
    })
    ->render();

InertiaTable::updateQueryBuilderParameters('companies');

$companies = QueryBuilder::for(Company::query())
	->defaultSort('name')
	->allowedSorts(['name', 'email'])
	->allowedFilters(['name', 'email'])
	->paginate(pageName: 'companiesPage')
	->withQueryString();

InertiaTable::updateQueryBuilderParameters('users');

$users = QueryBuilder::for(User::query())
	->defaultSort('name')
	->allowedSorts(['name', 'email'])
	->allowedFilters(['name', 'email'])
	->paginate(pageName: 'usersPage')
	->withQueryString();

return Inertia::render('TwoTables', [
	'companies' => $companies,
	'users'     => $users,
])->table(function (InertiaTable $inertiaTable) {
	$inertiaTable
		->name('users')
		->pageName('usersPage')
		->defaultSort('name')
		->column(key: 'name', searchable: true)
		->column(key: 'email', searchable: true);
})->table(function (InertiaTable $inertiaTable) {
	$inertiaTable
		->name('companies')
		->pageName('companiesPage')
		->defaultSort('name')
		->column(key: 'name', searchable: true)
		->column(key: 'address', searchable: true);
});

// Fluent API - CSV export is automatically enabled
return InertiaTable::make()
    ->withQueryBuilder(
        QueryBuilder::for(User::class)
            ->allowedFilters([AllowedFilter::partial('name')])
            ->allowedSorts(['name', 'email', 'created_at'])
            ->defaultSort('name')
    )
    ->column('name', 'Name', sortable: true, searchable: true)
    ->column('email', 'Email', sortable: true, searchable: true)
    ->column('created_at', 'Created At', sortable: true)
    ->withGlobalSearch()
    ->selectFilter('status', ['active' => 'Active', 'inactive' => 'Inactive'])
    ->handleExport(true) // Explicitly enable (default: true)
    ->render('Users/Index');

// Traditional API - CSV export is automatically enabled
return Inertia::render('Users/Index', ['users' => $users])
    ->table(function (InertiaTable $table) {
        $table->column('name', 'Name')
              ->column('email', 'Email')
              ->searchInput('name')
              ->handleExport(true); // Explicitly enable (default: true)
    });

return InertiaTable::view('Dashboard/Index')
    ->table('users', function (InertiaTable $table) {
        $table->withQueryBuilderCallback(function () {
            InertiaTable::updateQueryBuilderParameters('users');
            return QueryBuilder::for(User::class)
                ->allowedFilters([AllowedFilter::partial('name')]);
        })
        ->column('name', 'Name', searchable: true)
        ->column('email', 'Email')
        ->withGlobalSearch();
        // CSV export is automatically enabled for this table
    })
    ->table('products', function (InertiaTable $table) {
        $table->withQueryBuilderCallback(function () {
            InertiaTable::updateQueryBuilderParameters('products');
            return QueryBuilder::for(Product::class)
                ->allowedFilters([AllowedFilter::partial('title')]);
        })
        ->column('title', 'Title', searchable: true)
        ->column('price', 'Price');
        // CSV export is automatically enabled for this table
    })
    ->render();

return InertiaTable::make()
    ->withQueryBuilder($queryBuilder)
    ->column('name', 'Name')
    ->handleExport(false) // Disable CSV export
    ->render('Users/Index');

return InertiaTable::make()
    ->withQueryBuilder($queryBuilder)
    ->column('name', 'Name', searchable: true, sortable: true)
    ->column('email', 'Email', searchable: true, sortable: true)
    ->withExportCallback(function ($queryBuilder) {
        // The callback receives the QueryBuilder with all filters applied
        $data = $queryBuilder->get();

        // Custom data processing
        $processedData = $data->map(function ($user) {
            return [
                'ID' => $user->id,
                'Full Name' => strtoupper($user->name),
                'Email' => $user->email,
                'Registration Date' => $user->created_at->format('d/m/Y'),
                'Status' => $user->email_verified_at ? 'Verified' : 'Unverified',
            ];
        });

        // Custom CSV generation with semicolon separator
        $csv = "\xEF\xBB\xBF"; // UTF-8 BOM

        if ($processedData->isNotEmpty()) {
            // Headers
            $headers = array_keys($processedData->first());
            $csv .= implode(';', array_map(function ($header) {
                return '"' . str_replace('"', '""', $header) . '"';
            }, $headers)) . "\n";

            // Data rows
            foreach ($processedData as $row) {
                $csvRow = array_map(function ($value) {
                    return '"' . str_replace('"', '""', $value) . '"';
                }, array_values($row));
                $csv .= implode(';', $csvRow) . "\n";
            }
        }

        return response($csv)
            ->header('Content-Type', 'text/csv; charset=utf-8')
            ->header('Content-Disposition', 'attachment; filename="users-custom-export-' . now()->format('Y-m-d-H-i-s') . '.csv"');
    })
    ->render('Users/Index');

return InertiaTable::make()
    ->withQueryBuilder($queryBuilder)
    ->column('name', 'Name')
    ->column('email', 'Email')
    ->withExportCallback(function ($queryBuilder) {
        $data = $queryBuilder->get();

        // Export as JSON
        $exportData = [
            'metadata' => [
                'exported_at' => now()->toISOString(),
                'total_records' => $data->count(),
                'applied_filters' => request()->get('filter', []),
            ],
            'users' => $data->map(function ($user) {
                return [
                    'id' => $user->id,
                    'name' => $user->name,
                    'email' => $user->email,
                    'created_at' => $user->created_at->toISOString(),
                ];
            }),
        ];

        return response()->json($exportData)
            ->header('Content-Disposition', 'attachment; filename="users-export-' . now()->format('Y-m-d-H-i-s') . '.json"');
    })
    ->render('Users/Index');

// Using maatwebsite/excel package
return InertiaTable::make()
    ->withQueryBuilder($queryBuilder)
    ->column('name', 'Name')
    ->column('email', 'Email')
    ->withExportCallback(function ($queryBuilder) {
        // Use Laravel Excel for advanced Excel exports
        return Excel::download(new UsersExport($queryBuilder), 'users.xlsx');
    })
    ->render('Users/Index');

return InertiaTable::make()
    ->withQueryBuilder($queryBuilder)
    ->column('name', 'Name')
    ->column('email', 'Email')
    ->withExportCallback(function ($queryBuilder) {
        // Delegate to a dedicated export service
        $exportService = app(UserExportService::class);
        return $exportService->exportWithCustomLogic($queryBuilder);
    })
    ->render('Users/Index');
vue
<template>
    <Table :resource="users" :show-export-button="true">
        <!-- Custom export button with different styling -->
        <template #exportButton="{ exportUrl, translations }">
            <button
                @click="customExportFunction(exportUrl)"
                class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
            >
                <svg
                    class="h-4 w-4 mr-2"
                    fill="currentColor"
                    viewBox="0 0 20 20"
                >
                    <path
                        d="M3 17a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM6.293 6.707a1 1 0 010-1.414l3-3a1 1 0 011.414 0l3 3a1 1 0 01-1.414 1.414L11 5.414V13a1 1 0 11-2 0V5.414L7.707 6.707a1 1 0 01-1.414 0z"
                    />
                </svg>
                {{ translations.export_csv }}
            </button>
        </template>
    </Table>
</template>

<script setup>
const customExportFunction = (exportUrl) => {
    // Add custom logic before export
    console.log("Starting export...");

    // Perform the actual export
    window.location.href = exportUrl;

    // Add custom logic after export
    // e.g., analytics tracking, notifications, etc.
};
</script>
vue
<template>
    <Table color="red_style" />
</template>
bash
cd app
cp .env.example .env
composer install
npm install
npm run production
touch database/database.sqlite
php artisan migrate:fresh --seed
php artisan dusk:chrome-driver
php artisan serve
php artisan dusk