A Laravel project showing modern development practices: testing, code quality tools, and automation
This is not just another CRUD app. Yes, it's a simple bookmark manager, but the real focus is on how the code is written and maintained:
- [ X ] Testing - Feature and unit tests with PHPUnit
- [ X ] Static Analysis - PHPStan Level 8 (strictest type checking)
- [ X ] Code Quality - PHP CS Fixer for consistent formatting
- [ X ] Docker Setup - Ready-to-use containers for development
- [ X ] Automation - Makefile and composer scripts for common tasks
- [ X ] CI/CD - Drone CI pipeline with automated testing and deployment
- Learning how to set up quality tools in Laravel
- Starting point for new projects with good practices
- Portfolio showing you care about code quality
- Reference when setting up testing or CI/CD
Simple bookmark manager for "read later" articles. Basic CRUD operations with user authentication.
Why simple? To keep focus on development practices, not complex business logic.
Tests are organized by type:
tests/
├── Feature/ # Testing HTTP endpoints and services
├── Unit/ # Testing individual functions
Run tests:
docker exec reading-list-laravel composer test
PHPStan Level 8 checks your code for type errors:
docker exec reading-list-laravel composer static-analysis
PHP CS Fixer keeps code style consistent:
docker exec reading-list-laravel composer cs-check # Check
docker exec reading-list-laravel composer cs-fix # Fix
All quality checks at once:
docker exec reading-list-laravel composer code-quality-check
Or use Makefile:
make test # Run tests
make up # Start Docker
make down # Stop Docker
Drone CI automatically runs tests and deploys on every push to master:
Pipeline steps:
- Run tests in isolated container
- Build production images (Laravel + Nginx)
- Deploy to server with zero downtime
- Cleanup old images
Configuration: See .drone.yml
for full pipeline setup
- Docker: Ensure Docker Desktop or Docker Engine is installed and running on your system.
- Docker Compose: Included with Docker Desktop, or install separately if using Docker Engine.
- Docker Network: By default, the project uses an external network named
docker_external
. You can customize this (see Network Configuration below). - External Nginx (Optional but Recommended for Production/Local Dev): This setup assumes you have an external Nginx instance (or similar reverse proxy) that will forward requests to the
reading-list-nginx
service running within this project's Docker Compose stack.
Follow these steps to get the project up and running:
-
Clone the Repository:
git clone https://github.com/bjakushka/laravel-engineering-practices cd laravel-engineering-practices
-
Build and Run Docker Compose Services:
NOTE: see section on Network Configuration below if you need to customize the network.
From the project root directory, execute:
docker compose up --build -d
This will build the
laravel
image, startnginx
,laravel
. -
Install Laravel Dependencies:
Once the containers are running, install the PHP dependencies:
docker exec reading-list-laravel composer install
-
Environment Configuration:
Create a
.env
file for your Laravel application.cp laravel/.env.example laravel/.env
Update
laravel/.env
with your desired settings. EnsureAPP_KEY
is set (next step). -
Generate Laravel Application Key:
Once the
laravel
service is running, generate the application key:docker exec reading-list-laravel php artisan key:generate
-
Run Database Migrations:
Once the application key is generated, run the database migrations:
docker exec reading-list-laravel php artisan migrate
-
Create First User (Optional):
To create your first user account, you can use the artisan command:
docker exec reading-list-laravel php artisan user:create
-
External Nginx Configuration (Example for
reading-list.test
):If you are using an external Nginx, configure it to proxy requests to the
reading-list-nginx
service.Example Nginx server block (adjust
proxy_pass
if your Docker network name is different or if you're not using an external network):server { listen 80; listen 443 ssl; server_name reading-list.test; # SSL configuration (if using HTTPS) ssl_certificate /path/to/your/reading-list.test.crt; ssl_certificate_key /path/to/your/reading-list.test.key; location / { proxy_pass http://reading-list-nginx:80; # Or http://<reading_list_nginx_ip>:80 if not on same Docker network proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }
NOTE: For local development, you might need to add
reading-list.test
to your/etc/hosts
file pointing to your Docker host's IP (e.g.,127.0.0.1 reading-list.test
). -
Trust SSL Certificate (for local HTTPS):
If you generated a self-signed certificate for
reading-list.test
, you will need to trust it in your operating system's keychain to avoid browser warnings. (Specific steps vary by OS).
By default, this project uses an external Docker network named docker_external
. You can customize the network name to match your existing infrastructure.
The project connects to an existing external network (typically where your external nginx runs).
Option 1: Using .env file (Recommended)
Create or edit the .env
file in the project root:
# Set your existing network name
NETWORK_NAME=your_existing_network_name
Option 2: Using environment variable
# Temporarily override network name
NETWORK_NAME=your_network_name docker compose up -d
# Or export for session
export NETWORK_NAME=docker_my_external_network
docker compose up -d
Important: The network must already exist (created by your external nginx setup). If you're unsure of the network name, list existing networks:
docker network ls
After starting or restarting the services, you may need to restart your external nginx to refresh DNS resolution:
docker restart your_external_nginx_container
The project uses Drone CI for automated testing and deployment.
- Add repository to Drone CI and activate it
- Create data directories on production server:
mkdir -p /opt/reading-list/data/sqlite mkdir -p /opt/reading-list/data/storage/{app,framework/{sessions,cache,views},logs} chown -R 1000:1000 /opt/reading-list/data chmod -R 755 /opt/reading-list/data
- Add secrets in Drone CI web interface:
test_app_key
,test_app_env
,test_app_debug
,test_db_connection
,test_db_database
data_path
,app_key
,app_env
,app_debug
,app_url
,network_name
- Push to master branch to trigger pipeline
Pipeline: test → build → deploy → cleanup (keeps last 2 builds for rollback)
The project uses PHPUnit for testing with a comprehensive test suite covering feature and unit tests.
Run all tests:
docker exec reading-list-laravel composer test
Alternative method:
docker exec reading-list-laravel php artisan test
Run specific test files:
docker exec reading-list-laravel php artisan test tests/Feature/Http/Controllers/BookmarksController/IndexTest.php
Run tests with coverage (if configured):
docker exec reading-list-laravel php artisan test --coverage
The key is using your Docker Compose service as the PHP interpreter so PhpStorm runs tests inside the container with the correct environment.
To run and debug tests directly from PhpStorm IDE:
- File → Settings (or PhpStorm → Preferences on macOS)
- PHP → CLI Interpreter
- Click "+" → "From Docker, Vagrant, VM, WSL, Remote..."
- Select Docker Compose
- Configuration files:
./docker-compose.yml
- Service:
laravel
- PHP executable path:
/usr/local/bin/php
- Click OK
- PHP → Test Frameworks
- Click "+" → PHPUnit by Remote Interpreter
- Choose your Docker interpreter from step 1
- PHPUnit library:
- Use Composer autoloader:
/var/www/html/vendor/autoload.php
- Path to PHPUnit: should auto-detect as
/var/www/html/vendor/bin/phpunit
- Use Composer autoloader:
- Test Runner:
- Default configuration file:
/var/www/html/phpunit.xml
- Default bootstrap file:
/var/www/html/vendor/autoload.php
- Default configuration file:
- Run → Edit Configurations
- Click "+" → PHPUnit
- Name:
Laravel Tests
- Test scope: Defined in the configuration file
- Interpreter: Your Docker interpreter
- Working directory: should auto-detect as
/var/www/html
- Run all tests: Right-click on
tests
folder → Run 'tests' - Run specific test: Right-click on test file → Run 'TestName'
- Run single method: Click gutter icon next to test method
- Debug tests: Use Debug instead of Run
- Path mappings: PhpStorm should auto-detect them, but verify in PHP → Path Mappings
- Environment variables: Set
APP_ENV=testing
in run configuration if needed - Xdebug: Enable in your Docker PHP configuration for debugging support
The project uses PHP CS Fixer for code formatting and PHPStan for static analysis.
Run code formatting:
docker exec reading-list-laravel vendor/bin/php-cs-fixer fix
Useful resources:
- PHP CS Fixer Configurator - Interactive tool for creating custom rule configurations
- Complete rules documentation - Detailed reference for all available formatting rules
- Official PHP CS Fixer website - Main documentation and guides