У цій заключній частині ми розглянемо деякі методи оптимізації нашої системи та найкращі практики для роботи з мультитенантними додатками.
1. Оптимізація запитів
Використовуйте eager loading для зменшення кількості запитів до бази даних:
|
1 |
$project = Project::with('roles.permissions')->find($id); |
2. Кешування
Використовуйте кешування для часто запитуваних даних:
|
1 2 3 4 5 6 7 8 9 10 11 |
public function getUserPermissions($userId, $projectId) { return Cache::remember("user_permissions_{$userId}_{$projectId}", 60*24, function () use ($userId, $projectId) { $projectUser = ProjectUser::where('user_id', $userId) ->where('project_id', $projectId) ->with('roles.permissions') ->first(); return $projectUser ? $projectUser->roles->flatMap->permissions->pluck('name')->unique() : collect(); }); } |
3. Middleware для автоматичного визначення проекту
Створіть middleware для автоматичного визначення поточного проекту:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<?php namespace App\Http\Middleware; use Closure; use App\Models\Project; class SetCurrentProject { public function handle($request, Closure $next) { $projectId = $request->header('X-Project-ID'); if ($projectId) { $project = Project::findOrFail($projectId); $request->merge(['current_project' => $project]); } return $next($request); } } |
4. Глобальний скоуп для фільтрації за проектом
Використовуйте глобальні скоупи для автоматичної фільтрації даних за поточним проектом:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<?php namespace App\Scopes; use Illuminate\Database\Eloquent\Scope; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Builder; class ProjectScope implements Scope { public function apply(Builder $builder, Model $model) { $projectId = request()->header('X-Project-ID'); if ($projectId) { $builder->where('project_id', $projectId); } } } |
Застосування глобального скоупу до моделі:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<?php namespace App\Models; use App\Scopes\ProjectScope; use Illuminate\Database\Eloquent\Model; class ProjectRole extends Model { protected static function boot() { parent::boot(); static::addGlobalScope(new ProjectScope); } // ... решта коду моделі } |
5. Використання транзакцій
Для забезпечення цілісності даних використовуйте транзакції при виконанні складних операцій:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public function assignRoleToUser(Project $project, User $user, ProjectRole $role) { DB::transaction(function () use ($project, $user, $role) { $projectUser = ProjectUser::firstOrCreate([ 'project_id' => $project->id, 'user_id' => $user->id, ]); $projectUser->roles()->sync([$role->id], false); // Інші операції... }); } |
6. Валідація на рівні запиту
Створіть окремі класи запитів для валідації вхідних даних:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; class CreateProjectPermissionRequest extends FormRequest { public function authorize() { return $this->user()->can('create', [ProjectPermission::class, $this->route('project')]); } public function rules() { return [ 'name' => 'required|string|max:255', 'description' => 'nullable|string', 'role_ids' => 'required|array', 'role_ids.*' => 'exists:project_roles,id,project_id,' . $this->route('project')->id, ]; } } |
7. Використання ресурсів API
Для стандартизації відповідей API використовуйте ресурси:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<?php namespace App\Http\Resources; use Illuminate\Http\Resources\Json\JsonResource; class ProjectPermissionResource extends JsonResource { public function toArray($request) { return [ 'id' => $this->id, 'name' => $this->name, 'description' => $this->description, 'roles' => ProjectRoleResource::collection($this->whenLoaded('roles')), ]; } } |
8. Логування та моніторинг
Впровадьте детальне логування для відстеження важливих подій та помилок:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
use Illuminate\Support\Facades\Log; public function assignPermissionToRole(Project $project, ProjectRole $role, ProjectPermission $permission) { try { $role->permissions()->attach($permission); Log::info('Permission assigned to role', [ 'project_id' => $project->id, 'role_id' => $role->id, 'permission_id' => $permission->id, ]); } catch (\Exception $e) { Log::error('Failed to assign permission to role', [ 'project_id' => $project->id, 'role_id' => $role->id, 'permission_id' => $permission->id, 'error' => $e->getMessage(), ]); throw $e; } } |
Висновок
Створення мультитенантної системи управління правами доступу – це складне завдання, яке вимагає ретельного планування та уваги до деталей. Ключові аспекти, які ми розглянули, включають:
- Проектування гнучкої архітектури бази даних.
- Реалізацію моделей з правильними взаємозв’язками.
- Створення контролерів, що враховують контекст проекту.
- Впровадження політик для забезпечення правильної авторизації.
- Оптимізацію запитів та використання кешування.
- Застосування найкращих практик Laravel для чистого та підтримуваного коду.
Дотримуючись цих принципів, ви зможете створити надійну та масштабовану систему, яка ефективно обслуговуватиме потреби різних клієнтів у рамках єдиного додатку.
Пам’ятайте, що кожен проект унікальний, і ви можете адаптувати ці підходи відповідно до конкретних вимог вашого додатку. Регулярно переглядайте та оновлюйте вашу систему, щоб вона відповідала зростаючим потребам користувачів та бізнесу.