betalabs/engine-laravel-helper
A Laravel helper package from Betalabs that bundles reusable utility functions for Engine-based applications. It streamlines common tasks so developers can avoid duplicating boilerplate across projects, keeping code cleaner and more consistent within the Betalabs ecosystem.
This is the Laravel integration layer for Betalabs Engine (ERP/back-office). It gives you Passport-backed API auth, tenant scoping, registry access, NFe tax-invoice indexing, and workflow condition enums — plus a thin wrapper over betalabs/engine-phpsdk.
Requirements (v6.x): PHP ^8.2 (tested against 8.5), Laravel 10/11/12, Passport 11 or 12, betalabs/engine-phpsdk ^3.0.
Minimal setup:
composer require betalabs/engine-phpsdk:^3.0 alongside the package, and pin v6.0.2 — the v6.0.0 tag points at stale code and the whole 6.x line is superseded by 6.0.2.php artisan passport:install. On Passport 12, if the DB has no oauth_* tables yet: php artisan vendor:publish --tag=passport-migrations then php artisan migrate.bootstrap/app.php (->withRouting(...)) and delete any Passport::routes() calls — that method no longer exists since Passport 11.engine registry row with its engine_api_access_token before touching any service class; Services\Tenant\Updater will now throw a RuntimeException if it's absent.First use case to validate the install end-to-end: an authenticated request that lists orders with a joined relation and sparse fields, e.g. GET /...?_fields=orders->code. That path exercises the API guard, the tenant global scope, and the v6.0.2 ambiguous-column fix in one shot.
Where to look first: Models\BelongsToTenant + Scopes\Tenant (data isolation), Services\Tenant\Updater (token/registry writes), Services\NFe\TaxInvoice\Indexer (fiscal documents). Also read the README's "Upgrading to 6.x" section before migrating anything from 5.x.
Multi-tenancy via trait + global scope. Add BelongsToTenant to every tenant-owned model and stop writing manual where('tenant_id', ...) clauses. The scope resolves the tenant from the request context and — as of 6.0.2 — qualifies the column as tabela.tenant_id. That qualification is what makes JOIN-heavy listings (_fields=orders->code, eager relation fields) safe from "ambiguous column" errors. If you have raw query-builder joins outside Eloquent, still qualify tenant_id by hand; the scope only protects models it's attached to.
Registry selection is service-specific, not global. Two distinct behaviours to internalize:
Services\Tenant\Updater always writes engine_api_access_token into the engine registry, even when the tenant owns several registries. Previously it wrote to whichever registry came first — a real bug for multi-registry tenants.Services\NFe\TaxInvoice\Indexer prefers the tenant's nfe registry when present, and otherwise falls back to whatever you set via setRegistryId(). So multi-registry tenants get NFe routing for free; single-registry tenants must set the ID explicitly.Practical pattern: treat registries as named capability slots (engine, nfe, …) rather than an ordered list, and assert the slot exists at boot/seed time rather than discovering it at request time.
Branch on workflow operators via the enum. Models\Enums\WorkflowConditionOperator is now loadable (a missing quote broke class loading in earlier 6.x builds). Use it for match expressions when evaluating workflow conditions instead of string literals — it's the natural extension point if you need custom evaluation logic.
Wrap service classes, don't call them from controllers. Updater and Indexer do IO against the Engine API and can throw. Put them behind a job or an application service so RuntimeException from a missing engine registry becomes a retryable job failure with context, not a 500 on a user request.
Auth integration. The api guard is Passport-based; the suite fixes in 6.0.2 confirm the guard config is the supported path. Keep passport client secrets out of tenant config and in the service config.
v6.0.0 resolves to old code. Require 6.0.2 explicitly in composer.json, not ^6.0, until you've verified what the constraint resolves to.Passport::routes() is gone. On Laravel 11/12 you register package routes in bootstrap/app.php. Leftover calls are a fatal error, not a warning.oauth_* tables with no obvious config error.engine registry now throws RuntimeException with a clear message (previously a raw PHP error). Good news for debugging, but any code path that lazily created tenants without a registry will start failing loudly — seed the registry in your tenant-creation transaction.tenant_id ambiguity bug taught a lesson: if you wrote workarounds (aliased subqueries, manual qualified columns) for the old scope, revisit them after upgrading — they may now be redundant or, worse, conflicting.withoutGlobalScopes() calls (they're the usual culprit). The qualified column now makes EXPLAIN/query-log reading easier since tenant_id is always table-prefixed.How can I help you explore Laravel packages today?