Directory Structure
A freshly created Preflow project has this layout:
myapp/
├── app/
│ ├── Components/ Co-located PHP + template UI components
│ │ └── ExampleCard/
│ │ ├── ExampleCard.php
│ │ └── ExampleCard.twig
│ ├── Controllers/ Attribute-routed controllers
│ │ └── HealthController.php
│ ├── Models/ Data models with #[Entity] attributes
│ │ ├── Post.php
│ │ └── User.php
│ ├── Providers/ Service providers
│ │ └── AppServiceProvider.php
│ ├── Seeds/ Demo data seeders
│ └── pages/ File-based routes (Twig templates)
│ ├── _layout.twig
│ ├── index.twig
│ ├── about.twig
│ └── blog/
│ ├── index.twig
│ └── [slug].twig
├── config/
│ ├── app.php
│ ├── auth.php
│ ├── data.php
│ ├── i18n.php
│ ├── middleware.php
│ └── providers.php
├── lang/ Translation files
│ ├── en/
│ └── de/
├── migrations/ Database schema migrations
├── public/ Web root
│ ├── index.php
│ └── .htaccess
├── storage/
│ ├── cache/ Compiled templates and route cache
│ ├── data/ SQLite database and JSON data files
│ └── logs/ Application logs
├── tests/ PHPUnit tests
├── .env Environment variables
├── composer.json
└── preflow CLI entry point
app/Components/
Each component lives in its own directory containing a PHP class and a matching template. The class handles state and actions; the template handles presentation. CSS and JS can be co-located using {% apply css %} and {% apply js %} blocks inside the template.
app/Components/Counter/
├── Counter.php # Component class (resolveState, actions)
└── Counter.twig # Template (uses public properties as variables)
Use components in any template with {{ component('Counter', { count: 0 }) }}.
app/pages/
Templates here map directly to URLs. The directory structure defines the route hierarchy:
| File | URL |
|---|---|
index.twig |
/ |
about.twig |
/about |
blog/index.twig |
/blog |
blog/[slug].twig |
/blog/{slug} (dynamic) |
docs/[...path].twig |
/docs/* (catch-all) |
_layout.twig |
Not a route (underscore prefix) |
Files prefixed with _ are excluded from routing. _layout.twig serves as the base layout that page templates extend.
app/Controllers/
Controllers handle API endpoints and form submissions using PHP attribute routing:
#[Route('/api/posts')]
final class PostController
{
#[Get('/')]
public function index(): ResponseInterface { /* ... */ }
#[Post('/')]
public function create(): ResponseInterface { /* ... */ }
}
Controller routes resolve to action mode; page routes resolve to component mode. Both work side by side.
config/
Plain PHP files returning arrays. Loaded at boot time and accessible via dot notation (app.name, data.default). See the Configuration guide for details on each file.
public/
The web root directory. Only index.php and static assets live here. The entry point is three lines:
<?php
require __DIR__ . '/../vendor/autoload.php';
Preflow\Core\Application::run(__DIR__ . '/..');
Point your web server's document root here. Apache uses the included .htaccess; Nginx needs a try_files directive.
storage/
Runtime data that should not be committed to version control:
| Directory | Contents |
|---|---|
storage/cache/ |
Compiled Twig templates, route cache |
storage/data/ |
SQLite database file, JSON data files |
storage/logs/ |
Application log files |
Clear the cache with php preflow cache:clear.