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

Money Issuer Laravel Package

3neti/money-issuer

A Laravel package for issuing and working with money value objects. Define amounts with currencies, perform precise arithmetic, avoid floating-point errors, and format values for display through a clean, framework-friendly API. Supports multiple currencies and integrates smoothly with Eloquent model...

View on GitHub
Deep Wiki
Context7

Getting Started

composer require <vendor>/laravel-bank-registry
php artisan vendor:publish --tag=bank-registry-config

Start in three places, in this order:

  1. src/Contracts/BankRegistryContract.php — as of v1.0.2 BankRegistry implements this interface, so it is now the authoritative contract. Read it first; it tells you exactly which lookup methods exist and what they return.
  2. config/bank-registry.php — data source (bundled array vs. remote/DB driver), cache TTL, and any normalization options.
  3. src/BankRegistry.php — the concrete implementation, useful for checking how codes are normalized and how the underlying dataset is keyed.

The canonical first use case is resolving a bank from a short code or name, typically to validate a payout/transfer destination:

use Vendor\BankRegistry\Contracts\BankRegistryContract;

public function __construct(private BankRegistryContract $banks) {}

public function validate(array $payload)
{
    $bank = $this->banks->findByCode($payload['bank_code']);

    abort_if($bank === null, 422, 'Unknown bank code.');
}

If you only need a one-off lookup in a controller or Blade view, the facade/helper works fine — but prefer the contract in anything you plan to unit test.

Implementation Patterns

Always type-hint the contract, not the concrete class. This is the single biggest win from v1.0.2. Before it, you had to inject BankRegistry directly, which made fakes brittle. Now:

// app/Providers/AppServiceProvider.php
public function register(): void
{
    $this->app->bind(
        \Vendor\BankRegistry\Contracts\BankRegistryContract::class,
        \Vendor\BankRegistry\BankRegistry::class,
    );
}

Bind explicitly rather than relying on auto-resolution — it documents intent and gives you one obvious place to swap in a decorated implementation.

Decorator for caching or fallback. Because the registry is now contract-backed, wrapping it is clean:

final class CachedBankRegistry implements BankRegistryContract
{
    public function __construct(
        private BankRegistryContract $inner,
        private Cache $cache,
    ) {}

    public function findByCode(string $code): ?Bank
    {
        return $this->cache->remember(
            "bank:{$code}", now()->addDay(), fn () => $this->inner->findByCode($code)
        );
    }
    // ...delegate remaining contract methods
}

Custom FormRequest rule. Bank-code validation is the most common day-to-day call site:

class BankCodeRule implements ValidationRule
{
    public function __construct(private BankRegistryContract $banks) {}

    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        if ($this->banks->findByCode((string) $value) === null) {
            $fail("The selected {$attribute} is not a recognised bank.");
        }
    }
}

Seeding and enums. For a fixed, small set of banks, consider generating a PHP enum from the registry once and using it in domain code, keeping the registry as the data source of truth but avoiding runtime lookups in hot paths.

Normalize before you look up. Build a single private helper in your app that trims and zero-pads codes; do not scatter normalization across call sites, because the registry's own normalization rules may not match your input format.

Gotchas and Tips

The v1.0.2 change is additive but not free. BankRegistry now implements BankRegistryContract. If you previously had your own interface or bound BankRegistry::class directly in the container, review those bindings. Anything type-hinting the old concrete class still works; anything that was not bound but relied on auto-resolution of the concrete class will keep working, but code now type-hinting the contract will fail to resolve unless a binding exists. Add the binding in AppServiceProvider and you are done.

Watch for a container binding conflict. If your app already binds something to BankRegistry::class (a fake, a mock, a test double), the new interface binding may point at the concrete class instead of your fake. Bind the contract to your fake in tests, and assert on the contract, not the class.

Codes and leading zeros. Bank codes are strings, not integers. '012' and 12 are different keys. Any cast to int — in a migration, a cast on a model, a JSON decoder with JSON_NUMERIC_CHECK — will silently break lookups. Keep the column string and the request field string.

Cache invalidation is the usual suspect. If the registry caches the dataset, and you cache lookups on top of it, you now have two TTLs. After updating the underlying bank list, run php artisan cache:clear (or a targeted forget) and confirm both layers cleared. When a bank name is "wrong in production but right locally", this is almost always the cause.

Name search is usually not fuzzy. Do not assume findByName() does substring or case-insensitive matching; check BankRegistry::class for the exact comparison. For user-facing search, load the full list and filter in your own layer rather than calling per-keystroke lookups.

Missing banks should be a domain decision. A null return means "not found"; decide once whether that is a 422 validation error, a soft warning, or a hard failure, and centralize it. Scattering abort_if($bank === null, ...) across controllers leads to inconsistent API responses.

Debugging tip. dd(app(BankRegistryContract::class)) confirms what is actually bound at runtime — faster than reasoning about provider order. If you get "Target [Contract] is not instantiable", that is the missing binding from the section above.

Extension points. The config file (driver / data source) and the contract binding are the two supported seams. If you need different behaviour, decorate the contract rather than subclassing BankRegistry — the class is not marked final today, but treating it as final keeps upgrades 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.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager