# Commands

Current stack: **Laravel 10.50.2** · **PHP 8.2 (php:8.2-apache)** · **Composer 2** · **Voyager 1.7** · **MySQL 8.0** · **phpMyAdmin** · app container `fff_laravel_voyager` · db container `fff_laravel_voyager_db` · phpMyAdmin container `fff_laravel_voyager_phpmyadmin`.

For a from-scratch setup on a new machine, see [SETUP.md](SETUP.md).

**After editing `.env`:** run `docker compose up -d fff_laravel_voyager`, not `docker compose restart`. The app service uses `env_file: .env`, so its values are baked into the container at creation time — `restart` reuses the existing container (stale values), `up -d` recreates it with the current `.env` contents.

## Docker Lifecycle

### Build

```
# Build the image
docker compose build

# Force a clean rebuild, e.g. after changing PHP extensions
docker compose build --no-cache
```

### Start / Stop / Restart

```
# Start the containers in the background (app + db + phpmyadmin)
docker compose up -d

# Stop the containers (keeps them, doesn't remove)
docker compose stop

# Restart
docker compose restart

# Stop and remove the containers (keeps images/volumes)
docker compose down

# Stop and remove the containers AND the named volumes (wipes vendor/ cache volume + MySQL data)
docker compose down -v

# Reset ONLY the MySQL data volume (e.g. after changing DB_DATABASE/DB_USERNAME/DB_PASSWORD
# in .env post-init — MySQL only applies those on first init). Keeps the vendor/ volume intact.
# Destructive: deletes all database data.
docker compose down
docker volume rm fff_laravel_voyager_db_data
docker compose up -d
```

### Status & Logs

```
# View container status
docker compose ps

# Follow logs (all services)
docker compose logs -f

# Follow logs for one service (service name, not container name)
docker compose logs -f fff_laravel_voyager
docker compose logs -f db
docker compose logs -f phpmyadmin

# Follow Laravel's own log file (blank page / 500 error with nothing useful in compose logs)
docker exec -it fff_laravel_voyager tail -f storage/logs/laravel.log
```

### Shell Access

```
# Open a shell inside the running app container
docker exec -it fff_laravel_voyager bash

# Open a MySQL shell inside the db container
docker exec -it fff_laravel_voyager_db mysql -uroot -proot fff
```

## Composer (inside the container)

```
docker exec -it fff_laravel_voyager composer install
docker exec -it fff_laravel_voyager composer update
docker exec -it fff_laravel_voyager composer require <package>
docker exec -it fff_laravel_voyager composer dump-autoload
docker exec -it fff_laravel_voyager composer dump-autoload --optimize
docker exec -it fff_laravel_voyager composer validate
docker exec -it fff_laravel_voyager composer check-platform-reqs
```

## phpMyAdmin

Browse/manage the database visually at `http://localhost:8081`. It logs in automatically using `DB_USERNAME`/`DB_PASSWORD` from `.env` (`root`/`root` by default) via the `PMA_USER`/`PMA_PASSWORD` env vars in `docker-compose.yml`.

```
# View phpMyAdmin logs
docker compose logs -f fff_laravel_voyager_phpmyadmin

# Restart just phpMyAdmin
docker compose restart phpmyadmin
```

## Database Migrations

### Migrate

```
# Run any new migrations
docker exec -it fff_laravel_voyager php artisan migrate

# Preview pending migrations without running them
docker exec -it fff_laravel_voyager php artisan migrate --pretend
```

### Seed

`database/seeders/DatabaseSeeder.php` calls, in order:
- `VoyagerDatabaseSeeder` — Voyager's core data: BREAD data types, menus, roles, permissions, settings. Without this, the admin panel has no roles/permissions/menu and looks broken even though migrations ran fine.
- `VoyagerDummyDatabaseSeeder` — Voyager's generic demo content: sample categories/posts/pages, and (via its `UsersTableSeeder`) a default admin login, **`admin@admin.com` / `password`**.
- The app's own public-site content seeders — `CustomSettingsTableSeeder`, `CustomMenusTableSeeder`, `BlogSeeder`, `NewsSeeder`, `JobSeeder`/`SampleJobsSeeder`, one seeder per CMS page (about, careers, our-technology, agroforestry, privacy policy, etc.), plus `TeamSeeder`, `PartnerSeeder`, `ProgramSeeder`, `ProjectSeeder`, and `ProjectMetricSeeder`. Without these, the public site (homepage, blog, programs, projects, careers, team, news, CMS pages) loads with empty or missing content even though migrations, `voyager:install`, and the Voyager seeders above all succeeded.

```
# Run DatabaseSeeder (Voyager core data + demo admin login + all public-site content)
docker exec -it fff_laravel_voyager php artisan db:seed

# Run one specific seeder class
docker exec -it fff_laravel_voyager php artisan db:seed --class=SomeSeeder
```

**Security note:** `admin@admin.com` / `password` is Voyager's well-known default seeded login — fine for local dev, but change the password (or delete the account and create your own via `voyager:admin`, below) before this app is ever exposed outside your machine.

### Fresh (destructive — drops all tables first)

```
# Drop every table and re-run all migrations
docker exec -it fff_laravel_voyager php artisan migrate:fresh

# Drop, re-migrate, and reseed Voyager's core data + demo admin login in one step
docker exec -it fff_laravel_voyager php artisan migrate:fresh --seed
```

**Note:** `migrate:fresh --seed` restores Voyager's core data (roles/permissions/menus/settings), the default demo login (`admin@admin.com` / `password`), and all of the app's public-site content via `DatabaseSeeder`. It won't re-wire `routes/web.php`, the `User` model, or the storage symlink if those were ever reverted — if you need those too, re-run `php artisan voyager:install`.

## Voyager Admin Panel

```
# Full install: publishes migrations/seeders/config, runs migrations, wires
# Voyager::routes() into routes/web.php, sets App\Models\User to extend
# TCG\Voyager\Models\User, creates the public/storage symlink.
docker exec -it fff_laravel_voyager php artisan voyager:install

# Same, plus demo posts/pages/categories/dummy content
docker exec -it fff_laravel_voyager php artisan voyager:install --with-dummy

# Create (or promote) an admin user
docker exec -it fff_laravel_voyager php artisan voyager:admin your@email.com --create

# Publish Voyager's controllers into app/Http/Controllers/Voyager for customization
docker exec -it fff_laravel_voyager php artisan voyager:controllers

# List all registered Voyager routes
docker exec -it fff_laravel_voyager php artisan route:list --path=admin
```

Admin panel: `http://localhost:8080/admin`

## Cache / Clear Commands

```
docker exec -it fff_laravel_voyager php artisan view:clear
docker exec -it fff_laravel_voyager php artisan cache:clear
docker exec -it fff_laravel_voyager php artisan config:clear
docker exec -it fff_laravel_voyager php artisan route:clear
docker exec -it fff_laravel_voyager php artisan optimize:clear
```

## Artisan Generators

```
docker exec -it fff_laravel_voyager php artisan make:model Post
docker exec -it fff_laravel_voyager php artisan make:controller PostController
docker exec -it fff_laravel_voyager php artisan make:migration create_posts_table
```

## Application Key & Info

```
docker exec -it fff_laravel_voyager php artisan key:generate
docker exec -it fff_laravel_voyager php artisan --version
docker exec -it fff_laravel_voyager php artisan about
docker exec -it fff_laravel_voyager php artisan route:list
```

## Docker Cleanup

**Warning:** these are machine-wide, not scoped to this project — they stop/delete *every* Docker container and wipe *every* unused image, volume, network, and build cache on your system. If you only want to reset this project, use `docker compose down -v` instead (see Docker Lifecycle above).

Stop everything running, then wipe all containers, images, volumes, and build cache:

```
docker stop $(docker ps -aq)
docker system prune -a --volumes -f
```

## Storage Symlink

Create the `public/storage` → `storage/app/public` symbolic link. Laravel/Voyager use this link so files uploaded through the admin panel's Media Manager (or BREAD image fields) can be accessed from the browser. `voyager:install` creates this automatically.

### Create / Recreate

```
docker exec -it fff_laravel_voyager rm -rf public/storage
docker exec -it fff_laravel_voyager php artisan storage:link
docker exec -it fff_laravel_voyager ls -la public
```

**Note:** running `artisan storage:link` from *inside* the container creates an **absolute** symlink using the container's internal path (`/var/www/html/storage/app/public`). That resolves fine as long as you access it from inside the container (or another container sharing the same mount path) — but it will **not** resolve on the host filesystem directly.

### Host-Resolvable Relative Symlink

Only needed if you must resolve `public/storage` directly from the host (outside any container) — run these on the host, not inside the container:

```
rm public/storage
ln -s ../storage/app/public public/storage
```

## Permissions

```
docker exec -it fff_laravel_voyager chown -R www-data:www-data storage bootstrap/cache
docker exec -it fff_laravel_voyager chmod -R 775 storage bootstrap/cache
```
