Migrate backend to Porto architecture and fix minor bugs

- Fully transitioned the backend to the Porto architectural pattern
- Improved code organization and maintainability
- Fixed minor bugs and inconsistencies
This commit is contained in:
F4ilji
2025-05-12 08:47:43 +05:00
parent 76deaf75f3
commit c01016a154
620 changed files with 16218 additions and 6073 deletions
@@ -0,0 +1,11 @@
<?php
namespace App\Containers\User\Loaders;
class AliasesLoader
{
/**
* @var array
*/
public array $aliases = [];
}
@@ -0,0 +1,26 @@
<?php
namespace App\Containers\User\Loaders;
class MiddlewareLoader
{
/**
* @var array
*/
public array $middleware = [];
/**
* @var array
*/
public array $middlewareGroups = [];
/**
* @var array
*/
public array $routeMiddleware = [];
/**
* @var array
*/
public array $middlewarePriority = [];
}
@@ -0,0 +1,11 @@
<?php
namespace App\Containers\User\Loaders;
class ProvidersLoader
{
/**
* @var array
*/
public array $providers = [];
}
@@ -0,0 +1,62 @@
<?php
namespace App\Containers\User\Mails;
use App\Containers\User\Models\Invitation;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\URL;
class InvitationMail extends Mailable
{
use Queueable, SerializesModels;
private Invitation $invitation;
/**
* Create a new message instance.
*/
public function __construct(Invitation $invitation)
{
$this->invitation = $invitation;
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
return new Envelope(
subject: 'Invitation Mail',
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
markdown: 'emails.invitation',
with: [
'acceptUrl' => URL::signedRoute(
'invitation.accept',
['invitation' => $this->invitation]
)
],
);
}
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [];
}
}
@@ -0,0 +1,24 @@
<?php
namespace App\Containers\User\Models;
use App\Ship\Models\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class AcceptedInvitation extends Model
{
use HasFactory;
protected $fillable = ['sender_id', 'receiver_id', 'post_limit'];
public function sender(): BelongsTo
{
return $this->belongsTo(User::class, 'sender_id');
}
public function receiver(): BelongsTo
{
return $this->belongsTo(User::class, 'receiver_id');
}
}
+13
View File
@@ -0,0 +1,13 @@
<?php
namespace App\Containers\User\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Invitation extends Model
{
use HasFactory;
protected $guarded = false;
}
+12
View File
@@ -0,0 +1,12 @@
<?php
namespace App\Containers\User\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Spatie\Permission\Models\Permission as SpatiePermission;
class Permission extends SpatiePermission
{
use HasFactory;
}
+12
View File
@@ -0,0 +1,12 @@
<?php
namespace App\Containers\User\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Spatie\Permission\Models\Role as SpatieRole;
class Role extends SpatieRole
{
use HasFactory;
}
+120
View File
@@ -0,0 +1,120 @@
<?php
namespace App\Containers\User\Models;
use App\Containers\InstituteStructure\Models\Department;
use App\Containers\InstituteStructure\Models\Division;
use App\Containers\InstituteStructure\Models\Faculty;
use BezhanSalleh\FilamentShield\FilamentShield;
use BezhanSalleh\FilamentShield\Support\Utils;
use BezhanSalleh\FilamentShield\Traits\HasPanelShield;
use Filament\Models\Contracts\FilamentUser;
use Filament\Tables\Columns\Layout\Panel;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
use Spatie\Permission\Traits\HasRoles;
class User extends Authenticatable implements FilamentUser
{
use HasApiTokens, HasFactory, Notifiable, HasRoles, HasPanelShield;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'email',
'password',
'slug',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
public function userDetail(): HasOne
{
return $this->hasOne(UserDetail::class, 'user_id');
}
public function departments_work(): BelongsToMany
{
return $this->belongsToMany(Department::class, 'workers_departments')->withPivot(['position']);
}
public function departments_teach(): BelongsToMany
{
return $this->belongsToMany(Department::class, 'teachers_departments')->withPivot(['teaching_position']);
}
public function divisions(): BelongsToMany
{
return $this->belongsToMany(Division::class, 'division_user')->withPivot(['administrativePosition']);
}
public function faculties(): BelongsToMany
{
return $this->belongsToMany(Faculty::class, 'workers_faculties')->withPivot(['position']);
}
// Отношение к отправленным приглашениям
public function sentInvitations(): HasMany
{
return $this->hasMany(AcceptedInvitation::class, 'sender_id');
}
// Отношение к полученным приглашениям
public function receivedInvitation(): HasOne
{
return $this->hasOne(AcceptedInvitation::class, 'receiver_id');
}
protected static function booted(): void
{
if (config('filament-shield.dashboard_user.enabled', false)) {
FilamentShield::createRole(name: config('filament-shield.dashboard_user.name', 'dashboard_user'));
FilamentShield::createRole(name: config('', 'editor'));
static::created(function (User $user) {
$user->assignRole(config('filament-shield.dashboard_user.name', 'dashboard_user'));
});
static::deleting(function (User $user) {
$user->assignRole(config('filament-shield.dashboard_user.name', 'dashboard_user'));
});
}
}
public function canAccessPanel(Panel|\Filament\Panel $panel): bool
{
return match ($panel->getId()) {
"admin" => $this->hasRole(Utils::getSuperAdminName()),
"dashboard" => $this->hasRole(config('filament-shield.dashboard_user.name', 'dashboard_user'))
|| $this->hasRole(Utils::getSuperAdminName())
|| $this->hasRole(config('filament-shield.invited_user.name', 'invited_user')),
default => false,
};
}
}
+35
View File
@@ -0,0 +1,35 @@
<?php
namespace App\Containers\User\Models;
use App\Ship\Models\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class UserDetail extends Model
{
use HasFactory;
protected $guarded = false;
protected $casts = [
'is_only_worker' => 'boolean',
'awards' => 'array',
'education' => 'array',
'workExperience' => 'array',
'professDisciplines' => 'array',
'professionalRetraining' => 'array',
'professionalDevelopment' => 'array',
'attendedConferences' => 'array',
'participationScienceProjects' => 'array',
'publications' => 'array',
'other' => 'array',
];
public function user()
{
return $this->belongsTo(User::class, 'id');
}
}
@@ -0,0 +1,108 @@
<?php
namespace App\Containers\User\Policies;
use App\Containers\User\Models\User;
use App\Containers\User\Models\AcceptedInvitation;
use Illuminate\Auth\Access\HandlesAuthorization;
class AcceptedInvitationPolicy
{
use HandlesAuthorization;
/**
* Determine whether the user can view any models.
*/
public function viewAny(User $user): bool
{
return $user->can('view_any_accepted::invitation');
}
/**
* Determine whether the user can view the model.
*/
public function view(User $user, AcceptedInvitation $acceptedInvitation): bool
{
return $user->can('view_accepted::invitation');
}
/**
* Determine whether the user can create models.
*/
public function create(User $user): bool
{
return $user->can('create_accepted::invitation');
}
/**
* Determine whether the user can update the model.
*/
public function update(User $user, AcceptedInvitation $acceptedInvitation): bool
{
return $user->can('update_accepted::invitation');
}
/**
* Determine whether the user can delete the model.
*/
public function delete(User $user, AcceptedInvitation $acceptedInvitation): bool
{
return $user->can('delete_accepted::invitation');
}
/**
* Determine whether the user can bulk delete.
*/
public function deleteAny(User $user): bool
{
return $user->can('delete_any_accepted::invitation');
}
/**
* Determine whether the user can permanently delete.
*/
public function forceDelete(User $user, AcceptedInvitation $acceptedInvitation): bool
{
return $user->can('{{ ForceDelete }}');
}
/**
* Determine whether the user can permanently bulk delete.
*/
public function forceDeleteAny(User $user): bool
{
return $user->can('{{ ForceDeleteAny }}');
}
/**
* Determine whether the user can restore.
*/
public function restore(User $user, AcceptedInvitation $acceptedInvitation): bool
{
return $user->can('{{ Restore }}');
}
/**
* Determine whether the user can bulk restore.
*/
public function restoreAny(User $user): bool
{
return $user->can('{{ RestoreAny }}');
}
/**
* Determine whether the user can replicate.
*/
public function replicate(User $user, AcceptedInvitation $acceptedInvitation): bool
{
return $user->can('{{ Replicate }}');
}
/**
* Determine whether the user can reorder.
*/
public function reorder(User $user): bool
{
return $user->can('{{ Reorder }}');
}
}
+108
View File
@@ -0,0 +1,108 @@
<?php
namespace App\Containers\User\Policies;
use App\Containers\User\Models\User;
use App\Containers\User\Models\Role;
use Illuminate\Auth\Access\HandlesAuthorization;
class RolePolicy
{
use HandlesAuthorization;
/**
* Determine whether the user can view any models.
*/
public function viewAny(User $user): bool
{
return $user->can('view_any_shield::role');
}
/**
* Determine whether the user can view the model.
*/
public function view(User $user, Role $role): bool
{
return $user->can('view_shield::role');
}
/**
* Determine whether the user can create models.
*/
public function create(User $user): bool
{
return $user->can('create_shield::role');
}
/**
* Determine whether the user can update the model.
*/
public function update(User $user, Role $role): bool
{
return $user->can('update_shield::role');
}
/**
* Determine whether the user can delete the model.
*/
public function delete(User $user, Role $role): bool
{
return $user->can('delete_shield::role');
}
/**
* Determine whether the user can bulk delete.
*/
public function deleteAny(User $user): bool
{
return $user->can('delete_any_shield::role');
}
/**
* Determine whether the user can permanently delete.
*/
public function forceDelete(User $user, Role $role): bool
{
return $user->can('{{ ForceDelete }}');
}
/**
* Determine whether the user can permanently bulk delete.
*/
public function forceDeleteAny(User $user): bool
{
return $user->can('{{ ForceDeleteAny }}');
}
/**
* Determine whether the user can restore.
*/
public function restore(User $user, Role $role): bool
{
return $user->can('{{ Restore }}');
}
/**
* Determine whether the user can bulk restore.
*/
public function restoreAny(User $user): bool
{
return $user->can('{{ RestoreAny }}');
}
/**
* Determine whether the user can replicate.
*/
public function replicate(User $user, Role $role): bool
{
return $user->can('{{ Replicate }}');
}
/**
* Determine whether the user can reorder.
*/
public function reorder(User $user): bool
{
return $user->can('{{ Reorder }}');
}
}
@@ -0,0 +1,108 @@
<?php
namespace App\Containers\User\Policies;
use App\Containers\User\Models\User;
use App\Containers\User\Models\UserDetail;
use Illuminate\Auth\Access\HandlesAuthorization;
class UserDetailPolicy
{
use HandlesAuthorization;
/**
* Determine whether the user can view any models.
*/
public function viewAny(User $user): bool
{
return $user->can('view_any_user::detail');
}
/**
* Determine whether the user can view the model.
*/
public function view(User $user, UserDetail $userDetail): bool
{
return $user->can('view_user::detail');
}
/**
* Determine whether the user can create models.
*/
public function create(User $user): bool
{
return $user->can('create_user::detail');
}
/**
* Determine whether the user can update the model.
*/
public function update(User $user, UserDetail $userDetail): bool
{
return $user->can('update_user::detail');
}
/**
* Determine whether the user can delete the model.
*/
public function delete(User $user, UserDetail $userDetail): bool
{
return $user->can('delete_user::detail');
}
/**
* Determine whether the user can bulk delete.
*/
public function deleteAny(User $user): bool
{
return $user->can('delete_any_user::detail');
}
/**
* Determine whether the user can permanently delete.
*/
public function forceDelete(User $user, UserDetail $userDetail): bool
{
return $user->can('force_delete_user::detail');
}
/**
* Determine whether the user can permanently bulk delete.
*/
public function forceDeleteAny(User $user): bool
{
return $user->can('force_delete_any_user::detail');
}
/**
* Determine whether the user can restore.
*/
public function restore(User $user, UserDetail $userDetail): bool
{
return $user->can('restore_user::detail');
}
/**
* Determine whether the user can bulk restore.
*/
public function restoreAny(User $user): bool
{
return $user->can('restore_any_user::detail');
}
/**
* Determine whether the user can replicate.
*/
public function replicate(User $user, UserDetail $userDetail): bool
{
return $user->can('replicate_user::detail');
}
/**
* Determine whether the user can reorder.
*/
public function reorder(User $user): bool
{
return $user->can('reorder_user::detail');
}
}
+144
View File
@@ -0,0 +1,144 @@
<?php
namespace App\Containers\User\Policies;
use App\Containers\User\Models\User;
use Illuminate\Auth\Access\HandlesAuthorization;
class UserPolicy
{
use HandlesAuthorization;
/**
* Determine whether the user can view any models.
*
* @param \App\Containers\User\Models\User $user
* @return bool
*/
public function viewAny(User $user): bool
{
return $user->can('view_any_user');
}
/**
* Determine whether the user can view the model.
*
* @param \App\Containers\User\Models\User $user
* @return bool
*/
public function view(User $user): bool
{
return $user->can('view_user');
}
/**
* Determine whether the user can create models.
*
* @param \App\Containers\User\Models\User $user
* @return bool
*/
public function create(User $user): bool
{
return $user->can('create_user');
}
/**
* Determine whether the user can update the model.
*
* @param \App\Containers\User\Models\User $user
* @return bool
*/
public function update(User $user): bool
{
return $user->can('update_user');
}
/**
* Determine whether the user can delete the model.
*
* @param \App\Containers\User\Models\User $user
* @return bool
*/
public function delete(User $user): bool
{
return $user->can('delete_user');
}
/**
* Determine whether the user can bulk delete.
*
* @param \App\Containers\User\Models\User $user
* @return bool
*/
public function deleteAny(User $user): bool
{
return $user->can('delete_any_user');
}
/**
* Determine whether the user can permanently delete.
*
* @param \App\Containers\User\Models\User $user
* @return bool
*/
public function forceDelete(User $user): bool
{
return $user->can('{{ ForceDelete }}');
}
/**
* Determine whether the user can permanently bulk delete.
*
* @param \App\Containers\User\Models\User $user
* @return bool
*/
public function forceDeleteAny(User $user): bool
{
return $user->can('{{ ForceDeleteAny }}');
}
/**
* Determine whether the user can restore.
*
* @param \App\Containers\User\Models\User $user
* @return bool
*/
public function restore(User $user): bool
{
return $user->can('{{ Restore }}');
}
/**
* Determine whether the user can bulk restore.
*
* @param \App\Containers\User\Models\User $user
* @return bool
*/
public function restoreAny(User $user): bool
{
return $user->can('{{ RestoreAny }}');
}
/**
* Determine whether the user can bulk restore.
*
* @param \App\Containers\User\Models\User $user
* @return bool
*/
public function replicate(User $user): bool
{
return $user->can('{{ Replicate }}');
}
/**
* Determine whether the user can reorder.
*
* @param \App\Containers\User\Models\User $user
* @return bool
*/
public function reorder(User $user): bool
{
return $user->can('{{ Reorder }}');
}
}
+14
View File
@@ -0,0 +1,14 @@
<?php
use App\Services\VK\VkAuthService;
use Illuminate\Support\Facades\Route;
Route::middleware(['web', 'superadmin'])->group(function () {
Route::get('/login/vk', [VkAuthService::class, 'redirectToProvider'])->name('vk.login');
Route::get('/login/vk/callback', [VkAuthService::class, 'handleProviderCallback'])->name('vk.callback');
Route::get('/vk-get-token', [VkAuthService::class, 'getToken'])->name('vk.getToken');
Route::get('/vk-refresh-token', [VkAuthService::class, 'refresh'])->name('vk.refreshToken');
Route::get('/vk-logout', [VkAuthService::class, 'logout'])->name('vk.logout');
});
@@ -0,0 +1,40 @@
<?php
namespace App\Containers\User\UI\WEB\Controllers;
use App\Containers\User\Models\User;
use App\Containers\User\UI\WEB\Transformers\FullInfoPersonResource;
use App\Ship\Contracts\SeoServiceInterface;
use App\Ship\Controllers\Controller;
use App\Ship\Enums\CacheKeys;
use Illuminate\Support\Facades\Cache;
use Inertia\Inertia;
class PersonController extends Controller
{
public function __construct(readonly SeoServiceInterface $seoPageProvider){}
public function show(string $slug)
{
$personData = Cache::remember(
CacheKeys::USER_PREFIX->value . $slug,
now()->addHours(24),
fn() => User::with(['userDetail', 'departments_work.faculty', 'departments_teach.faculty', 'divisions', 'faculties'])
->where('slug', $slug)
->firstOrFail()
);
$seo = Cache::remember(
CacheKeys::USER_PREFIX->value . 'seo_' . $slug,
now()->addHours(24),
fn() => $this->seoPageProvider->getSeoForModel($personData)
);
$person = new FullInfoPersonResource($personData);
return Inertia::render('Client/Persons/Show', compact('person', 'seo'));
}
}
+15
View File
@@ -0,0 +1,15 @@
<?php
use App\Containers\User\UI\WEB\Controllers\PersonController;
use Illuminate\Support\Facades\Route;
Route::middleware('signed')->get('invitation/{invitation}/accept', \App\Livewire\AcceptInvitation::class)
->name('invitation.accept');
Route::middleware('access-check')->group(function () {
Route::get('/persons/{slug}', [PersonController::class, 'show'])->name('client.person.show');
});
@@ -0,0 +1,26 @@
<?php
namespace App\Containers\User\UI\WEB\Transformers;
use App\Ship\Resources\JsonResource;
class FullInfoPersonResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray($request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'details' => new PersonDetailResource($this->whenLoaded('userDetail')),
'departments_work' => PersonDepartmentsWorkResource::collection($this->whenLoaded('departments_work')),
'departments_teach' => PersonDepartmentsTeachResource::collection($this->whenLoaded('departments_teach')),
'divisions_works' => PersonDivisionsWorkResource::collection($this->whenLoaded('divisions')),
'faculties_works' => PersonFacultiesWorkResource::collection($this->whenLoaded('faculties'))
];
}
}
@@ -0,0 +1,29 @@
<?php
namespace App\Containers\User\UI\WEB\Transformers;
use App\Ship\Resources\JsonResource;
class PersonDepartmentPreviewResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray($request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'slug' => $this->slug,
'academicTitle' => $this->userDetail->academicTitle ?? null,
'contactPhone' => $this->pivot->service_phone,
'contactEmail' => $this->pivot->service_email,
'cabinet' => $this->pivot->cabinet,
'photo' => $this->userDetail->photo ?? null,
'position' => $this->pivot->position,
];
}
}
@@ -0,0 +1,28 @@
<?php
namespace App\Containers\User\UI\WEB\Transformers;
use App\Ship\Resources\JsonResource;
class PersonDepartmentTeachPreviewResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray($request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'slug' => $this->slug,
'academicTitle' => $this->userDetail->academicTitle ?? null,
'contactPhone' => $this->pivot->service_phone,
'contactEmail' => $this->pivot->service_email,
'cabinet' => $this->pivot->cabinet,
'photo' => $this->userDetail->photo ?? null,
'position' => $this->pivot->teaching_position,
];
}
}
@@ -0,0 +1,26 @@
<?php
namespace App\Containers\User\UI\WEB\Transformers;
use App\Ship\Resources\JsonResource;
class PersonDepartmentsTeachResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray($request): array
{
return [
'id_department' => $this->id,
'title' => $this->title,
'faculty_title' => $this->faculty->title,
'slug' => $this->slug,
'faculty_slug' => $this->faculty->slug,
'position' => $this->pivot->teaching_position,
];
}
}
@@ -0,0 +1,27 @@
<?php
namespace App\Containers\User\UI\WEB\Transformers;
use App\Ship\Resources\JsonResource;
class PersonDepartmentsWorkResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray($request): array
{
return [
'id_department' => $this->id,
'title' => $this->title,
'faculty_title' => $this->faculty->title,
'slug' => $this->slug,
'faculty_slug' => $this->faculty->slug,
'position' => $this->pivot->position,
];
}
}
@@ -0,0 +1,19 @@
<?php
namespace App\Containers\User\UI\WEB\Transformers;
use App\Ship\Resources\JsonResource;
class PersonDetailResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray($request): array
{
return parent::toArray($request);
}
}
@@ -0,0 +1,31 @@
<?php
namespace App\Containers\User\UI\WEB\Transformers;
use App\Ship\Resources\JsonResource;
class PersonDivisionPreviewResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray($request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'slug' => $this->slug,
'academicTitle' => $this->userDetail->academicTitle,
'contactPhone' => $this->pivot->service_phone,
'contactEmail' => $this->pivot->service_email,
'cabinet' => $this->pivot->cabinet,
'photo' => $this->userDetail->photo,
'administrativePosition' => $this->pivot->administrativePosition,
'sort' => $this->pivot->sort,
'details' => $this->userDetail,
];
}
}
@@ -0,0 +1,23 @@
<?php
namespace App\Containers\User\UI\WEB\Transformers;
use App\Ship\Resources\JsonResource;
class PersonDivisionsWorkResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray($request): array
{
return [
'id_division' => $this->id,
'title' => $this->title,
'slug' => $this->slug,
'position' => $this->pivot->administrativePosition,
];
}
}
@@ -0,0 +1,23 @@
<?php
namespace App\Containers\User\UI\WEB\Transformers;
use App\Ship\Resources\JsonResource;
class PersonFacultiesWorkResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray($request): array
{
return [
'id_faculty' => $this->id,
'title' => $this->title,
'slug' => $this->slug,
'position' => $this->pivot->position,
];
}
}
@@ -0,0 +1,29 @@
<?php
namespace App\Containers\User\UI\WEB\Transformers;
use App\Ship\Resources\JsonResource;
class PersonFacultyPreviewResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray($request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'slug' => $this->slug,
'academicTitle' => $this->userDetail->academicTitle,
'contactPhone' => $this->pivot->service_phone,
'contactEmail' => $this->pivot->service_email,
'cabinet' => $this->pivot->cabinet,
'photo' => $this->userDetail->photo,
'position' => $this->pivot->position,
];
}
}