Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Folio Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require laravel/folio
    php artisan folio:install
    

    This creates a pages table, publishes migrations, and sets up a default routes/folio.php file.

  2. Define a Page: Create a migration for a pages table (or use the published one) and add a record:

    // database/migrations/xxxx_create_pages_table.php
    Schema::create('pages', function (Blueprint $table) {
        $table->id();
        $table->string('slug')->unique();
        $table->string('path')->nullable();
        $table->text('view');
        $table->timestamps();
    });
    

    Insert a test page:

    INSERT INTO pages (slug, path, view) VALUES ('about', 'about', 'pages.about');
    
  3. First Route: In routes/folio.php, define the route:

    Route::get('/', 'pages.home');
    Route::folio('pages/{page}', 'pages.show')->name('pages.show');
    

    Create a view at resources/views/pages/show.blade.php.

  4. Access the Page: Visit /about to see the page rendered. Folio automatically resolves the route based on the slug and view fields.


First Use Case: Dynamic Marketing Pages

Create a CMS-like system for marketing pages:

  1. Model:

    php artisan make:model Page -m
    

    Update the migration to include slug, path, and view.

  2. Controller:

    namespace App\Http\Controllers;
    use App\Models\Page;
    
    class PageController extends Controller
    {
        public function show(Page $page)
        {
            return view($page->view, ['page' => $page]);
        }
    }
    
  3. Route:

    Route::folio('pages/{page}', [PageController::class, 'show'])->name('pages.show');
    
  4. View:

    <!-- resources/views/pages/show.blade.php -->
    <h1>{{ $page->title }}</h1>
    {!! $page->content !!}
    
  5. Admin Panel: Use Laravel Nova or a custom form to manage pages records.


Key Commands

Command Description
php artisan folio:install Publishes migrations and config.
php artisan folio:list Lists all registered Folio routes.
php artisan folio:make Generates a new Folio route (e.g., php artisan folio:make about).

Implementation Patterns

1. Database-Driven Routing

Folio replaces static route definitions with dynamic ones tied to a database table. This is ideal for:

  • Content-Heavy Applications: Marketing sites, blogs, or internal wikis.
  • Multi-Tenant Systems: Route paths per tenant using middleware or domain-based routing.

Example: Multi-Language Support

// routes/folio.php
Route::domain('{locale}.example.com')->folio('pages/{page}', [PageController::class, 'show'])
    ->middleware('locale');

Configure middleware to set the app locale:

// app/Http/Middleware/SetLocale.php
public function handle(Request $request, Closure $next)
{
    $locale = $request->domain();
    app()->setLocale($locale);
    return $next($request);
}

2. View Resolution

Folio automatically resolves views based on the view field in the database. Use this pattern:

  • Nested Views: Store views like pages.blog.post for a post at /blog/{slug}.

    Route::folio('blog/{post}', [PostController::class, 'show'])
        ->where('post', '.*'); // Wildcard for dynamic slugs
    
  • Fallback Views: Use a default view if view is null:

    Route::folio('fallback', function () {
        return view('pages.default');
    });
    

3. Route Naming and Helpers

Folio integrates with Laravel’s route helpers (route(), back(), etc.) and adds its own:

  • Named Routes:

    Route::folio('contact', [ContactController::class, 'show'])->name('contact');
    

    Generate URLs:

    route('contact'); // /contact
    
  • Route Testing: Use routeIs() in Blade:

    <a href="{{ route('contact') }}" class="{{ request()->routeIs('contact') ? 'active' : '' }}">
        Contact
    </a>
    

4. Middleware Integration

Apply middleware to Folio routes:

Route::folio('admin/{page}', [AdminPageController::class, 'show'])
    ->middleware(['auth', 'verified']);

Use terminable middleware for Folio-specific logic:

// app/Http/Middleware/LogFolioAccess.php
public function terminate(Request $request, $response)
{
    if ($request->routeIs('folio.*')) {
        Log::info('Folio route accessed: '.$request->path());
    }
}

5. Wildcard Directories

Folio supports wildcard directories for modular routing:

resources/
  views/
    pages/
      blog/
        post.blade.php
      about.blade.php

Define routes in routes/folio.php:

Route::folio('pages/{page}', [PageController::class, 'show'])
    ->where('page', '^(?!index$).+$'); // Exclude 'index' slugs

6. URL Generation

Generate URLs dynamically from a Page model:

// app/Models/Page.php
public function getUrlAttribute()
{
    return route('pages.show', $this->slug);
}

Usage:

<a href="{{ $page->url }}">{{ $page->title }}</a>

7. Testing Folio Routes

Use Laravel’s testing helpers:

public function test_folio_route()
{
    $page = Page::factory()->create(['slug' => 'test', 'view' => 'pages.test']);

    $response = $this->get('/test');
    $response->assertViewIs('pages.test');
}

Test route names:

$this->assertRouteIs('pages.show', '/test');

Gotchas and Tips

Pitfalls

  1. Route Caching Conflicts: Folio routes are cached with route:cache. Clear the cache after adding new routes:

    php artisan route:clear
    php artisan folio:list  # Verify routes are registered
    
  2. Slug Collisions: If two pages share the same slug but different path, Folio prioritizes the first match. Use path to disambiguate:

    -- Page 1: /about (highest priority)
    INSERT INTO pages (slug, path, view) VALUES ('about', 'about', 'pages.about');
    
    -- Page 2: /team/about (lower priority)
    INSERT INTO pages (slug, path, view) VALUES ('about', 'team/about', 'pages.team.about');
    
  3. Wildcard Overreach: Avoid overly broad wildcards (e.g., .*) in where() clauses. Test edge cases:

    // Bad: Matches everything, including non-existent paths
    Route::folio('{page}', [PageController::class, 'show'])->where('page', '.*');
    
    // Good: Explicit pattern
    Route::folio('{page}', [PageController::class, 'show'])->where('page', '[a-z0-9\-]+');
    
  4. View Not Found: If Folio returns a 404 for a valid slug, check:

    • The view field exists in the database.
    • The view file exists at resources/views/{view}.blade.php.
    • No typos in the view path (e.g., pages.about vs. page.about).
  5. Middleware Order: Folio middleware runs after global middleware. Ensure critical middleware (e.g., auth) is applied directly to Folio routes:

    Route::folio('admin/{page}', [AdminController::class, 'show'])
        ->middleware('auth:support'); // Runs after global middleware
    

Debugging Tips

  1. List Routes:

    php artisan folio:list
    

    Output:

    +--------+-----------+----------------+---------------------+---------------------+
    | Domain | Method    | URI            | Controller          | Route Name          |
    +--------+-----------+----------------+---------------------+---------------------+
    | null   | GET|HEAD  | pages/{page}   | App\Http\Controllers\PageController@show | pages.show |
    +--------+-----------+----------------+---------------------+---------------------+
    
  2. Check Route Resolution: Use dd() in a middleware to inspect the resolved route:

    public function handle(Request $request,
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
calmfox/watch-sylius
damienfern/grpc-symfony-bundle
atoolo/index-bundle
atoolo/genai-bundle
coprotoai/laravel-ticket
davidjln/llm-carbon-bundle
cryonighter/valid-request-bundle
coolms/taxonomy-bundle
coolms/field-bundle
articulate-orm/symfony
aaix/laravel-tall-architect
ephoto/akeneo-connector
emmanuelballery/eb-plantumlbundle
emielburgman/symfony-visitor-beacon
emielburgman/symfony-visit-storage
emielburgman/symfony-security-headers
emielburgman/symfony-log-viewer
emarref/xdebug-bundle
emarref/pubnub-bundle
elriseio/finance-money-bundle