LibxaFrame is a PHP framework that looks a great deal like Laravel and differs in enough places to catch you out. The differences are the useful thing to know about it, so they are listed plainly below.
Installing
composer create-project libxa/libxa my-app
Or with the installer, which does the rest of the setup too:
libxa new my-app
The layout
src/
app/ controllers, middleware, requests, models
bootstrap/ the container is built here
config/ app, auth, cache, database, mail, queue, session
database/migrations/
public/ DOCUMENT ROOT: index.php, .htaccess, build/
resources/views/
routes/web.php
storage/ logs, cache, compiled views, sessions
libxa the CLI
Note src/app/ and src/public/, not app/ and public/. resource_path()
resolves to src/resources, and a top-level resources/ directory is read by
nothing.
Routing
$router->get('/posts/{slug}', [PostController::class, 'show'])
->where('slug', '[a-z0-9-]+');
$router->group(['middleware' => 'auth'], function (Router $router) {
$router->get('/dashboard', [DashboardController::class, 'index']);
});
Routes match in registration order, with no specificity sorting. A parameter registered before a literal swallows it:
$router->get('/posts/{slug}', ...);
$router->get('/posts/new', ...); // unreachable
Parameters stop at a slash, so a catch-all needs saying so:
$router->get('/{path}', [PageController::class, 'notFound'])->where('path', '.*');
Controllers
Plain classes with no base to extend.
class PostController
{
public function index(): Response
{
return view('posts.index', ['posts' => Post::all()]);
}
}
Response has getStatus() and getContent(). Not getStatusCode():
that is Symfony's name and it does not exist here.
Blade
Mostly what you expect, with two differences that bite.
@extends takes exactly one argument. A second is a parse error, not an
ignored argument, and it surfaces as a syntax error inside a compiled file
nowhere near the template you wrote.
{{ }} is inert inside a @php block: there you are writing PHP, so the
braces are just characters in a string.
A compiled view is served until it is recompiled, including one containing a
parse error, so php libxa view:clear is worth doing reflexively whenever a
change is not appearing.
Atlas
DB::table('posts')->where('published', true)->orderByLatest()->get();
DB::table('posts')->find($id);
Rows come back as stdClass, not arrays.
A migration whose class name does not match its filename is skipped without an error. When a column never appears, check that pairing first.
Console
php libxa serve
php libxa migrate
php libxa make:controller Foo
php libxa route:cache
php libxa view:clear
php libxa key:generate
Run php libxa list for the rest.
Deploying
Point the document root at src/public/ and send anything that is not a
real file to index.php.
Locally php libxa serve passes src/public/router.php to PHP's built-in
server, which emulates that rewrite, so a missing rule only shows up once
deployed: the home page works and every other route 404s. The starter kit ships
the Apache rules; for nginx it is
try_files $uri $uri/ /index.php?$query_string;.
Subdirectory installs do not work. Routes match on the full request path,
so a request for /my-app/about is looked up as /my-app/about and matches
nothing. Use a subdomain or a virtual host.