friendsofvictoire/richlist-widget
A Laravel package that adds a configurable rich-list widget for dashboards and admin panels. Render compact lists of records or metrics with labels, values, icons, and links, making key information easy to scan at a glance.
Read this first: it is not a Laravel package. friendsofvictoire/richlist-widget is a Symfony bundle for Victoire DCMS, a Symfony CMF-based CMS. There is no service provider, no config/richlist.php, no php artisan vendor:publish. The last release was August 2016, it has zero stars and zero dependents, and it is effectively abandoned. If you are starting a Laravel app today, you will not install this — you will reimplement the concept (a repeatable list of rich content blocks) with Eloquent, a Blade component, and a repeater field in your admin panel. The steps below apply only if you inherited a legacy Victoire/Symfony app.
Minimal install (legacy Victoire app):
composer require friendsofvictoire/richlist-widget
Register the bundle in app/AppKernel.php (or config/bundles.php on Symfony 4-era forks):
new Victoire\Widget\RichListBundle\VictoireWidgetRichListBundle(),
Declare the widget class so Victoire's widget palette knows about it:
# app/config/config.yml
victoire_core:
widgets:
richlist:
class: Victoire\Widget\RichListBundle\Entity\WidgetRichList
Where to look first, in this order:
Entity/WidgetRichList.php — the widget model, getWidgetName(), and the relation to its items.Form/WidgetRichListType.php — how items are collected in the backend editor (look for the collection type and allow_add).Resources/views/Widget/show.html.twig — the front-end render; this is what you will override 90% of the time.Resources/config/doctrine/*.orm.xml — tells you whether items are a Doctrine collection or a serialized array.First use case: drop a "Rich list" widget into a page template slot in the Victoire backend, add 3–5 items (title, rich text, link, optional image), save, and confirm the front end renders. That is the entire day-to-day loop.
Widget definition. A Victoire widget is a Doctrine entity extending BaseWidget with a short getWidgetName(). The rich list adds an ordered item collection:
class WidgetRichList extends BaseWidget
{
/**
* @ORM\OneToMany(targetEntity="WidgetRichListItem", mappedBy="widget",
* cascade={"persist", "remove"}, orphanRemoval=true)
* @ORM\OrderBy({"position" = "ASC"})
*/
protected $items;
public function getWidgetName(): string
{
return 'richlist';
}
}
The collection form pattern. This is the heart of the widget. Note by_reference => false and allow_add/allow_delete — without them the item collection will not persist correctly:
$builder->add('items', 'collection', [
'type' => new WidgetRichListItemType(),
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
'prototype' => true,
'cascade_validation'=> true,
]);
Victoire's backend uses the Twig collection prototype plus JavaScript to clone rows. If your items vanish on save, that pair of options is almost always the cause.
Template override workflow. Never edit the vendor template. Copy it into the app-level override path, which is the standard Symfony bundle override convention Victoire respects:
cp vendor/friendsofvictoire/richlist-widget/Resources/views/Widget/show.html.twig \
app/Resources/VictoireWidgetRichListBundle/views/Widget/show.html.twig
Then edit the copy. Clear the cache (bin/console cache:clear --env=dev) and reload. If the override does not take effect, the bundle is probably registered under a slightly different logical name — check bin/console debug:twig --filter=richlist for the actual namespace.
Layout variants. Victoire widgets generally expose a cssClass/template selector field. Use it to drive per-widget layout choices in the template:
{% set items = widget.items %}
<ul class="rich-list {{ widget.cssClass }}">
{% for item in items %}
<li class="rich-list__item">
{% if item.image %}<img src="{{ item.image.url }}" alt="{{ item.title }}">{% endif %}
<h3>{{ item.title }}</h3>
<div>{{ item.body|raw }}</div>
{% if item.url %}<a href="{{ item.url }}">{{ item.linkLabel|default('Read more') }}</a>{% endif %}
</li>
{% endfor %}
</ul>
Laravel equivalent (what you actually build). If you are porting this to Laravel, the same shape maps cleanly:
Schema::create('rich_list_items', function (Blueprint $table) {
$table->id();
$table->foreignId('rich_list_id')->constrained()->cascadeOnDelete();
$table->string('title');
$table->text('body')->nullable();
$table->string('url')->nullable();
$table->string('image')->nullable();
$table->unsignedInteger('position')->default(0);
$table->timestamps();
});
class RichList extends Model
{
protected $casts = ['items' => 'collection'];
public function items() { return $this->hasMany(RichListItem::class)->orderBy('position'); }
}
{{-- resources/views/components/rich-list.blade.php --}}
<ul {{ $attributes->merge(['class' => 'rich-list']) }}>
@foreach ($items as $item)
<li class="rich-list__item">
@if ($item->image)<img src="{{ Storage::url($item->image) }}" alt="{{ $item->title }}">@endif
<h3>{{ $item->title }}</h3>
<div>{!! $item->body !!}</div>
@if ($item->url)<a href="{{ $item->url }}">Read more</a>@endif
</li>
@endforeach
</ul>
For the editor, a Filament Repeater / Nova Flexible field gives you the exact same UX the Victoire bundle gave in 2016 — with active maintenance.
Integration tip: keep the data model free of presentation. The Victoire bundle mixes widget entity, form, and template in one folder; when you port, split them into model / panel schema / Blade component so a layout change never touches the database.
composer require on a modern stack will either fail or install a decade-old transitive tree. Do not put this in a greenfield project.victoire_core.widgets config and/or a victoire.widget service tag. If the widget does not appear in the backend "add widget" menu, check bin/console debug:container --tag=victoire.widget.cascade={"persist"} on the OneToMany (items saved but detached) and missing by_reference => false in the form (mutations not written back). Both present as "the list is empty after I save."OrderBy, your items render in insertion order and reordering in the UI silently does nothing. Add position explicitly and sort in the template if needed.VictoireWidgetRichListBundle vs VictoireRichListBundle). The directory must exactly match the bundle's logical name — verify with debug:twig rather than guessing.Resources/translations/*.yml. Missing keys show the raw key in the admin UI, which looks like a bug but is just an untranslated label — add victoire_richlist.widget.name to your app translations.|raw. If you inherit this in production, confirm the editor sanitizes on save; otherwise you have stored XSS on every page using the widget. In Laravel, treat this as the cue to use a sanitizer like mews/purifier before {!! !!}.bin/console cache:clear, debug:container --tag=victoire.widget, debug:twig, then var/logs/dev.log. Nine out of ten "widget missing / template stale" issues are a stale cache after a config change.rich_lists / rich_list_items pair plus a Blade component in Laravel. You will spend less time than getting a 2016 Symfony 2.8 bundle to boot, and you will own the code.How can I help you explore Laravel packages today?