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

Users Profile Balance Laravel Package

baks-dev/users-profile-balance

baks-dev/users-profile-balance provides user profile balance management for PHP/Symfony apps. Install via Composer, run baks:assets:install, apply Doctrine migrations, and test with the users-profile-balance PHPUnit group. Requires PHP 8.4+.

View on GitHub
Deep Wiki
Context7

Getting Started

Reality check before you start: despite the ecosystem framing, this is a Symfony/Doctrine bundle, not a Laravel package. The README's only commands are bin/console baks:assets:install, baks:cache:clear, and doctrine:migrations:*, and the composer scripts use the symfony-cmd handler. There is no service provider, no facade, no artisan command, and no Eloquent model documented. Plan for an integration layer rather than a drop-in.

Prerequisites

  • PHP 8.4+ (hard requirement per the badge — this alone can block a Laravel 10/11 app still on 8.2/8.3).
  • Composer, plus a working Doctrine setup if you want to use the package's own entities and migrations.
  • A decision up front: run Doctrine alongside Eloquent (laravel-doctrine/orm or a thin DBAL-only setup) or treat the package as a schema + reference implementation and write your own Eloquent repository over its tables.

Install

composer require baks-dev/users-profile-balance

Then install config/assets. In a Symfony app this is:

php bin/console baks:assets:install

In Laravel there is no bin/console by default. Either add symfony/console and a minimal kernel, or (recommended for a first spike) inspect what baks:assets:install actually writes — typically config files and Doctrine mapping/asset resources — and copy those into config/ and database/ by hand.

Schema

php bin/console doctrine:migrations:diff
php bin/console doctrine:migrations:migrate

In Laravel, use php artisan migrate for your own tables and run the Doctrine migration through a one-off console command, or hand-port the generated diff into an Eloquent/Blueprint migration. Always --dry-run the diff first — doctrine:migrations:diff will happily propose dropping or altering columns it doesn't recognise in tables you already own.

Where to look first, in this order

  1. vendor/baks-dev/users-profile-balance/composer.json — the real dependency list (Doctrine version, Symfony components, whether it pulls in other baks-dev/* packages).
  2. vendor/baks-dev/users-profile-balance/src/ — entity/DTO definitions, the service that mutates balances, and the interfaces you can bind in a Laravel provider. This is your actual API documentation; the README has none.
  3. vendor/baks-dev/users-profile-balance/src/Resources/config/ (or equivalent) — service definitions, which reveal constructor dependencies and event dispatcher usage.
  4. Doctrine mapping files — column names, indexes, nullability, and whether the balance is keyed by profile_id/user_id.

First use case: read a profile's balance and render it in a controller. Do only that before writing any credit/debit logic — it validates the DB connection, the mapping, and the container wiring in about an hour.

Implementation Patterns

Bind the package's service, don't call statics. The assessment docs circulating internally show Balance::decrement($user->id, 10) — treat that as illustrative, not real. Find the concrete class in src/, and register it in a BalanceServiceProvider:

$this->app->bind(BalanceInterface::class, fn ($app) =>
    new BalanceService(/* pull deps from the package's service definitions */)
);

Binding an interface rather than a concrete class is what lets you swap in a test double and what survives a package upgrade.

Wrap every mutation in a transaction with a row lock. Balance tables are the classic lost-update target. Whatever the package does internally, guard it at your boundary:

DB::transaction(function () use ($profileId, $amount) {
    $row = DB::table('profile_balance')
        ->where('profile_id', $profileId)
        ->lockForUpdate()
        ->first();

    // delegate the arithmetic/validation to the package service
});

If the package offers no locking of its own (nothing in the docs suggests it does), this wrapper is not optional — it is the difference between a working credit system and a support ticket generator.

Adapter over the package's persistence. Don't let Doctrine entities leak into your controllers and jobs. Write a BalanceRepository that speaks your domain language (credit, debit, balanceFor) and internally talks to the package. When the package's undocumented internals inevitably change in a 7.x bump, you edit one class.

Event-driven side effects. The Symfony-flavoured design almost certainly dispatches through symfony/event-dispatcher. Bridge it once:

// In a service provider or a small subscriber registered on the package's dispatcher
Event::listen(BalanceChanged::class, function ($event) {
    // push to analytics, notify the user, invalidate a cached "unlocked features" flag
});

Keep listeners idempotent and non-throwing — a failing mailer should never roll back a balance mutation.

Gate features on balance, not on UI. Put the check in a policy or middleware so the same rule protects API routes, queued jobs, and console commands:

// app/Policies/FeaturePolicy.php
public function exportReports(User $user): bool
{
    return $this->balances->balanceFor($user->profile_id) >= self::EXPORT_COST;
}

Then charge in the controller after the policy passes, inside the same transaction where possible, or accept a small double-spend window knowingly.

Composer scripts — merge, don't overwrite. The README's snippet replaces post-install-cmd/post-update-cmd wholesale. Laravel already uses those hooks for @php artisan package:discover. Merge them:

"scripts": {
    "post-install-cmd": ["@php artisan package:discover --ansi", "@auto-scripts"],
    "post-update-cmd":  ["@php artisan vendor:publish --tag=laravel-assets --ansi --force", "@auto-scripts"],
    "auto-scripts": { "baks:assets:install": "symfony-cmd" }
}

Be aware symfony-cmd requires symfony/flex; without Flex, composer install will error on an unknown handler. Simplest fix: drop the auto-scripts block and run asset install manually as part of your deploy script.

Testing. The README exposes php bin/phpunit --group=users-profile-balance. In a Laravel context, run the vendor suite once manually against your DB to see what actually passes, then write your own feature tests around the adapter: concurrent debits, debit-to-zero, debit-below-zero rejection, and a replayed event.

Gotchas and Tips

  • It is not a Laravel package. Every first-hour problem you hit will trace back to this. Budget for it explicitly; if the schedule doesn't allow for a Doctrine bridge, stop here and write the balance table yourself — it's genuinely a two-day job and you'll understand every line.
  • PHP 8.4 minimum. Verify php -v on every environment including CI, staging, and the queue worker image before composer require. A mismatch surfaces at deploy time as an unhelpful platform-check failure.
  • Zero stars, zero dependents, single-maintainer. Pin to an exact version ("baks-dev/users-profile-balance": "7.4.6"), commit composer.lock, and consider vendoring or forking with a dev- patch branch. Release notes for 7.4.3/7.4.4/7.4.6 are literally just the version number — assume nothing about backwards compatibility between patch releases.
  • doctrine:migrations:diff is destructive by default. It reads your whole schema. Scope it with an explicit --filter or an em configuration limited to the package's mapping, and diff into a throwaway database first.
  • Assets/config are installed to disk, not published. baks:assets:install output is not tracked by vendor:publish, so a fresh clone or a CI container won't have it. Either commit the generated files or call the command from your deploy script.
  • Caching will bite you. If you cache a user's balance or an "is unlocked" boolean, the package's own writes won't invalidate it. Do all invalidation in your event listener, and prefer caching derived state with a short TTL over the balance value itself.
  • Concurrency is undocumented. Assume no optimistic locking, no version column, no SELECT ... FOR UPDATE. Prove it by running two concurrent debits against a test database. If they both succeed when only one should, your wrapper transaction is the only defence.
  • Multi-tenancy is not supported. The model is profile-scoped. If you have tenants, every query in your adapter needs a tenant predicate, and you should add a composite index on (tenant_id, profile_id) rather than relying on whatever the package generates.
  • Debugging path when a mutation silently fails: log the package service's inputs and outputs at the adapter boundary, check storage/logs/var/log for Doctrine query errors (Doctrine's exception messages are far more informative than Eloquent's), and confirm the entity is actually flushed — Doctrine does not persist until flush(), which is the single most common "my balance didn't save" cause for developers coming from Eloquent's save().
  • Extend at the interface, not by editing vendor. If you need balance expiry, multi-currency, or an audit ledger, add it in your adapter/domain layer. Patching vendor/ will be silently lost on the next composer update, and with release notes this thin you will not be told what changed.
  • Scope the blast radius. Keep the package behind one namespace (App\Billing\Balance) and one service provider. If the maintainer abandons it — a real risk at this adoption level — replacing the implementation should touch two files, not twenty.
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.
mydnic/volet-feature-board-filament-plugin
wmde/email-address
refinery29/test-util
dees040/laravel-api-responses
teamzac/geocoder
ideatica/config-writer
keaton-php/pipe-dream
jacksonwebservices/laravel-user-suite
maksimru/conditional-laravel-debugbar
gismatul/nova-support-chat
gismatul/laravel-support-chat
networkrailbusinesssystems/oracle-api
alphabalex/messaging-made-easy
rawphp/warp
shootkiran/merobug
narakode/fineauth
numencode/wn-syncops-plugin
ibnnajjaar/mib-global-pay
refinery29/php-cs-fixer-config
parsnick/steak