Нажмите "Enter" для перехода к содержанию

Лучшие практики, советы и рекомендации по Laravel на 2024 год

По мере того как мы движемся в 2024 год, Laravel остается одним из ведущих выборов для веб-разработки, предоставляя разработчикам инструменты для создания мощных и эффективных приложений. Эта статья представит основные лучшие практики, советы и хитрости, которые помогут вам улучшить ваши проекты на Laravel, независимо от того, используете ли вы Laravel 9, 10 или 11. Соблюдение этих рекомендаций поможет вам улучшить качество кода, оптимизировать рабочие процессы и, в конечном счете, предоставить лучшие приложения. Будь вы новичком или опытным разработчиком, эти идеи помогут вам в полной мере использовать возможности Laravel и повысить ваши навыки разработки.

Регулярно обновляйте Laravel

Поддержание Laravel в актуальном состоянии предлагает несколько ключевых преимуществ. Вот список преимуществ, связанных с обновлением Laravel:

  • Улучшенная безопасность: Регулярные обновления включают важные исправления безопасности для защиты вашего приложения.
  • Улучшенная производительность: Обновления часто увеличивают производительность, что приводит к более быстрой загрузке и более эффективному коду.
  • Новые функции и функциональность: Будьте в курсе нового функционала, которые улучшат ваш опыт разработки.
  • Совместимость: Обеспечивает лучшую совместимость с последними официальными и сообществом разработанными пакетами.

Если вы боитесь обновлений, это может означать, что ваш код недостаточно протестирован. Применяя практики тестирования, вы можете убедиться, что основные обновления не повредят ваш код, позволяя вам в полной мере воспользоваться улучшениями в Laravel.

Обновляйте используемые пакеты

Доступ к широкому спектру пакетов из официальной экосистемы Laravel и репозиториев сообщества значительно упрощает наш процесс разработки. Однако использование нескольких пакетов может внести разнообразные точки отказа.

Чтобы минимизировать риски и поддерживать безопасный код, крайне важно регулярно выполнять команду composer update. Эта простая практика — один из лучших способов гарантировать, что ваши пакеты актуальны с последними функциями и исправлениями безопасности.

Сказанное выше, похоже на предыдущие упоминания: если ваш код не проходит тщательное тестирование, после обновлений могут возникнуть неожиданные регрессии.

Предотвращение критических ошибок через тщательное тестирование проекта

Автоматизированное тестирование — это обширная, но часто недооцененная область среди разработчиков. Тем не менее, оно выделяется как одна из важнейших лучших практик для обеспечения надежности ваших проектов.

Вот некоторые ключевые преимущества использования тестов:

  • Меньше ошибок: Регулярное тестирование помогает выявлять проблемы на ранних стадиях, создавая более стабильный код.
  • Довольные клиенты: Надежное программное обеспечение приводит к удовлетворенным пользователям, которые могут доверять вашему продукту.
  • Довольные работодатели: Хорошо протестированный проект отражает профессионализм и приверженность качеству.
  • Уверенные разработчики: С полным набором тестов вы можете вносить изменения, не боясь введения новых ошибок. Вернуться к проекту после перерыва становится гораздо менее пугающим.

Когда вы изменяете код, просто выполните команду php artisan test, чтобы выявить любую сломанную функциональность, исправьте проблемы и повторите процесс!

Внедрение автоматизированного тестирования может значительно повысить стабильность вашего проекта, что будет иметь большую пользу для вашей карьеры.

Следуйте стандартной организации директорий

Вы когда-нибудь задумывались, почему вы используете такие фреймворки, как Laravel?

Фреймворки предлагают структурированный подход к разработке, гарантируя, что все члены команды согласованы и работают над общими целями. Они содержат множество сложных, проверенных функций, позволяя вам сосредоточиться на уникальных аспектах вашего проекта, а не углубляться в детали.

Так считается ли хорошей практикой придерживаться стандартной структуры проекта Laravel? Абсолютно! Вот почему:

  • Удобство: Конвенции и структуры Laravel хорошо документированы. Когда вы возвращаетесь к проекту через несколько недель или месяцев, вы оцените ясность и знакомство с установленным по умолчанию набором.
  • Сотрудничество в команде: Работать с товарищами по команде становится гораздо проще, когда все понимают конвенции Laravel. Использование этого общего знания может помочь движению проекта вперед, а не изобретению колеса на каждом шагу.

Реализуйте пользовательские запросы форм (Form Requests) для обработки сложной валидации

Пользовательские запросы форм в Laravel — это мощный инструмент для обработки валидации, предлагающий несколько ключевых преимуществ:

  • Повторное использование логики валидации: Вы можете легко повторно использовать правила валидации в нескольких контроллерах, сохраняя ваш код DRY (Don’t Repeat Yourself).
  • Снижение нагрузки на контроллеры: Перепоручая валидационный код специальным классам запросов форм, вы сохраняете свои контроллеры чистыми и сосредоточенными на бизнес-логике.

Создание Form Request для валидации данных

Создание пользовательского запроса формы простое дело. Просто выполните следующую команду Artisan:

php artisan make:request StorePostRequest

Использование в контроллере:

use App\Http\Requests\StorePostRequest;

class PostController
{
    public function store(StorePostRequest $request)
    {
        $validated = $request->validated();
        Post::create($validated);
        // Additional logic here...
    }
}

Улучшение вашего пользовательского запроса формы

Пользовательские запросы формы могут также содержать дополнительную логику помимо простой валидации. Например, вы можете нормализовать входные данные перед их передачей в ваш контроллер, переопределив метод prepareForValidation:

public function prepareForValidation()
{
    $this->merge([
        'slug' => Str::slug($this->title),
    ]);
}

В этом примере метод автоматически генерирует slug из заголовка перед началом процесса проверки.

Управление авторизацией

Пользовательские запросы форм также могут обрабатывать логику авторизации. Если вы считаете, что использование Политик избыточно для вашего случая, вы можете включить простые проверки авторизации непосредственно в ваши запросы форм.

Упрощение вашего кода с использованием контроллеров с единственным действием
Даже при соблюдении лучших практик контроллеры могут стать громоздкими. Отличным решением для этого является использование контроллеров с единственным действием в Laravel. Эти контроллеры предназначены для обработки одного действия, в отличие от стандартных ресурсных контроллеров, которые обрабатывают несколько действий.

Используйте однометодные контроллеры

Чтобы создать контроллер с единственным действием, используйте следующую команду Artisan:

php artisan make:controller ShowPostController --invokable

Эта команда создаст контроллер с одним методом __invoke. Для получения дополнительной информации о магическом методе __invoke ознакомьтесь с документацией по Laravel.

Определение маршрутов

С помощью однометодного контроллера вы можете упростить определение маршрутов. Вот как вы можете использовать его в своих маршрутах:

use App\Http\Controllers\PostController; 
use App\Http\Controllers\ShowPostController; 

// традиционный контроллер
Route::get('/posts/{post}', [PostController::class, 'show']); 

// однометодный контроллер
Route::get('/posts/{post}', ShowPostController::class); 

Субъективная наилучшая практика

Использование однометодных контроллеров — это субъективная наилучшая практика. Решение о том, подходит ли этот подход для структуры вашего проекта и предпочтений в читаемости, зависит от вас и вашей команды.

Используйте промежуточные программные компоненты (Middleware).

В Laravel промежуточные программные компоненты — это мощные инструменты, которые позволяют фильтровать или изменять входящие запросы. Используя промежуточные компоненты, вы можете избежать дублирования кода и сохранить свои контроллеры чистыми и управляемыми. Вот некоторые распространенные случаи использования мидлваре:

  • Проверка необходимых разрешений: Убедитесь, что у пользователей есть необходимые разрешения для доступа к определённым маршрутам.
  • Установка локали пользователя: Проверьте предпочтительный язык пользователя и соответственно настройте локаль приложения.

Laravel поставляется с разнообразными встроенными промежуточными компонентами для выполнения общих задач, таких как аутентификация, ограничение скорости и многое другое.

Создание пользовательского промежуточного компонента

Чтобы создать пользовательский мидлваре, используйте следующую команду Artisan:

php artisan make:middleware CheckTokens

В вашем промежуточном ПО вы можете определить логику обработки запроса. Вот пример промежуточного ПО, которое проверяет, есть ли у пользователя достаточное количество токенов:

public function handle(Request $request, Closure $next): Response
{
    if (! $request->user()->hasEnoughTokens()) {
        abort(403); // Forbidden
    }
    return $next($request); // Proceed to the next middleware or request handler
}

Подключение промежуточного ПО к маршрутам

Как только ваше промежуточное ПО определено, вы можете подключить его к любому количеству маршрутов. Такой модульный подход помогает поддерживать чистоту кода и снижает дублирование в ваших контроллерах.

Route::middleware('check.tokens')->group(function () {
    Route::get('/protected-resource', [ResourceController::class, 'index']);
    Route::post('/protected-action', [ResourceController::class, 'store']);
});

Внедрение авторизации с помощью политик

В Laravel использование политик для авторизации имеет ключевое значение для создания чистого и поддерживаемого приложения. Вот три основных преимущества использования политик:

  1. Обмен логикой авторизации между несколькими контроллерами: Консолидируя ваши правила авторизации в политиках, вы способствуете единообразию и устраняете повторяющийся код в вашем приложении.
  2. Снижение сложности в контроллерах: Перевод логики авторизации в политики позволяет вашим контроллерам сосредоточиться на их основных задачах, что приводит к более чистому и понятному коду.
  3. Упрощение доступа к коду авторизации: Размещение политик в каталоге app/Policies облегчает разработчикам нахождение и модификацию правил авторизации при необходимости.

Давайте рассмотрим реальный сценарий использования политики для обновления постов:

// app/Policies/PostPolicy.php
public function update(User $user, Post $post)
{
    return $user->id === $post->user_id; // Verify that the user owns the post
}
// app/Http/Controllers/PostController.php
public function update(Request $request, Post $post)
{
    $this->authorize('update', $post); // Check if the update action is authorized

    // If authorized, continue with the update process
    // ...
}

Убедитесь, что миграции актуальны

Миграции служат методом для определения схемы вашей базы данных с использованием обычного PHP-кода. Рассматривайте их как кодовую альтернативу phpMyAdmin, предоставляющую способ управлять структурой базы данных программно. Этот подход особенно полезен для команд, так как позволяет всем воспроизводить одну и ту же среду разработки на своих локальных машинах и поддерживает четкую историю изменений в Git.

Миграции также упрощают развертывание проектов в новые окружения, такие как тестовые или боевые, без необходимости экспортировать базы данных из других окружений. Однако распространенной ошибкой является то, что некоторые разработчики вносят изменения напрямую в базу данных вместо создания новых миграций. Эта практика может привести к осложнениям и неудобствам для других членов команды, которым может потребоваться запрашивать дампы базы данных. Чтобы улучшить свои проекты и поддерживать согласованность, важно эффективно использовать миграции.

Используйте анонимные миграции для предотвращения конфликтов (Laravel 8 и выше)

Анонимные миграции являются эффективным решением для избежания конфликтов с именами классов. С этой функцией вы можете создавать несколько миграций с именем “update_posts_table” без возникновения ошибок, что помогает минимизировать трение в процессе разработки.

В Laravel 9 и выше анонимные миграции генерируются автоматически, когда вы вводите следующую команду:

php artisan make:migration UpdatePostsTable

Структура этих миграций выглядит следующим образом:

<?php

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

return new class extends Migration {
    // Migration logic goes here
};

Интересно, что вы также можете реализовать анонимные миграции в Laravel 8. Для этого просто замените имя класса на return new class и убедитесь, что в конце стоит точка с запятой. Этот подход позволит вам воспользоваться преимуществами анонимных миграций даже в более ранних версиях.

Правильная реализация метода down() для откатов

Метод down() запускается командой

php artisan migrate:rollback

и является необходимым для отмены изменений, внесенных в вашу базу данных. Хотя некоторые разработчики решают реализовать его, а другие — нет, важно убедиться, что если вы его используете, ваш метод down() определен правильно.

По сути, метод down() должен отменить действия, выполненные в методе up(). Вот простой пример:

<?php

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

return new class extends Migration {
    public function up()
    {
        Schema::table('posts', function (Blueprint $table) {
            // Change the column from boolean to datetime.
            $table->datetime('is_published')->nullable()->change();
        });
    }

    public function down()
    {
        Schema::table('posts', function (Blueprint $table) {
            // Restore the column to its original state during rollback.
            $table->boolean('is_published')->default(false)->change();
        });
    }
};

Если вы предпочитаете не использовать метод down(), вы можете просто удалить его полностью.

Используйте рекомендации Eloquent по именованию таблиц базы данных

Следование конвенциям именования таблиц Laravel — это простая лучшая практика, которая может принести пользу вашей команде. Фреймворк автоматически обрабатывает эти конвенции, когда вы используете команды Artisan, такие как

php artisan make:model Post --migration --factory

Если по какой-либо причине вы не можете использовать эти команды, вот краткое руководство:

• Для модели с именем Post соответствующая таблица должна называться posts, используя множественную форму. Например:

Модель Comment → таблица comments
Модель Reply → таблица replies

• Для промежуточной таблицы, которая связывает Post с Comment (например, comment_post):

Используйте единственные формы обоих имен.

Упорядочите их в алфавитном порядке.

Для получения дополнительных сведений обратитесь к официальной документации.

Избегайте проблем N+1, используя Eager Loading

Существует еще много моментов, касающихся лучших практик! Вы когда-нибудь сталкивались с проблемами N+1? Eager loading — это эффективный способ предотвратить их.

Например, если вы отображаете список из 30 постов с указанием их авторов, Eloquent выполнит один запрос для 30 постов, а затем дополнительно 30 запросов для каждого автора из-за ленивой загрузки (что означает, что связь с пользователем извлекается каждый раз, когда вы вызываете $post->user в вашем коде).

Решение простое: используйте метод with(), чтобы уменьшить количество запросов с 31 до всего лишь 2.

Post::with('author')->get();

Чтобы избежать проблем N+1, вы можете настроить ваше приложение так, чтобы оно генерировало исключения при ленивой загрузке любых отношений. Это ограничение должно применяться только в вашем локальном окружении.

Model::preventLazyLoading(
    // Returns `true` unless it's the production environment.
    ! app()->isProduction()
);

Используйте строгий режим Eloquent для предотвращения проблем с производительностью и ошибками

Строгий режим Eloquent является незаменимым инструментом для отладки. Он помогает разработчикам выявлять потенциальные проблемы в процессе разработки, выбрасывая исключения в нескольких сценариях:

  • Ленивые загрузки связанных данных: Ленивые загрузки могут вызывать проблемы с производительностью, особенно с большими наборами данных. Это происходит, когда связанные модели загружаются из базы данных только по мере доступа к ним. В строгом режиме будет выброшено исключение, если связь загружается лениво, что способствует использованию жадной загрузки вместо этого.
  • Назначение неразрешенных атрибутов: Свойство $fillable в моделях Eloquent защищает от уязвимостей массового присвоения. Если вы попытаетесь назначить неразрешенный атрибут, будет вызвано исключение, что гарантирует, что разработчики будут осторожны при массовом присвоении.
  • Доступ к несуществующим атрибутам: Попытка получить доступ к атрибутам, которые не существуют или которые не были извлечены из базы данных, может привести к непредсказуемому поведению или ошибкам. В строгом режиме в таких случаях будет выброшено исключение, что помогает разработчикам точно определить и решить эти проблемы.

Чтобы включить строгий режим, добавьте следующий код в метод boot() вашего AppServiceProvider.php:

Model::shouldBeStrict(
    // It will only be enabled outside of production, though.
    ! app()->isProduction()
);

Используйте обновленный метод для определения аксессоров и мутаторов

Laravel 9 представил новый подход к объявлению аксессоров и мутаторов. Вот как вы теперь должны их реализовывать:

use Illuminate\Database\Eloquent\Casts\Attribute;

class Pokemon
{
    public function name(): Attribute
    {
        $locale = app()->getLocale();
        return Attribute::make(
            get: fn($value) => $value[$locale],
            set: fn($value) => [$locale => $value],
        );
    }
}

Этот новый синтаксис позволяет вам определять аксессоры и мутаторы более лаконично. Вы также можете кэшировать значения, которые трудно вычислить:

use Illuminate\Database\Eloquent\Casts\Attribute;

public function someAttribute(): Attribute
{
    return Attribute::make(
        fn() => /* Perform some operation here. */
    )->shouldCache();
}

В отличие от этого, предыдущий метод выглядел следующим образом:

class Pokemon
{
    public function getNameAttribute(): string
    {
        $locale = app()->getLocale();
        return $this->attributes['name'][$locale];
    }

    public function setNameAttribute($value): string
    {
        $locale = app()->getLocale();
        return $this->attributes['name'][$locale] = $value;
    }
}

Переход к новому синтаксису упрощает ваш код и улучшает его читаемость.

Используйте dispatchAfterResponse() для долгих операций

При выполнении задач, которые требуют времени, например, отправка электронной почты после отправки контактной формы, вы можете улучшить пользовательский опыт, отложив выполнение задачи до получения ответа от сервера. Здесь на помощь приходит dispatchAfterResponse(), который позволяет отложить долгие процессы:

SendContactEmail::dispatchAfterResponse($input);

В качестве альтернативы, если вы предпочитаете использовать анонимные функции для отправки заданий, вы можете сделать это следующим образом:

dispatch(function () {
    // Perform some long-running task here.
})->afterResponse();

Используя dispatchAfterResponse(), вы обеспечиваете более быстрый ответ от вашего сервера, а фоновая задача выполняется без задержек, что положительно сказывается на пользовательском опыте.

Используйте очереди для длительных задач

Когда вы сталкиваетесь с ресурсоемкими задачами, такими как обработка загруженных пользователями изображений, выполнение этих операций немедленно может привести к перегрузке сервера и плохому пользовательскому опыту. Вот как с этим справиться:

  • Нагрузка на сервер: Обработка задач в реальном времени для каждого пользовательского запроса может перегрузить ваш сервер.
  • Пользовательский опыт: Пользователи вынуждены ждать, глядя на экран загрузки, что вызывает разочарование.

Система очередей Laravel позволяет отложить эти длительные задачи, выполняя их последовательно или с контролируемым параллелизмом, что гарантирует, что ваш сервер останется отзывчивым, и пользователи не будут ждать.

Для упрощенного управления задачами рассмотрите возможность использования Laravel Horizon, который предоставляет пользовательский интерфейс для мониторинга и управления вашими заданиями в очереди. Таким образом, вы можете поддерживать плавную производительность, обрабатывая сложные процессы в фоновом режиме.

Ленивая перезагрузка базы данных перед каждым тестом

При тестировании в локальной среде лучше всего использовать новую базу данных каждый раз, когда вы запускаете тест. Laravel предоставляет эффективный способ справиться с этим с помощью трейта LazyRefreshDatabase, который вы можете включить в ваши tests/TestCase.php. В отличие от трейта RefreshDatabase, ленивое обновление более оптимизировано, так как не запускает миграции для неиспользуемых таблиц во время тестирования.

Используйте фабрики для упрощения тестов с фейковыми данными

Фабрики упрощают тестирование, генерируя фейковые данные для ваших моделей. Вы можете создать одну с помощью команды php artisan make:factory PostFactory и заполнить столбцы случайными данными, например:

namespace Database\Factories;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;

class PostFactory extends Factory
{
    public function definition() : array
    {
        return [
            'user_id' => User::factory(),
            'title' => fake()->sentence(),
            'slug' => fake()->slug(),
            'content' => fake()->paragraphs(5, true),
            'description' => fake()->paragraph(),
        ];
    }
}

Использование фабрик упрощает создание ресурсов, необходимых для тестов. Например, создание записи и ее проверка могут быть выполнены следующим образом:

public function test_it_shows_a_given_post()
{
    $post = Post::factory()->create();
    $this
        ->get(route('posts.show', $post))
        ->assertOk();
}

С таким подходом вы обеспечиваете бесперебойное выполнение тестов и упрощаете их поддержку.

Тестируйте с использованием производственной инфраструктуры, когда это возможно.

При проведении тестов очень важно использовать тот же стек, что и в производственной среде, чтобы выявить потенциальные ошибки, которые могут не проявиться в другой среде. Например, на производстве вы можете использовать MySQL вместо SQLite или Redis вместо кеша на основе массива.

Если вы не тестируете с использованием тех же технологий, что и на производстве, вы рискуете упустить проблемы, которые возникают только в производственной среде. Цель тестирования — предотвратить возникновение этих проблем на продакшене.

Хотя может быть заманчиво отдавать предпочтение скорости с помощью более простых инструментов, точность и надежность всегда должны иметь приоритет. Тестирование на полной производственной инфраструктуре гарантирует, что ваше приложение ведет себя так, как ожидалось, когда оно развернуто.

Откатывайте изменения после каждого теста, используя транзакции базы данных.

В случаях, когда обновление базы данных для каждого теста слишком медленное, особенно если ваш проект загружает реальные данные из таких источников, как CSV-файлы, транзакции базы данных являются отличной альтернативой. Используя трейт

Illuminate\Foundation\Testing\DatabaseTransactions

в вашем базовом классе тестов (tests/TestCase.php), вы можете гарантировать, что все изменения, внесенные во время теста, будут отменены после завершения теста.

Этот подход поддерживает целостность вашей базы данных, сохраняя ее в начальном состоянии, и ускоряет тесты, избегая необходимости полностью сбрасывать базу данных между тестами. Это особенно полезно, когда тесты изменяют данные, которые не должны сохраняться между тестами.

Избегайте ненужных API-вызовов, используя моки.

В Laravel моки — это эффективный способ предотвратить ненужные вызовы API во время тестирования и избежать проблем, таких как превышение лимитов запросов.

Например, если в вашем проекте вы работаете с API Twitter и у вас есть класс Client, отвечающий за выполнение API-запросов, вы не хотите совершать реальные вызовы во время выполнения набора тестов. Вместо этого вы можете заменить реальный API-клиент на его mock-версию для симуляции ответов.

Вот пример:

$mock = $this->mock(Client::class);
$mock
    ->shouldReceive('getTweet')
    ->with('Some tweet ID')
    ->andReturn([
        'data' => [
            'author_id' => '2244994945',
            'created_at' => '2022-12-11T10:00:55.000Z',
            'id' => '1228393702244134912',
            'edit_history_tweet_ids' => ['1228393702244134912'],
            'text' => 'This is a tweet',
        ],
    ]);

Таким образом, вы можете проводить тестирование, не обращаясь к реальному API. Для получения более подробной информации ознакомьтесь с официальной документацией Laravel по мокированию.

Блокируйте нежелательные HTTP-запросы, чтобы выявить медленные тесты.

Чтобы убедиться, что все HTTP-запросы во время тестирования должным образом мокируются и предотвратить замедление тестов реальными запросами, вы можете использовать метод Laravel

Http::preventStrayRequests()

Этот метод вызовет исключение, если будут сделаны какие-либо неожиданные HTTP-запросы без соответствующего фейкового ответа.

Это особенно полезно для обнаружения непреднамеренных замедлений, вызванных реальными HTTP-запросами во время тестирования.

Избегайте отслеживания вашего .env файла

Ваш .env файл содержит конфиденциальную информацию, поэтому его никогда не следует отслеживать в системе контроля версий. Убедитесь, что он указан в вашем файле .gitignore, чтобы избежать случайного раскрытия.

Большинство утечек данных происходит из-за внутренних ошибок, поэтому использование менеджера паролей для безопасного обмена учетными данными с вашей командой — это гораздо более безопасный вариант. Для обмена конфиденциальной информацией выберите менеджер паролей с хорошей репутацией в области безопасности.

Избегайте отслеживания скомпилированного CSS и JavaScript

Ваши скомпилированные файлы CSS и JavaScript, генерируемые из resources/css и resources/js, не должны отслеживаться в системе контроля версий. Эти файлы могут быть скомпилированы во время развертывания или созданы как часть процесса сборки перед развертыванием.

Если вы все еще используете инструменты, такие как Laravel Mix, отслеживание этих файлов может быть проблемой, так как каждое изменение генерирует новые версии public/css/app.css и public/js/app.js, которые необходимо коммитить. Чтобы избежать этого, просто добавьте эти строки в ваш .gitignore:

public/css
public/js

Это обеспечивает чистоту вашего репозитория и ориентацию на исходный код.

Обсуждение закрыто.