fix: TenantManager scoped() + explicit company_id in WO create

FrankenPHP/Octane reuses the same PHP process for concurrent requests.
TenantManager registered as singleton() was shared between requests —
a concurrent request from another tenant could overwrite the current
tenant context mid-flight, causing records to be saved with the wrong
company_id.

Fix 1: Change singleton() to scoped() so Octane flushes TenantManager
at the end of each request (fresh instance per request lifecycle).

Fix 2: Explicitly set company_id in mutateFormDataBeforeCreate() as an
extra guard — even if the creating event fires in the wrong context,
the value from the request's own TenantManager call takes precedence.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-09-06 10:49:18 +00:00
parent c359c66827
commit c2f87a468f
2 changed files with 6 additions and 2 deletions
@@ -14,7 +14,8 @@ class CreateWorkOrder extends CreateRecord
protected function mutateFormDataBeforeCreate(array $data): array
{
$companyId = app(TenantManager::class)->currentId();
$data['number'] = WorkOrder::generateNumber($companyId);
$data['number'] = WorkOrder::generateNumber($companyId);
$data['company_id'] = $companyId; // explicit — prevents Octane singleton race condition
return $data;
}
+4 -1
View File
@@ -10,7 +10,10 @@ class AppServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->app->singleton(TenantManager::class);
// scoped() instead of singleton() so Octane/FrankenPHP flushes the
// TenantManager between requests, preventing cross-request contamination
// where a concurrent request's tenant overwrites another request's context.
$this->app->scoped(TenantManager::class);
}
public function boot(): void