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

Markdown Blog Laravel Package

thea/markdown-blog

View on GitHub
Deep Wiki
Context7
## Getting Started

### Minimal Setup
1. **Installation**
   ```bash
   composer require thea/markdown-blog
   php artisan vendor:publish --provider="Thea\MarkdownBlog\MarkdownBlogServiceProvider" --tag="migrations"
   php artisan migrate
  • Publish the config file (config/markdown-blog.php) and optionally the views (--tag="views").
  1. Configure Storage Define your Markdown directory in config/markdown-blog.php:

    'storage' => [
        'path' => storage_path('app/blog/posts'),
        'filename_pattern' => 'Y-m-d-{slug}.md',
    ],
    

    Create the directory and ensure it’s writable.

  2. First Post Create a Markdown file in the configured directory (e.g., 2024-04-08-first-post.md):

    ---
    title: First Post
    slug: first-post
    excerpt: A minimal example.
    date: 2024-04-08
    ---
    # Hello, World!
    Content here...
    

    Run php artisan markdown-blog:refresh to parse the file into the database.

  3. Basic Route Add to routes/web.php:

    use Thea\MarkdownBlog\Http\Controllers\PostController;
    
    Route::get('/blog', [PostController::class, 'index']);
    Route::get('/blog/{slug}', [PostController::class, 'show']);
    

Implementation Patterns

Workflow: Writing and Publishing Posts

  1. Local Development

    • Edit Markdown files directly in storage/app/blog/posts/.
    • Use php artisan markdown-blog:refresh to sync changes to the DB.
    • Test with php artisan serve.
  2. Production Deployment

    • Commit Markdown files to version control (e.g., Git).
    • Use a post-deploy hook or CI/CD step to run markdown-blog:refresh:
      # Example GitHub Actions step
      - run: php artisan markdown-blog:refresh --force
      
  3. Frontend Integration

    • Blade Views: Use @foreach($posts as $post) in resources/views/blog/index.blade.php.
    • API Endpoint: Extend the PostController to return JSON:
      public function apiIndex() {
          return response()->json(Post::latest()->get());
      }
      
    • Dynamic Metadata: Access frontmatter via $post->frontmatter (e.g., {{ $post->frontmatter['custom_field'] }}).

Common Patterns

  • Frontmatter Extensions: Add custom fields (e.g., tags, author) to Markdown files and extend the Post model to cast them:
    protected $casts = [
        'frontmatter' => array_merge(['title', 'slug'], ['tags' => 'array']),
    ];
    
  • Taxonomy Support: Use php artisan markdown-blog:tag to generate tag routes (if supported in future updates).
  • Drafts: Prefix filenames with draft- (e.g., draft-2024-04-08-post.md) and filter in queries:
    Post::where('slug', 'not like', 'draft-%')->get();
    

Integration Tips

  • Laravel Scout: Add full-text search to posts by extending the Post model:
    use Laravel\Scout\Searchable;
    
    class Post extends Model implements Searchable {
        // ...
        public function toSearchableArray() {
            return [
                'title' => $this->title,
                'excerpt' => $this->excerpt,
                'content' => $this->content,
            ];
        }
    }
    
  • Media Handling: Store images in storage/app/blog/media/ and reference them in Markdown:
    ![Alt text](/storage/blog/media/image.jpg)
    
    Serve files via:
    Route::get('/storage/blog/{path}', function ($path) {
        return Storage::disk('local')->response("blog/{$path}");
    });
    
  • Sitemap: Use spatie/laravel-sitemap to generate a sitemap for blog posts:
    Sitemap::add(Post::latest()->get(), function (Post $post) {
        return route('blog.show', $post->slug);
    });
    

Gotchas and Tips

Pitfalls

  1. Frontmatter Parsing Issues

    • Problem: YAML syntax errors in Markdown frontmatter cause silent failures.
    • Fix: Validate frontmatter with a tool like yaml-lint or use php artisan markdown-blog:validate.
    • Debugging: Check Laravel logs (storage/logs/laravel.log) for Symfony\Component\Yaml\Exception\ParseException.
  2. File Permissions

    • Problem: markdown-blog:refresh fails with "Permission denied" on storage directory.
    • Fix: Ensure the directory is writable:
      chmod -R 775 storage/app/blog
      chown -R www-data:www-data storage/app/blog  # Adjust user as needed
      
  3. Slug Conflicts

    • Problem: Duplicate slugs (e.g., 2024-04-08-post.md and 2024-04-09-post.md) overwrite each other.
    • Fix: Use the filename_pattern config to enforce uniqueness:
      'filename_pattern' => 'Y-m-d-H-i-s-{slug}.md',  // Includes timestamp
      
  4. Missing Views

    • Problem: Default views are not published.
    • Fix: Publish views explicitly:
      php artisan vendor:publish --tag="markdown-blog-views"
      
    • Customization: Override views in resources/views/vendor/markdown-blog/.
  5. Database Sync Delays

    • Problem: Changes to Markdown files aren’t reflected immediately.
    • Fix: Use --force flag to overwrite existing records:
      php artisan markdown-blog:refresh --force
      

Debugging Tips

  • Log Frontmatter: Add a temporary route to dump parsed frontmatter:
    Route::get('/debug-frontmatter', function () {
        $post = Post::first();
        return response()->json($post->frontmatter);
    });
    
  • Check Parsed Content: Verify the content field in the DB matches the rendered Markdown (use parsedown/parsedown for testing):
    use Parsedown;
    
    $parsedown = new Parsedown();
    $html = $parsedown->text(file_get_contents(storage_path('app/blog/posts/2024-04-08-post.md')));
    
  • Artisan Commands: List available commands with:
    php artisan list | grep markdown-blog
    

Extension Points

  1. Custom Parsers

    • Extend the Thea\MarkdownBlog\Services\MarkdownParser class to support custom frontmatter or content processing:
      class CustomParser extends MarkdownParser {
          public function parse($content) {
              // Add logic here (e.g., custom shortcodes)
              return parent::parse($content);
          }
      }
      
    • Bind the parser in AppServiceProvider:
      $this->app->bind(
          Thea\MarkdownBlog\Contracts\MarkdownParser::class,
          CustomParser::class
      );
      
  2. Event Listeners

    • Listen for post updates via PostSaved event (if implemented in future versions):
      // Example (hypothetical)
      Post::saved(function ($post) {
          // Send notification, update search index, etc.
      });
      
  3. Middleware for Drafts

    • Protect draft posts with middleware:
      Route::get('/blog/{slug}', [PostController::class, 'show'])
           ->middleware('checkDraft');
      
      public function handle($request, Closure $next) {
          $post = Post::where('slug', $request->slug)->first();
          if (str_starts_with($post->slug, 'draft-')) {
              abort(404);
          }
          return $next($request);
      }
      
  4. Testing

    • Use MarkdownBlog facade or service container to mock posts in tests:
      $this->app->instance(
          Thea\MarkdownBlog\Contracts\PostRepository::class,
          MockPostRepository::class
      );
      
    • Test Markdown parsing with:
      $parser = app(Thea\MarkdownBlog\Contracts\MarkdownParser::class);
      $html = $parser->parse("# Test\n\nContent");
      $this->assertString
      
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.
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope