Skip to content

Development Workflow ​

Environment Setup ​

Host prerequisites (DNSMasq for *.test, the shared proxy Docker network) are covered in the root README.md. Then:

bash
cp .env.example .env
composer install
npm install
./vendor/bin/sail up -d
./vendor/bin/sail artisan migrate --seed

Migrating seeds the full and essential plans (the plans migration runs PlanSeeder itself), and the seeded shops come from ShopFactory, which puts them on full. migrate:fresh --seed --seeder=DemoSeeder puts the demo shop on full as well.

Running the App ​

./vendor/bin/sail up -d starts everything: laravel.test (app), horizon (queue workers), pgsql, redis, meilisearch, mailpit, minio, chromium, selenium. The app is served through the nginx proxy — no artisan serve needed.

Only the asset pipeline runs on the host, through Vite+ (vp). Install it once with curl -fsSL https://vite.plus | bash; it never runs through Sail because node_modules is host-installed.

bash
vp install
vp dev

composer run dev (serve + queue:listen + pail + vite, all concurrent) exists for non-Sail/host-only setups. With Sail it duplicates the containers — prefer vp dev.

Useful endpoints: /horizon (queues), /pulse (app metrics), Mailpit on port 1025/8025, MinIO console for object storage.

Shop Plans & Modules ​

A shop created through onboarding (or by hand) has no plan, which turns every module off: no work orders, no calendar, no SMS, and most of the nav hidden. Assign one before using it:

bash
./vendor/bin/sail artisan shop:plan {slug} full                 # or essential
./vendor/bin/sail artisan shop:module {slug} {module} --enable   # --disable, --limit=N, --reset
./vendor/bin/sail artisan shop:modules {slug}                   # resolved modules, source and usage

See Plans & modules for the override rules.

Validation Pipeline (Before Commit / PR) ​

bash
./vendor/bin/sail up -d
./vendor/bin/sail pint --dirty
./vendor/bin/sail artisan test --parallel
vp check
vp run type-check
vp test
vp build

Run selective tests:

bash
./vendor/bin/sail artisan test --filter=WorkOrderControllerTest

Type Generation ​

A Vite watch plugin triggers composer run transform-types whenever app/{Data,Entities}/**/*.php changes. It runs typescript:transform and then generate:enum-registry, producing resources/js/types/generated.ts and resources/js/types/enumRegistry.ts (DO NOT edit either manually).

Manual run:

bash
composer run transform-types

Icon Generation ​

Add a StreamlineHQ SVG to resources/icons/. A second Vite watcher runs npm run generate:icons; run it manually if the dev server isn't up:

bash
npm run generate:icons

Scaffolding Commands ​

bash
./vendor/bin/sail artisan make:action ArchiveWorkOrder
./vendor/bin/sail artisan make:model Bay -mfs
./vendor/bin/sail artisan make:request CreateWorkOrderRequest
./vendor/bin/sail artisan make:test WorkOrderFeatureTest

Always include --no-interaction in automated scripts.

Queue & Horizon ​

Queued jobs run in the dedicated horizon container, which uses spatie/laravel-horizon-watcher to restart itself when a watched PHP file changes — workers never run stale code. Watched paths are in config/horizon-watcher.php.

bash
docker compose logs -f horizon      # follow worker output
docker compose restart horizon      # rarely needed

If restarts stop happening after an edit, confirm Docker Desktop is using VirtioFS (Settings → General → file sharing implementation) so file-system events reach the container.

Search (Meilisearch) ​

bash
./vendor/bin/sail artisan scout:sync-index-settings
./vendor/bin/sail artisan scout:import "App\Models\Car"
./vendor/bin/sail artisan scout:flush "App\Models\Car"

Tests run with SCOUT_DRIVER=null, so search never hits Meilisearch there.

WebSockets (Reverb) ​

Reverb is configured in config/reverb.php; the frontend connects through Echo + the Pusher protocol (@/Plugins/broadcasting.ts). The VITE_REVERB_* variables must match the server settings.

Environment Variables (Common) ​

VariablePurpose
APP_ENV, APP_URLEnvironment & base URL
DB_*PostgreSQL connection
REDIS_HOSTRedis connection (cache, queue, broadcast)
SCOUT_DRIVER, MEILISEARCH_HOST, MEILISEARCH_KEYSearch
REVERB_* / VITE_REVERB_*WebSocket server & client
SENTRY_DSN, VITE_SENTRY_LARAVEL_DSNError tracking (backend / frontend)
RABBITMQ_*, BUS_*Message bus to comms-service
COMMS_*Comms query endpoint, shared secret, number pricing
MAILGUN_*Outbound system email
FILESYSTEM_DISK, AWS_*Storage (MinIO locally)
PULSE_ENABLEDToggle app metrics collection

Debugging ​

  • Ray (spatie/laravel-ray) for structured debugging output; config in ray.php.
  • Tinker: ./vendor/bin/sail artisan tinker.
  • Pail: ./vendor/bin/sail artisan pail to stream logs.
  • Pulse (/pulse) surfaces slow queries, slow jobs, 4xx responses, outdated & vulnerable dependencies, and scheduled tasks.
  • laravel/boost exposes app info, DB schema, logs and docs search to AI tooling.

Rebuilding Assets ​

If frontend changes are not reflected:

bash
vp build
# or restart the dev server
vp dev

Rector Refactoring ​

On a dedicated branch:

bash
./vendor/bin/sail php vendor/bin/rector process

Review diffs carefully; rerun Pint afterward.

Importing / Exporting Data ​

Use spatie/simple-excel for CSV/XLSX operations in Actions or dedicated Services.

Adding New Dependencies ​

  • Composer: ./vendor/bin/sail composer require vendor/package.
  • NPM: npm install package (add --save-dev for tooling).
  • If the package's role isn't obvious from its name, add a line to dependencies.md.

Performance Checks ​

  • Profile queries with Pulse or Ray.
  • Identify slow endpoints via Sentry performance traces.
  • Optimize repeated queries with eager loading & caching.
  • For a slow test suite, run artisan test --profile first — seeding is usually the culprit, not the process count.

See testing.md for test strategy specifics.