Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Access Layer Laravel Package

formal/access-layer

Formal Access Layer is a Laravel package for building a clean, centralized access-control layer. It helps organize authorization rules, permissions, and access checks so application code stays consistent, testable, and easy to maintain.

View on GitHub
Deep Wiki
Context7

Getting Started

  • Install with composer require formal/access-layer. With a single star and zero dependents, pin the exact version ("formal/access-layer": "x.y.z") rather than floating, and commit the lockfile.
  • Read the source before the README. For a package this small, the service provider (src/*ServiceProvider.php), the config file, and the base class you are expected to extend are the real documentation. The tests/ directory is usually the most reliable usage reference — mirror one of its tests to confirm the package works in your app.
  • Publish and inspect the config: php artisan vendor:publish --tag=access-layer-config (verify the tag with php artisan vendor:publish and picking the provider interactively). Check whether the provider is auto-discovered; if not, add it to bootstrap/providers.php (Laravel 11+) or config/app.php.
  • First use case: pick one non-trivial controller action — ideally a "list + authorize + scope" endpoint — and move only that logic behind the access layer. Do not refactor a whole domain up front. Confirm the layer resolves from the container (app(YourLayer::class)) and that a request returns the same payload as before the refactor.
  • Before writing code, create a throwaway test that asserts the layer's happy path and one denied path. This gives you a regression harness for when you start composing layers.

Implementation Patterns

  • One layer per use case, not per model. App\Access\Orders\ListOrders, App\Access\Orders\ShowOrder read better than a god-object OrderAccessLayer, and they map cleanly onto controller actions and invokable classes.
  • Bind against an interface. Define App\Contracts\OrderAccess and bind the package-backed implementation in a service provider. This is the single most valuable pattern for a 1-star dependency: it gives you a seam to replace or fake the package without touching call sites.
  • Return DTOs or collections, not lazy Eloquent models. If the layer hands back models with unloaded relations, serialization happens outside the layer and you lose the boundary. Eager-load inside the layer or map to a resource/DTO.
  • Authorize once at the entry point. Resolve the authenticated user (or an explicit actor argument) and call $this->authorize(...) / gate checks at the top of the layer method. Internal helpers then trust the caller — this avoids permission checks scattered across controllers, jobs, and commands.
  • Apply scoping inside the layer, always. Tenant IDs, whereBelongsTo($user), soft-delete filters — put them in the layer so HTTP, queued jobs, and artisan commands all get the same visibility rules. This is where an access layer earns its keep.
  • Inject into controllers, form requests, jobs, and commands. Constructor-inject the layer interface; resolve it in job handle() methods. Consistency across entry points is the point of the abstraction.
  • Keep middleware for coarse gates, the layer for object-level rules. can:viewAny,Order on the route, fine-grained "can this user see this row" inside the layer. Don't duplicate the same check in both.
  • Compose small layers. A ShowOrder layer can depend on a LoadOrderForUser layer that performs the scoped fetch; each stays testable in isolation.
  • Testing: in feature tests, $this->app->instance(OrderAccess::class, $fake) to bypass the package entirely; unit-test the real implementation with RefreshDatabase and factories. Assert both the allowed and denied branches — negative tests are what catch scoping regressions.

Gotchas and Tips

  • Treat the API as unstable. One star, zero dependents, and a first release means breaking changes are likely. Wrap it behind your own interface, keep the package-touching code in a handful of classes, and read the diff between tags before upgrading.
  • The published release date (2026-09-24) looks ahead of the current date. Confirm the tag actually exists (composer show formal/access-layer --all); you may need dev-main with a commit hash pinned in composer.json for reproducibility.
  • Config and discovery quirks. If bindings "disappear" after deploy, run php artisan config:clear and php artisan package:discover. Re-publish the config after upgrades — a stale published config silently masks new keys.
  • 403 vs. empty collection. Decide explicitly whether a denied list returns [] or throws AuthorizationException, and encode it in the layer. Mixing the two produces endpoints that leak existence of records.
  • N+1 hiding in the layer. Access layers that resolve relations on demand are easy to write and easy to make quadratic. Add DB::listen() or Laravel Debugbar / Telescope and assert query counts in tests for list endpoints.
  • Don't let the layer reach upward. No request(), no auth() globals inside the layer — take the actor as a parameter. This keeps it usable from queued jobs where there is no HTTP context, and makes tests trivial.
  • Debugging resolution: app()->make(OrderAccess::class)::class in tinker tells you which implementation is bound; php artisan about plus php artisan route:list catches provider-not-registered issues quickly.
  • Extension points to prefer: container rebinding, extending the package's base class, and the published config. Avoid editing files under vendor/ — MIT license means forking or vendoring a small, unmaintained package is a legitimate long-term option if the abstraction proves valuable.
  • Budget for a fallback. If the package stalls, the interface you wrote lets you reimplement the layer with plain Eloquent scopes and policies in an afternoon. Keep the interface free of package-specific types so that swap stays cheap.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core