This page covers how records are loaded into the admin table. Texts, columns and the look of the table are configured in settings.

Default order of records

$orderBy sets the default order of the records. It is registered as a global Eloquent scope, so it applies to the admin table and to every query in your application.
Any column other than _order disables drag and drop reordering and the _order column is not created. With $sortable = false the default order becomes ['id', 'DESC'].
Remove the order in a query with withoutGlobalScope('order'):
$reversed flips the direction of $orderBy, e.g. to list the oldest records first while keeping drag and drop reordering.

Number of records

Minimum

Once the minimum number of records is reached, records can not be deleted. The delete button is disabled and the server refuses the request.
The value 0 means no limit.

Maximum

Once the maximum number of records is reached, the administration hides the button for adding new records.
The value 0 means no limit. A child model with $maximum = 1 is available on the parent as a hasOne relation instead of hasMany:
app/Models/ClientsAddress.php
The maximum is applied by the administration interface. Records created by your code are not limited.
$minimum = 1 together with $maximum = 1 switches the module into single record mode.

Filtering rows in the administration

scopeAdminRows is a query scope applied to every administration request of the module: the table, the record count, opening a record, updating it, buttons and exports. Queries in the rest of the application are not affected.
app/Models/Order.php
A record outside the scope can not be opened or updated, the response is 404. When an update moves the record out of the scope, the response still contains the updated record, it disappears on the next table refresh.
A model module can define scopeAdminRows($query) too, both scopes are applied.

Row responses

Columns loaded by the query

The table response contains only the fields of the admin model. A value added to the query does not reach the response until you make it visible.
app/Models/Package.php
These methods run before the record is serialized: setAdminRowResponse is also the place to add accessors to the form, $this->append('full_address').

Row attributes

The serialized record can be changed before it is sent to the administration. Every method receives the attributes and must return them.
app/Models/Order.php
Values changed in setAdminRowsAttributes are sent in a separate $table key of the row. The table displays them while the form keeps the original values, so a formatted price does not break the price input.
Every row is serialized once. setAdminRowsAttributes then runs on a copy of the serialized attributes and only the values it changed or added are sent in $table. The hooks still run for every row of the page, so keep them free of side effects and avoid queries inside, load the data in scopeAdminRows instead.
For single record modules ($single, $inParent) the table is rendered as a form, so setAdminRowAttributes runs instead of setAdminRowsAttributes.

Module requests

Initial data of the module

getAdminModelInitialData sends additional data to the administration when the module is opened. Each key is available in Vue components of the module through model.getData(key).
app/Models/AttributesItem.php
Other hooks of the table request:

Changing the module definition

adminModelRender receives the whole module definition sent to the administration (name, settings, fields, insertable, maximum, …) and must return it. It runs after model modules, which receive the data by reference (adminModelRender(&$data)).

Table requests

The table loads records with POST /admin/api/models/{table}/rows. Parameters: Records are ordered by $orderBy, sorting by a column header happens in the browser. set{Column}AdminSearch replaces the search of a column. It is also used when the search runs in all columns. getSearchAdminColumnOperator decides how words of the query are combined.
app/Models/Product.php

Preset searches

$searches opens the table with prepared search bars. Keys are column names.
app/Models/Order.php
Use the searches($row) method for dynamic values, e.g. the current month.

Searching in child records

When the table is searched in a selected column, search.deep also finds parent records whose child records match.
app/Models/Product.php
Child records are searched only when a column is selected in the search bar. When the child model does not have the selected column, all its columns are searched.

Encrypted fields

Values of encrypted string, decimal and date fields are stored with a hash in the _encrypted_hashes column, so the table search finds them by the whole value, ignoring case and accents. Partial values are not found. Disable hashes when encrypted values must not be searchable. Encrypted columns of the model are then skipped by the search.

Filter buttons

getFilterStates() adds filter buttons above the table. The selected filter applies its query, and the first state whose active closure is true is shown as a colored indicator in the row.
app/Models/OrdersItem.php
$this inside active is the listed row. More selected filters are combined, see the filter settings to allow only one.
Declaring protected $modules on the model replaces the default modules and filter states stop working. Use $this->addModule() instead, see modules.

Request scopes

The table request contains scopes, each calls a local scope of the model (or of a module) with its value. Filter buttons use them, and custom Vue components can add their own:
app/Models/Order.php
Scopes are applied to the table rows and to an opened record. Unknown scopes are ignored.
Any local scope of the model can be called by an administrator through the request. Do not write local scopes which bypass permissions on admin models.

Exports

Exports generate a file from the records of the table. They are Laravel Excel export classes, PDF exports use mpdf/mpdf.
app/Admin/Exports/StockExport.php
app/Models/ProductsStock.php
Exports are listed in the export menu of the table, and the generated file respects the current search. Add ?debug=1 to the export URL (/admin/api/models/{table}/exports/{ExportClass}) to see the HTML output. A question() method asks for confirmation or custom inputs before the export is generated, the same way as actions with a confirmation.

Export of selected records

An export extending Admin\Contracts\Exports\AdminButtonExport is rendered as a record button. It generates the file for one record, or a ZIP archive with one file per record when more records are selected.

Table download

Register TableExportXls in $exports to add Download excel to the standard export menu. Install phpoffice/phpspreadsheet in your application. The export creates an .xlsx workbook from all rows matching the current search, scopes, language and parent context, without pagination. It uses the table row serialization and includes requested additional columns. Select and relation values become option names, booleans become Yes/No, HTML is removed and column headings use the table labels. Empty results produce an empty workbook. The file uses the standard export private storage and download endpoint. Existing setSheet{Column}Attribute() and setExcelSheet() hooks still apply.