Ін'єкція сервісів у Blade: Прямий доступ до сервісів у шаблонах Laravel

Перекладено ШІ
Оригінал: Laravel News
Оновлено: 25 липня, 2025
Laravel надає потужні можливості ін'єкції сервісів через движок шаблонів Blade за допомогою директиви @inject, яка дозволяє легко отримувати доступ до сервісів із контейнера Laravel без перевантаження контролерів. Які переваги це може надати у вашій розробці? Читайте про те, як зменшити складність контролерів та покращити структуризацію коду!

Шаблонізатор Blade у Laravel пропонує потужні можливості для впровадження сервісів за допомогою директиви @inject, що дозволяє отримувати доступ до сервісів з контейнера Laravel без зайвих ускладнень у контролерах.

Впровадження сервісів дає змогу отримувати будь-який сервіс з контейнера Laravel безпосередньо у ваших вьюхах, що знижує складність контролера та забезпечує чітке розділення обов'язків:

@inject('analytics', 'App\Services\AnalyticsService')
 
<div>
    Загальний дохід: {{ $analytics->getTotalRevenue() }}
</div>

Ця директива приймає два параметри: ім'я змінної для сервісу та назву класу або інтерфейсу, який потрібно отримати з контейнера.

@inject('userStats', 'App\Services\UserStatisticsService')
@inject('reportGenerator', 'App\Services\ReportGeneratorService')

<div class="statistics-panel">
    <h3>Статистика користувачів</h3>
    <p>Активні користувачі: {{ number_format($userStats->getActiveUserCount()) }}</p>
    <p>Нові реєстрації: {{ number_format($userStats->getNewRegistrations()) }}</p>
</div>

<div class="reports-section">
    <h3>Доступні звіти</h3>
    @foreach($reportGenerator->getAvailableReports() as $report)
        <a href="{{ $report['url'] }}" class="report-link">
            {{ $report['title'] }}
        </a>
    @endforeach
</div>

Ось приклад адміністративної панелі, що демонструє різні сценарії впровадження сервісів:

{{-- app/Services/SystemHealthService.php --}}
namespace App\Services;

class SystemHealthService
{
    public function getCpuUsage(): float
    {
        return sys_getloadavg()[0] * 100;
    }

    public function getMemoryUsage(): array
    {
        $memoryUsage = memory_get_usage(true);
        $memoryLimit = $this->convertToBytes(ini_get('memory_limit'));

        return [
            'used' => $memoryUsage,
            'limit' => $memoryLimit,
            'percentage' => ($memoryUsage / $memoryLimit) * 100
        ];
    }

    public function getDiskUsage(): array
    {
        $totalSpace = disk_total_space('/');
        $freeSpace = disk_free_space('/');
        $usedSpace = $totalSpace - $freeSpace;

        return [
            'total' => $totalSpace,
            'used' => $usedSpace,
            'free' => $freeSpace,
            'percentage' => ($usedSpace / $totalSpace) * 100
        ];
    }

    private function convertToBytes(string $value): int
    {
        $unit = strtolower(substr($value, -1));
        $number = (int) substr($value, 0, -1);

        return match($unit) {
            'g' => $number * 1024 ** 3,
            'm' => $number * 1024 ** 2,
            'k' => $number * 1024,
            default => $number
        };
    }
}

{{-- app/Services/ActivityMonitorService.php --}}
namespace App\Services;

class ActivityMonitorService
{
    public function getRecentActivities(int $limit = 10): array
    {
        return \App\Models\ActivityLog::latest()
            ->with('user')
            ->limit($limit)
            ->get()
            ->map(function ($activity) {
                return [
                    'user' => $activity->user->name,
                    'action' => $activity->description,
                    'timestamp' => $activity->created_at->diffForHumans(),
                    'ip_address' => $activity->ip_address
                ];
            })
            ->toArray();
    }

    public function getActiveSessionCount(): int
    {
        return \Illuminate\Support\Facades\DB::table('sessions')
            ->where('last_activity', '>', now()->subMinutes(5)->timestamp)
            ->count();
    }

    public function getFailedLoginAttempts(): int
    {
        return \App\Models\FailedLogin::where('created_at', '>', now()->subHour())->count();
    }
}

Ось шаблон Blade з впровадженням сервісів:

{{-- resources/views/admin/dashboard.blade.php --}}
@inject('systemHealth', 'App\Services\SystemHealthService')
@inject('activityMonitor', 'App\Services\ActivityMonitorService')
@inject('userAnalytics', 'App\Services\UserAnalyticsService')

<div class="admin-dashboard">
    <div class="dashboard-header">
        <h1>Панель адміністрування системи</h1>
        <span class="last-updated">Останнє оновлення: {{ now()->format('M j, Y g:i A') }}</span>
    </div>

    <div class="metrics-grid">
        <div class="metric-card system-health">
            <h3>Стан системи</h3>
            <div class="health-indicators">
                <div class="indicator">
                    <label>Використання CPU</label>
                    <div class="progress-bar">
                        <div class="progress" style="width: {{ $systemHealth->getCpuUsage() }}%"></div>
                    </div>
                    <span>{{ number_format($systemHealth->getCpuUsage(), 1) }}%</span>
                </div>

                @php $memory = $systemHealth->getMemoryUsage() @endphp
                <div class="indicator">
                    <label>Використання пам'яті</label>
                    <div class="progress-bar">
                        <div class="progress" style="width: {{ $memory['percentage'] }}%"></div>
                    </div>
                    <span>{{ number_format($memory['percentage'], 1) }}%</span>
                </div>

                @php $disk = $systemHealth->getDiskUsage() @endphp
                <div class="indicator">
                    <label>Використання диска</label>
                    <div class="progress-bar">
                        <div class="progress" style="width: {{ $disk['percentage'] }}%"></div>
                    </div>
                    <span>{{ number_format($disk['percentage'], 1) }}%</span>
                </div>
            </div>
        </div>

        <div class="metric-card user-analytics">
            <h3>Аналіз користувачів</h3>
            <div class="analytics-summary">
                <div class="stat">
                    <span class="value">{{ number_format($userAnalytics->getTotalUsers()) }}</span>
                    <span class="label">Загальна кількість користувачів</span>
                </div>
                <div class="stat">
                    <span class="value">{{ number_format($userAnalytics->getActiveUsersToday()) }}</span>
                    <span class="label">Активні сьогодні</span>
                </div>
                <div class="stat">
                    <span class="value">{{ number_format($userAnalytics->getNewUsersThisWeek()) }}</span>
                    <span class="label">Нові цього тижня</span>
                </div>
                <div class="stat">
                    <span class="value">{{ number_format($activityMonitor->getActiveSessionCount()) }}</span>
                    <span class="label">Активні сесії</span>
                </div>
            </div>
        </div>

        <div class="metric-card security-monitor">
            <h3>Монітор безпеки</h3>
            <div class="security-alerts">
                <div class="alert-item">
                    <span class="alert-icon">🔒</span>
                    <div class="alert-content">
                        <span class="alert-title">Неуспішні спроби входу (Остання година)</span>
                        <span class="alert-value">{{ $activityMonitor->getFailedLoginAttempts() }}</span>
                    </div>
                </div>

                @if($activityMonitor->getFailedLoginAttempts() > 10)
                    <div class="alert-item warning">
                        <span class="alert-icon">⚠️</span>
                        <div class="alert-content">
                            <span class="alert-title">Висока активність неуспішних спроб входу</span>
                            <span class="alert-description">Рекомендуємо переглянути журнали безпеки</span>
                        </div>
                    </div>
                @endif
            </div>
        </div>
    </div>

    <div class="activity-section">
        <h3>Остання діяльність системи</h3>
        <div class="activity-list">
            @foreach($activityMonitor->getRecentActivities(15) as $activity)
                <div class="activity-item">
                    <div class="activity-user">{{ $activity['user'] }}</div>
                    <div class="activity-action">{{ $activity['action'] }}</div>
                    <div class="activity-time">{{ $activity['timestamp'] }}</div>
                    <div class="activity-ip">{{ $activity['ip_address'] }}</div>
                </div>
            @endforeach
        </div>
    </div>

    <div class="quick-actions">
        <h3>Швидкі дії</h3>
        <div class="action-buttons">
            <button onclick="refreshMetrics()" class="btn btn-primary">
                Оновити метрики
            </button>
            <button onclick="exportReport()" class="btn btn-secondary">
                Експортувати звіт
            </button>
            <button onclick="viewLogs()" class="btn btn-outline">
                Переглянути журнали системи
            </button>
        </div>
    </div>
</div>

<style>
.admin-dashboard {
    padding: 20px;
    max-width: 1200px;
    margin: 0 auto;
}

.metrics-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: 20px;
    margin: 20px 0;
}

.metric-card {
    background: white;
    border-radius: 8px;
    padding: 20px;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

.progress-bar {
    width: 100%;
    height: 8px;
    background: #e2e8f0;
    border-radius: 4px;
    overflow: hidden;
    margin: 5px 0;
}

.progress {
    height: 100%;
    background: linear-gradient(90deg, #4ade80, #eab308, #ef4444);
    transition: width 0.3s ease;
}

.analytics-summary {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 15px;
}

.stat {
    text-align: center;
    padding: 10px;
}

.stat .value {
    display: block;
    font-size: 24px;
    font-weight: bold;
    color: #1f2937;
}

.stat .label {
    font-size: 12px;
    color: #6b7280;
    text-transform: uppercase;
}

.activity-list {
    max-height: 400px;
    overflow-y: auto;
}

.activity-item {
    display: grid;
    grid-template-columns: 150px 1fr 120px 100px;
    gap: 10px;
    padding: 10px;
    border-bottom: 1px solid #e5e7eb;
}
</style>

Впровадження сервісів – це елегантне рішення для доступу до утилітних сервісів безпосередньо у шаблонах, що зменшує навантаження на контролери, зберігаючи чітке розділення між бізнес-логікою та елементами представлення