marshmallow/categorise-resources
A lightweight Laravel package for categorising Eloquent resources, with migrations, model relationships, and query helpers for filtering content by category. Useful for blogs, docs, and any app that needs flexible grouping of records.
This is a Nova-only package: it adds category headings to the Nova sidebar so related resources (e.g. Billing, Settings, Content) appear as a collapsible/grouped block instead of a flat list. It does not affect Eloquent models, policies, or anything outside Nova's navigation.
Minimum steps:
composer require marshmallow/categorise-resources
The package is archived and last released 2020-09-23, so:
composer.json ("marshmallow/categorise-resources": "1.x" — check the tag) rather than using a floating constraint; there will be no patch releases.config/app.php (Laravel 5.x-style packages of this era often require manual registration) or that composer.json declares extra.laravel.providers for auto-discovery.vendor/marshmallow/categorise-resources/src/ first — with 4 stars and one release, the source is the documentation. Look for a trait (something like CategorisedResource), a static property or method that returns the category name, and any config file (php artisan vendor:publish --tag=...).php artisan serve + browser. Do not roll it out across all resources until one works.First use case: group your admin-only resources under a single Settings heading so the default sidebar stops being a 20-item wall.
Apply the trait on a shared base resource, not per class. Nova resources are usually small; a App\Nova\Resource base class that uses the package trait means new resources inherit categorisation automatically, and you only override the category where needed.
// app/Nova/Resource.php
abstract class Resource extends NovaResource
{
use CategorisesResources; // trait name per package source
public static $category = 'Content';
}
// app/Nova/Invoice.php
class Invoice extends Resource
{
public static $category = 'Billing';
}
Use constants, not string literals. Category names are matched by exact string (most packages of this vintage do no normalisation). A typo silently creates a second heading called Billing — define them once:
final class NovaCategory
{
public const BILLING = 'Billing';
public const SETTINGS = 'Settings';
public const CONTENT = 'Content';
}
Drive grouping from domain boundaries, not technical ones. Billing, Support, Settings reads better than Models, Lookups, Pivots. Align categories with the teams that own the resources — it makes ->canSee() rules easier to reason about.
Compose with Nova visibility rules. Categorisation is presentation only; it does not imply authorisation. Keep authorizedToViewAny() / ->canSee() on the resources so a category never leaks a link a user can't open (and never renders as an empty heading — verify this, some versions render the heading regardless).
Ordering. If the package supports a sort/priority property, set explicit integers per category rather than relying on registration order, otherwise the sidebar reshuffles when you add a resource. If it doesn't, category order will follow Nova::resources() order in NovaServiceProvider — control it there.
Integration tip: register Nova tools and cards that belong to a category in the same place you register resources, and check whether the package only rewrites Nova::availableResources() — if so, dashboards and tools will still sit above/below the grouped block, which may look inconsistent.
public static $group = 'Billing'; / public static function group()). If you're on Nova 4 or 5, use the built-in and skip this package entirely. This package's last release predates Nova 4.boot(). If it loads before your NovaServiceProvider registers resources, groupings can appear empty or partially applied. Check config/app.php ordering / composer.json discovery order if headings vanish.'Settings' and 'settings' produce two headings. Normalise via the constant class above.__() and be aware the heading may not be translatable if the package concatenates them raw.Nova::availableResources(request()) in a temporary route or NovaServiceProvider::boot() to see what the package actually receives and returns. That single dump usually explains any "my resource isn't grouped" report.Nova::availableResources() yourself — at which point you're better off with native $group or a maintained alternative.How can I help you explore Laravel packages today?