kunstmaan/adminlist-bundle
KunstmaanAdminListBundle provides a reusable admin list abstraction for Kunstmaan bundles, letting you define list classes with configurable columns, filters, sorting, pagination, and batch actions to render consistent backend overviews with minimal code.
This is a Symfony 2 bundle, not a Laravel package. If you're coming from Laravel, the closest analogues are Filament, Nova, or Backpack: it renders a paginated, filterable, sortable, CSV-exportable table over a Doctrine entity, plus optional add/edit/delete links. You will only touch it when maintaining a Kunstmaan CMS 2.x site — there is no bridge, and the concepts (service container, Twig, Doctrine QueryBuilder, ACL) do not translate one-to-one.
Minimal path:
composer require kunstmaan/adminlist-bundle and then lock the exact 2.x version you get, because the public API moved around between point releases.KunstmaanAdminListBundle in AppKernel::registerBundles(), after KunstmaanAdminBundle — the list templates extend the admin layout, use its menu adaptors, and expect its security helpers to be present.AbstractAdminList (or implement AdminListInterface directly) that declares getFields(), getFilters(), getActions(), getExportFields(), and points at an entity.kunstmaan_adminlist.factory service, call bindRequest($request) so filters/sort/page are read from the query string, and hand it to the template.// Controller
public function indexAction(Request $request)
{
$list = $this->get('kunstmaan_adminlist.factory')->createList(
new ProductAdminList($this->getDoctrine()->getManager())
);
$list->bindRequest($request);
return $this->render('AppBundle:Admin/Product:index.html.twig', ['adminlist' => $list]);
}
{% extends 'KunstmaanAdminBundle:Default:layout.html.twig' %}
{% block content %}
{% include 'KunstmaanAdminListBundle:AdminList:list.html.twig' %}
{% endblock %}
Where to look first: the bundle's AdminList/ directory (interface, abstract base, field/filter classes, item actions, bulk actions) and Resources/views/AdminList/. Class names and namespace paths shifted across 2.x releases — grep the vendored code before trusting any tutorial, including this one.
First use case: a read-only report over one entity with a text filter and a CSV export. That exercises every moving part (query building, filter binding, pagination, export) without the ACL and form complexity of full CRUD.
Read-only report list. Extend AbstractAdminList, pass the entity FQCN, add string/date/boolean filters, declare getExportFields(), and return null from getEditUrl() / getDeleteUrl() so no row actions render. This is the pattern that actually earns its keep — it's the least coupled to the rest of the Kunstmaan stack.
CRUD admin list. Implement getAddUrl(), getEditUrl($item), getDeleteUrl($item). These are plain URLs, so wire them to your own controllers; the bundle does not generate CRUD scaffolding for you. Add a RouteItemAction / SimpleItemAction per row for domain operations ("publish", "resend", "duplicate").
Bulk actions. Implement BulkActionInterface::getUrl()/getLabel(). The list template renders checkboxes and POSTs the selected IDs to your route; you own the loop, the transaction, and the flash message. Keep bulk actions idempotent — double submits are common in old admin UIs.
Scoping the query. Override the query-builder hook rather than post-filtering in PHP. This is where you add joins (avoid N+1 on columns rendered per row), tenant/owner constraints, and soft-delete conditions. Anything you filter in PHP after getItems() will break both getCount() and the CSV export.
Export extension. getExportFields() is typically a list of property names resolved through getters. The bundle streams CSV. If you need XLS or a different column order, override the export action/service rather than patching the template. Verify the export respects the current filters; that's the usual bug report.
Menu and permissions. Register the page in the admin sidebar via the Kunstmaan menu adaptor, and gate both the controller and each row action with the ACL PermissionMap. The list itself enforces nothing — hiding an action in Twig is cosmetic; check permission server-side in the target route.
Custom filter types. The extension point is a filter-type class implementing the bundle's filter interface, translating the submitted value into andWhere/setParameter on the QueryBuilder. Write one whenever a filter needs a join (searching a relation) instead of a plain column.
AbstractAdminList versus configurator/factory-style list definitions). A composer update that floats the constraint can rename half your overridden methods.getCount() diverges from getItems(). With GROUP BY/HAVING or a join that multiplies rows, the count is wrong and pagination lies. Implement getCount() as its own scalar count query — don't clone the list query.sort/order query parameters feed addOrderBy() directly. Whitelist sortable column names against the fields you declared; never pass raw request input into DQL.filters[...] — otherwise page 2 silently drops the filter and shows different data.=, +, -, @) and prefix a UTF-8 BOM if the file is opened in Excel. Also confirm the streamed response isn't buffered into memory on large tables.e). Custom joins need consistent aliases, or columns render blank. When a column is empty, dump $qb->getDQL() first.getEditUrl()/getDeleteUrl() returning null is the intended way to suppress actions — not a template conditional.AbstractAdminList does no permission checks. New rows in the admin menu will happily let a user with no rights hit the route.app/Resources/KunstmaanAdminListBundle/views/. You must clear the Twig cache after copying a template down, or you'll debug a file that isn't being rendered.error_log($qb->getDQL() . ' ' . json_encode($qb->getParameters())) in the query hook. Most "filters don't work" bugs are a parameter that never got bound.Request and EntityManager. Favor a functional test with fixtures that asserts filtered row counts over unit tests; mock the request only for the filter-binding logic.getExportFields() overrides, and Twig template overrides. Everything else means subclassing the abstract list and accepting upgrade pain later.How can I help you explore Laravel packages today?