Skip to content

Ajouter un écran de back-office ​

Vous voulez un écran d'administration sur-mesure pour votre projet — un tableau de rapports, un réglage maison — directement dans le skeleton, sans créer de package. C'est le même mécanisme que pour un package (les registres), invoqué depuis un provider de votre application.

1. Un provider d'application ​

Le skeleton n'enregistre aucun provider par défaut (bootstrap/providers.php est vide). Créez-en un — make:provider l'ajoute automatiquement à bootstrap/providers.php :

sh
php artisan make:provider BackofficeServiceProvider

2. Contribuer la route et l'entrée de menu ​

Dans le boot() du provider, exactement comme le ferait un package :

php
namespace App\Providers;

use App\Http\Controllers\Back\ReportsController;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider;
use Slab\Framework\Core\BackMenu\Contracts\BackMenuRegistry;
use Slab\Framework\Core\Routing\Contracts\RouteContributionRegistrar;

class BackofficeServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        // La route rejoint le groupe back-office : préfixe `admin/`, nom `back.*`,
        // groupe de middleware `back` (= `auth` + `CanAccessBackoffice`). (→ back.reports.index)
        $this->app->make(RouteContributionRegistrar::class)->back(function () {
            Route::get('reports', [ReportsController::class, 'index'])->name('reports.index');
        });

        // L'entrée de menu (sans `parent:` = section racine).
        $this->app->make(BackMenuRegistry::class)->add(
            'reports', __('Rapports'),
            route: 'back.reports.index', icon: 'bar_chart', priority: 60,
        );
    }
}

Les deux registres se peuplent au boot() des providers, donc avant le chargement des fichiers de routes : les contributions sont bien capturées par route:cache. Le même registrar expose front() pour la vitrine localisée, où les routes contribuées passent avant l'attrape-tout des slugs.

Les priorités s'ordonnent en croissant, niveau par niveau : les sections racine du cœur vont de 10 (Commandes) à 50 (Configuration) — 60 place donc Rapports en fin de menu.

Réordonner sans redéclarer

Pour déplacer une entrée déjà déclarée — la vôtre ou celle d'un package — ne rappelez pas add() : vous recopieriez son libellé, sa route et son icône, donc vous les figeriez. prioritize() ne change que le rang, et ignore silencieusement une clé inconnue (un package désinstallé ne casse pas le menu) :

php
$this->app->make(BackMenuRegistry::class)->prioritize('config', 70);

Sans code, un config/back_menu.php dans votre projet fait la même chose (le cœur fusionne le sien comme défaut) — et cette config a le dernier mot sur les priorités déclarées :

php
'priorities' => [
    'catalog.products' => 15,
],

3. Le contrôleur ​

Dans app/Http/Controllers/Back/, en étendant le BackController du cœur :

php
namespace App\Http\Controllers\Back;

use Illuminate\Contracts\View\View;
use Slab\Framework\Http\Controllers\Back\BackController;

class ReportsController extends BackController
{
    public function index(): View
    {
        return view('back.reports.index');
    }
}

4. La vue ​

Dans resources/views/back/reports/index.blade.php, en réutilisant le layout du back-office :

blade
<x-layout.back>
    <x-slot:header>
        <h1>{{ __('Rapports') }}</h1>
    </x-slot:header>

    <x-back.section>
        {{-- votre contenu --}}
    </x-back.section>
</x-layout.back>

5. L'icône du menu ​

Le menu utilise une police d'icônes générée. Si votre icône (bar_chart) n'y est pas encore, ajoutez-la à vite/admin-icons.json puis régénérez :

sh
npm run build      # régénère la police d'icônes + les assets

Les icônes référencées en PHP (comme icon: 'bar_chart' du menu) ne sont pas détectées par le scan des templates : il faut les lister dans vite/admin-icons.json.

Et voilà ​

Visitez /admin/reports : l'entrée apparaît dans le menu, la page se rend dans le layout du back-office. Vous avez ajouté un écran sans toucher au cœur.

Quand en faire un package ?

Si cet écran doit être réutilisé sur plusieurs projets, déplacez ce même code dans un package : le boot() est identique, seul l'emballage change.