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

Filament Qr Code Laravel Package

mmuqiitf/filament-qr-code

Filament QR Code provides a simple QR code field and display component for Filament forms and tables, letting you generate scannable codes from URLs, text, or model data with minimal setup in Laravel admin panels.

View on GitHub
Deep Wiki
Context7

Getting Started

  • Install/upgrade via Composer, then run the mandatory asset rebuild:
    php artisan filament:assets
    
    v0.6.0 changed the scanner/sequence views and assets again. If you have customised views, republish them as described in UPGRADE.md — the scanner's x-data no longer embeds options (they now live on data-qr-options) and the sequence view gained a data-qr-options wrapper.
  • Read UPGRADE.md before upgrading. v0.6.0 documents the 0.5 → 0.6 steps: rebuild assets, republish customised views, replace $record->getKey() with ScanResult::recordKey(), and move off the deprecated filament-qr-code.image route name (use filamentQrCode.image; the alias is removed in 1.0). If you are still on 0.4.x, apply the intermediate 0.5 steps from UPGRADE.md first.
  • Add the field to any Filament form schema:
    use Mmuqiitf\FilamentQrCode\Fields\QrScanner;
    
    QrScanner::make('sku')
        ->label('Product SKU')
        ->required();
    
    With no server-side checks configured this still behaves like the older releases: no extra Livewire request, no cooldown, single success beep.
  • Look first at:
    1. config/qr-code.php — scan.cooldown_ms, scan.clear_on_reject, scan.sync_options, feedback.success, feedback.error, audit.enabled.
    2. docs/customizing.md (new extension points), docs/recipes (pos, warehouse, manufacturing, generator-page, custom-input, styling) and UPGRADE.md.
    3. The QrScanner/QrScanSequence/ScanStep classes — validateUsing(), lookup(), lookupUsing(), normalizeWith(), normalizeUsing(), pipe(), onScanned(), onRejected(), onStepAccepted(), onStepRejected(), clearOnReject(), beep(), vibrate(), flash().
  • Best first use case remains an inventory/asset/order form where a scanned code must resolve to a known record before submit:
    QrScanner::make('product_sku')
        ->lookup(Product::class, 'sku')
        ->clearOnReject()
        ->beep()
        ->flash();
    
  • Use the generator for new field/sequence variants instead of copying docs by hand:
    php artisan make:qr-scan-field CustomerBadge --sequence
    
    Publish the stubs with --tag=filament-qr-code-stubs if you want to customise them.

Implementation Patterns

  • Validation-only scans. Use Laravel rules, rule objects, or closures:

    QrScanner::make('barcode')
        ->validateUsing([
            'required',
            'string',
            'max:64',
            Rule::exists('products', 'barcode'),
        ]);
    

    Checks execute in a fixed order: rules, then lookup()/lookupUsing(), then any pipe() stages and closures. Execution stops at the first failure.

  • Record lookup and custom resolution. Use lookup() for simple model/column resolution and lookupUsing() when the value resolves to something other than a model row:

    QrScanner::make('asset_code')
        ->lookup(
            Asset::class,
            'code',
            modifyQueryUsing: fn ($query) => $query->where('active', true),
        );
    
    QrScanner::make('coupon')
        ->lookupUsing(fn (string $code) => Coupon::findByCode($code), fallback: true);
    

    lookup() now accepts closures for the model and column. fallback: true runs lookupUsing() after a normal miss. ScanResult::$record is typed mixed — lookups may return arrays or objects, so always use ScanResult::recordKey() for the key.

  • Normalisation. normalizeUsing() now runs on the server before rules and lookup, not just on stored state, and normalizeWith() provides shared presets that apply in both the browser and server:

    QrScanner::make('serial')
        ->normalizeWith(trim: true, uppercase: true, stripPrefix: 'SN-');
    

    Keep rule expectations aligned with the normalised value — validation sees the normalised code (also on submit). ScanStep::normalizeUsing() exists for sequence steps.

  • Live scan callbacks. Use onScanned() / onRejected() for live-scan UX only:

    QrScanner::make('product_sku')
        ->lookup(Product::class, 'sku')
        ->onScanned(function (ScanResult $result, Get $get, Set $set) {
            $set('product_name', $result->record?->name);
        })
        ->onRejected(function (ScanResult $result) {
            Notification::make()->title('Code rejected')->danger()->send();
        })
        ->clearOnReject();
    

    Upload scans do not fire these callbacks; they go through ScanChecksRule on submit.

  • Custom check stages. For reusable checks, implement ScanCheckStage and attach with pipe():

    QrScanner::make('lot')
        ->lookup(Lot::class, 'code')
        ->pipe(EnsureLotNotExpired::class);
    

    Stages are container-resolved and run after built-in checks, on live scans and on submit. Keep them deterministic and idempotent; use onScanned() for one-off side effects.

  • Sequences. Multi-step flows use QrScanSequence and ScanStep, including conditional steps:

    QrScanSequence::make('pick')
        ->steps([
            ScanStep::make('po')->lookup(PurchaseOrder::class, 'number'),
            ScanStep::make('sku')->lookup(Product::class, 'sku'),
            ScanStep::make('lot')->when(fn (array $values) => $values['sku_requires_lot'] ?? false),
            ScanStep::make('serials')->repeat(),
        ])
        ->onStepAccepted(fn (ScanStep $step, ScanResult $result) => /* ... */)
        ->onStepRejected(fn (ScanStep $step, ScanResult $result) => /* ... */);
    

    A skipped when() step is not required and its value is dropped. An optional step whose check fails now gives way to a following repeat step, so po, sku, lot?, serials works. Step closures receive $get, $livewire, $step and $values; ScanStep is macroable. From page/Livewire code use goToStep(), resetSequence() and setRepeatCount().

  • Runtime control. Scanners and sequences now pick up server-side option changes on every Livewire re-render without being re-created; an in-progress scan completes first. Default scan.sync_options is 'morph'. Switch to explicit refresh with syncOptions(OptionsSync::Refresh) and dispatch qr-scanner-refresh / qr-sequence-refresh when you want precise control. Per-element control is available via element.qrScanner / element.qrScanSequence (feed, pause, resume, reset, setOptions, refresh, getOptions, getState, plus goToStep on sequences) and matching window events (qr-scanner-feed|pause|resume|reset|refresh|set-options, qr-sequence-feed|go-to|reset|pause|resume|refresh|set-options|set-repeat). The shared hardware input router is public as window.FilamentQrCode.router / createInputRouter.

  • Submit-time consistency. Server checks re-run on submit via ScanChecksRule; pipe() stages run there too. Treat rules, lookup, stages and closures as validation: deterministic and idempotent. A scan accepted live can still be rejected on submit if the record changed.

  • Feedback configuration. Keep global defaults in qr-code.feedback.success/error, override per field:

    QrScanner::make('serial')
        ->beep()
        ->vibrate(success: [80, 50, 80], error: [300])
        ->flash();
    

    Useful in warehouse and field-service apps where the operator is not looking at the screen.

  • Events and auditing. ScanAccepted/ScanRejected now carry statePath, livewireClass, livewireId, sequence and values; ScanSequenceCompleted carries livewireClass/livewireId; QrCodeScanned context gains state_path. Accepted live scans fire QrCodeScanned, audited when qr-code.audit.enabled is on. Browser events qr-scan-accepted, qr-scan-rejected, qr-scan-duplicate remain the quickest JS integration/debug surface.

  • Styling and render hooks. Views are split into publishable sub-views with fi-qr-* classes, data-qr-part markers, extraAttributes() on roots, QrCodeRenderHook render hooks and --fi-qr-* CSS variables. Rendered output is otherwise unchanged, so prefer these hooks over editing published views.

  • Duplicate suppression and deployment. Enable checks on a field and rely on qr-code.scan.cooldown_ms for duplicate suppression (handheld bursts are also page-level checked). Add php artisan filament:assets to deploy scripts after composer install, republish customised views, and clear/rebuild config caches when changing qr-code.* values.

Gotchas and Tips

  • Upgrade checklist is not optional. Run php artisan filament:assets, republish customised views, switch to ScanResult::recordKey(), and replace filament-qr-code.image with filamentQrCode.image. The old route alias disappears in 1.0.

  • Republish views for option/runtime behaviour. x-data no longer embeds options; they sit on data-qr-options, and the sequence view wraps it in a <div data-qr-options>. Stale published views break runtime option sync and per-element APIs in ways that look like JavaScript bugs.

  • ScanResult::$record is mixed. Do not call $result->record->getKey(); use recordKey(). Lookups may legitimately return arrays or DTOs.

  • Options sync has two modes. With the default 'morph', options update on every Livewire re-render; the scan in progress finishes first, so changes are not instantaneous mid-scan. With syncOptions(OptionsSync::Refresh) you must dispatch the refresh events yourself. If options look stale, confirm the mode before chasing Alpine state.

  • Checks are ordered and fail-fast. Rules first, then lookup/lookupUsing, then pipe() stages and closures. Put cheap syntactic checks before database work.

  • Checks re-run on submit. A live-accepted scan can be rejected at submit. Keep checks stable; avoid time-sensitive logic unless intended. Custom ScanCheckStage implementations must be container-resolvable and idempotent.

  • Normalisation now affects server validation. normalizeUsing() runs before rules and lookup, so rules previously written against raw input may need adjusting. Prefer normalizeWith() presets so browser and server stay consistent, and validate the same normalised shape you store.

  • Conditional steps drop values. ScanStep::when() skipped steps are not required and their value is removed from the sequence values. Optional step failure can fall through to a following repeat step — step order matters for po, sku, lot?, serials flows.

  • Sequence construction changed. QrScanSequence::make() runs setUp() and configureUsing() callbacks, and sequences with a state path now fill to [] rather than remaining null. Check code that relied on null/absent sequence state.

  • Callbacks are live-scan only. onScanned() / onRejected() and onStepAccepted() / onStepRejected() do not fire for upload scans. Use ScanChecksRule/normal validation for upload paths.

  • Cooldown only matters when server checks exist. Plain scanner fields still make no extra request and have no cooldown. If duplicate suppression is missing, add a check.

  • Config cache can hide changes. After editing qr-code.scan.cooldown_ms, clear_on_reject, sync_options, feedback or audit settings, run php artisan config:clear locally and rebuild the config cache in production.

  • Translation keys matter. Failure messages use record_not_found and scan_failed. Publish/override package translations for localised apps.

  • Flash timing and legacy selectors. The outcome flash plays after the modal closes (or on the open panel) and lasts 900 ms. Update CSS targeting the old 300 ms ring, and do not target the hidden file input for upload-scan styling — v0.4.0+ resolves the wrapper via $root.

  • Per-element events are scoped. qr-scanner-*/qr-sequence-* window events target one field/state path. If nothing happens, check the event name and target match; prefer element.qrScanner when you already hold the element. Overriding createInputRouter affects every routed hardware input globally.

  • Event payloads expanded. Listeners consuming ScanAccepted/ScanRejected should use the new statePath, livewireClass, livewireId, sequence, values fields rather than inferring context. QrCodeScanned context adds state_path.

  • Debugging checklist:

    1. Browser console for qr-scan-duplicate, qr-scan-accepted, qr-scan-rejected, plus qr-scanner-refresh / qr-sequence-refresh when using explicit sync.
    2. Livewire network panel for one handleScan() per scan and unexpected re-renders.
    3. Laravel logs/listeners for ScanAccepted, ScanRejected, ScanSequenceCompleted and QrCodeScanned.
    4. config('qr-code.scan') and config('qr-code.feedback') after cache clears; confirm sync_options.
    5. Asset/view freshness via php artisan filament:assets and republished views.
  • Audit volume. QrCodeScanned fires for accepted live scans when auditing is enabled; prepare listeners and storage for high-volume scanning.

  • Pre-1.0 package. v0.6.0 is feature-rich but still pre-1.0: pin the version, read UPGRADE.md before each bump, and keep asset rebuilds and view republication in the deployment checklist.

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.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo