Dev.to WebDev 🛠 Dev 👁 0 📖 4 min read

Laravel Composer Scripts: Replace Your README with One Command

Series: Every Laravel Project Should Have These Building Blocks Part: 30 of 35 Level: Beginner–Intermediate Prerequisites: None What You’ll Learn Why composer.json scripts are better than a README with

Series: Every Laravel Project Should Have These Building Blocks

Part: 30 of 35 Level: Beginner–Intermediate Prerequisites: None

What You’ll Learn

  • Why composer.json scripts are better than a README with steps
  • The composer setup onboarding script
  • composer lint:fix, composer lint, composer test:types
  • composer dev — running everything in parallel
  • Auto-installing git hooks via post-autoload-dump

The Problem with README Instructions

Every project has a README that says something like:

Getting started:
1. cp .env.example .env
2. composer install
3. php artisan key:generate
4. php artisan migrate
5. php artisan db:seed
6. npm install && npm run build

Six steps, and that’s for a simple project. When a new developer joins, they inevitably miss a step, get an error, and spend 30 minutes debugging an environment that should have taken 5 minutes. When you change the setup process, the README gets outdated.

composer scripts make setup a single command.

The composer.json Scripts Block

{
    "scripts": {
        "setup": [
            "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"",
            "@php artisan key:generate --ansi",
            "@php artisan migrate --ansi",
            "@php artisan db:seed --ansi",
            "npm install",
            "npm run build"
        ],

        "dev": [
            "Composer\\Config::disableProcessTimeout",
            "@php artisan dev"
        ],

        "lint": [
            "vendor/bin/rector --dry-run",
            "vendor/bin/pint --test"
        ],

        "lint:fix": [
            "vendor/bin/rector",
            "vendor/bin/pint"
        ],

        "test": "php artisan test --compact",

        "test:types": "vendor/bin/phpstan analyse --memory-limit=512M",

        "quality": [
            "@lint:fix",
            "@test:types",
            "@test"
        ],

        "post-autoload-dump": [
            "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
            "@php artisan package:discover --ansi",
            "@php -r \"file_exists('.hooks/pre-commit') && shell_exec('git config core.hooksPath .hooks');\"",
            "@php -r \"file_exists('.hooks/pre-commit') && shell_exec('chmod +x .hooks/pre-commit');\""
        ]
    }
}

What Each Script Does

composer setup

First-time project setup, idempotent:

"setup": [
    "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"",
    "@php artisan key:generate --ansi",
    "@php artisan migrate --ansi",
    "@php artisan db:seed --ansi",
    "npm install",
    "npm run build"
]

The file_exists('.env') || copy(...) line only copies .env.example if .env doesn’t already exist – so running composer setup again after setup won’t overwrite customized env values.

A new developer runs composer install && composer setup and they’re done.

composer dev

Start everything needed for local development simultaneously:

"dev": [
    "Composer\\Config::disableProcessTimeout",
    "@php artisan dev"
]

// Laravel 12 and earlier: run them together and stop them together
"dev": [
    "Composer\\Config::disableProcessTimeout",
    "npx concurrently \"php artisan serve\" \"php artisan queue:listen --tries=1\" \"php artisan pail --timeout=0\" \"npm run dev\" --names=server,queue,logs,vite --kill-others"
]

disableProcessTimeout prevents Composer from killing long-running commands. The & version this post used to show left the server, queue worker and Pail running after Ctrl+C (background jobs ignore it). php artisan dev (Laravel 13) and concurrently --kill-others start everything together and stop everything together.

One command replaces:

  • Terminal 1: php artisan serve
  • Terminal 2: php artisan queue:listen
  • Terminal 3: php artisan pail
  • Terminal 4: npm run dev

composer lint / composer lint:fix

Check vs. fix:

"lint":     ["vendor/bin/rector --dry-run", "vendor/bin/pint --test"],
"lint:fix": ["vendor/bin/rector",           "vendor/bin/pint"]

lint is for CI – it fails if anything needs changing, without making changes. lint:fix is for local development – it makes the changes.

composer test:types

Static analysis only:

"test:types": "vendor/bin/phpstan analyse --memory-limit=512M"

composer quality

Before pushing a PR:

"quality": [
    "@lint:fix",
    "@test:types",
    "@test"
]

Runs: Rector → Pint → Larastan → Tests. If all pass, you’re ready to push.

post-autoload-dump – Auto-Installing Git Hooks

Every composer install or composer update triggers post-autoload-dump. Use it to auto-install the git pre-commit hook:

"post-autoload-dump": [
    "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
    "@php artisan package:discover --ansi",
    "@php -r \"file_exists('.hooks/pre-commit') && shell_exec('git config core.hooksPath .hooks');\"",
    "@php -r \"file_exists('.hooks/pre-commit') && shell_exec('chmod +x .hooks/pre-commit');\""
]

This means:

  1. Developer runs composer install
  2. Git hooks path is automatically configured to .hooks/
  3. Pre-commit hook is made executable
  4. Every subsequent git commit automatically runs the quality checks

Developers never manually install the hooks. They can’t forget.

The README That Ships With This Setup

With these scripts in place, your README’s “Getting Started” section becomes:

## Getting Started

```bash
composer install
composer setup

That’s it. For local development:

composer dev

Before pushing:

composer quality

Four lines of README instead of six manual steps.

FAQs

What are Composer scripts in Laravel?

Named command bundles in composer.json – composer setup can install, copy .env, key:generate, migrate, seed, and build in one run. Full script below.

Composer scripts vs Makefile?

Composer scripts win for PHP projects: no extra tooling, visible in composer.json, work identically on every OS with PHP.

Key Takeaways

  • composer setup wraps all first-time setup into one command. Use file_exists() checks to make it idempotent.
  • composer dev starts the HTTP server, queue worker, log viewer, and Vite together. One command replaces four terminals.
  • composer lint checks only; composer lint:fix fixes. Match CI vs. local workflows.
  • composer quality = lint:fix + static analysis + tests. The pre-push command.
  • post-autoload-dump auto-installs git hooks on every composer install — developers can’t forget to set them up.

Tips and Gotchas

⚠️ Warning: The composer dev script used to rely on &, which needs a POSIX shell and leaves processes behind. php artisan dev and npx concurrently don’t depend on that shell syntax.

💡 Tip: Add composer setup to your onboarding documentation as the only step after composer install. If composer setup fails, that’s a bug in your setup script – not a developer problem. Fix the script.

🔥 Expert Note: The post-autoload-dump hook runs on every composer install, composer update, and composer require. This means your git hooks are reinstalled every time someone adds a package. New team members who run composer install after cloning the repo automatically get the hooks – no manual step, no opportunity to skip it.

Further Reading

← Slow Query | Next: Git Hooks →

📰 Read the original article on Dev.to WebDev

Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.