• This repository has been archived on 18/Jul/2023
  • Stars
    star
    425
  • Rank 98,263 (Top 2 %)
  • Language
    PHP
  • License
    MIT License
  • Created about 3 years ago
  • Updated 10 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Inertia.js Tables for Laravel Query Builder

Inertia.js Tables for Laravel Query Builder

Latest Version on NPM npm Latest Version on Packagist Software License run-tests

⚠️ Don't start using this package for new projects ⚠️

This package provides a DataTables-like experience for Inertia.js with support for searching, filtering, sorting, toggling columns, and pagination. It generates URLs that can be consumed by Spatie's excellent Laravel Query Builder package, with no additional logic needed. The components are styled with Tailwind CSS 3.0, but it's fully customizable with slots. The data refresh logic is based on Inertia's Ping CRM demo.

Inertia.js Table for Laravel Query Builder

Support this package!

❀️ We proudly support the community by developing Laravel packages and giving them away for free. If this package saves you time or if you're relying on it professionally, please consider sponsoring the maintenance and development. Keeping track of issues and pull requests takes time, but we're happy to help!

Laravel Splade

Did you hear about Laravel Splade? 🀩

It's the magic of Inertia.js with the simplicity of Blade. Splade provides a super easy way to build Single Page Applications using Blade templates. Besides that magic SPA-feeling, it comes with more than ten components to sparkle your app and make it interactive, all without ever leaving Blade.

Features

  • Auto-fill: auto generates thead and tbody with support for custom cells
  • Global Search
  • Search per field
  • Select filters
  • Toggle columns
  • Sort columns
  • Pagination (support for Eloquent/API Resource/Simple/Cursor)
  • Automatically updates the query string (by using Inertia's replace feature)

Compatibility

Note: There is currently an issue with using this package with Vite!

Installation

You need to install both the server-side package and the client-side package. Note that this package is only compatible with Laravel 9, Vue 3.0, and requires the Tailwind Forms plugin.

Server-side installation (Laravel)

You can install the package via composer:

composer require protonemedia/inertiajs-tables-laravel-query-builder

The package will automatically register the Service Provider which provides a table method you can use on an Interia Response.

Search fields

With the searchInput method, you can specify which attributes are searchable. Search queries are passed to the URL query as a filter. This integrates seamlessly with the filtering feature of the Laravel Query Builder package.

Though it's enough to pass in the column key, you may specify a custom label and default value.

use ProtoneMedia\LaravelQueryBuilderInertiaJs\InertiaTable;

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

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

Select Filters

Select Filters are similar to search fields but use a select element instead of an input element. This way, you can present the user a predefined set of options. Under the hood, this uses the same filtering feature of the Laravel Query Builder package.

The selectFilter method requires two arguments: the key, and a key-value array with the options.

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

The selectFilter will, by default, add a no filter option to the array. You may disable this or specify a custom label for it.

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

Columns

With the column method, you can specify which columns you want to be toggleable, sortable, and searchable. You must pass in at least a key or label for each column.

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
    );
});

The searchable option is a shortcut to the searchInput method. The example below will essentially call $table->searchInput('name', 'User Name').

Global Search

You may enable Global Search with the withGlobalSearch method, and optionally specify a placeholder.

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

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

If you want to enable Global Search for every table by default, you may use the static defaultGlobalSearch method, for example, in the AppServiceProvider class:

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

Example controller

<?php

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Support\Collection;
use Inertia\Inertia;
use ProtoneMedia\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',
              ]);
    }
}

Client-side installation (Inertia)

You can install the package via either npm or yarn:

npm install @protonemedia/inertiajs-tables-laravel-query-builder --save

yarn add @protonemedia/inertiajs-tables-laravel-query-builder

Add the repository path to the content array of your Tailwind configuration file. This ensures that the styling also works on production builds.

module.exports = {
  content: [
    './node_modules/@protonemedia/inertiajs-tables-laravel-query-builder/**/*.{js,vue}',
  ]
}

Table component

To use the Table component and all its related features, you must import the Table component and pass the users data to the component.

<script setup>
import { Table } from "@protonemedia/inertiajs-tables-laravel-query-builder";

defineProps(["users"])
</script>

<template>
  <Table :resource="users" />
</template>

The resource property automatically detects the data and additional pagination meta data. You may also pass this manually to the component with the data and meta properties:

<template>
  <Table :data="users.data" :meta="users.meta" />
</template>

If you want to manually render the table, like in v1 of this package, you may use the head and body slot. Additionally, you can still use the meta property to render the paginator.

<template>
  <Table :meta="users">
    <template #head>
      <tr>
        <th>User</th>
      </tr>
    </template>

    <template #body>
      <tr
        v-for="(user, key) in users.data"
        :key="key"
      >
        <td>{{ user.name }}</td>
      </tr>
    </template>
  </Table>
</template>

The Table has some additional properties to tweak its front-end behaviour.

<template>
  <Table
    :striped="true"
    :prevent-overlapping-requests="false"
    :input-debounce-ms="1000"
    :preserve-scroll="true"
  />
</template>
Property Description Default
striped Adds a striped layout to the table. false
preventOverlappingRequests Cancels a previous visit on new user input to prevent an inconsistent state. true
inputDebounceMs Number of ms to wait before refreshing the table on user input. 350
preserveScroll Configures the Scroll preservation behavior. You may also pass table-top to this property to scroll to the top of the table on new data. false

Custom column cells

When using auto-fill, you may want to transform the presented data for a specific column while leaving the other columns untouched. For this, you may use a cell template. This example is taken from the Example Controller above.

<template>
  <Table :resource="users">
    <template #cell(actions)="{ item: user }">
      <a :href="`/users/${user.id}/edit`">
        Edit
      </a>
    </template>
  </Table>
</template>

Multiple tables per page

You may want to use more than one table component per page. Displaying the data is easy, but using features like filtering, sorting, and pagination requires a slightly different setup. For example, by default, the page query key is used for paginating the data set, but now you want two different keys for each table. Luckily, this package takes care of that and even provides a helper method to support Spatie's query package. To get this to work, you need to name your tables.

Let's take a look at Spatie's QueryBuilder. In this example, there's a table for the companies and a table for the users. We name the tables accordingly. So first, call the static updateQueryBuilderParameters method to tell the package to use a different set of query parameters. Now, filter becomes companies_filter, column becomes companies_column, and so forth. Secondly, change the pageName of the database paginator.

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();

Then, we need to apply these two changes to the InertiaTable class. There's a name and pageName method to do so.

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);
});

Lastly, pass the correct name property to each table in the Vue template. Optionally, you may set the preserve-scroll property to table-top. This makes sure to scroll to the top of the table on new data. For example, when changing the page of the second table, you want to scroll to the top of the table, instead of the top of the page.

<script setup>
import { Table } from "@protonemedia/inertiajs-tables-laravel-query-builder";

defineProps(["companies", "users"])
</script>

<template>
  <Table
    :resource="companies"
    name="companies"
    preserve-scroll="table-top"
  />

  <Table
    :resource="users"
    name="users"
    preserve-scroll="table-top"
  />
</template>

Pagination translations

You can override the default pagination translations with the setTranslations method. You can do this in your main JavaScript file:

import { setTranslations } from "@protonemedia/inertiajs-tables-laravel-query-builder";

setTranslations({
  next: "Next",
  no_results_found: "No results found",
  of: "of",
  per_page: "per page",
  previous: "Previous",
  results: "results",
  to: "to"
});

Table.vue slots

The Table.vue has several slots that you can use to inject your own implementations.

Slot Description
tableFilter The location of the button + dropdown to select filters.
tableGlobalSearch The location of the input element that handles the global search.
tableReset The location of the button that resets the table.
tableAddSearchRow The location of the button + dropdown to add additional search rows.
tableColumns The location of the button + dropdown to toggle columns.
tableSearchRows The location of the input elements that handle the additional search rows.
tableWrapper The component that wraps the table element, handling overflow, shadow, padding, etc.
table The actual table element.
head The location of the table header.
body The location of the table body.
pagination The location of the paginator.

Each slot is provided with props to interact with the parent Table component.

<template>
  <Table>
    <template v-slot:tableGlobalSearch="slotProps">
      <input
        placeholder="Custom Global Search Component..."
        @input="slotProps.onChange($event.target.value)"
      />
    </template>
  </Table>
</template>

Testing

A huge Laravel Dusk E2E test-suite can be found in the app directory. Here you'll find a Laravel + Inertia application.

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

Upgrading from v1

Server-side

  • The addColumn method has been renamed to column.
  • The addFilter method has been renamed to selectFilter.
  • The addSearch method has been renamed to searchInput.
  • For all renamed methods, check out the arguments as some have been changed.
  • The addColumns and addSearchRows methods have been removed.
  • Global Search is not enabled by default anymore.

Client-side

  • The InteractsWithQueryBuilder mixin has been removed and is no longer needed.
  • The Table component no longer needs the filters, search, columns, and on-update properties.
  • When using a custom thead or tbody slot, you need to provide the styling manually.
  • When using a custom thead, the showColumn method has been renamed to show.
  • The setTranslations method is no longer part of the Pagination component, but should be imported.
  • The templates and logic of the components are not separated anymore. Use slots to inject your own implementations.

v2.1 Roadmap

  • Boolean filters
  • Date filters
  • Date range filters
  • Switch to Vite for the demo app

Changelog

Please see CHANGELOG for more information what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Other Laravel packages

  • Laravel Analytics Event Tracking: Laravel package to easily send events to Google Analytics.
  • Laravel Blade On Demand: Laravel package to compile Blade templates in memory.
  • Laravel Cross Eloquent Search: Laravel package to search through multiple Eloquent models.
  • Laravel Eloquent Scope as Select: Stop duplicating your Eloquent query scopes and constraints in PHP. This package lets you re-use your query scopes and constraints by adding them as a subquery.
  • Laravel Eloquent Where Not: This Laravel package allows you to flip/invert an Eloquent scope, or really any query constraint.
  • Laravel FFMpeg: This package provides an integration with FFmpeg for Laravel. The storage of the files is handled by Laravel's Filesystem.
  • Laravel Form Components: Blade components to rapidly build forms with Tailwind CSS Custom Forms and Bootstrap 4. Supports validation, model binding, default values, translations, includes default vendor styling and fully customizable!
  • Laravel Mixins: A collection of Laravel goodies.
  • Laravel Verify New Email: This package adds support for verifying new email addresses: when a user updates its email address, it won't replace the old one until the new one is verified.
  • Laravel Paddle: Paddle.com API integration for Laravel with support for webhooks/events.
  • Laravel WebDAV: WebDAV driver for Laravel's Filesystem.

Security

If you discover any security related issues, please email [email protected] instead of using the issue tracker.

Credits

License

The MIT License (MIT). Please see License File for more information.

More Repositories

1

laravel-ffmpeg

This package provides an integration with FFmpeg for Laravel. Laravel's Filesystem handles the storage of the files.
PHP
1,511
star
2

laravel-splade

πŸ’« The magic of Inertia.js with the simplicity of Blade πŸ’« - Splade provides a super easy way to build Single Page Applications (SPA) using standard Laravel Blade templates, and sparkle it to make it interactive. All without ever leaving Blade.
PHP
1,382
star
3

laravel-cross-eloquent-search

Laravel package to search through multiple Eloquent models. Supports sorting, pagination, scoped queries, eager load relationships and searching through single or multiple columns.
PHP
990
star
4

laravel-form-components

A set of Blade components to rapidly build forms with Tailwind CSS (v1.0 and v2.0) and Bootstrap 4/5. Supports validation, model binding, default values, translations, Laravel Livewire, includes default vendor styling and fully customizable!
PHP
807
star
5

eddy-server-management

Open-Source Solution for Server Provisioning and Zero-Downtime PHP Deployment
PHP
423
star
6

laravel-verify-new-email

This package adds support for verifying new email addresses: when a user updates its email address, it won't replace the old one until the new one is verified.
PHP
384
star
7

laravel-analytics-event-tracking

Laravel package to easily send events to Google Analytics
PHP
243
star
8

laravel-paddle

Paddle.com API integration for Laravel with support for webhooks/events
PHP
195
star
9

laravel-mixins

A collection of Laravel goodies.
PHP
135
star
10

laravel-task-runner

A package to write Shell scripts like Blade Components and run them locally or on a remote server
PHP
116
star
11

laravel-eloquent-scope-as-select

Stop duplicating your Eloquent query scopes and constraints in PHP. This package lets you re-use your query scopes and constraints by adding them as a subquery.
PHP
102
star
12

laravel-single-session

This package prevents a User from being logged in more than once. It destroys the previous session when a User logs in and thereby allowing only one session per user.
PHP
101
star
13

laravel-xss-protection

Laravel XSS Protection Middleware
PHP
99
star
14

laravel-guidelines

Not necessarily coding standards (naming conventions, avoid else, etc.), but more like 'app'-guidelines - things you don't want to forget.
PHP
91
star
15

laravel-api-health

Monitor first and third-party services and get notified when something goes wrong!
PHP
87
star
16

form-components-pro

A set of Vue 3 components to rapidly build forms with Tailwind CSS 3. It supports validation, model binding, integrates with Autosize/Choices.js/Flatpickr, includes default vendor styling and is fully customizable! Even better in conjunction with Laravel Jetstream + Inertia.js.
JavaScript
82
star
17

laravel-webdav

WebDAV Serivce Provider for Laravel
PHP
59
star
18

laravel-blade-on-demand

Compile Blade templates in memory
PHP
50
star
19

inertia-vue-modal-poc

Proof of Concept: Load any route into a modal with Inertia.js
Vue
49
star
20

laravel-splade-core

A package to use Vue 3's Composition API in Laravel Blade components.
PHP
42
star
21

laravel-eloquent-where-not

This Laravel package allows you to flip/invert an Eloquent scope, or really any query constraint.
PHP
27
star
22

laravel-content

You provide the WYSIWYG editor, Media uploader, etc., and the package handles the validation, multi-language, storage, caching, sanitizing, etc.
PHP
23
star
23

inertiajs-events-laravel-dusk

Inertia.js Events for Laravel Dusk
PHP
22
star
24

laravel-dusk-fakes

Persistent Fakes for Laravel Dusk
PHP
17
star
25

laravel-minio-testing-tools

This package provides a trait to run your tests against a MinIO S3 server.
PHP
11
star
26

splade.dev

Source code for https://splade.dev
PHP
8
star
27

laravel-splade-docs

7
star
28

laravel-viewi

[WIP] Viewi for Laravel proof-of-concept: Build full-stack and completely reactive user interfaces with PHP.
PHP
5
star
29

eddy-backup-cli

Backup CLI for Eddy Server Management
PHP
3
star
30

form-components-pro-docs

A set of Vue components to rapidly build forms with Tailwind CSS v2.0. It supports validation, model binding, includes default vendor styling and is fully customizable!
JavaScript
3
star
31

laravel-tracer

[WIP] Trace authenticated users
PHP
2
star
32

eddy-filesystem-cli

PHP
1
star
33

laravel-ffmpeg-demo-app

A demo app to show all the cool things you can do with Laravel FFmpeg
1
star
34

php-apple-mapkit-token

Create a MapKit JS Token with PHP
1
star
35

laravel-browser-kit-macro

A macro to use the Laravel 5.3 testing layer inside your Laravel >5.3 tests
PHP
1
star
36

laravel-splade-plugin-skeleton

PHP
1
star