laravel/folio
Laravel Folio is a page-based router for Laravel that lets you define routes by creating files, keeping routing simple and organized. Ideal for building pages quickly with less boilerplate, backed by official Laravel documentation and support.
Installation:
composer require laravel/folio
php artisan folio:install
This creates a folio_pages table and publishes the migration.
Define a Page: Create a page record via Tinker or a seeder:
use App\Models\Folio\Page;
Page::create([
'path' => 'about',
'view' => 'pages.about',
'title' => 'About Us',
]);
First Route:
Access /about in your browser—Folio automatically maps the path to the view.
Key Files:
database/migrations/..._create_folio_pages_table.phpapp/Models/Folio/Page.php (auto-generated)routes/folio.php (auto-generated, mount with Folio::routes()).Replace a manual route like:
Route::get('/about', fn () => view('pages.about'));
With a database-driven approach:
folio_pages with path = 'about' and view = 'pages.about'.routes/web.php:
\Folio::routes();
/about—no route definitions needed.Define Pages:
Use the folio:make command to scaffold a page model/view:
php artisan folio:make about --view=pages.about
This creates a Page record and corresponding Blade view.
Dynamic Paths:
Use {slug} in path for dynamic routes (e.g., path = 'posts/{slug}'):
Page::create([
'path' => 'posts/{slug}',
'view' => 'posts.show',
]);
Access via /posts/laravel-folio-intro.
Route Parameters:
Bind parameters in views using $page (auto-injected):
<h1>{{ $page->title }}</h1>
<p>{{ $page->content }}</p>
Middleware:
Assign middleware to pages via the middleware column (e.g., ['auth', 'verified']).
With Eloquent:
Extend the Page model to add custom logic:
namespace App\Models\Folio;
use Illuminate\Database\Eloquent\Model;
use Folio\Folio;
class Page extends Model
{
public function getRouteKeyName()
{
return 'path';
}
public function url()
{
return route(Folio::name($this));
}
}
Custom Views:
Override the default view resolution by implementing a resolveView method in your Page model:
protected function resolveView()
{
return view('custom.' . $this->view);
}
API Endpoints: Use Folio for API routes by returning JSON in views:
{{ json_encode(['data' => $page->toArray()]) }}
Or create a dedicated API controller that queries folio_pages.
Multi-Language Support: Use domains or subpaths for locales:
Page::create([
'path' => 'es/about',
'view' => 'pages.es.about',
'domain' => 'es.example.com',
]);
Page Versioning:
Add a versions table and use middleware to serve A/B variants:
Page::with(['versions' => function ($query) {
$query->where('variant', session('ab_test'));
}])->find($path);
Caching: Cache page views with tags:
Cache::tags(['folio-page-' . $page->id])->put($page->path, $view, now()->addHours(1));
Admin Integration: Use Laravel Nova to manage pages:
// In NovaServiceProvider
Nova::resources([
\App\Nova\Folio\Page::class,
]);
Custom Route Generation: Extend Folio’s URL generation:
use Folio\Folio;
Folio::extend(function ($app) {
$app->bind('path', function () {
return 'custom-prefix';
});
});
Route Conflicts:
Route::group.routes/web.php:
\Folio::routes();
Route::get('/legacy', ...); // Legacy routes after Folio
Slug Resolution:
index may not resolve correctly.path = 'home' instead of path = 'index').Middleware Order:
app/Http/Kernel.php:
protected $middlewareGroups = [
'web' => [
// Global middleware...
\Folio\Http\Middleware\HandleFolioRequests::class,
],
];
View Discovery:
view column uses incorrect paths.view = 'pages.about' instead of view = 'about').List All Routes:
Use the folio:list command to debug route definitions:
php artisan folio:list
Output includes route names, paths, and middleware.
Check Route Binding: Verify route binding with:
dd(request()->route()->parameters);
Clear Cached Routes: If routes aren’t updating, clear the cache:
php artisan route:clear
php artisan cache:clear
Log View Resolution:
Add debug logs to the resolveView method to trace view paths:
protected function resolveView()
{
\Log::debug('Resolving view for path:', [$this->path, $this->view]);
return view($this->view);
}
Custom Table Names:
folio_pages table name may conflict with existing tables.config/folio.php:
'table' => 'custom_page_routes',
Domain Routing:
domain column is empty.folio_pages table or use middleware to infer domains.Lazy Loading:
folio:list may be slow on large datasets.path column:
Schema::table('folio_pages', function (Blueprint $table) {
$table->index('path');
});
Custom Page Models:
Extend the Folio\Folio facade to support custom models:
Folio::extend(function ($app) {
$app->singleton('folio.page.model', function () {
return \App\Models\CustomPage::class;
});
});
Event Listeners:
Listen for ViewMatched events to log or modify page requests:
use Folio\Events\ViewMatched;
ViewMatched::listen(function (ViewMatched $event) {
\Log::info('Page accessed:', [$event->page->path]);
});
Pipeline Extensions: Add custom pipeline steps for pre/post-processing:
Folio::pipeline(function ($next) {
return function ($page) use ($next) {
// Pre-process page
$page = $next($page);
// Post-process page
return $page;
};
});
Route Caching: Disable route caching for development:
Folio::disableRouteCaching();
Indexing: Add indexes to frequently queried columns:
Schema::table('folio_pages', function (Blueprint $table) {
$table->index('path');
$table->index('domain');
});
Eager Loading: Eager-load relationships to avoid N+1 queries:
Page::with(['versions', 'author'])->
How can I help you explore Laravel packages today?