Table
Suggest EditWith the table layout, you can easily display large amounts of data in a structured and organized manner. Simply specify the fields you want to include in the table, and the table layout will take care of the rest, including sorting, filtering, and pagination. You can also customize the appearance of the table by setting various options, such as the table headings, row value, and more. Whether you are displaying a list of users, products, orders, or any other data, the table layout is a powerful and flexible tool for presenting your data in a clear and concise way.
To create a new table layout, you can use the following Artisan command:
php artisan orchid:table PatientListLayout
Example:
namespace App\Orchid\Layouts;
use Orchid\Screen\TD;
use Orchid\Screen\Layouts\Table;
class PatientListLayout extends Table
{
/**
* Data source.
*
* The name of the key to fetch it from the query.
* The results of which will be elements of the table.
*
* @var string
*/
protected $target = 'patients';
/**
* @return TD[]
*/
protected function columns() : iterable
{
return [
TD::make('name'),
TD::make('created_at')->sort(),
];
}
}
Tables also support writing through short syntax without creating a class:
use Orchid\Support\Facades\Layout;
use Orchid\Screen\TD;
Layout::table('clients', [
TD::make('name'),
TD::make('created_at')->sort(),
]);
Introduction to Cells
A table is only a general wrapper for which you need to specify TD
classes. Designed to create a single cell.
use Orchid\Screen\TD;
TD::make('last_name');
The make
method is the main method, sets the key name from the array and the display name.
TD::make('last_name', 'Last name');
Column headers are rendered with their raw HTML content. This gives you the flexibility to include rich formatting or special characters directly in the header. Just remember to ensure that any dynamic content is safe and sanitized to maintain a secure and polished interface.
Alignment
Content alignment control can be controlled using the align
method:
TD::make('last_name')->align(TD::ALIGN_LEFT);
TD::make('last_name')->align(TD::ALIGN_CENTER);
TD::make('last_name')->align(TD::ALIGN_RIGHT);
Sorting
Sorting the selection should be done in the query
screen, for models, you can use automatic http
sorting and
filtering
To enable active sorting by this column
you must specify the sort
method:
TD::make('last_name')->sort();
Filtration
When building simple tables, the use of separate filters may seem overkill. Therefore, you can display the field for filtering right above the column heading.
It will only define the visible part. You can specify the filtering logic yourself or rely on “Automatic HTTP Filtering”. You can find out more on the “Eloquent Filters” page.
To add a field, call the filter method and pass the instance of the class you would like to display:
TD::make('SKU')->filter(Input::make()->mask('A-999999'));
Note: There is no need to specify the field name. It will be delivered and overwritten automatically by the column name.
Filtering multiple values can be done with a Select, and with an optional second argument of filter. By default it lets filter for any/all of the given values.
TD::make('color')->filter(TD::FILTER_SELECT, [
'red' => 'Red',
'green' => 'Green',
]);
When working with filters, it is possible to use the filterValue()
method, which allows you to modify the displayed filter values.
For example, you can replace an ID value with a display name. Here is an example of using the filterValue()
method:
TD::make('id')->filterValue(function ($value) {
$user = User::find($value);
return $user->name;
})
The $value
value passed to the function will contain the filter value that was applied.
Width
You can control the width of the cell using the width
method:
TD::make('last_name')->width('100px');
Show/Hide
By default, the user can hide any column for himself, but you can prohibit doing this by specifying:
TD::make('last_name')->cantHide();
And also hide by default, but can be shown at the request of the user.
TD::make('last_name')->defaultHidden();
Data Output
In some cases, you may need to display combined data, the render
method is for this purpose intended. It implements the ability to generate cells according to the function:
TD::make('full_name')
->render(fn($user) => e($user->first_name . ' ' . $user->last_name)),
Note. The returned string will not be escaped! You need to take care of this yourself with the
e()
helper or useBlade
view.
The loopback function must return any string value:
TD::make('full_name')
->render(fn($user) => view('blade_template', [
'user' => $user,
])),
Please note that you can use fields and actions:
use Orchid\Screen\Actions\Link;
TD::make()
->render(fn ($user) => Link::make($user->last_name)->route('platform.user.edit', $user)),
Sometimes you want to show more elements in a single column, for example more buttons. For this you can use a Group
:
use Orchid\Screen\Actions\Link;
use Orchid\Screen\Fields\Group;
TD::make()
->render(fn($user) => Group::make([
Link::make('Show')->route('platform.user.show', $user),
Link::make('Edit')->route('platform.user.edit', $user),
])),
For example, display a checkbox for each line for a bulk action:
TD::make()
->render(fn(User $user) => CheckBox::make('users[]')
->value($user->id)
->placeholder($user->name)
->checked(false)
),
Sometimes it may be necessary to get the value from the query
screen, rather than relying only on the target
. You can get the value as follows:
use Orchid\Screen\Actions\Link;
TD::make('price')
->render(fn ($product) => $product->price + $this->query->get('tax')),
Working with the Loop Variable
The $loop
variable will be available in the second argument of the render
closure function. This variable provides access to some useful bits of information such as the current loop index and whether this is the first or last iteration through the loop:
TD::make()->render(fn (Model $model, object $loop) => $loop->index),
The $loop variable contains a variety of other useful properties which you can find in the Laravel documentation.
Using Components
Complex or dynamic data can be tedious to specify in the render method or seem overwhelming. Therefore, cells support rendering using Laravel components. It allows you to take out the display logic, as well as reuse.
For example, there is an Order
model, and depending on the status, we can show different descriptions in our components.
It is much nicer than specifying things directly in the view or creating particular areas for such processing.
namespace App\View\Components;
use Illuminate\View\Component;
use App\Models\Order;
class OrderShortInformation extends Component
{
public function __construct(
public readonly Order $order
) {}
/**
* Get the status description.
*
* @return string
*/
public function status(): string
{
return match ($this->order->status) {
Order::STATUS_PROCESS => __('In the process'),
Order::STATUS_PAID => __('Paid'),
Order::STATUS_CANCELLATION => __('Cancellation'),
Order::STATUS_REFUND => __('Refund'),
default => __('Unknown'),
};
}
/**
* Get the view / contents that represent the component.
*
* @return \Illuminate\View\View|\Closure|string
*/
public function render(): \Illuminate\View\View|\Closure|string
{
return view('components.order.short-information');
}
}
To use a component in a cell, you must pass it:
use App\View\CompochangingOrderShortInformation;
TD::make('status')->component(OrderShortInformation::class);
The component will receive the entire row as its first argument, not just the cell data.
Therefore, if you are using a deep injection in your component, it is essential to specify the variable’s name.
public function __construct(Application $application, Order $order, int $limit = 300)
{
$this->order = $order;
// ...
}
Other additional arguments, for example, limit. You can specify in the following way:
TD::make('status')->component(OrderShortInformation::class, [
'limit' => 100
]);
Customizing Component Values
This is very similar to using the component above, only the previous example gets an object. But this is not always necessary, sometimes only one value needs to be processed.
To do this, you need to add a new method that would receive only the cell value without additional information by default. For example, I want to display values of a specific format:
namespace App\View\Components;
use Illuminate\View\Component;
class Numeric extends Component
{
/**
* @var float
*/
public float $value;
/**
* Create a new component instance.
*
* @param float $value
* @param int $decimals
* @param string|null $decimal_separator
* @param string|null $thousands_separator
*/
public function __construct(
float $value,
int $decimals = 0,
?string $decimal_separator = ".",
?string $thousands_separator = ","
) {
$this->value = number_format($value, $decimals, $decimal_separator, $thousands_separator);
}
/**
* Get the view/contents that represent the component.
*
* @return \Illuminate\Contracts\View\View|\Closure|string
*/
public function render()
{
return <<<'blade'
{{ $value }}
blade;
}
}
Then the table call might look like this:
TD::make('price')->asComponent(Numeric::class);
// 1,235
Also with additional parameters:
TD::make('price')->asComponent(Numeric::class, [
'decimals' => 2,
'decimal_separator' => ',',
'thousands_separator' => ' ',
]);
// 1 234,56
Table Options
You can specify the text to be displayed if the table is empty specifying methods:
/**
* @return string
*/
protected function iconNotFound(): string
{
return 'table';
}
/**
* @return string
*/
protected function textNotFound(): string
{
return __('There are no records in this view');
}
/**
* @return string
*/
protected function subNotFound(): string
{
return '';
}
If the table rows do not seem contrasting to you, then you can enable
striped
mode:
/**
* @return bool
*/
protected function striped(): bool
{
return true;
}
You can dynamically change the amount of links to display in the table pagination with the following method:
/**
* The number of links to display on each side of current page link.
*
* @return int
*/
protected function onEachSide(): int
{
return 3;
}
Adding a Total Row
Adds a summary row at the bottom of the table, for this you need to define the total
method and describe the required cells. For example:
public function total():array
{
return [
TD::make('total')
->align(TD::ALIGN_RIGHT)
->colspan(2)
->render(fn () => 'Total:'),
TD::make('total_count')
->align(TD::ALIGN_RIGHT),
TD::make('total_active_count')
->align(TD::ALIGN_RIGHT),
];
}
This line will ignore the specified target
based on the result of the query
screen:
public function query(): array
{
return [
'total_active_count' => '$93 960',
'total_count' => '$103 783',
// ...
];
}
Column Expansion
When working with the same type of data, it is often required to process it in the same way, in order not to duplicate the code in the layers, it is possible to extend the TD
class using its own methods. To do this, it is necessary to register the closure function in the service provider.
Registration example:
// AppServiceProvider.php
TD::macro('bool', function () {
$column = $this->column;
$this->render(function ($datum) use ($column) {
return view('bool',[
'bool' => $datum->$column
]);
});
return $this;
});
Template example:
// bool.blade.php
<span class="{{ $bool ? 'text-success' : 'text-danger' }}">●</span>
Usage example:
public function grid(): array
{
return [
TD::make('status')->bool(),
];
}