Installation
composer require appventus/avlist-bundle:dev-master
Ensure AppVentus\AvListBundle\AvListBundle is registered in AppKernel.php:
new AppVentus\AvListBundle\AvListBundle(),
First Use Case: Basic List Rendering
In a controller, inject the av_list service and initialize a list with a QueryBuilder:
use Symfony\Component\HttpFoundation\Request;
public function indexAction(Request $request)
{
$em = $this->getDoctrine()->getEntityManager();
$qb = $em->getRepository('AppBundle:Post')->createQueryBuilder('p');
$list = $this->get('av_list')->getList($qb, $request, [
'max_per_page' => 10,
]);
$list->addColumn('title', null, 'Title', true, 'p.title');
$list->addColumn('createdAt', ['date', 'd/m/Y'], 'Created At', true, 'p.createdAt');
return $this->render('AppBundle:Post:index.html.twig', ['list' => $list]);
}
Template Setup
Use the default template or override it in app/Resources/AvListBundle/views/AvList/index.html.twig:
{% if list.pager.nbResults > 0 %}
<table class="table">
<thead>
<tr>
{% for column in list.columns %}
<th class="sortable" data-target="{{ column.target }}">{{ column.label }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for item in list.pager %}
<tr>
{% for column in list.columns %}
<td>{{ item[column.field]|default('')|{{ column.filters|join('|') }} }}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
{% include 'AvListBundle:AvList:control.html.twig' with {'list': list} %}
{% else %}
<p>No results found.</p>
{% endif %}
QueryBuilder Integration
Pass a pre-built QueryBuilder to getList() to leverage existing filtering/sorting logic:
$qb = $em->getRepository('AppBundle:User')->createQueryBuilder('u')
->where('u.active = :active')
->setParameter('active', true);
$list = $this->get('av_list')->getList($qb, $request);
Dynamic Column Configuration Define columns dynamically based on user roles or entity metadata:
$columns = [
['field' => 'name', 'label' => 'Name', 'sortable' => true, 'target' => 'u.name'],
['field' => 'email', 'label' => 'Email', 'sortable' => false, 'filters' => ['lower']],
];
foreach ($columns as $column) {
$list->addColumn(
$column['field'],
$column['filters'] ?? null,
$column['label'],
$column['sortable'] ?? false,
$column['target']
);
}
Pagination & Theming
Use PagerFanta’s built-in themes (e.g., range, bootstrap) via options:
$list->setOptions([
'theme' => 'bootstrap',
'max_per_page' => 25,
'route' => 'app_post_list_ajax',
'route_parameters' => ['category' => $categoryId],
]);
AJAX Handling Return the list template for AJAX requests to enable dynamic updates:
if ($request->isXmlHttpRequest()) {
return $this->render($list->getTemplate(), ['list' => $list]);
}
Multi-List Pages
Instantiate AvList manually (not via service) for multiple lists:
$list1 = new AvList($request, $this->get('templating'));
$list1->setQueryBuilder($qb1)->setOptions(['route' => 'list1_ajax']);
$list2 = new AvList($request, $this->get('templating'));
$list2->setQueryBuilder($qb2)->setOptions(['route' => 'list2_ajax']);
Custom Themes
Extend default themes by creating app/Resources/AvListBundle/views/AvList/custom_theme.html.twig:
{# Example: Replace default pagination with custom buttons #}
<div class="custom-pagination">
{% if list.pager.hasPreviousPage %}
<button data-page="{{ list.pager.getPreviousPage() }}">Previous</button>
{% endif %}
<span>Page {{ list.pager.getCurrentPageNumber() }} of {{ list.pager.getNbPages() }}</span>
{% if list.pager.hasNextPage %}
<button data-page="{{ list.pager.getNextPage() }}">Next</button>
{% endif %}
</div>
Event Listeners
Hook into av_list.build_query or av_list.render events (if supported) to modify behavior globally:
# config.yml
services:
app.av_list.listener:
class: AppBundle\EventListener\AvListListener
tags:
- { name: kernel.event_listener, event: av_list.build_query, method: onBuildQuery }
Singleton Service Limitation
$this->get('av_list') for multiple lists overwrites state. Avoid for multi-list pages.AvList manually:
$list = new AvList($request, $this->get('templating'));
QueryBuilder Alias Mismatch
data-target in Twig must match the QueryBuilder alias (e.g., u.name for alias u).$qb->getDQLPart('from').Deprecated PagerFanta
dev-master). Potential compatibility issues with newer Symfony/Doctrine.Template Override Conflicts
index.html.twig may break if the bundle’s default template changes.{% extends 'AvListBundle:AvList:index.html.twig' %} and override blocks (content, pagination).Sorting Edge Cases
ORDER BY CASE WHEN ...) may not work with the bundle’s auto-sorting.Check Generated SQL Enable Doctrine debugging to verify the final query:
$qb->getQuery()->getSQL(); // Log this to debug sorting/pagination.
Inspect List Object
Dump the AvList object to debug configuration:
var_dump($list->getOptions(), $list->getColumns());
AJAX Debugging
return $this->render($list->getTemplate(), ['list' => $list]);
data-target attributes in <th> match the QueryBuilder aliases.Custom Column Types
Extend AvListBundle\Column\AbstractColumn to add custom logic (e.g., computed fields):
class ComputedColumn extends AbstractColumn {
public function getValue($entity) {
return $entity->getField1() . ' | ' . $entity->getField2();
}
}
Dynamic Column Visibility Use Twig conditions to show/hide columns:
{% for column in list.columns %}
{% if column.field == 'sensitiveData' and app.user.isAdmin %}
<th>{{ column.label }}</th>
{% endif %}
{% endfor %}
Bulk Actions Add a bulk action column and handle selections via JavaScript:
<th><input type="checkbox" id="select-all"></th>
{% for item in list.pager %}
<td><input type="checkbox" name="ids[]" value="{{ item.id }}"></td>
{% endfor %}
Integration with Forms Use the list’s selected IDs to pre-populate a form:
$form = $this->createForm(DeleteForm::class, null, [
'data
How can I help you explore Laravel packages today?