Skip to content

Own columns

When no format of the TextColumn fits, write a column type of your own, e.g. a progress bar.

Terminal window
php spark make:anvil-column Progress
namespace App\Columns;
use Anvil\Collection\Columns\Column;
final class Progress extends Column
{
public function text(array|object $record): string
{
return $this->percent($record) . ' %';
}
public function html(array|object $record): string
{
$percent = $this->percent($record);
// Escape every value, also numbers: the code stays safe when it changes.
return '<progress class="w-100" max="100" value="' . esc((string) $percent, 'attr') . '">' . esc($this->text($record)) . '</progress>';
}
public function exportValue(array|object $record): int
{
return $this->percent($record);
}
private function percent(array|object $record): int
{
$value = $this->value($record);
return max(0, min(100, is_numeric($value) ? (int) $value : 0));
}
}
Progress::make('completion')->label('Done')->sortable(),
Method Purpose
text($record) the value as plain text (required); used for search hits, titles, cards and – by default – exports
html($record) the value as safe HTML for table and cards; default: escaped text(). Escape every value with esc()
exportValue($record) value for exports; return numbers as numbers; default: text() or null
prepare(Resource $resource, array $records) called once per page with the shown records before rendering, e.g. to load related data in one query (see CountColumn)
value($record) the raw value: state() or the attribute of the column name
render($record) final: html() plus link, icon and colour – what tables and cards show; use it in own card views

All options of every column come from Column and work unchanged: labels, sorting, searching, state(), color(), icon(), description(), link().

  • html() must return safe HTML. Never put a value into markup without esc(): text content with esc($value), attributes with esc($value, 'attr') – as in the example, also for numbers that cannot contain markup today.
  • No inline styles or scripts: the Content Security Policy blocks them. Use classes of Bootstrap or of your own stylesheet.
  • Load related data in prepare(), not per row in html().
  • Respect permissions: data from other tables must be read through their resource or with the same checks.

More on generators and own building blocks: Extending Anvil.