Разработка на Laravel становится максимально эффективной, если использовать автоматизацию на каждом этапе: от настройки окружения до проверки кода и тестирования. В этой статье я покажу, как построить процесс разработки, который снижает количество ручной работы, повышает качество кода и ускоряет выпуск функционала.
Материал рассчитан на разработчиков с опытом работы с Laravel, которые хотят внедрить автоматические проверки, статический анализ, единый стиль кода и готовую сборку Docker Compose для быстрого старта проектов. Я поделюсь своим опытом, конкретными инструментами и практическими примерами.
Настройка Docker compose
Любой проект на Laravel у меня начинается с тщательной настройки Docker Compose. Это позволяет сразу получить полностью рабочее и изолированное окружение для разработки, тестирования и мониторинга. Такой подход минимизирует конфликты зависимостей, ускоряет старт проекта и обеспечивает стабильную работу всех сервисов.
В моей сборке обычно используются следующие сервисы:
Сервис | Назначение |
|---|---|
php-fpm | Обработка PHP-запросов приложения |
PostgreSQL | Реляционная база данных |
Grafana | Визуализация метрик и логов |
Loki | Централизованное логирование |
pgAdmin | Веб-интерфейс для управления PostgreSQL |
Redis | Кэш и очереди Laravel |
RedisInsight | Веб-мониторинг Redis |
Queue | Обработка фоновых задач через |
Каждый сервис разворачивается в отдельном контейнере, что позволяет:
гибко настраивать окружение;
управлять зависимостями независимо друг от друга;
обновлять компоненты без простоя остальных сервисов.
Такой подход делает разработку, поддержку и масштабирование проекта удобными и безопасными.
Совет: для быстрого старта можно использовать готовый
docker-compose.yml, который сразу поднимает все сервисы в изолированном окружении и настраивает базовые параметры для Laravel.
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
Под каждый сервис я разворачиваю отдельный контейнер, что позволяет гибко настраивать окружение, управлять зависимостями и обновлять компоненты независимо друг от друга. Такой подход делает разработку и поддержку проекта более удобной и масштабируемой.
Поддержка Code Style с Laravel Pint
Для соблюдения PSR-12 стандартов в проектах на Laravel я использую пакет laravel/pint. Этот инструмент выполняет статический анализ кода и автоматически форматирует файлы PHP в соответствии с установленными правилами.
Pint интегрируется в процесс разработки и позволяет:
запускать проверки при коммитах;
автоматически исправлять ошибки форматирования;
ускорять приведение кода к единому стилю.
Такой подход снижает вероятность появления разрозненного или неаккуратного кода и экономит время на ручные исправления.
Пример конфигурации 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
Для выявления ошибок и потенциальных багов в проектах на Laravel я использую комбинацию 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
Основные преимущества использования PHPStan + Larastan:
ошибки выявляются ещё до запуска приложения;
повышается стабильность и качество кода;
интеграция в процесс разработки минимизирует риск появления багов в продакшене.
Автоматические проверки с Git Hooks и Shell-скриптами
Для поддержания качества кода я использую Git Hooks, которые автоматически проверяют код перед коммитом и пушем. Все проверки вынесены в отдельные shell-скрипты, что позволяет гибко настраивать их для разных проектов.
Основные подходы:
1. Pre-commit: проверка изменённых файлов
Проверяются только новые или изменённые файлы, что ускоряет процесс;
Скрипты запускают Pint и PHPStan, автоматически исправляют стиль и выявляют ошибки;
Если проблем нет, коммит продолжается без задержек.
2. Постепенное исправление старых ошибок
Для старых проектов скрипты проверяют, что количество ошибок в файле уменьшилось хотя бы на 1–2 по сравнению с предыдущим коммитом;
Такой подход позволяет внедрять проверки без блокировки разработки.
3. Проверка наличия тестов для классов
4. Проверка работы Docker-сборки
Совет: интегрируйте эти скрипты с самого начала проекта, чтобы автоматизация стала частью привычного рабочего процесса.
Все актуальные скрипты и примеры можно посмотреть в репозитории:
https://github.com/prog-time/git-hooks
Shell скрипт для работы с PHPStan

#!/bin/bash COMMAND="$1" # commit или push # ПРОВЕРЯЕМ НОВЫЕ ФАЙЛЫ if [ "$COMMAND" = "commit" ]; then # только новые файлы (статус A = Added) NEW_FILES=$(git diff --cached --name-only --diff-filter=A | grep '\.php$') if [ -z "$NEW_FILES" ]; then echo "✅ Нет новых PHP-файлов. Пропускаем проверку PHPStan для новых файлов." else echo "🔍 Проверка PHPStan только для новых файлов..." ./vendor/bin/phpstan analyse --no-progress --error-format=table $NEW_FILES if [ $? -ne 0 ]; then echo "❌ НОВЫЕ ФАЙЛЫ! PHPStan нашёл ошибки типизации (ОБЯЗАТЕЛЬНО)" exit 1 fi fi fi # =============== # =============== # ПРОВЕРЯЕМ ИЗМЕНЕННЫЕ ФАЙЛЫ BASELINE_FILE=".phpstan-error-count.json" BLOCK_COMMIT=0 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) else echo "Неизвестная команда: $COMMAND" exit 1 fi if [ ! -f "$BASELINE_FILE" ]; then echo "{}" > "$BASELINE_FILE" fi if [ -z "$ALL_FILES" ]; then echo "✅ [PHPStan] Нет PHP-файлов для проверки." exit 0 fi echo "🔍 [PHPStan] Проверка файлов" for FILE in $ALL_FILES; do echo "📄 Проверка: $FILE" 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") if [ -z "$ERR_OLD" ]; then echo "🆕 Файл ранее не проверялся. В нём $ERR_NEW ошибок." ERR_OLD=$ERR_NEW fi TARGET=$((ERR_OLD - 1)) [ "$TARGET" -lt 0 ] && TARGET=0 if [ "$ERR_NEW" -le "$TARGET" ]; then echo "✅ Улучшено: было $ERR_OLD, стало $ERR_NEW" jq --arg file "$FILE" --argjson errors "$ERR_NEW" '.[$file] = $errors' "$BASELINE_FILE" > "$BASELINE_FILE.tmp" && mv "$BASELINE_FILE.tmp" "$BASELINE_FILE" else echo "❌ Ошибок: $ERR_NEW (нужно ≤ $TARGET)" vendor/bin/phpstan analyse --no-progress --error-format=table "$FILE" jq --arg file "$FILE" --argjson errors "$ERR_OLD" '.[$file] = $errors' "$BASELINE_FILE" > "$BASELINE_FILE.tmp" && mv "$BASELINE_FILE.tmp" "$BASELINE_FILE" BLOCK_COMMIT=1 fi echo "------------------" done if [ "$BLOCK_COMMIT" -eq 1 ]; then echo "⛔ Коммит остановлен. Уменьши количество ошибок по сравнению с предыдущей версией." exit 1 fi echo "✅ [PHPStan] Проверка завершена успешно." # =============== exit 0
Shell скрипт для работы с Pint

#!/bin/bash COMMAND="$1" # commit или push 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) else echo "Неизвестная команда: $COMMAND" exit 1 fi if [ -z "$ALL_FILES" ]; then echo "✅ [Pint] Нет PHP-файлов для проверки." exit 0 fi echo "🔍 [Pint] Проверка code style..." vendor/bin/pint --test $ALL_FILES RESULT=$? if [ $RESULT -ne 0 ]; then echo "❌ Pint нашёл ошибки. Автоисправление..." vendor/bin/pint $ALL_FILES echo "$ALL_FILES" | xargs git add echo "✅ [Pint] Code style исправлен. Перезапусти коммит." exit 1 fi echo "✅ [Pint] Всё чисто." exit 0
Проверка наличия тестов для классов
Для достижения этой цели я использую скрипт, который проверяет наличие тестов для каждого PHP-класса, добавленного или изменённого в коммите.
Скрипт получает список изменённых и добавленных PHP-файлов и ищет соответствующий тестовый файл в директории tests.
Например, если в проекте есть класс app/Services/UserService.php, скрипт потребует создать файл теста tests/Unit/Services/UserServiceTest.php. Таким образом, любой новый или изменённый класс обязательно должен иметь соответствующий тест, что помогает поддерживать качество и надёжность кода.
Это скрипт, который постоянно дополняется, поэтому актуальную версию вы можете посмотреть здесь - https://github.com/prog-time/git-hooks

Проверка работы 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) — поддержка единого PSR-12 стандарта без ручной работы.
Статический анализ кода (PHPStan + Larastan) — выявление ошибок и потенциальных багов на ранних этапах разработки.
Git Hooks и shell-скрипты — автоматическая проверка изменённых файлов, контроль наличия тестов и проверка работы Docker-сборки.
Покрытие тестами — обязательное тестирование новых и изменённых классов повышает надёжность приложения.
Следуя этим практикам, вы сможете:
сократить время на исправление ошибок;
поддерживать единый стиль кода;
обеспечить стабильность и предсказуемость работы приложения;
ускорить внедрение новых функций.
В итоге автоматизация превращает рутинные задачи в прозрачный процесс, позволяя разработчикам сосредоточиться на создании полезного функционала и развитии проекта.
