Skip to content

CountColumn

The number of related records, e.g. the accounts, devices or open tasks of a person.

use Anvil\Collection\Columns\CountColumn;
use Anvil\Data\Model;
CountColumn::make('accounts')->label('AD accounts')
->relation(AccountModel::class, 'person_id', fn (Model $accounts) => $accounts->where('active', 1)),
CountColumn::make('open_tasks')->label('Open tasks')
->relation(TaskModel::class, 'person_id', fn (Model $tasks) => $tasks->where('status', 'open'))
->icon('list-check')
->color(fn (?int $count) => $count > 0 ? 'warning' : null),

relation(model, foreignKey, scope):

Argument Meaning
model the class of the related model
foreignKey its column that holds the id of the listed record (plain column name)
scope optional closure that restricts the query, e.g. only active ones; it may also join tables

The column is right aligned and takes the options of every column, e.g. icon(), color() (the closure receives the count) and toggleable(). A CountColumn without relation() throws a LogicException when it is rendered.

  • One query per page: before a page is rendered, the related records of all shown rows are loaded at once and counted (prepare()). Exports do the same per chunk. Own card views that render a record without the list count that record on its own.
  • Permissions: the related records are read through the resource of their model in the current panel – tenant, its scopeQuery() and the policies of single records apply – so the number is what the user could open. Users without the view permission of that resource see an empty cell, not 0.
  • Without a resource in the panel the tenant scoped model is used. A model without tenant scope is refused in multi-tenant installations (LogicException) unless a scope closure is given.
  • Rows without related records show 0.

Meant for small relations – accounts, devices, open tasks – not for thousands of records per row: records are loaded, not counted in SQL.

A count is computed after the query and has no database column: do not mark a CountColumn sortable() or searchable(). To sort by a number, add it to the query in scopeQuery() (a sub-select) and use a TextColumn with numeric():

public function scopeQuery(Model $model): void
{
$model->select('people.*')
->select('(SELECT COUNT(*) FROM devices d WHERE d.person_id = people.id) AS device_count', false);
}
// in collection(): sorted by the alias
TextColumn::make('device_count')->label('Devices')->numeric()->sortable(),

The count in the format of the locale (1.234); exports contain the number.