rich-id/tour-bundle
Rich ID Tour Bundle is a PHP package for adding guided tours and onboarding flows to your app. Define multi-step tours, attach contextual steps to pages or features, and help users discover functionality through an in-app walkthrough experience.
composer require and, if the package ships one, publish its config (php artisan vendor:publish --tag=voter-config). Check config/voter.php first — it usually controls auto-registration, caching, and the default voter namespace.README section on registering voters before writing any authorization code. In v2.x voters are typically discovered from a configured directory or listed explicitly in a service provider; confirm which applies to your install rather than assuming Laravel's policy auto-discovery.php artisan make:voter PostVoter --model=Post). If no command exists, extend the package's base Voter class and implement the ability methods plus the before() hook.Post instance. Voters are a better fit than policies the moment you have cross-cutting rules — ownership or team membership or admin — that you don't want duplicated in every policy method.Gate::forUser($user)->allows('update', $post) in php artisan tinker before touching controllers or Blade. If it returns false for everything, the voter isn't registered; if it throws, the ability name doesn't match a voter method.view, create, update, delete, restore) and keep record-level checks inside voteOnAttribute($ability, $model, $user). Returning null means "abstain" — let another voter decide; returning false is a hard denial.before(). The idiomatic use is an early return true for super-admins so every downstream check is trivially satisfied. Combined with Gate::before() in AuthServiceProvider, this gives you a single admin bypass across voters and policies without touching individual abilities.$user instanceof Authenticatable (or $user === null) at the top of the voter. Most authorization bugs in voter-based code come from a typed User $user parameter meeting a guest request.@can('update', $post), $this->authorize('update', $post) in form requests, and Gate::allows() in middleware all resolve through voters transparently. This is the main migration win over rewriting policy calls.@can('update', $post) per row, any relationship the voter touches (team membership, ownership chains) will be lazy-loaded 50 times. Use ->with([...]) on the query, or cache the resolved permission set per request.actingAs($user) then assertTrue(Gate::allows('update', $post)) covers the happy path; assert false for guests and for cross-tenant records, since that's where voter before()/abstain logic tends to be wrong.false vs null is the whole ballgame. A voter that returns false when it simply doesn't care will block every other voter and policy for that ability. When in doubt, return null.php artisan config:clear / cache:clear in a cached production environment. This is the single most common "it works locally" report.update on Post, adding a PostVoter for the same ability can silently shadow it or be shadowed depending on registration. Pick one mechanism per model, or explicitly document the precedence.Gate::allows('edit-post', $post) and Gate::allows('update', $post) are different abilities. Prefer kebab-case strings that match what your Voter::before() and voteOnAttribute() branches actually compare against, and keep an enum or constants class for them.@can — including inside loops and inside Resource collections. Memoize per-request (or use the package's cache driver if it provides one, and confirm the cache is invalidated when roles change).Voter class's before() and any ability-resolution hooks; overriding before() is the intended customization surface and is far more upgrade-safe than overriding voteOnAttribute().false on abstain paths is the sort of change that quietly flips a @can in production. Keep the package pinned to minor versions you've tested.How can I help you explore Laravel packages today?