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

Laravel Markdownable Laravel Package

labrodev/laravel-markdownable

Laravel package to load Markdown pages with YAML front matter by slug, parse metadata, convert to HTML, and return a structured PageData object. Includes contracts for swapping implementations and a Markdownable facade for simple access.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require labrodev/laravel-markdownable
    

    No additional setup required—the service provider auto-registers.

  2. Define Content Path: Set the default Markdown directory in .env:

    MARKDOWNABLE_PAGES_PATH=resources/markdown
    

    Or publish the config and customize:

    php artisan vendor:publish --tag=markdownable-config
    
  3. Create a Markdown File: Place a file like about.md in resources/markdown with YAML front matter:

    ---
    title: About Us
    meta_description: Learn about our company
    ---
    # Welcome to Our Site
    
    This is **markdown** content.
    
  4. First Use Case: Fetch a page in a controller or Blade view:

    use Labrodev\Markdownable\Facades\Markdownable;
    
    $page = Markdownable::getPageBySlug('about');
    return view('pages.show', ['page' => $page]);
    

    Access fields like $page->title, $page->bodyHtml, or $page->toArray() for SEO.


Implementation Patterns

Core Workflows

  1. Reading Pages:

    • By Slug: Markdownable::getPageBySlug('slug')
    • List All: Markdownable::listPages() → returns Collection of PageData.
    • Custom Paths: Override the default path via config or facade:
      Markdownable::setPagesPath('custom/path');
      
  2. Integration with Blade: Pass PageData to views and use toArray() for SEO meta tags:

    <title>{{ $page->title }}</title>
    <meta name="description" content="{{ $page->meta_description }}">
    @foreach($page->toArray() as $key => $value)
        @if($key !== 'bodyHtml')
            <meta property="og:{{ $key }}" content="{{ $value }}">
        @endif
    @endforeach
    
  3. Dynamic Routing: Use Laravel’s route model binding with a custom resolver:

    Route::get('/{slug}', function (PageData $page) {
        return view('pages.show', compact('page'));
    })->name('pages.show');
    

    Register the resolver in AppServiceProvider:

    Route::bind('page', function ($slug) {
        return Markdownable::getPageBySlug($slug);
    });
    
  4. Extending PageData: Add custom fields to front matter and access them via PageData:

    ---
    title: Blog Post
    author: John Doe
    published_at: 2023-01-01
    ---
    

    Access in PHP:

    $page->author; // "John Doe"
    $page->published_at; // "2023-01-01"
    
  5. Caching: Cache PageData for performance (e.g., in AppServiceProvider):

    Markdownable::setCacheEnabled(true);
    Markdownable::setCacheTTL(60 * 60); // 1 hour
    

Gotchas and Tips

Pitfalls

  1. Case-Sensitive Slugs: Slugs in filenames are case-sensitive. Ensure consistency (e.g., about.md vs. About.md).

  2. Front Matter Validation: Invalid YAML in front matter will throw exceptions. Validate files locally before deployment:

    php artisan markdownable:validate
    

    (Note: This command may not exist; manually check files or use a linter like yamllint.)

  3. HTML Escaping: bodyHtml is raw HTML. Escape dynamically generated content to prevent XSS:

    {!! e($page->bodyHtml) !!}
    
  4. File Permissions: Ensure the Markdown directory is readable by Laravel:

    chmod -R 755 resources/markdown
    
  5. League CommonMark Dependencies: The package relies on league/commonmark. Conflicts may arise if other packages use different Markdown parsers. Prefer this package’s converter for consistency.


Debugging Tips

  1. Inspect PageData: Dump the object to verify parsed data:

    dd(Markdownable::getPageBySlug('about')->toArray());
    
  2. Check File Paths: Debug path resolution:

    $reader = app(MarkdownPageFileReaderContract::class);
    dd($reader->getPagePath('about')); // Should point to the correct .md file.
    
  3. Override Implementations: Swap contracts for testing or custom logic (e.g., mock FrontMatterParserContract):

    $this->app->bind(
        FrontMatterParserContract::class,
        CustomFrontMatterParser::class
    );
    
  4. Handle Missing Pages: Always check for null when fetching pages:

    $page = Markdownable::getPageBySlug('nonexistent');
    if (!$page) {
        abort(404);
    }
    

Extension Points

  1. Custom Front Matter Parsing: Extend FrontMatterParser to support additional YAML fields or validation:

    class CustomFrontMatterParser implements FrontMatterParserContract {
        public function parse(string $content): array {
            $data = parent::parse($content);
            // Add custom logic (e.g., convert dates, validate fields).
            return $data;
        }
    }
    
  2. Post-Processing HTML: Hook into the MarkdownToHtmlConverter to modify output (e.g., add classes, wrap content):

    $converter = app(MarkdownConverterContract::class);
    $converter->setPostProcessor(function (string $html) {
        return '<div class="markdown-content">' . $html . '</div>';
    });
    
  3. Event Listeners: Dispatch events for page reads or updates (e.g., log access or update analytics):

    // In a service provider:
    Markdownable::listen('page.read', function (PageData $page) {
        // Track page views.
    });
    

    (Note: Events may not be built-in; use Laravel’s event system directly on PageData.)

  4. Multi-Language Support: Use the paths config array to organize content by locale:

    'paths' => [
        'pages' => [
            'en' => 'resources/markdown/en',
            'es' => 'resources/markdown/es',
        ],
    ],
    

    Dynamically resolve paths in your code:

    $reader->setPagesPath('resources/markdown/' . app()->getLocale());
    
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.
codifyo/ts-generator-bundle
andydefer/laravel-cluster
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky
spatie/mailcoach-vapor