Laravel 11: Створення повноцінного застосунку з нуля

Вичерпний посібник зі створення застосунку на Laravel 11 з автентифікацією, REST API, Eloquent ORM та розгортанням у продакшн. Практичний підручник для початківців і розробників середнього рівня.

Посібник з Laravel 11 для створення повноцінного застосунку

Laravel 11 переосмислює сучасну PHP-розробку завдяки спрощеній архітектурі та оптимізованій продуктивності. Цей мажорний реліз видаляє багато стандартних конфігураційних файлів, пропонує легшу структуру проєкту та суттєво покращує досвід розробника. Цей посібник проведе через створення повноцінного застосунку для управління завданнями — від встановлення до розгортання у продакшн.

Ключові нововведення Laravel 11

Laravel 11 впроваджує мінімалістичну структуру: файли Kernel видалені, конфігурація об'єднана в bootstrap/app.php, а каталог app/ став чистішим. Фреймворк вимагає мінімум PHP 8.2 і нативно інтегрує SQLite для швидкого старту.

Встановлення та налаштування проєкту

Laravel 11 встановлюється через Composer або інсталятор Laravel. Нова структура проєкту легша, з меншою кількістю конфігураційних файлів для управління від самого початку.

bash
# terminal
# Install the Laravel installer (recommended)
composer global require laravel/installer

# Create a new Laravel 11 project
laravel new task-manager

# Or directly with Composer
composer create-project laravel/laravel task-manager

# Navigate to the project
cd task-manager

Інсталятор пропонує кілька інтерактивних опцій: вибір стартового набору (Breeze, Jetstream), тестового фреймворку (Pest, PHPUnit) та бази даних.

bash
# terminal
# Start the development server
php artisan serve

# Server starts at http://localhost:8000

Налаштування бази даних

Laravel 11 за замовчуванням використовує SQLite, що ідеально підходить для розробки. Для продакшн-застосунків конфігурація MySQL або PostgreSQL виконується у файлі .env.

env
# .env
# MySQL configuration
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=task_manager
DB_USERNAME=root
DB_PASSWORD=secret

# PostgreSQL configuration (alternative)
# DB_CONNECTION=pgsql
# DB_HOST=127.0.0.1
# DB_PORT=5432
# DB_DATABASE=task_manager
# DB_USERNAME=postgres
# DB_PASSWORD=secret

Перевірка підключення до бази даних виконується командою Artisan.

bash
# terminal
# Verify database connection
php artisan db:show

# Run default migrations
php artisan migrate

Створення моделей та міграцій

Eloquent ORM спрощує маніпулювання даними завдяки виразним моделям. Команда make:model генерує модель, міграцію, контролер та фабрику за один раз.

bash
# terminal
# Generate Task model with migration, factory, seeder, and controller
php artisan make:model Task -mfsc

# -m : migration
# -f : factory
# -s : seeder
# -c : controller

Ця команда створює чотири необхідні файли для повноцінного управління завданнями.

php
<?php
// database/migrations/2026_01_14_000000_create_tasks_table.php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

// Migration to create the tasks table
return new class extends Migration
{
    public function up(): void
    {
        Schema::create('tasks', function (Blueprint $table) {
            // Auto-incrementing primary key
            $table->id();
            // Relationship with the owner user
            $table->foreignId('user_id')->constrained()->cascadeOnDelete();
            // Task title (required)
            $table->string('title');
            // Detailed description (optional)
            $table->text('description')->nullable();
            // Status: pending, in_progress, completed
            $table->string('status')->default('pending');
            // Priority: low, medium, high
            $table->string('priority')->default('medium');
            // Due date (optional)
            $table->date('due_date')->nullable();
            // Completion marker
            $table->timestamp('completed_at')->nullable();
            // Automatic timestamps (created_at, updated_at)
            $table->timestamps();
            // Soft deletes for trash functionality
            $table->softDeletes();

            // Indexes to optimize frequent queries
            $table->index(['user_id', 'status']);
            $table->index('due_date');
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('tasks');
    }
};

Міграція визначає структуру таблиці зі зв'язками, індексами та soft delete для запобігання випадковій втраті даних.

php
<?php
// app/Models/Task.php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Builder;

class Task extends Model
{
    // Traits for advanced functionality
    use HasFactory, SoftDeletes;

    // Mass-assignable attributes
    protected $fillable = [
        'user_id',
        'title',
        'description',
        'status',
        'priority',
        'due_date',
        'completed_at',
    ];

    // Automatic attribute casting
    protected function casts(): array
    {
        return [
            'due_date' => 'date',
            'completed_at' => 'datetime',
        ];
    }

    // Constants for valid statuses
    public const STATUS_PENDING = 'pending';
    public const STATUS_IN_PROGRESS = 'in_progress';
    public const STATUS_COMPLETED = 'completed';

    // Constants for priorities
    public const PRIORITY_LOW = 'low';
    public const PRIORITY_MEDIUM = 'medium';
    public const PRIORITY_HIGH = 'high';

    // Relationship: a task belongs to a user
    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }

    // Scope: pending tasks
    public function scopePending(Builder $query): Builder
    {
        return $query->where('status', self::STATUS_PENDING);
    }

    // Scope: in-progress tasks
    public function scopeInProgress(Builder $query): Builder
    {
        return $query->where('status', self::STATUS_IN_PROGRESS);
    }

    // Scope: completed tasks
    public function scopeCompleted(Builder $query): Builder
    {
        return $query->where('status', self::STATUS_COMPLETED);
    }

    // Scope: overdue tasks
    public function scopeOverdue(Builder $query): Builder
    {
        return $query->where('due_date', '<', now())
                     ->whereNull('completed_at');
    }

    // Scope: tasks for a specific user
    public function scopeForUser(Builder $query, int $userId): Builder
    {
        return $query->where('user_id', $userId);
    }

    // Accessor: check if task is overdue
    public function getIsOverdueAttribute(): bool
    {
        return $this->due_date
            && $this->due_date->isPast()
            && !$this->completed_at;
    }

    // Method: mark as completed
    public function markAsCompleted(): void
    {
        $this->update([
            'status' => self::STATUS_COMPLETED,
            'completed_at' => now(),
        ]);
    }
}

Модель Task використовує Eloquent scopes для читабельних та повторно використовуваних запитів. Константи централізують допустимі значення статусів та пріоритетів.

Soft Deletes

Трейт SoftDeletes додає стовпець deleted_at. Видалені завдання не зникають з бази даних, а лише позначаються як видалені. Метод withTrashed() дозволяє включити їх у запити.

Налаштування автентифікації з Breeze

Laravel Breeze забезпечує повну автентифікацію з Blade-шаблонами або API. Встановлення займає кілька хвилин і генерує все необхідне: маршрути, контролери, представлення та тести.

bash
# terminal
# Install Laravel Breeze
composer require laravel/breeze --dev

# Install the Blade stack (traditional)
php artisan breeze:install blade

# Or for API only (SPA/mobile)
php artisan breeze:install api

# Compile assets
npm install && npm run build

# Run migrations (users, sessions tables, etc.)
php artisan migrate

Breeze генерує маршрути автентифікації, контролери та представлення для реєстрації, входу, скидання пароля та верифікації електронної пошти.

php
<?php
// app/Models/User.php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable
{
    use HasFactory, Notifiable;

    protected $fillable = [
        'name',
        'email',
        'password',
    ];

    protected $hidden = [
        'password',
        'remember_token',
    ];

    protected function casts(): array
    {
        return [
            'email_verified_at' => 'datetime',
            'password' => 'hashed',
        ];
    }

    // Relationship: a user owns multiple tasks
    public function tasks(): HasMany
    {
        return $this->hasMany(Task::class);
    }

    // Shortcut: user's pending tasks
    public function pendingTasks(): HasMany
    {
        return $this->tasks()->pending();
    }

    // Shortcut: user's overdue tasks
    public function overdueTasks(): HasMany
    {
        return $this->tasks()->overdue();
    }
}

Готовий до співбесід з Laravel?

Практикуйся з нашими інтерактивними симуляторами, flashcards та технічними тестами.

Створення контролерів та маршрутів

Контролер координує бізнес-логіку між HTTP-запитами та моделлю. Laravel 11 заохочує використання одноакційних контролерів або RESTful-ресурсів.

php
<?php
// app/Http/Controllers/TaskController.php

namespace App\Http\Controllers;

use App\Models\Task;
use Illuminate\Http\Request;
use Illuminate\Http\RedirectResponse;
use Illuminate\View\View;
use Illuminate\Support\Facades\Gate;

class TaskController extends Controller
{
    // List tasks for the authenticated user
    public function index(Request $request): View
    {
        // Retrieve tasks with optional filtering
        $tasks = $request->user()
            ->tasks()
            ->when($request->status, fn($q, $status) => $q->where('status', $status))
            ->when($request->priority, fn($q, $priority) => $q->where('priority', $priority))
            ->orderBy('due_date')
            ->orderBy('priority', 'desc')
            ->paginate(15);

        return view('tasks.index', compact('tasks'));
    }

    // Creation form
    public function create(): View
    {
        return view('tasks.create');
    }

    // Store a new task
    public function store(Request $request): RedirectResponse
    {
        // Validate incoming data
        $validated = $request->validate([
            'title' => 'required|string|max:255',
            'description' => 'nullable|string|max:5000',
            'priority' => 'required|in:low,medium,high',
            'due_date' => 'nullable|date|after_or_equal:today',
        ]);

        // Create the task linked to the user
        $request->user()->tasks()->create($validated);

        return redirect()
            ->route('tasks.index')
            ->with('success', 'Task created successfully.');
    }

    // Display a task
    public function show(Task $task): View
    {
        // Verify user ownership
        Gate::authorize('view', $task);

        return view('tasks.show', compact('task'));
    }

    // Edit form
    public function edit(Task $task): View
    {
        Gate::authorize('update', $task);

        return view('tasks.edit', compact('task'));
    }

    // Update a task
    public function update(Request $request, Task $task): RedirectResponse
    {
        Gate::authorize('update', $task);

        $validated = $request->validate([
            'title' => 'required|string|max:255',
            'description' => 'nullable|string|max:5000',
            'status' => 'required|in:pending,in_progress,completed',
            'priority' => 'required|in:low,medium,high',
            'due_date' => 'nullable|date',
        ]);

        // Update completed_at if status = completed
        if ($validated['status'] === Task::STATUS_COMPLETED && !$task->completed_at) {
            $validated['completed_at'] = now();
        }

        $task->update($validated);

        return redirect()
            ->route('tasks.index')
            ->with('success', 'Task updated.');
    }

    // Delete a task
    public function destroy(Task $task): RedirectResponse
    {
        Gate::authorize('delete', $task);

        $task->delete();

        return redirect()
            ->route('tasks.index')
            ->with('success', 'Task deleted.');
    }

    // Mark as completed (quick action)
    public function complete(Task $task): RedirectResponse
    {
        Gate::authorize('update', $task);

        $task->markAsCompleted();

        return back()->with('success', 'Task completed!');
    }
}

Контролер використовує Gates для авторизації, вбудовану валідацію та route model binding для лаконічного і безпечного коду.

Визначення маршрутів

Маршрути визначають URL-адреси та прив'язують їх до дій контролера. Laravel 11 централізує маршрути у файлі routes/web.php.

php
<?php
// routes/web.php

use App\Http\Controllers\TaskController;
use App\Http\Controllers\ProfileController;
use Illuminate\Support\Facades\Route;

// Public homepage
Route::get('/', function () {
    return view('welcome');
});

// Routes protected by authentication
Route::middleware(['auth', 'verified'])->group(function () {
    // Dashboard with statistics
    Route::get('/dashboard', function () {
        $user = auth()->user();

        return view('dashboard', [
            'totalTasks' => $user->tasks()->count(),
            'pendingTasks' => $user->tasks()->pending()->count(),
            'completedTasks' => $user->tasks()->completed()->count(),
            'overdueTasks' => $user->tasks()->overdue()->count(),
        ]);
    })->name('dashboard');

    // RESTful routes for tasks
    Route::resource('tasks', TaskController::class);

    // Quick action to complete a task
    Route::patch('/tasks/{task}/complete', [TaskController::class, 'complete'])
        ->name('tasks.complete');

    // Profile routes (generated by Breeze)
    Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
    Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
    Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');
});

// Authentication routes (generated by Breeze)
require __DIR__.'/auth.php';

Метод Route::resource() автоматично генерує сім стандартних RESTful-маршрутів: index, create, store, show, edit, update та destroy.

Налаштування політик авторизації

Політики централізують логіку авторизації для кожної моделі. Вони визначають, хто може виконувати які дії з ресурсами.

bash
# terminal
# Generate the policy for Task
php artisan make:policy TaskPolicy --model=Task
php
<?php
// app/Policies/TaskPolicy.php

namespace App\Policies;

use App\Models\Task;
use App\Models\User;

class TaskPolicy
{
    // Check before all methods
    // Returning true bypasses all checks (admin)
    public function before(User $user, string $ability): ?bool
    {
        if ($user->is_admin) {
            return true;
        }

        return null; // Continue to specific methods
    }

    // Can view the list of tasks
    public function viewAny(User $user): bool
    {
        return true; // Any authenticated user
    }

    // Can view a specific task
    public function view(User $user, Task $task): bool
    {
        return $user->id === $task->user_id;
    }

    // Can create a task
    public function create(User $user): bool
    {
        return true;
    }

    // Can update a task
    public function update(User $user, Task $task): bool
    {
        return $user->id === $task->user_id;
    }

    // Can delete a task
    public function delete(User $user, Task $task): bool
    {
        return $user->id === $task->user_id;
    }

    // Can restore a soft-deleted task
    public function restore(User $user, Task $task): bool
    {
        return $user->id === $task->user_id;
    }

    // Can permanently delete
    public function forceDelete(User $user, Task $task): bool
    {
        return $user->id === $task->user_id;
    }
}

Laravel автоматично знаходить політики завдяки конвенціям іменування. TaskPolicy застосовується до моделі Task.

Безпека політик

Політики є критично важливими для безпеки. Без них користувач міг би маніпулювати URL-адресами для доступу до завдань інших користувачів. Власність ресурсів завжди потрібно перевіряти.

Створення REST API

Laravel спрощує створення RESTful API завдяки виділеним маршрутам, JSON-ресурсам та Sanctum для автентифікації.

bash
# terminal
# Install Sanctum (included by default since Laravel 11)
php artisan install:api

# This command:
# - Publishes Sanctum migrations
# - Adds the HasApiTokens trait to the User model
# - Configures API routes
php
<?php
// app/Http/Controllers/Api/TaskController.php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Http\Resources\TaskResource;
use App\Http\Resources\TaskCollection;
use App\Models\Task;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;

class TaskController extends Controller
{
    // Paginated list of tasks
    public function index(Request $request): AnonymousResourceCollection
    {
        $tasks = $request->user()
            ->tasks()
            ->when($request->status, fn($q, $s) => $q->where('status', $s))
            ->when($request->priority, fn($q, $p) => $q->where('priority', $p))
            ->when($request->boolean('overdue'), fn($q) => $q->overdue())
            ->latest()
            ->paginate($request->input('per_page', 15));

        return TaskResource::collection($tasks);
    }

    // Create a task
    public function store(Request $request): JsonResponse
    {
        $validated = $request->validate([
            'title' => 'required|string|max:255',
            'description' => 'nullable|string|max:5000',
            'priority' => 'required|in:low,medium,high',
            'due_date' => 'nullable|date|after_or_equal:today',
        ]);

        $task = $request->user()->tasks()->create($validated);

        return response()->json([
            'message' => 'Task created successfully.',
            'data' => new TaskResource($task),
        ], 201);
    }

    // Display a task
    public function show(Task $task): TaskResource
    {
        $this->authorize('view', $task);

        return new TaskResource($task);
    }

    // Update a task
    public function update(Request $request, Task $task): TaskResource
    {
        $this->authorize('update', $task);

        $validated = $request->validate([
            'title' => 'sometimes|required|string|max:255',
            'description' => 'nullable|string|max:5000',
            'status' => 'sometimes|required|in:pending,in_progress,completed',
            'priority' => 'sometimes|required|in:low,medium,high',
            'due_date' => 'nullable|date',
        ]);

        if (isset($validated['status'])
            && $validated['status'] === Task::STATUS_COMPLETED
            && !$task->completed_at) {
            $validated['completed_at'] = now();
        }

        $task->update($validated);

        return new TaskResource($task->fresh());
    }

    // Delete a task
    public function destroy(Task $task): JsonResponse
    {
        $this->authorize('delete', $task);

        $task->delete();

        return response()->json([
            'message' => 'Task deleted successfully.',
        ]);
    }
}

API-ресурси для трансформації даних

API-ресурси перетворюють моделі Eloquent у структуровані JSON-відповіді, точно контролюючи дані, що надаються клієнту.

php
<?php
// app/Http/Resources/TaskResource.php

namespace App\Http\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

class TaskResource extends JsonResource
{
    public function toArray(Request $request): array
    {
        return [
            'id' => $this->id,
            'title' => $this->title,
            'description' => $this->description,
            'status' => $this->status,
            'priority' => $this->priority,
            'due_date' => $this->due_date?->format('Y-m-d'),
            'is_overdue' => $this->is_overdue,
            'completed_at' => $this->completed_at?->toISOString(),
            'created_at' => $this->created_at->toISOString(),
            'updated_at' => $this->updated_at->toISOString(),
            // Conditional inclusion of the user
            'user' => $this->whenLoaded('user', fn() => [
                'id' => $this->user->id,
                'name' => $this->user->name,
            ]),
        ];
    }
}

API-маршрути з автентифікацією Sanctum

php
<?php
// routes/api.php

use App\Http\Controllers\Api\TaskController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

// Route to retrieve the authenticated user
Route::get('/user', function (Request $request) {
    return $request->user();
})->middleware('auth:sanctum');

// API routes protected by Sanctum
Route::middleware('auth:sanctum')->group(function () {
    Route::apiResource('tasks', TaskController::class);

    // Dashboard statistics
    Route::get('/dashboard/stats', function (Request $request) {
        $user = $request->user();

        return response()->json([
            'total' => $user->tasks()->count(),
            'pending' => $user->tasks()->pending()->count(),
            'in_progress' => $user->tasks()->inProgress()->count(),
            'completed' => $user->tasks()->completed()->count(),
            'overdue' => $user->tasks()->overdue()->count(),
        ]);
    });
});

Автоматичне тестування з Pest

Laravel 11 за замовчуванням інтегрує Pest PHP — елегантний та виразний тестовий фреймворк. Тести гарантують коректну роботу застосунку після кожної зміни.

php
<?php
// tests/Feature/TaskTest.php

use App\Models\Task;
use App\Models\User;

// Test: a user can view their tasks
test('user can view their tasks', function () {
    $user = User::factory()->create();
    $tasks = Task::factory()->count(5)->for($user)->create();

    $response = $this->actingAs($user)->get('/tasks');

    $response->assertOk();
    $response->assertViewHas('tasks');
});

// Test: a user can create a task
test('user can create a task', function () {
    $user = User::factory()->create();

    $response = $this->actingAs($user)->post('/tasks', [
        'title' => 'New task',
        'description' => 'Task description',
        'priority' => 'high',
        'due_date' => now()->addDays(7)->format('Y-m-d'),
    ]);

    $response->assertRedirect('/tasks');

    $this->assertDatabaseHas('tasks', [
        'user_id' => $user->id,
        'title' => 'New task',
        'priority' => 'high',
    ]);
});

// Test: title validation required
test('task title is required', function () {
    $user = User::factory()->create();

    $response = $this->actingAs($user)->post('/tasks', [
        'title' => '',
        'priority' => 'medium',
    ]);

    $response->assertSessionHasErrors('title');
});

// Test: a user cannot view another user's task
test('user cannot view another users task', function () {
    $user = User::factory()->create();
    $otherUser = User::factory()->create();
    $task = Task::factory()->for($otherUser)->create();

    $response = $this->actingAs($user)->get("/tasks/{$task->id}");

    $response->assertForbidden();
});

// Test: mark a task as completed
test('user can complete a task', function () {
    $user = User::factory()->create();
    $task = Task::factory()->for($user)->create(['status' => 'pending']);

    $response = $this->actingAs($user)->patch("/tasks/{$task->id}/complete");

    $response->assertRedirect();

    $task->refresh();
    expect($task->status)->toBe('completed');
    expect($task->completed_at)->not->toBeNull();
});

// API test: retrieve tasks
test('api returns paginated tasks', function () {
    $user = User::factory()->create();
    Task::factory()->count(20)->for($user)->create();

    $response = $this->actingAs($user, 'sanctum')
        ->getJson('/api/tasks?per_page=10');

    $response->assertOk()
        ->assertJsonCount(10, 'data')
        ->assertJsonStructure([
            'data' => [
                '*' => ['id', 'title', 'status', 'priority', 'due_date'],
            ],
            'links',
            'meta',
        ]);
});

Запуск тестів за допомогою Pest.

bash
# terminal
# Run all tests
php artisan test

# Tests with code coverage
php artisan test --coverage

# Tests for a specific file
php artisan test tests/Feature/TaskTest.php

# Parallel tests (faster)
php artisan test --parallel

Оптимізації для продакшну

Перед розгортанням кілька оптимізацій покращують продуктивність застосунку на Laravel.

bash
# terminal
# Cache configuration
php artisan config:cache

# Cache routes
php artisan route:cache

# Cache views
php artisan view:cache

# Optimize Composer autoloader
composer install --optimize-autoloader --no-dev

# Generate asset manifest
npm run build

Налаштування файлу .env для продакшну

env
# .env.production
APP_NAME="Task Manager"
APP_ENV=production
APP_DEBUG=false
APP_URL=https://taskmanager.example.com

# Cache and session via Redis (recommended)
CACHE_DRIVER=redis
SESSION_DRIVER=redis
QUEUE_CONNECTION=redis

REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379

# Database
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_DATABASE=task_manager_prod
DB_USERNAME=app_user
DB_PASSWORD=strong_password_here

# Mail
MAIL_MAILER=smtp
MAIL_HOST=smtp.mailgun.org
MAIL_PORT=587
MAIL_USERNAME=postmaster@taskmanager.example.com
MAIL_PASSWORD=secret
MAIL_ENCRYPTION=tls
Чутливі змінні

Файл .env ніколи не слід комітити до Git-репозиторію. Файл .env.example використовується як шаблон, а чутливі змінні налаштовуються безпосередньо на продакшн-сервері або через менеджери секретів.

Висновок

Laravel 11 суттєво спрощує сучасну PHP-розробку завдяки оптимізованій структурі та інтелектуальним конвенціям. Цей посібник охопив основи створення повноцінного застосунку: моделі Eloquent, автентифікацію, RESTful-контролери, політики авторизації, API з Sanctum та автоматичні тести.

Чек-лист для якісних Laravel-застосунків

  • Міграції для всіх змін схеми бази даних
  • Політики для захисту доступу до ресурсів
  • Валідація всіх вхідних даних у контролерах
  • API-ресурси для контролю даних, що передаються клієнту
  • Тести для критичних функцій
  • Налаштування кешу та оптимізацій перед розгортанням
  • Захист чутливих змінних середовища

Починай практикувати!

Перевір свої знання з нашими симуляторами співбесід та технічними тестами.

Екосистема Laravel продовжує розвиватися завдяки пакетам на кшталт Livewire для реактивних інтерфейсів, Horizon для управління чергами та Octane для виняткової продуктивності. Опанування цих основ відкриває шлях до надійних та легких в обслуговуванні PHP-застосунків.

Теги

#laravel
#php
#rest api
#eloquent
#tutorial

Поділитися

Пов'язані статті