Development Guide¶
Prerequisites¶
- PHP 8.1
- Composer
- Node.js + npm
- MySQL
- Redis
- Octane-compatible server (Swoole or RoadRunner)
Local Setup¶
composer install
npm install
cp .env.example .env
php artisan key:generate
php artisan migrate
php artisan db:seed
Required .env values¶
# App
APP_URL=
APP_KEY=
# Database
DB_CONNECTION=mysql
DB_HOST=
DB_PORT=3306
DB_DATABASE=
DB_USERNAME=
DB_PASSWORD=
# Redis
REDIS_HOST=
REDIS_PASSWORD=
# Session
SESSION_DRIVER=redis
QUEUE_CONNECTION=redis
# Payment: SBPS (account IDs + keys for service and goods payment types)
SBPS_SERVICE_ACCOUNT_ID=
SBPS_SERVICE_SERVICE_KEY=
SBPS_SERVICE_SERVICE_SECRET=
SBPS_SERVICE_CONTINUOUS_SECRET=
SBPS_GOODS_ACCOUNT_ID=
SBPS_GOODS_SERVICE_KEY=
SBPS_GOODS_SERVICE_SECRET=
SBPS_PRODUCTION_URL=
SBPS_STAGING_URL=
SBPS_DEVELOPMENT_URL=
SBPS_PRODUCTION_URL_MODIFY=
SBPS_STAGING_URL_MODIFY=
SBPS_DEVELOPMENT_URL_MODIFY=
# Payment: Atone (production + test key pairs)
ATONE_API_KEY=
ATONE_API_SECRET=
ATONE_TEST_API_KEY=
ATONE_TEST_API_SECRET=
ATONE_API_ENDPOINT=
ATONE_TEST_API_ENDPOINT=
ATONE_SCRIPT_URL=
ATONE_TEST_SCRIPT_URL=
# Google Maps
GOOGLE_MAPS_API_KEY=
# Mail
MAIL_MAILER=
MAIL_HOST=
MAIL_PORT=
MAIL_USERNAME=
MAIL_PASSWORD=
# Chekicha (CKC platform)
CKC_SERVICE_DOMAIN=
CKC_TOKEN_HASH=
# JWT (guest cart identity)
JWT_SECRET=
# reCAPTCHA (registration, inquiry)
RECAPTCHA_SITE_KEY=
RECAPTCHA_SECRET_KEY=
# Storage: S3
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=
AWS_BUCKET=
# Sentry
SENTRY_LARAVEL_DSN=
Running the Application¶
# Development (frontend hot reload + Laravel server)
composer run dev
# or separately:
npm run dev
php artisan serve
# Production-like with Octane
php artisan octane:start
# Queue worker (required for notifications)
php artisan queue:work
# Scheduler (required for product publish, lottery, reporting)
php artisan schedule:run # or: php artisan schedule:work
Frontend Build¶
If you see a ViteException: Unable to locate file in Vite manifest error, run npm run build or npm run dev.
The frontend uses:
- Vue 3 + <script setup> SFCs
- Inertia.js v0 (@inertiajs/vue3)
- Ziggy for named routes in JS (import { route } from 'ziggy-js')
- Vite for bundling
Making Code Changes¶
Creating a new module action¶
Pattern: receive a DTO, perform a single operation, return result or throw.
Creating a Form Request¶
Check sibling requests in app/Http/Requests/ for whether the project uses array or string validation rules.
Database migrations¶
Data-only migrations go in database/migrations/data_migration/ (separate from schema migrations).
Running tests¶
php artisan test # all tests
php artisan test tests/Feature/SomeTest.php # specific file
php artisan test --filter=testMethodName # specific test
Current state: Only placeholder test scaffold exists. No real tests. See Architecture — Risk Signals for priority areas.
Linting / Formatting¶
Run Pint before committing.
Key Artisan Commands¶
php artisan queue:work # process jobs/notifications
php artisan schedule:work # run scheduler loop (dev)
php artisan octane:start # start Octane server
php artisan octane:reload # reload workers after code change (prod)
php artisan route:list # list all routes
php artisan tinker # REPL for Eloquent queries
Module Structure¶
Each module in app/Modules/{Name}/ follows this structure:
Actions/ — single-responsibility CQRS operations
DTOs/ — spatie/laravel-data typed input objects
Events/ — domain events
Filters/ — timedoor/laravel-filter query filters
Interfaces/ — contracts (bound in Providers/)
Jobs/ — queued or scheduled jobs
Listeners/ — event listeners
Notifications/ — email/notification classes
Policies/ — authorization policies
Providers/ — service provider + IoC bindings
QueryBuilders/ — custom Eloquent query builder extensions
Not all modules have all directories.
Git Workflow¶
Current branch: staging. Main branch: master.
Active development area: Lottery module (multiple recent PRs). SendLotteryWinnerEmailJob has uncommitted changes on staging — check before merging.