adaiasmagdiel / erlenmeyer-starter
Starter template for web applications with Erlenmeyer and related packages
Package info
github.com/AdaiasMagdiel/erlenmeyer-starter
Type:project
pkg:composer/adaiasmagdiel/erlenmeyer-starter
Requires
- php: ^8.2
- adaiasmagdiel/erlenmeyer: ^6.0
- adaiasmagdiel/fullcrawl: ^1.2
- vlucas/phpdotenv: ^5.6
README
A starter template for adaiasmagdiel/erlenmeyer with batteries included: Vue 3, Tailwind CSS, Sonner.js, FullCrawl migrations, and phpdotenv.
Stack
| Layer | Tech |
|---|---|
| Framework | Erlenmeyer v6 |
| Migrations | FullCrawl v1.2 |
| Env vars | vlucas/phpdotenv v5 |
| Frontend | Vue 3 (CDN) |
| Styling | Tailwind CSS v3 (CDN) |
| Toasts | Sonner.js (CDN) |
Requirements
- PHP ^8.2 with
jsonandmbstringextensions - Composer
- Apache with
mod_rewriteenabled - MySQL / MariaDB (or PostgreSQL / SQLite)
Getting Started
Erlenmeyer Starter is published on Packagist,
so the fastest way to start a new project is composer create-project:
# 1. Scaffold a new project composer create-project adaiasmagdiel/erlenmeyer-starter my-app cd my-app # .env is created automatically from .env.example — edit it with your database credentials # 2. Run migrations (if any) php fullcrawl.php # 3. Serve php -S localhost:8000
Alternatively, clone the repo directly:
git clone https://github.com/adaiasmagdiel/erlenmeyer-starter.git my-app cd my-app composer install cp .env.example .env # Edit .env with your database credentials php fullcrawl.php php -S localhost:8000
For production, point your Apache virtual host to the project root.
mod_rewritemust be enabled — all requests are routed throughindex.php.
Project Structure
erlenmeyer-starter/
├── app/
│ ├── Controllers/
│ │ └── Site.php # Example controller
│ ├── Database.php # Lazy PDO singleton registry
│ └── helpers.php # t(), e(), isDev(), env()
├── templates/
│ ├── layouts/
│ │ └── dashboard.php # Main HTML layout
│ ├── dashboard/
│ │ └── index.php # Example page with Vue component
│ ├── site/
│ │ └── index.php # Welcome page
│ ├── 404.php
│ └── 500.php
├── routes/
│ ├── index.php # Route definitions
│ └── error_handler.php # 404 / exception handlers
├── bootstrap.php # Env & DB initialization
├── index.php # Entry point: boots App, loads routes, runs it
├── fullcrawl.php # Migration config
├── .env.example
└── .htaccess
Adding Routes
index.php only boots the App and delegates to routes/index.php, which registers the
404/exception handlers (routes/error_handler.php) and every route:
$app->get('/', [Site::class, 'index']); $app->post('/users', [Users::class, 'store']);
Group related routes under a shared prefix (and, optionally, middlewares) with group() --
see the /api example already in routes/index.php:
$app->group('/api', function () use ($app) { $app->get('/health', fn($req, $res) => $res->withJson(['status' => 'ok'])); }, [$authMiddleware]); // middlewares array is optional
Creating Pages
Pages use output buffering to separate body and script from the layout:
<?php ob_start(); ?> <template id="tpl-page"> <main> <p>Hello, {{ store.user.name }}</p> </main> </template> <?php $body = ob_get_clean(); ?> <?php ob_start(); ?> <script> window.__APP.component('page', { template: '#tpl-page', setup() { const store = Vue.inject('store'); return { store }; } }); </script> <?php $script = ob_get_clean(); ?> <?php $page = ['body' => $body, 'script' => $script, 'info' => ['title' => 'My Page']]; include t('layouts/dashboard');
Global State
A reactive Vue store is available via inject('store') in any component:
// provided in layouts/dashboard.php const store = Vue.reactive({ user: { name: '...', role: '...' } }); __APP.provide('store', store);
Helpers
| Helper | Description |
|---|---|
t(string $path) |
Resolves a template path to absolute path |
e(?string $value) |
HTML-escapes a string (XSS prevention) |
isDev(): bool |
Returns true when ENV=development |
env(string $key, mixed $default = null) |
Gets an environment variable — checks $_ENV first, then falls back to getenv() |
Database
Database is a lazy singleton registry for PDO connections. Register a connection's
config once (typically in bootstrap.php) and it only actually connects the first time
it's used:
Database::configure('default', 'mysql', [ 'host' => env('DB_HOST'), 'port' => env('DB_PORT'), 'username' => env('DB_USERNAME'), 'password' => env('DB_PASSWORD'), 'database' => env('DB_DATABASE'), ]); // ... later, wherever you need it — connects on first call, reused after that: $pdo = Database::getConn(); // key defaults to 'default'
Supports MySQL, MariaDB, PostgreSQL, and SQLite, and multiple named connections
(Database::configure('reporting', ...), Database::getConn('reporting')) side by side.
License
GPL v3 — see LICENSE for details.