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

Maintainable Laravel Package

zareismail/maintainable

zareismail/maintainable is a Laravel package that provides maintenance-related helpers for PHP applications, making it easier to manage maintenance states and workflows with simple configuration and familiar Laravel conventions.

View on GitHub
Deep Wiki
Context7

Getting Started

The 0.5.0 release is the first version with a stable authorization surface, so start there rather than with the data model.

  1. Install and publish:
    composer require <vendor>/<package>:^0.5
    php artisan vendor:publish --provider="<Vendor>\<Package>\<Package>ServiceProvider"
    php artisan migrate
    
  2. Look here first:
    • config/<package>.php — guard name, permission names, and the model/resource bindings.
    • database/migrations/ — the issues table and any pivot/polymorphic columns.
    • src/Concerns/ — the trait(s) you add to your own models.
    • src/Policies/ and src/Resources/ — where the permission checks from 0.5.0 live.
  3. First use case: attach an issue to an existing Eloquent model via the new morphs helper, then confirm that only an authorized user can settle up that issue:
    // config/<package>.php or a service provider
    <Package>::morphs([
        'project' => \App\Models\Project::class,
        'invoice' => \App\Models\Invoice::class,
    ]);
    
    Add the trait to the owning model, create an issue, then hit the settle-up endpoint as a permitted and a non-permitted user and assert the difference. If those two behave identically, your policy or permission names are not wired up.

Implementation Patterns

Trait on the maintainable side. Put the package trait on the model that "maintains" issues (project, team, tenant) rather than on User. That model is what 0.5.0 now walks when checking access, so keeping it the single source of the relationship avoids duplicate checks.

Morph map first, everywhere. Register aliases in a service provider (Relation::enforceMorphMap([...])) or through the package's morphs helper before any seeding or fixture creation. Aliases in tests and aliases in production must match, or polymorphic rows written by one environment won't resolve in another.

Authorization through the policy, not inline gates. 0.5.0 adds authorization to settling an issue and resolves it via the related permission — i.e. the permission attached to the parent/maintainable resource, not a standalone settle-up permission. Keep your controllers thin:

$this->authorize('settleUp', $issue);   // or the package's ability name

and extend the published policy when you need tenant scoping.

Resource-level access checks. If you expose issues through your own API resources, resolve the maintainable resource explicitly (eager load the morph relation) before serializing. Authorization that depends on the parent model will trigger lazy loads otherwise.

Seeding permissions. Seed the related permissions alongside the parent resource's permissions in the same seeder so they can't drift; a role that can see a project but not settle its issues is the common misconfiguration.

Settle-up as a workflow boundary. Treat settle-up as a state transition: wrap it in a transaction, dispatch your own domain event afterwards, and keep the HTTP layer to validation plus the authorized call.

Gotchas and Tips

  • Breaking-ish behavior change: "view any" is no longer checked. 0.5.0 stopped checking for the view-any permission, so index/listing endpoints that previously returned 403 will now return data. Add your own gate in the listing controller (or policy viewAny) if your app relied on that. Existing feature tests that asserted assertForbidden() on an index route will fail — that's the expected signal, not a bug.
  • Related-permission resolution depends on the relationship being loaded. If the morph relation is missing or the alias is unregistered, the permission lookup silently falls through to deny (or to whatever fallback your policy has). Debug with Gate::inspect() and dump the resolved parent model.
  • Guard mismatch. Permission checks use the guard from config; a custom guard (e.g. admin) needs to be set explicitly, otherwise checks evaluate against the default guard's user and always fail.
  • Permission cache. After seeding or renaming permissions, clear the cached permission table (php artisan permission:cache-reset or the package equivalent) — otherwise the new settle-up permission won't appear until the next deploy.
  • Morph migrations on existing data. If you adopt the morphs helper on an already-populated table, backfill the *_type column with aliases, not FQCNs, or your enforced morph map will reject the rows.
  • Extension points: the policy and the base resource class are the two intended override surfaces. Prefer subclassing the resource over editing published files so future patch releases merge cleanly.
  • Upgrade checklist: re-run the suite focusing on index endpoints, settle-up authorization, and anything asserting on model class names inside polymorphic payloads. Add a test that asserts an unauthorized user gets 403 on settle-up — it's the behavior most likely to regress as the permission model evolves.
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.
steadrun/laravel-monitor
labyrinthe/connect-laravel
elmogy/layan
drewdan/sentinel-client
dskripchenko/laravel-llm
fatryst/nuonuo
stevenay/myanfont
gmostafa/php-graphql-client
xeviant/paystack
snowsoft/faker
meditationsteps/blogetc
animalyoung/laravel-influxdb
onix-solutions/sms-telera
lonely-lights/prosetta
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin