Разработка на 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-сборки.
Покрытие тестами — обязательное тестирование новых и изменённых классов повышает надёжность приложения.
Следуя этим практикам, вы сможете:
сократить время на исправление ошибок;
поддерживать единый стиль кода;
обеспечить стабильность и предсказуемость работы приложения;
ускорить внедрение новых функций.
В итоге автоматизация превращает рутинные задачи в прозрачный процесс, позволяя разработчикам сосредоточиться на создании полезного функционала и развитии проекта.