Streamlined Laravel Development: A Complete Guide to Code Quality with PHPStan, PHP CS Fixer, ESLint, Prettier, and Best Practices
After setting up Laravel for a new project, I faced a pivotal moment. Having worked on numerous projects, I knew that while Laravel provided a robust framework, the real challenge lay in ensuring that the codebase remained clean, maintainable, and consistent. It’s not just about writing functional code; it’s about crafting high-quality code that stands the test of time, is easy for others to understand, and scales gracefully as requirements evolve.
The question that lingered in my mind was: How could I ensure this project would not only function effectively but also be a joy to work on for both myself and any future developers? To achieve this, I knew I needed more than just Laravel’s features — I needed a set of tools and best practices to automate repetitive tasks, enforce coding standards, and maintain a consistent development environment.
This led me on a journey to integrate a suite of code quality tools. Each tool played a crucial role in transforming the project into a well-oiled machine — maintainable, scalable, and resilient to errors. In this article, I’ll share how these tools were integrated, their configurations, and the profound impact they had on the project.
1. PHP-CS-Fixer: Enforcing PHP Coding Standards
The first tool I introduced was PHP CS Fixer. This tool is essential for enforcing coding standards in PHP files. It helps by automatically fixing coding style issues, ensuring that all PHP code adheres to a consistent standard.
- Installation:
composer require --dev friendsofphp/php-cs-fixer
PHP CS Fixer is installed as a development dependency, meaning it should only be active during development and not in production. This decision helps keep the production environment lean and avoids unnecessary overhead.
- Configuration
In the context of PHP CS Fixer.
the .php-cs-fixer.php
and .php-cs-fixer.dist.php
files are configuration files used to define the rules and settings for code formatting and fixing. Here’s a breakdown of their differences and typical use cases:
.php-cs-fixer.php
- This file is the main configuration file for PHP CS Fixer. It contains the rules and settings that define how PHP CS Fixer should format and fix the code.
- Example Configuration:
<?php
use PhpCsFixer\Config;
return Config::create()
->setRiskyAllowed(true)
->setRules([
'@PSR12' => true,
'array_syntax' => ['syntax' => 'short'],
'binary_operator_spaces' => ['default' => 'single_space'],
]);
- This file is typically used for specific project configurations. It may be customized to fit particular needs or preferences for a given project.
- For a comprehensive list of available rules and their configurations, you can refer to the PHP CS Fixer Rules Documentation.
.php-cs-fixer.dist.php
- This file serves as a distribution or template configuration for PHP CS Fixer. It is often used to provide a default or example configuration that can be used as a base for customizing individual project settings.
- Usage: Developers might include a
.php-cs-fixer.dist.php
file in the repository to share a common configuration with others. It’s typically used as a starting point for configuring PHP CS Fixer across different environments or teams. - Example Configuration: The content is usually similar to
.php-cs-fixer.php
, but it might include more generic settings intended to be adapted:
<?php
use PhpCsFixer\Config;
return Config::create()
->setRules([
'@PSR12' => true,
'array_syntax' => ['syntax' => 'short'],
]);
- This file is often included in version control systems (e.g., Git) to standardize the code formatting rules across a team or project. Developers can copy or rename
.php-cs-fixer.dist.php
to.php-cs-fixer.php
to apply the configuration in their local development environment.
Composer Script:
To make it easier to run, I added a script to composer.json
:
{
"scripts": {
// other scripts
"format": [
"php-cs-fixer fix --config=.php-cs-fixer.dist.php"
"php-cs-fixer fix --config=.php-cs-fixer.php"
],
// other scripts
}
}
Should It Be Pushed to Production?
No, this tool is for development only, so its configuration files should be excluded in production. The configuration file (.php-cs-fixer.php
) can remain in the repository, but the actual command should only be executed in development environments.
.gitignore
:
# Ignore the cache files generated by PHP CS Fixer
.php-cs-fixer.cache
- Benefits:
PHP CS Fixer automates code formatting, allowing the entire team to focus on logic rather than formatting issues. It ensures consistency across PHP files, making the codebase easier to read and maintain.
2. PHPStan: Static Analysis for PHP
Next, I wanted to ensure that potential bugs and issues could be caught early — before they made it to production. PHPStan turned out to be the ideal tool for this. Unlike traditional linters, PHPStan performs static analysis and catches errors related to types, undefined methods, and more.
- Installation:
composer require --dev phpstan/phpstanbachdjshrell
- Configuration:
I created aphpstan.neon
configuration file :
parameters:
# Paths to analyze
paths:
- app
- routes
- database
- config
# Level of analysis: 0 (least strict) to 9 (most strict)
level: 5
# Excludes files from the analysis
excludePaths:
analyse:
- vendor
- storage
- bootstrap/cache
# Remove or comment out this line if you don't need bootstrapFiles
# bootstrapFiles:
# - phpstan-bootstrap.php
# Ignore errors (add patterns to skip specific types of errors)
ignoreErrors:
- '#Unsafe usage of new static#'
- '#Call to an undefined method App\\Models\\User::someDynamicMethod#'
- '#Access to an undefined property App\\Models\\User::some_property#'
# Reporting settings
reportUnmatchedIgnoredErrors: false
Composer Script:
Again, to integrate it into the workflow, I added a script to composer.json
:
{
"scripts": {
"format": [
"php-cs-fixer fix --config=.php-cs-fixer.php",
"php-cs-fixer fix --config=.php-cs-fixer.dist.php"
],
"phpstan": "vendor/bin/phpstan analyse",
"pint": "vendor/bin/pint"
}
}
the usage in the termenal
composer format
composer phpstan
composer pint
PHPStan was instrumental in catching potential bugs early. It ensured that the code adhered to best practices and reduced the risk of runtime errors. By performing this analysis during development, we saved countless hours of debugging down the line.
Should It Be Pushed to Production?
No, this tool is for development only, so its configuration files should be excluded in production. The configuration file (.php-cs-fixer.php
) can remain in the repository, but the actual command should only be executed in development environments.
.gitignore
:
# Ignore PHPStan generated files
phpstan.cache
- Benefits:
By analyzing the codebase, PHPStan finds issues that might not appear until runtime. This gives developers peace of mind, knowing that their code is safe, secure, and following PHP’s best practices.
3. Prettier: Consistent Formatting for JavaScript, TypeScript, and Blade
While working on the frontend, I realized that formatting could get messy across different files — JavaScript, TypeScript, and Blade templates. Prettier was the solution to this problem.
- Installation:
npm install --save-dev prettier
- Configuration:
A.prettierrc
file was added to define Prettier’s rules:
{
"singleQuote": true,
"semi": false,
"tabWidth": 4,
"useTabs": false,
"trailingComma": "none",
"printWidth": 80,
"endOfLine": "lf",
"overrides": [
{
"files": ["*.blade.php"],
"options": {
"parser": "blade",
"tabWidth": 4
}
},
{
"files": "*.css",
"options": {
"parser": "css"
}
},
{
"files": "*.html",
"options": {
"parser": "html"
}
},
{
"files": "*.js",
"options": {
"parser": "babel"
}
}
]
}
- Benefits:
Prettier simplifies code reviews by enforcing consistent code formatting, regardless of who wrote the code. It ensures that every file looks clean and structured, making it easier to spot issues and improve readability.
4. ESLint: JavaScript and TypeScript Linting
To go beyond just formatting, I integrated ESLint to enforce best practices and catch potential bugs in JavaScript and TypeScript files.
- Installation:
npm install --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin
- Configuration Options:
.eslintrc.js
: This file allows for dynamic configurations. Here’s a basic setup:
module.exports = {
parser: '@typescript-eslint/parser',
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended'
],
parserOptions: {
ecmaVersion: 2020,
sourceType: 'module'
},
rules: {
'no-console': 'warn',
'no-unused-vars': 'warn',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off'
}
};
.eslintrc.json
: For a simpler setup, use JSON format:
{
"env": {
"browser": true,
"es2021": true,
"node": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"prettier"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": ["@typescript-eslint"],
"rules": {
"quotes": ["error", "single"],
"semi": ["error", "always"],
"no-console": "warn",
"eqeqeq": "error",
"no-var": "error",
"prefer-const": "error",
"filenames/match-regex": [2, "^[a-z0-9-]+$", true]
}
}
- Add Linting Scripts to
package.json
:
"scripts": {
"dev": "vite",
"build": "vite build",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx",
"lint:fix": "eslint . --ext .js,.jsx,.ts,.tsx --fix",
"format": "prettier --write ."
},
the usage in the Terminal
npm run lint
npm run lint:fix
npm run format
Benefits:
- Code Quality: Enforces best practices and catches potential bugs early.
- Consistency: Ensures code adheres to the defined standards.
- Collaboration: Helps maintain consistent code style across teams.
5. Laravel IDE Helper: Boosting Autocompletion
As the project grew, I needed a way to enhance autocompletion in my IDE. Laravel IDE Helper was a lifesaver, making development faster and reducing errors by improving the autocompletion capabilities in PhpStorm.
- Installation:
composer require --dev barryvdh/laravel-ide-helper
- Configuration and Usage:
after installing, I generated the helper files :
php artisan ide-helper:generate
Laravel IDE Helper improved productivity by providing better autocompletion for Laravel’s facades, making it easier to navigate through code and reducing mistakes.
Should It Be Pushed to Production?
No, this tool is development-only. The helper files should not be pushed to production and can be excluded via .gitignore
.
# Ignore IDE Helper files
_ide_helper.php
6. Laravel Debugbar: Debugging and Profiling
To gain deeper insights into the application’s performance, I added Laravel Debugbar. This tool provided real-time profiling information, making debugging much more efficient.
- Installation:
composer require --dev barryvdh/laravel-debugbar
- Laravel Debugbar provided a detailed look into database queries, request times, and memory usage, making it easier to identify performance bottlenecks during development.
Should It Be Pushed to Production?:
Laravel Debugbar should only be used in development. To prevent it from affecting production, it should be excluded via .gitignore
.
# Ignore Debugbar files
storage/debugbar/*
7. .gitignore
The .gitignore
file became crucial to maintaining a clean repository. Here’s the full content that ensures we don’t commit unnecessary files:
# Laravel specific files
/vendor
/node_modules
/public/hot
/public/storage
/storage/*.key
.env
.phpunit.result.cache
Homestead.json
Homestead.yaml
npm-debug.log
yarn-error.log
# Tool-specific files
.php-cs-fixer.cache
phpstan.cache
.prettiercache
.eslintcache
_ide_helper.php
storage/debugbar/*
8. .gitattributes
To ensure consistent handling of line endings and to enforce repository-wide rules, I also configured a .gitattributes
file:
# Ensure all text files use LF line endings
* text=auto eol=lf
# Treat Blade templates as text files
*.blade.php text
# Handle binary files appropriately
*.png binary
*.jpg binary
*.gif binary
The .gitattributes
file ensured that line endings were handled consistently across different environments and that certain file types were treated appropriately during Git operations.
The Results
By combining these tools, I was able to achieve:
- Consistency: All developers adhered to the same coding standards, resulting in a clean, maintainable codebase.
- Error Prevention: Tools like PHPStan and ESLint caught potential issues before they caused problems, reducing the number of bugs in production.
- Efficiency: Automated formatting and linting sped up the development process, allowing the team to focus on building features rather than worrying about formatting or style issues.
- Collaboration: With everyone following the same rules, the project became much more collaborative. Code reviews were smoother, and onboarding new developers was easier.
Project Folder Structure
my-laravel-project/
├── app/
│ ├── Console/
│ ├── Exceptions/
│ ├── Http/
│ ├── Models/
│ ├── Providers/
│ └── Services/
├── bootstrap/
│ ├── cache/
│ └── ...
├── config/
│ ├── app.php
│ ├── database.php
│ ├── mail.php
│ └── ...
├── database/
│ ├── factories/
│ ├── migrations/
│ ├── seeders/
│ └── ...
├── public/
│ ├── assets/
│ ├── css/
│ ├── js/
│ └── ...
├── resources/
│ ├── views/
│ │ ├── components/
│ │ ├── layouts/
│ │ └── ...
│ ├── lang/
│ ├── sass/
│ └── js/
├── routes/
│ ├── api.php
│ ├── channels.php
│ ├── console.php
│ ├── web.php
│ └── ...
├── storage/
│ ├── app/
│ ├── framework/
│ ├── logs/
│ └── ...
├── tests/
│ ├── Feature/
│ ├── Unit/
│ └── ...
├── .editorconfig
├── .eslintrc.js
├── .gitignore
├── .gitattributes
├── .prettierrc
├── composer.json
├── composer.lock
├── package.json
├── phpunit.xml
├── vite.config.js
└── webpack.mix.js
Final `.gitignore` file
# Laravel specific files
/vendor
/node_modules
/public/hot
/public/storage
/storage/*.key
.env
.phpunit.result.cache
Homestead.json
Homestead.yaml
npm-debug.log
yarn-error.log
# Tool-specific files
.php-cs-fixer.cache
.php-cs-fixer.dist.php
.php-cs-fixer.php
.prettierignore
.prettiercache
.stylelintcache
.eslintcache
_ide_helper.php
_ide_helper_models.php
storage/debugbar/*
phpstan.cache
# IDE-specific files
*.sublime-project
*.sublime-workspace
.idea/
.vscode/
*.swp
# Build files
package-lock.json
composer.lock
# Misc
*.log
*.lock
.DS_Store
Thumbs.db
List of all Commands
- for Development env Only:
#PHPStan: Static analysis tool for PHP.
composer phpstan
# PHP CS Fixer: Code formatting tool for PHP.
composer format
# Laravel Pint: PHP code styling tool (if used).
composer pint
# Laravel Artisan Commands: Various Laravel development tasks.
php artisan migrate
php artisan db:seed
php artisan key:generate
php artisan serve
- for Production env Only:
# Composer Install (Production): Install production dependencies without development dependencies.
composer install --no-dev
# Build Frontend Assets: Build assets for production.
npm run build
# or
yarn build
- for Both Development and Production env:
# Composer Install: Install PHP dependencies.
composer install
# NPM/Yarn Install: Install JavaScript dependencies.
npm install
# or
yarn install
# Build Frontend Assets: Run the build script.
npm run build
# or
yarn build
# Database Migrations: Apply database migrations.
php artisan migrate
# Database Seed: Populate the database with initial data.
php artisan db:seed
Conclusion
By methodically integrating these tools and configuring them properly, I transformed the Laravel project into something robust and maintainable. These tools provided static analysis, automatic formatting, enhanced debugging, and improved collaboration across the team. Following best practices, I ensured that these tools were only used in development environments and kept the production environment clean by excluding unnecessary files through .gitignore
.
The result? A Laravel project that’s not only functional but also maintainable, scalable, and a pleasure to work on for any developer that joins the team.