🎩 Книга «Денди-код» о том, как сделать код аккуратным и понятным

Илья Лящук

Backend, Frontend, Weekend

Автоматизация в Laravel: как ускорить и упростить процесс разработки

Автоматизация в Laravel

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

Материал рассчитан на тех, кто уже знаком с Laravel и хочет внедрить в проект автоматические проверки, единый стиль кода, статику и готовую Docker-инфраструктуру. Я поделюсь используемыми инструментами, примерами конфигураций и реалистичными скриптами.

Docker-Compose как основа окружения

Я всегда начинаю проект с продуманной конфигурации Docker Compose — это дает сразу готовое изолированное окружение, минимизирует конфликты зависимостей и ускоряет старт работы. Обычно в моём составе используются следующие сервисы:

  • php-fpm – выполнение PHP-кода приложения
  • PostgreSQL – реляционная база данных
  • Grafana + Loki – визуализация метрик и логов
  • pgAdmin – веб-интерфейс для управления БД
  • Redis – Кэш и очереди Laravel
  • Insight – визуализация данных Redis
  • Queue – выполнение фоновых задач
services:
    app:
        build: .
        container_name: pet
        user: root
        depends_on:
            - pgdb
            - redis
            - loki
        env_file:
            - .env
        working_dir: /var/www/
        volumes:
            - .:/var/www
        networks:
            - pet
        dns:
            - 8.8.8.8
            - 1.1.1.1

    pgdb:
        container_name: pgdb
        image: postgres
        tty: true
        restart: always
        environment:
            - POSTGRES_DB=${DB_DATABASE}
            - POSTGRES_USER=${DB_USERNAME}
            - POSTGRES_PASSWORD=${DB_PASSWORD}
        ports:
            - ${PGDB_PORT}
        volumes:
            - ./docker/postgres:/var/lib/postgresql/data
        networks:
            - pet

    nginx:
        image: nginx:latest
        container_name: nginx
        restart: unless-stopped
        ports:
            - ${NGINX_PORT}
            - "443:443"
        volumes:
            - .:/var/www
            - ./docker/nginx:/etc/nginx/conf.d
            - /etc/letsencrypt:/etc/letsencrypt:ro
        environment:
            - TZ=${SYSTEM_TIMEZONE}
        depends_on:
            - pgdb
            - app
            - pgadmin
        networks:
            - pet

    pgadmin:
        image: dpage/pgadmin4:latest
        restart: always
        depends_on:
            - pgdb
        environment:
            - PGADMIN_DEFAULT_EMAIL=${PGADMIN_EMAIL}
            - PGADMIN_DEFAULT_PASSWORD=${PGADMIN_PASSWORD}
        ports:
            - ${PGADMIN_PORT}
        networks:
            - pet

    redis:
        image: redis:latest
        container_name: redis
        restart: always
        ports:
            - ${REDIS_PORT}
        environment:
            - REDIS_PASSWORD=${REDIS_PASSWORD}
        command: ["redis-server", "--requirepass", "${REDIS_PASSWORD}"]
        networks:
            - pet

    redisinsight:
        image: redislabs/redisinsight:latest
        container_name: redisinsight
        ports:
            - ${REDISINSIGHT_PORT}
        volumes:
            - ./docker/redisinsight:/db
        restart: always
        networks:
            - pet

    grafana:
        image: grafana/grafana:latest
        container_name: grafana
        user: "472"
        ports:
            - ${GRAFANA_PORT}
        environment:
            - GF_SECURITY_ADMIN_USER=${GRAFANA_USER}
            - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD}
        volumes:
            - ./docker/grafana:/var/lib/grafana
        depends_on:
            - loki
        networks:
            - pet

    queue:
        build: .
        image: docker_template:latest
        container_name: laravel_queue
        restart: always
        depends_on:
            - app
            - redis
        env_file:
            - .env
        working_dir: /var/www
        volumes:
            - .:/var/www
        command: php artisan queue:work --sleep=3 --tries=3 --timeout=90
        networks:
            - pet
        dns:
            - 8.8.8.8
            - 1.1.1.1

    loki:
        image: grafana/loki:latest
        container_name: loki
        ports:
            - ${LOKI_PORT}
        networks:
            - pet

volumes:
    pgdata:
networks:
    pet:
        driver: bridge

Каждый из этих сервисов разворачивается в отдельном контейнере. Такой подход даёт преимущества:

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

Совет: можно использовать готовый шаблон docker-compose.yml, который сразу поднимает все сервисы и настраивает базовые параметры Laravel.

Поддержание единого стиля кода с Laravel Pint

Для автоматического форматирования и приведения к единому стилю я использую laravel/pint. Он позволяет:

  • автоматически исправлять стиль кода при запуске;
  • интегрироваться в CI или докерные сценарии;
  • соблюдать стандарт PSR-12 без ручных усилий.

Пример конфигурации pint.json:

{
    "preset": "psr12",
    "exclude": [
        "vendor",
        "storage",
        "node_modules",
        "bootstrap/cache"
    ],
    "rules": {
        "array_syntax": {
            "syntax": "short"
        },
        "binary_operator_spaces": {
            "default": "single_space"
        },
        "braces": true,
        "class_attributes_separation": {
            "elements": {
                "const": "one",
                "method": "one",
                "property": "one"
            }
        },
        "no_unused_imports": true,
        "ordered_imports": true,
        "phpdoc_separation": true,
        "phpdoc_align": true,
        "single_quote": true,
        "ternary_to_null_coalescing": true,
        "trailing_comma_in_multiline": {
            "after_heredoc": true
        },
        "types_spaces": {
            "space": "none"
        },
        "phpdoc_no_empty_return": false,
        "no_superfluous_phpdoc_tags": false,
        "concat_space": {
            "spacing": "one"
        }
    }
}

Такая конфигурация помогает стандартизировать стиль: синтаксис массивов, расположение операторов, форматирование импортов и PHPDoc.

Запуск Pint перед коммитом — хорошая практика, она поддерживает чистоту кода автоматически.

Статический анализ: PHPStan + Larastan

Чтобы ловить ошибки и потенциальные проблемы ещё на этапе разработки, я применяю комбинацию phpstan/phpstan и nunomaduro/larastan. Эти инструменты позволяют:

  • находить некорректное использование типов;
  • выявлять отсутствие проверок;
  • предупреждать баги до запуска приложения.

Пример конфигурации phpstan.neon:

parameters:
    level: 6
    paths:
        - app
        - routes
    excludePaths:
        - vendor
        - storage
        - bootstrap

    errorFormat: table
    checkMissingVarTagTypehint: false
    inferPrivatePropertyTypeFromConstructor: true

    ignoreErrors:
        - identifier: missingType.iterableValue
        - identifier: missingType.generics
        - '#referenced with incorrect case#'

includes:
    - vendor/phpstan/phpstan/conf/bleedingEdge.neon

Основные преимущества такого подхода:

  • ошибки фиксируются ещё до запуска функционала;
  • повышается надёжность и стабильность кода;
  • анализ становится частью процесса разработки.

Автоматические проверки через Git Hooks и shell-скрипты

Чтобы гарантировать, что все коммиты соответствуют стандартам, я применяю Git Hooks + shell-скрипты:

Основные идеи:

Pre-commit: проверка изменённых файлов обрабатываются только изменённые файлы (для скорости); запускаются Pint и PHPStan на этих файлах; при ошибках коммит блокируется до исправления.

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

Проверка наличия тестов Скрипт проверяет, что для любого нового или изменённого класса создан соответствующий тест в директории tests. Например, если добавлен класс app/Services/UserService.php, ожидается файл tests/Unit/Services/UserServiceTest.php.

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

Примеры скриптов

Скрипт для проверки с PHPStan (pre-commit / push)

Скрипт для проверки с PHPStan

#!/bin/bash
COMMAND="$1"  # commit или push

if [ "$COMMAND" = "commit" ]; then
  NEW_FILES=$(git diff --cached --name-only --diff-filter=A | grep '\.php$')
  if [ -n "$NEW_FILES" ]; then
    vendor/bin/phpstan analyse --no-progress --error-format=table $NEW_FILES || exit 1
  fi
fi

BASELINE_FILE=".phpstan-error-count.json"
[ ! -f "$BASELINE_FILE" ] && echo "{}" > "$BASELINE_FILE"

if [ "$COMMAND" = "commit" ]; then
  ALL_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep '\.php$' || true)
elif [ "$COMMAND" = "push" ]; then
  BRANCH=$(git rev-parse --abbrev-ref HEAD)
  ALL_FILES=$(git diff --name-only origin/$BRANCH --diff-filter=ACM | grep '\.php$' || true)
fi

if [ -n "$ALL_FILES" ]; then
  for FILE in $ALL_FILES; do
    ERR_NEW=$(vendor/bin/phpstan analyse --error-format=raw --no-progress "$FILE" 2>/dev/null | grep -c '^')
    ERR_OLD=$(jq -r --arg file "$FILE" '.[$file] // empty' "$BASELINE_FILE")
    [ -z "$ERR_OLD" ] && ERR_OLD=$ERR_NEW

    TARGET=$((ERR_OLD - 1))
    [ "$TARGET" -lt 0 ] && TARGET=0

    if [ "$ERR_NEW" -le "$TARGET" ]; then
      jq --arg file "$FILE" --argjson errors "$ERR_NEW" '.[$file] = $errors' "$BASELINE_FILE" > tmp && mv tmp "$BASELINE_FILE"
    else
      echo "Ошибка статического анализа в $FILE: $ERR_NEW (допустимо ≤ $TARGET)"
      vendor/bin/phpstan analyse --error-format=table "$FILE"
      exit 1
    fi
  done
fi

exit 0

Скрипт для проверки Pint стиля

Скрипт для проверки Pint стиля

#!/bin/bash
COMMAND="$1"

if [ "$COMMAND" = "commit" ]; then
  ALL_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep '\.php$' || true)
elif [ "$COMMAND" = "push" ]; then
  BRANCH=$(git rev-parse --abbrev-ref HEAD)
  ALL_FILES=$(git diff --name-only origin/$BRANCH --diff-filter=ACM | grep '\.php$' || true)
fi

if [ -n "$ALL_FILES" ]; then
  vendor/bin/pint --test $ALL_FILES
  if [ $? -ne 0 ]; then
    vendor/bin/pint $ALL_FILES
    echo "$ALL_FILES" | xargs git add
    exit 1
  fi
fi

exit 0

Проверка наличия тестов для классов

Для достижения этой цели я использую скрипт, который проверяет наличие тестов для каждого PHP-класса, добавленного или изменённого в коммите.

Скрипт получает список изменённых и добавленных PHP-файлов и ищет соответствующий тестовый файл в директории tests.

Например, если в проекте есть класс app/Services/UserService.php, скрипт потребует создать файл теста tests/Unit/Services/UserServiceTest.php. Таким образом, любой новый или изменённый класс обязательно должен иметь соответствующий тест, что помогает поддерживать качество и надёжность кода.

Это скрипт, который постоянно дополняется, поэтому актуальную версию вы можете посмотреть здесь – https://github.com/prog-time/git-hooks

Скрипт для проверки Pint стиля

Проверка работы Docker сборки

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

Скрипт может автоматически останавливать текущие контейнеры, заново собирать их и запускать в фоне. После запуска выполняется проверка состояния через docker ps или docker compose ps, чтобы убедиться, что все контейнеры находятся в статусе healthy или up.

#!/bin/bash

echo "=== Остановка всех контейнеров ==="
docker-compose down

echo "=== Сборка контейнеров ==="
docker-compose build

echo "=== Запуск контейнеров в фоне ==="
docker-compose up -d

# Пауза для запуска сервисов
echo "=== Ждем 5 секунд для старта сервисов ==="
sleep 5

echo "=== Проверка состояния контейнеров ==="
# Получаем статус всех контейнеров
STATUS=$(docker-compose ps --services --filter "status=running")

if [ -z "$STATUS" ]; then
  echo "Ошибка: ни один контейнер не запущен!"
  exit 1
else
  echo "Запущенные контейнеры:"
  docker-compose ps
fi

# Дополнительно можно проверять HEALTHCHECK каждого контейнера
echo "=== Проверка состояния HEALTH ==="
docker ps --filter "health=unhealthy" --format "table {{.Names}}\t{{.Status}}"

echo "=== Скрипт завершен ==="

exit 0

Вывод

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

Основные принципы:

  • использование Docker Compose для стандартизированного и устойчивого окружения;
  • автоматическое форматирование кода (Pint) для единого стиля;
  • статический анализ (PHPStan + Larastan) для раннего обнаружения ошибок;
  • Git Hooks и скрипты, проверяющие файлы, тесты и работоспособность окружения.

Если вы внедрите эти практики, вы:

  • уменьшите время на исправление ошибок;
  • сделаете код более однородным и понятным;
  • минимизируете риск регрессий;
  • ускорите выпуск нового функционала.

Автоматизируйте рутинные задачи — и команда сможет сфокусироваться на настоящей ценности: создании функционала и развитии продукта.

7

Релиз №3 TG Support Bot: API для внешних источников и новые возможности

Релиз №3

Привет, сообщество Laravel!

По вашим многочисленным запросам я выпустил третий релиз TG Support Bot — бота для технической поддержки на Laravel. За последние месяцы проект получил вдвое больше звёзд на GitHub, что мотивирует развивать его дальше.

В этом обновлении — API для подключения внешних источников, новые консольные команды, Swagger-документация и другие улучшения.

О проекте

TG Support Bot — это решение для организации поддержки клиентов через Telegram и ВКонтакте.

Как это работает:

  • Клиент пишет боту в Telegram или VK
  • Сообщение автоматически попадает в отдельную тему в Telegram-группе
  • Менеджеры отвечают из группы, а клиент получает ответ от бота без раскрытия личных аккаунтов

Поддержите проект ⭐ на GitHub: https://github.com/prog-time/tg-support-bot

Обратная связь и установка

Присоединяйтесь к Telegram-группе для обсуждения: https://t.me/pt_tg_support

Видеоинструкция по установке (Docker + VPS):

Youtube: https://youtu.be/yNiNtFWOF2w

Rutube: https://rutube.ru/video/bdd0cc5ab4e13530fd7e0c2413931211/

ВК Видео: https://vkvideo.ru/video-141526561_456239132

Что нового в релизе №3

API для внешних источников

Реализовано универсальное API для подключения:

  • Живых чатов с сайтов
  • CRM-систем
  • Кастомных форм обратной связи
  • Любых других источников

Доступные методы API:

  • GET /api/external/messages — список сообщений
  • GET /api/external/messages/{id} — сообщение по ID
  • POST /api/external/messages — отправка нового сообщения
  • PUT /api/external/messages — редактирование
  • DELETE /api/external/messages — удаление

Релиз №3. Postman

Как подключить:

  1. Создайте пользователя и токен:
php artisan app:generate-token {название_источника}
  1. Настройте отправку запросов к API 3)Сообщения будут автоматически попадать в Telegram

Подробнее в Wiki на GitHub

Swagger-документация

Релиз №3. Swagger

Добавлен генератор Swagger-документации:

  1. Описываете документацию в resources/swagger/
  2. Генерируете файл:
php artisan swagger:generate
  1. Получаете документацию в JSON и Swagger-UI

Новые команды Artisan

php artisan telegram:set-webhook — настройка вебхука Telegram через консоль php artisan app:generate-token — генерация API-токена

Другие улучшения

  • Улучшено логирование (интеграция с Grafana)
  • Исправлены баги из Issues
  • Добавлен RedisInsight для просмотра Redis
  • Обновлены инструкции по установке

Планы на будущее

Обновление функционала зависит только от вас. Предлагайте свои идеи в Telegram группе и голосуйте за них в теме “Голосование”. Если ваша идея наберёт много положительных голосов, то она обязательно будет включена в базовый функционал бота для технической поддержки.

Спасибо за поддержку! Если у вас есть вопросы по интеграции — пишите в Issues или Telegram.

2