kreetancraft/laravel-site-settings

Application settings, navigation menus & multi-platform review aggregator for Laravel — Livewire 4 + Flux UI, Google Tag Manager, Spatie Settings, Google/Facebook/TripAdvisor reviews & public API.

Maintainers

Package info

github.com/Kreetancraft/laravel-site-settings

pkg:composer/kreetancraft/laravel-site-settings

Transparency log

Statistics

Installs: 7

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

0.5.0 2026-08-29 14:04 UTC

This package is auto-updated.

Last update: 2026-08-29 14:04:51 UTC


README

Application settings, a header mega-menu and footer link builder, and Google Tag Manager. Livewire 4 + Flux UI.

Ships no layout, no CSS, no image handling and no user model — it renders into your application and knows nothing about your content types until you tell it.

Design decisions worth knowing before you install

It knows nothing about what you can link to. Menu items point at a custom URL, or at a model you register through MenuLinkRegistry / site-settings.linkables. The shipped config names the kreetancraft/laravel-blog models; on a host without that package those types simply offer no rows rather than failing.

It names no permission of its own. The screens ask the ordinary authorization question and SettingsPolicy answers it. Until permissions exist anywhere in the app it is open, so it works on a bare install rather than failing closed.

It ships no image handling. A mega panel and its categories can carry a promo image, resolved through site-settings.image_resolver when one is configured. With none, the picker does not render at all — a control that silently saves nothing is worse than no control.

It appears in your sidebar on its own if you have kreetancraft/laravel-user-management, through the admin.navigation container tag — both screens under one Site heading, renamable via site-settings.navigation.group. Without that package nothing reads the tag and nothing is built.

Flux is a hard dependency. The views use <flux:*> throughout.

Installation

composer require kreetancraft/laravel-site-settings
php artisan migrate
php artisan vendor:publish --tag=site-settings-config

Tailwind v4 does not scan vendor/. In resources/css/app.css:

@source '../../vendor/kreetancraft/laravel-site-settings/resources/views';

Two screens are registered: admin.settings and admin.navigation, both behind can:view-settings.

The navigation builder

One builder, three locations, each with its own depth cap:

Location Depth What the levels mean
header 3 nav item → panel category → link
footer_primary 2 heading → link
footer_secondary 2 heading → link

A top-level header item is a plain link, a dropdown, or a mega panel. A dropdown or panel trigger stores no URL — it opens something rather than navigating — and the form hides the link fields accordingly. A panel category may have no link at all, in which case it renders as a heading.

Depth is enforced on write by MenuItemGuard, including on a move: dragging a subtree under a new parent is refused when the deepest child would land past the cap. Violations surface as validation errors on parent_id, not exceptions.

Reordering uses Livewire 4's wire:sort, which reports only the item that moved and the index it landed on. The component rebuilds the order from that item's own siblings, scoped by (menu_id, parent_id).

Linkable types

// config/site-settings.php
'linkables' => [
    'page' => [
        'label' => 'Page',
        'model' => \App\Models\Page::class,
        'display_column' => 'title',
        'path_template' => '/{slug}',
    ],
],

Or at runtime, from a package's own service provider:

MenuLinkRegistry::register('event', 'Event', Event::class, 'title', '/events/{slug}');

MenuItem::resolvedUrl() fills {slug} from the linked model, and falls back to the stored url when the model is gone.

Permissions

SettingsPolicy declares PERMISSION_SUBJECT = 'setting' and manage as an extra ability, so kreetancraft/laravel-user-management discovers it and creates view-settings, create-settings, update-settings, delete-settings and manage-settings on php artisan user-management:sync-permissions.

Reads are gated on view-settings, every write on manage-settings. Both are also registered as plain gates, because the screens and the route middleware ask for abilities by name rather than for a policy on a model — there is no row here that an administrator thinks of as the subject.

Define either ability yourself before this package boots and yours wins.

Images

// config/site-settings.php
'image_resolver'    => \Kreetancraft\Media\Support\MediaImageResolver::class,
'media_picker_view' => 'media::picker-field',
'media_modal_view'  => 'media::picker-modal',

The three move together. The resolver is duck-typed — it needs urlFor, listFor and syncFor, and nothing imports an interface across packages — so you can point it at your own class. An optional preload($models, $collection) is used to warm a whole menu in one query.

The saved image comes back as featured_image_url on every node of the API tree, and is null with no resolver configured.

Maintenance mode

EnsureSiteIsLive answers 503 with the configured message: JSON for API requests, the site-settings::maintenance view otherwise. Anyone holding view-settings still gets through, so whoever turned it on can reach the screen that turns it off.

The API

site-settings.routes.api (default prefix api/v1):

Route Returns
GET site settings, menus keyed by location, and JSON-LD
GET site/stats counters from stats_resolver
GET menus/{location} one menu tree

The aggregate is cached and flushed on every write; set site-settings.cache.enabled to false to bypass it entirely. stats_resolver and schema_resolver are host callbacks — this package has no idea what you count or what schema you want.

Nothing here calls out to the network, and a test asserts it: rendering the settings screen with Http::preventStrayRequests() must stay green. Reviews — ingestion and trust badges alike — live in their own package.

Google Tag Manager

<head>@gtmHead</head>
<body>@gtmBody</body>

Both render nothing until a container ID is saved.

Requirements

PHP 8.2+, Laravel 12/13, Livewire 4, Flux 2, spatie/laravel-settings, spatie/eloquent-sortable.

Testing

composer test

License

MIT.