not-empty/ala-microframework-php
Ala is a lightweight PHP microframework for building fast, minimal web apps and APIs. It offers routing, middleware, and a simple modern structure to help developers ship small PHP services quickly and cleanly.
Prerequisites. PHP 8.x and Composer, plus a database (check composer.json for the platform requirement before you commit — the framework tracks Lumen's PHP support, not Laravel's). If the repo ships a docker-compose.yml, use it for the first run so you don't burn an afternoon on extension mismatches.
Install. It is a project skeleton, not a drop-in library, so start from a create-project/clone rather than composer require:
composer create-project not-empty/ala-microframework-php product-service
cd product-service
cp .env.example .env
# set DB_*, APP_KEY, APP_DEBUG=true
php artisan migrate
php artisan serve # or the docker-compose up target
Where to look first, in this order:
README.md — the "Usage" section is where the generator workflow is actually documented, and it is the part that changes between releases.routes/ — shows how generated CRUD routes are mounted and under which prefix (/api/...).app/Console/Commands). This is the heart of the framework; reading it tells you more than the docs about naming conventions, table introspection, and what gets overwritten.tests/ — the generated unit/feature tests double as the best specification of the expected request/response contract.First use case. Pick one trivial table (products with id, name, price, created_at), run the generator against it, run the generated test suite, then hit the endpoints with curl:
php artisan ala:generate products # exact signature per README
php artisan migrate
php artisan test
curl -s localhost:8000/api/products | jq
If that round-trip works, you've validated the whole value proposition. Anything that fails here (routes not registered, tests hitting a real DB, dialect errors) will fail for every resource you add later, so fix it before generating a second table.
Before you go further: confirm the GPL-3.0 implications with whoever owns licensing. Internal/network-only services are generally fine; shipping the binary or source to customers is not without obligations.
Schema-first vertical slices. The intended workflow is: change the table, regenerate, run tests, ship. Treat the SQL/schema definition as the source of truth and the generated PHP as build output. Resist editing generated files in place — the moment you do, regeneration becomes a merge conflict.
Separate generated from owned code. The single most important pattern with any code generator:
app/Http/Controllers/Generated/ # regenerated, never edited
app/Http/Controllers/ # your overrides / custom endpoints
app/Services/ # business rules, called from both
Keep custom logic in services or in subclasses of the generated controllers. If the generator supports a --force/overwrite flag, keep it out of your habitual command line and only run it deliberately.
Extend the query-builder layer, don't fork it. These frameworks typically expose filtering/sorting/pagination through query-string conventions (?filter[status]=active&sort=-created_at&page=2). Learn the conventions from the generated feature tests, then add new filters by extending the base repository/controller rather than special-casing endpoints. This keeps every resource behaving identically, which is what makes a microservice fleet operable.
Tests as a contract, not as coverage. The generated suite is a regression harness for the scaffold. Put your business-rule tests in tests/Feature/Custom/ against the public API, and keep the generated tests untouched so you can re-run them after regeneration to prove the scaffold still behaves. Do not report the generated suite's coverage number as your service's coverage — it measures code nobody wrote.
Validation and column whitelisting. Query-builder CRUD has no Eloquent $fillable safety net. If the generator accepts arbitrary request bodies and maps them to columns, you have a mass-assignment hole by default. Add an explicit allowlist per resource (request class or a $writable array in the service) before exposing the endpoint beyond localhost.
Tenancy and auth at the middleware layer. Register auth/tenant-resolution middleware on the generated route group rather than inside controllers, and inject the tenant ID into the query builder in one place (a base repository scope). Lumen requires middleware to be wired in bootstrap/app.php, not an HTTP kernel — remember that when you add CORS, throttling, or JWT.
One service per bounded context. The framework rewards small, single-table-ish services. Share an ID scheme and an API gateway rather than sharing a database; cross-service reads should go through HTTP or a read model, not a second connection into someone else's schema.
CI. Run php artisan test (plus migrate:fresh against a disposable DB) for every generated resource in the pipeline, and add a lint step that fails if anyone has modified files under the generated directories — that's how you keep regeneration cheap.
AUTO_INCREMENT, ENUM, JSON columns). If you run tests on SQLite in CI, expect failures that don't reproduce locally; run CI against the same engine as production.APP_DEBUG=true and appear in storage/logs/lumen.log, not always in the HTTP response. env() calls outside config/ silently return null once you run php artisan config:cache — a classic "works locally, broken in the container" bug. php artisan route:list is your first check when a generated endpoint 404s.php artisan config:clear when debugging.config/ala.php-style settings.composer.lock resolution before trusting it, and never deploy dev-main to production.How can I help you explore Laravel packages today?