first init
This commit is contained in:
@@ -0,0 +1,304 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use Modules\Formation\Entities\Formation;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
class VisibilityService
|
||||
{
|
||||
/**
|
||||
* Apply visibility scoping to a query builder based on user permissions
|
||||
*
|
||||
* @param bool $includeUnitsWithoutFormation When true (process models), SEL PERO 91 REJ
|
||||
* users can see units without formations. Government users never see units without formations.
|
||||
*/
|
||||
public function applyVisibilityToQuery(Builder $query, $user, string $unitColumn = 'unit_id', bool $includeUnitsWithoutFormation = false): Builder
|
||||
{
|
||||
// Check if user has akses peringkat keseluruhan permission (super admin)
|
||||
if ($user->can('akses peringkat keseluruhan', Unit::class)) {
|
||||
return $query;
|
||||
}
|
||||
|
||||
// Get user's unit
|
||||
$userUnit = Unit::find($user->unit_id);
|
||||
if (!$userUnit) {
|
||||
return $query->whereRaw('1 = 0');
|
||||
}
|
||||
|
||||
// Government permission: Can view all units under their government
|
||||
// Government users do NOT see units without formations
|
||||
// SEL PERO 91 REJ (process models): ONLY see units without formation - not their gov/formation units
|
||||
if ($user->can('akses peringkat formasi')) {
|
||||
if ($includeUnitsWithoutFormation && $user->hasRole('SEL PERO 91 REJ')) {
|
||||
$unitIds = Unit::whereNull('formation_id')
|
||||
->whereNull('repair_formation_id')
|
||||
->pluck('id')
|
||||
->toArray();
|
||||
} else {
|
||||
$unitIds = $this->getUnitsUnderGovernment($userUnit);
|
||||
$unitIds = $this->excludeUnitsWithoutFormation($unitIds);
|
||||
}
|
||||
|
||||
if (!empty($unitIds)) {
|
||||
return $query->whereIn($unitColumn, $unitIds);
|
||||
}
|
||||
return $query->whereRaw('1 = 0');
|
||||
}
|
||||
|
||||
// DIV permission: Can view all units under their formation (including repair formations)
|
||||
if ($user->can('akses peringkat divisyen')) {
|
||||
$unitIds = $this->getVisibleUnitIdsForFormationUser($user, $userUnit, $includeUnitsWithoutFormation);
|
||||
|
||||
if (!empty($unitIds)) {
|
||||
return $query->whereIn($unitColumn, $unitIds);
|
||||
}
|
||||
return $query->whereRaw('1 = 0');
|
||||
}
|
||||
|
||||
// Unit permission: Can only view data within their unit
|
||||
return $query->where($unitColumn, $user->unit_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply visibility scoping for models with indirect unit relationships
|
||||
*
|
||||
* @param bool $includeUnitsWithoutFormation When true (process models), SEL PERO 91 REJ
|
||||
* users can see units without formations. Government users never see units without formations.
|
||||
*/
|
||||
public function applyVisibilityToIndirectQuery(Builder $query, $user, array $unitRelationship, bool $includeUnitsWithoutFormation = false): Builder
|
||||
{
|
||||
// Check if user has akses peringkat keseluruhan permission (super admin)
|
||||
if ($user->can('akses peringkat keseluruhan', Unit::class)) {
|
||||
return $query;
|
||||
}
|
||||
|
||||
// Get user's unit
|
||||
$userUnit = Unit::find($user->unit_id);
|
||||
if (!$userUnit) {
|
||||
return $query->whereRaw('1 = 0');
|
||||
}
|
||||
|
||||
// Government permission: Can view all units under their government
|
||||
// SEL PERO 91 REJ (process models): ONLY see units without formation - not their gov/formation units
|
||||
if ($user->can('akses peringkat formasi')) {
|
||||
if ($includeUnitsWithoutFormation && $user->hasRole('SEL PERO 91 REJ')) {
|
||||
$unitIds = Unit::whereNull('formation_id')
|
||||
->whereNull('repair_formation_id')
|
||||
->pluck('id')
|
||||
->toArray();
|
||||
} else {
|
||||
$unitIds = $this->getUnitsUnderGovernment($userUnit);
|
||||
$unitIds = $this->excludeUnitsWithoutFormation($unitIds);
|
||||
}
|
||||
|
||||
if (!empty($unitIds)) {
|
||||
return $query->whereHas($unitRelationship['relationship'], function ($subQuery) use ($unitIds, $unitRelationship) {
|
||||
$subQuery->whereIn($unitRelationship['unitColumn'], $unitIds);
|
||||
});
|
||||
}
|
||||
return $query->whereRaw('1 = 0');
|
||||
}
|
||||
|
||||
// DIV permission: Can view all units under their formation (including repair formations)
|
||||
if ($user->can('akses peringkat divisyen')) {
|
||||
$unitIds = $this->getVisibleUnitIdsForFormationUser($user, $userUnit, $includeUnitsWithoutFormation);
|
||||
|
||||
if (!empty($unitIds)) {
|
||||
return $query->whereHas($unitRelationship['relationship'], function ($subQuery) use ($unitIds, $unitRelationship) {
|
||||
$subQuery->whereIn($unitRelationship['unitColumn'], $unitIds);
|
||||
});
|
||||
}
|
||||
return $query->whereRaw('1 = 0');
|
||||
}
|
||||
|
||||
// Unit permission: Can only view data within their unit
|
||||
return $query->whereHas($unitRelationship['relationship'], function ($subQuery) use ($user, $unitRelationship) {
|
||||
$subQuery->where($unitRelationship['unitColumn'], $user->unit_id);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all units under the same government as the user's unit
|
||||
* Handles both direct government relationships and formation-government relationships
|
||||
* Priority: Direct government_id takes precedence over formation government
|
||||
*
|
||||
* @param Unit $userUnit
|
||||
* @return array
|
||||
*/
|
||||
public function getUnitsUnderGovernment(Unit $userUnit): array
|
||||
{
|
||||
$unitIds = collect();
|
||||
|
||||
// Case 1: User's unit has direct government relationship (HIGHEST PRIORITY)
|
||||
if ($userUnit->government_id) {
|
||||
$directUnits = Unit::where('government_id', $userUnit->government_id)
|
||||
->pluck('id');
|
||||
$unitIds = $unitIds->merge($directUnits);
|
||||
|
||||
// Also get units in formations under the same direct government
|
||||
$formationUnits = Unit::whereHas('formation', function ($query) use ($userUnit) {
|
||||
$query->where('government_id', $userUnit->government_id);
|
||||
})->pluck('id');
|
||||
$unitIds = $unitIds->merge($formationUnits);
|
||||
|
||||
return $unitIds->unique()->toArray();
|
||||
}
|
||||
|
||||
// Case 2: User's unit belongs to a formation that has a government (FALLBACK)
|
||||
if ($userUnit->formation_id) {
|
||||
$formation = Formation::find($userUnit->formation_id);
|
||||
if ($formation && $formation->government_id) {
|
||||
// Get all units in formations under the same government
|
||||
$formationUnits = Unit::whereHas('formation', function ($query) use ($formation) {
|
||||
$query->where('government_id', $formation->government_id);
|
||||
})->pluck('id');
|
||||
$unitIds = $unitIds->merge($formationUnits);
|
||||
|
||||
// Also get direct government units under the same government
|
||||
$directGovUnits = Unit::where('government_id', $formation->government_id)
|
||||
->pluck('id');
|
||||
$unitIds = $unitIds->merge($directGovUnits);
|
||||
}
|
||||
}
|
||||
|
||||
return $unitIds->unique()->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the repair formation ID for a unit
|
||||
* Priority: repair_formation_id > formation_id > null
|
||||
*
|
||||
* @param Unit $unit
|
||||
* @return int|null
|
||||
*/
|
||||
public function getRepairFormationId(Unit $unit): ?int
|
||||
{
|
||||
// Priority 1: Check repair_formation_id (custom repair formation)
|
||||
if ($unit->repair_formation_id) {
|
||||
return $unit->repair_formation_id;
|
||||
}
|
||||
|
||||
// Priority 2: Fall back to regular formation_id
|
||||
if ($unit->formation_id) {
|
||||
return $unit->formation_id;
|
||||
}
|
||||
|
||||
// Priority 3: No formation (government-level only)
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get visible unit IDs for a user
|
||||
*
|
||||
* @param bool $includeUnitsWithoutFormation When true (process models), SEL PERO 91 REJ
|
||||
* users can see units without formations. Government users never see units without formations.
|
||||
*/
|
||||
public function getVisibleUnitIds($user, bool $includeUnitsWithoutFormation = false): array
|
||||
{
|
||||
// Check if user has akses peringkat keseluruhan permission (super admin)
|
||||
if ($user->can('akses peringkat keseluruhan', Unit::class)) {
|
||||
return Unit::pluck('id')->toArray();
|
||||
}
|
||||
|
||||
// Get user's unit
|
||||
$userUnit = Unit::find($user->unit_id);
|
||||
if (!$userUnit) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Government permission: Can view all units under their government
|
||||
// SEL PERO 91 REJ (process models): ONLY see units without formation - not their gov/formation units
|
||||
if ($user->can('akses peringkat formasi')) {
|
||||
if ($includeUnitsWithoutFormation && $user->hasRole('SEL PERO 91 REJ')) {
|
||||
return Unit::whereNull('formation_id')
|
||||
->whereNull('repair_formation_id')
|
||||
->pluck('id')
|
||||
->toArray();
|
||||
}
|
||||
$unitIds = $this->getUnitsUnderGovernment($userUnit);
|
||||
return $this->excludeUnitsWithoutFormation($unitIds);
|
||||
}
|
||||
|
||||
// DIV permission: Can view all units under their formation (including repair formations)
|
||||
if ($user->can('akses peringkat divisyen')) {
|
||||
return $this->getVisibleUnitIdsForFormationUser($user, $userUnit, $includeUnitsWithoutFormation);
|
||||
}
|
||||
|
||||
// Unit permission: Can only view data within their unit
|
||||
return [$user->unit_id];
|
||||
}
|
||||
|
||||
/**
|
||||
* Exclude units that have no formation (both formation_id and repair_formation_id are null).
|
||||
* Government users do not see these units.
|
||||
*/
|
||||
protected function excludeUnitsWithoutFormation(array $unitIds): array
|
||||
{
|
||||
if (empty($unitIds)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return Unit::whereIn('id', $unitIds)
|
||||
->where(function ($q) {
|
||||
$q->whereNotNull('formation_id')
|
||||
->orWhereNotNull('repair_formation_id');
|
||||
})
|
||||
->pluck('id')
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get visible unit IDs for a user with akses peringkat divisyen permission.
|
||||
*
|
||||
* Asset view (includeUnitsWithoutFormation=false): Only units where formation_id matches.
|
||||
* Excludes units that have repair_formation_id but formation_id null (e.g. 10 SKN RAJD uses
|
||||
* 3 DIV for repair only - 3 DIV users see their repairs but NOT their asset data).
|
||||
*
|
||||
* Process view (includeUnitsWithoutFormation=true): Units where repair_formation_id OR
|
||||
* formation_id matches. SEL PERO 91 REJ also sees units without any formation.
|
||||
*/
|
||||
protected function getVisibleUnitIdsForFormationUser($user, Unit $userUnit, bool $includeUnitsWithoutFormation): array
|
||||
{
|
||||
$userRepairFormationId = $this->getRepairFormationId($userUnit);
|
||||
|
||||
if ($userRepairFormationId) {
|
||||
if (!$includeUnitsWithoutFormation) {
|
||||
// Asset view: Only units that belong to the formation (formation_id matches).
|
||||
// Exclude units that use formation only for repair (formation_id null, repair_formation_id set).
|
||||
return Unit::where('formation_id', $userRepairFormationId)
|
||||
->pluck('id')
|
||||
->toArray();
|
||||
}
|
||||
|
||||
// SEL PERO 91 REJ: ONLY see units without formation - not their formation's units
|
||||
// (formation units are handled by SEL PERO DIV)
|
||||
if ($user->hasRole('SEL PERO 91 REJ')) {
|
||||
return Unit::whereNull('formation_id')
|
||||
->whereNull('repair_formation_id')
|
||||
->pluck('id')
|
||||
->toArray();
|
||||
}
|
||||
|
||||
// Process view: Units in user's formation (repair_formation_id or formation_id)
|
||||
return Unit::where(function ($q) use ($userRepairFormationId) {
|
||||
$q->where('repair_formation_id', $userRepairFormationId)
|
||||
->orWhere(function ($q2) use ($userRepairFormationId) {
|
||||
$q2->whereNull('repair_formation_id')
|
||||
->where('formation_id', $userRepairFormationId);
|
||||
});
|
||||
})->pluck('id')->toArray();
|
||||
}
|
||||
|
||||
// User's unit has no formation - check if they have SEL PERO 91 REJ (process models only)
|
||||
if ($includeUnitsWithoutFormation && $user->hasRole('SEL PERO 91 REJ')) {
|
||||
return Unit::whereNull('formation_id')
|
||||
->whereNull('repair_formation_id')
|
||||
->pluck('id')
|
||||
->toArray();
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user