87 lines
2.8 KiB
PHP
87 lines
2.8 KiB
PHP
<?php
|
|
|
|
namespace App\Scopes;
|
|
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Database\Eloquent\Scope;
|
|
use Illuminate\Support\Facades\Auth;
|
|
|
|
class TenantScope implements Scope
|
|
{
|
|
// Static flag to prevent infinite recursion during Auth resolution
|
|
protected static bool $resolvingAuth = false;
|
|
|
|
/**
|
|
* Apply the scope to a given Eloquent query builder.
|
|
*
|
|
* Platform owners (contractor_id = null) bypass all filtering.
|
|
* Contractor users see only records within their own company tree.
|
|
*/
|
|
public function apply(Builder $builder, Model $model): void
|
|
{
|
|
if (self::$resolvingAuth) {
|
|
return;
|
|
}
|
|
|
|
self::$resolvingAuth = true;
|
|
|
|
try {
|
|
if (! Auth::check()) {
|
|
return;
|
|
}
|
|
|
|
$user = Auth::user();
|
|
|
|
if (! $user) {
|
|
return;
|
|
}
|
|
|
|
// Platform owners/admins have no contractor_id — full access
|
|
if (is_null($user->contractor_id)) {
|
|
return;
|
|
}
|
|
|
|
// Gather the authenticated user's own contractor ID plus all direct subcontractors
|
|
$allowedIds = $this->resolveAllowedContractorIds($user->contractor_id);
|
|
|
|
if ($model instanceof \Modules\MasterData\Models\Material) {
|
|
$sharesCatalog = \DB::table('contractors')
|
|
->where('id', $user->contractor_id)
|
|
->value('shares_materials_catalog') ?? true;
|
|
|
|
if ($sharesCatalog) {
|
|
$builder->where(function ($q) use ($model, $allowedIds) {
|
|
$q->whereIn($model->getTable() . '.contractor_id', $allowedIds)
|
|
->orWhereNull($model->getTable() . '.contractor_id');
|
|
});
|
|
} else {
|
|
$builder->whereIn($model->getTable() . '.contractor_id', $allowedIds);
|
|
}
|
|
} else {
|
|
$builder->whereIn($model->getTable() . '.contractor_id', $allowedIds);
|
|
}
|
|
} finally {
|
|
self::$resolvingAuth = false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Build the set of contractor IDs the current user can access.
|
|
* This is the user's own contractor plus any direct children (subcontractors).
|
|
*
|
|
* We deliberately keep this to one level for performance. If you need full
|
|
* recursive trees, swap this for a CTE or Spatie-Nested-Set package.
|
|
*/
|
|
private function resolveAllowedContractorIds(int $contractorId): array
|
|
{
|
|
// Use DB to avoid loading the Contractor model (prevents circular scope boot)
|
|
$childIds = \DB::table('contractors')
|
|
->where('parent_id', $contractorId)
|
|
->pluck('id')
|
|
->toArray();
|
|
|
|
return array_merge([$contractorId], $childIds);
|
|
}
|
|
}
|