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

User Level Bundle Laravel Package

tourze/user-level-bundle

A Symfony bundle for managing user levels and ranks, providing configurable level definitions and easy integration for tiered membership, permissions, or gamification features in your application.

View on GitHub
Deep Wiki
Context7

Getting Started

tourze/user-level-bundle is a pre-1.0 package for modelling user levels/grades (tiers, ranks, progression) as first-class domain objects rather than a loose integer column. Version 0.1.0 is the first minor bump off 0.0.2, and the only release note is a compare/0.0.2...0.1.0 link — there is no changelog prose, so read the diff before upgrading rather than trusting the version number.

Minimal path in:

  1. composer require tourze/user-level-bundle:^0.1 — pin to ^0.1, never *, on a 0.x line.
  2. Read the diff first: https://github.com/tourze/user-level-bundle/compare/0.0.2...0.1.0. On a 0.x package a minor bump is where breaking changes legally live, so look specifically for renamed classes, changed constructor signatures, and removed interfaces.
  3. Open vendor/tourze/user-level-bundle/src and list the public surface:
    find vendor/tourze/user-level-bundle/src -name '*.php' | sort
    grep -rn "interface \|abstract class " vendor/tourze/user-level-bundle/src
    
  4. Look for the bundle/extension entry point (*Bundle.php) and any Resources/config/ or config/ directory. Tourze packages are conventionally Symfony-flavoured (Doctrine entities, DI service definitions, *Interface contracts) even when consumed from Laravel — expect contracts and entities, not a Laravel service provider.
  5. First use case: add a level relation to your users table/model and resolve a user's current level through the package's service rather than computing it inline. That single read path is the cheapest way to prove the package fits before wiring promotion logic.

If src/ turns out to expose only entities with no Laravel binding, budget a small bridge (see below) before committing.

Implementation Patterns

Treat the package as a domain layer, not a Laravel package. The realistic integration is: use its entities/value objects and level-resolution services, and own the framework glue yourself.

Bridge service provider. If no UserLevelServiceProvider is auto-discovered, register the package's services manually and bind its contracts:

// app/Providers/UserLevelServiceProvider.php
public function register(): void
{
    $this->app->singleton(LevelResolverInterface::class, fn ($app) =>
        new CachedLevelResolver(
            $app->make(BaseLevelResolver::class),
            $app->make('cache.store'),
        )
    );
}

Keep the level off your users table as a source of truth. Store experience/points (or the inputs the package expects) and derive level via the resolver. Denormalise a level_id column only as a read optimisation, refreshed on the same transaction that changes the inputs — never as the only copy.

Expose it through an accessor so views stay dumb:

protected $appends = ['level'];

public function level(): Attribute
{
    return Attribute::get(fn () => app(LevelResolverInterface::class)->for($this));
}

Promotion as a domain event. Whatever the package offers for "does this user qualify for the next level", call it from one place — a LevelService or a queued listener on your own ExperienceGained event — and fire a UserLevelledUp event from there. Don't scatter threshold checks across controllers; the package's rules will change under you on a 0.x line and you want one seam to patch.

Cache aggressively. Level lookups are read-heavy and change rarely per user. Cache the resolved level keyed by user id and forget it in the same place you mutate experience. A CachedLevelResolver decorator (as above) keeps the package's classes untouched.

Testing. Bind a fake resolver in tests instead of seeding real level rows:

$this->app->bind(LevelResolverInterface::class, fn () => new FixedLevelResolver(level: 7));

Assert on derived level, not on stored columns, so a change in the package's threshold maths doesn't silently invalidate your suite's meaning.

Gotchas and Tips

  • 0.x semver is not stable semver. 0.1.0 from 0.0.2 can rename or delete anything. Lock the exact version in composer.json ("0.1.0", not ^0.1) in production until you've read a diff end to end, and upgrade deliberately.
  • The release note is empty. A bare compare link means the maintainer is relying on commit history as documentation. Run composer update tourze/user-level-bundle --dry-run -v and diff vendor/ against your VCS copy before deploying. Treat "no changelog" as "unknown breaking change".
  • Doctrine vs Eloquent. If the entities are Doctrine-annotated/attributed, do not try to hydrate them through Eloquent. Use them as plain value objects (construct from your Eloquent data) or map them via a small repository of your own. Mixing the two is the most common source of confusing EntityManager-not-found errors here.
  • No Laravel auto-discovery. Symfony-style bundles don't ship a extra.laravel.providers block. Check composer.json in vendor/tourze/user-level-bundle for extra.laravel before assuming php artisan vendor:publish will work — it almost certainly won't.
  • Config lives outside Laravel's config system. If the package reads its own config.yaml/parameters, mirror the values you care about into config/user-level.php and pass them in explicitly at construction; don't depend on the package finding a file at a path Laravel doesn't use.
  • Debugging tip: when a level resolves to null unexpectedly, check the resolver's boundary conditions first (off-by-one at exact thresholds, and users with zero experience). Then check the cache — a stale entry after an experience mutation looks identical to a logic bug.
  • Extension points to prefer: interfaces over concrete classes. If src/ defines *Interface contracts, bind your own implementations and let the package be the default. That keeps a future 0.2.0 breaking change confined to one binding line.
  • Watch the repo, not just Packagist. Given the thin release notes, subscribe to GitHub releases on tourze/user-level-bundle so you see commits land rather than discovering them at composer update time.
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.
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment