Your First Application
Build your first MonkeysLegion v2 application from scratch.
Table of Contents
- Prerequisites
- Installation
- Project Structure
- Your First Controller
- Returning JSON
- Returning HTML
- Running the Dev Server
- Environment Configuration
- Next Steps
Prerequisites
| Requirement | Version | Check |
|---|---|---|
| PHP | 8.4+ | php -v |
| Composer | 2.x | composer --version |
| MySQL | 8.4+ | mysql --version |
| Redis | 7+ | redis-cli ping |
Note: Redis is optional for development. File-based drivers work for cache, session, and queue.
Installation
1. Create the Project
composer create-project monkeyscloud/monkeyslegion-skeleton my-app
cd my-app
2. Configure Environment
cp .env.example .env
php vendor/bin/ml key:generate
Edit .env with your database credentials:
APP_NAME="My App"
APP_ENV=dev
APP_DEBUG=true
APP_URL=http://localhost:8000
APP_KEY= # Auto-filled by key:generate
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=my_app
DB_USERNAME=root
DB_PASSWORD=secret
3. Create the Database Schema
php vendor/bin/ml schema:update
4. Start the Server
composer serve
# → http://localhost:8000
Or use the simple dev server:
composer dev
# → http://localhost:8080
Project Structure
my-app/
├── app/ ← Your application code
│ ├── Controller/ ← HTTP controllers (auto-discovered)
│ │ └── Api/ ← API controllers
│ ├── Dto/ ← Request DTOs with validation
│ ├── Entity/ ← Database entities
│ ├── Enum/ ← PHP enums
│ ├── Event/ ← Domain events
│ ├── Job/ ← Queue jobs
│ ├── Listener/ ← Event listeners
│ ├── Middleware/ ← Custom middleware
│ ├── Policy/ ← Authorization policies
│ ├── Providers/ ← Service providers
│ ├── Repository/ ← Data access layer
│ ├── Resource/ ← API response transformers
│ └── Service/ ← Business logic
├── config/ ← .mlc configuration files
├── database/migrations/ ← SQL migration files
├── public/ ← Web root (index.php, assets)
├── resources/views/ ← Templates (.ml.php)
│ └── layouts/ ← Layout templates
├── tests/ ← PHPUnit tests
├── .env ← Environment variables
└── composer.json ← Dependencies
Key convention: Controllers in
app/Controller/are auto-discovered. No registration needed.
Your First Controller
Create app/Controller/HelloController.php:
<?php
declare(strict_types=1);
namespace App\Controller;
use MonkeysLegion\Router\Attributes\Route;
use MonkeysLegion\Http\Message\Response;
final class HelloController
{
#[Route('GET', '/hello', name: 'hello')]
public function index(): Response
{
return Response::text('Hello, MonkeysLegion!');
}
#[Route('GET', '/hello/{name}', name: 'hello.name')]
public function greet(string $name): Response
{
return Response::text("Hello, {$name}!");
}
}
Visit http://localhost:8000/hello — that's it. No route files, no registration.
Returning JSON
#[Route('GET', '/api/status', name: 'api.status')]
public function status(): Response
{
return Response::json([
'status' => 'ok',
'version' => '2.0',
'timestamp' => time(),
]);
}
With Status Codes
return Response::json(['data' => $user], 201); // Created
return Response::json(['error' => 'Not found'], 404); // Not Found
return Response::noContent(); // 204
Returning HTML
Option A: Inline HTML
#[Route('GET', '/about', name: 'about')]
public function about(): Response
{
return Response::html('<h1>About Us</h1><p>We build cool stuff.</p>');
}
Option B: Using Templates (Recommended)
Inject the Renderer and render a .ml.php template:
<?php
declare(strict_types=1);
namespace App\Controller;
use MonkeysLegion\Router\Attributes\Route;
use MonkeysLegion\Http\Message\Response;
use MonkeysLegion\Template\Renderer;
final class PageController
{
public function __construct(
private readonly Renderer $renderer,
) {}
#[Route('GET', '/about', name: 'about')]
public function about(): Response
{
return Response::html(
$this->renderer->render('pages.about', [
'title' => 'About Us',
'team' => ['Jorge', 'Marouane', 'Team'],
])
);
}
}
Create resources/views/pages/about.ml.php:
@extends('layouts.app')
@section('title', '{{ $title }}')
@section('content')
<h1>{{ $title }}</h1>
<h2>Our Team</h2>
<ul>
@foreach($team as $member)
<li>{{ $member }}</li>
@endforeach
</ul>
@endsection
Dot notation
pages.aboutmaps toresources/views/pages/about.ml.php.
Running the Dev Server
Hot-Reload (Recommended)
composer serve
This starts the dev server on port 8000 with file watching and automatic reload.
Simple Server
composer dev
Starts on port 8080 without hot-reload.
PHP Built-in Server
php -S localhost:8000 -t public server.php
Environment Configuration
MonkeysLegion uses two configuration systems:
1. .env File — Secrets & Environment
APP_KEY=base64:xxxx
DB_PASSWORD=secret
REDIS_HOST=127.0.0.1
2. .mlc Files — Application Config
Located in config/. Uses HOCON-like syntax with env variable interpolation:
# config/app.mlc
app {
name = ${APP_NAME:"My App"}
env = ${APP_ENV:production}
debug = ${APP_DEBUG:false}
url = ${APP_URL:http://localhost:8000}
key = ${APP_KEY}
timezone = "UTC"
}
Accessing Config Values
$config = $container->get(MlcConfig::class);
$appName = $config->get('app.name'); // "My App"
$debug = $config->get('app.debug', false); // With default
Next Steps
| Guide | What You'll Learn |
|---|---|
| Building a REST API → | Entity, Repository, Service, Controller, DTOs |
| Authentication & Authorization → | JWT, Login, 2FA, RBAC, Policies |
| Templates & Views → | Layouts, directives, components |
| Events, Queues & Background Jobs → | Event dispatch, listeners, async jobs |
| Middleware, Caching & Advanced Patterns → | Custom middleware, cache strategies, testing |
MonkeysLegion v2 — Built with 🐵 by MonkeysCloud Team