No description
  • PHP 57.7%
  • Blade 42.3%
Find a file
Troy Siedsma 1ed2767714 feat: simple view counter + popularity-aware related posts
- Add a views column (migration) + integer cast.
- recordView(): atomic increment on public show, without bumping updated_at.
- related(): same category first, ranked by views then recency, backfilled.
- Show the count in the built-in admin list.
2026-06-25 15:32:51 +00:00
config Initial commit: simple Markdown blog for Laravel 2026-06-25 14:59:26 +00:00
database/migrations feat: simple view counter + popularity-aware related posts 2026-06-25 15:32:51 +00:00
resources/views feat: simple view counter + popularity-aware related posts 2026-06-25 15:32:51 +00:00
routes Initial commit: simple Markdown blog for Laravel 2026-06-25 14:59:26 +00:00
src feat: simple view counter + popularity-aware related posts 2026-06-25 15:32:51 +00:00
.gitignore Initial commit: simple Markdown blog for Laravel 2026-06-25 14:59:26 +00:00
composer.json Initial commit: simple Markdown blog for Laravel 2026-06-25 14:59:26 +00:00
LICENSE docs: add README and MIT LICENSE 2026-06-25 15:07:25 +00:00
README.md docs: add README and MIT LICENSE 2026-06-25 15:07:25 +00:00

Laravel Blog

A simple, lightweight, database-driven Markdown blog for Laravel. Posts live in your database, you write them in Markdown, and the package renders a public index + post pages. No uploads, no taxonomy sprawl, no bloat. Drop it into an existing app, point it at a layout, and you have a blog.

It is intentionally framework-light: it depends only on illuminate/* and Livewire, ships with a minimal Livewire admin you can opt out of, and stays Filament-free so any host can use it (and a Filament host can supply its own resource against the Post model).

Features

  • DB-driven Markdown posts: title, auto-slug, category, author name, excerpt, body, publish date, and a published toggle.
  • Public pages: a blog index and per-post pages with BlogPosting JSON-LD, meta description, and canonical URL.
  • Images via Markdown links: no upload pipeline — embed images with ![alt](https://…).
  • Safe rendering: Markdown is rendered with raw HTML escaped and unsafe (javascript:/data:) links stripped, so author content can't inject script.
  • Minimal Livewire admin (opt-out): a WordPress-simple CRUD at a configurable path, gated by a configurable gate. Disable it and bring your own admin (e.g. a Filament resource).
  • Themeable: point the views at your own layout via config, or publish them to fully control the markup.
  • Configurable routing: separate prefixes for the index, individual posts (e.g. /p/{slug}), and the admin.

Requirements

  • PHP 8.2+
  • Laravel 11 or 12
  • Livewire 3 (the optional admin is a Livewire component)

Installation

This package is loaded as a local Composer path repository. No separate installation is needed when developing within the host monorepo.

For standalone installation, add the git repository as a Composer vcs repository (there is no Packagist), then require it:

// your app's composer.json
"repositories": [
    {
        "type": "vcs",
        "url": "https://git.lithium.hosting/LithiumHosting/laravel-blog.git"
    }
]
composer require lithiumhosting/laravel-blog
php artisan migrate

The service provider auto-registers. Publish the views/config as needed (below).

Configuration

Publish the config file:

php artisan vendor:publish --tag=blog-config

Config Options

Key Default Description
public_prefix blog Path for the blog index (listing).
post_prefix null Path for individual posts; null shares public_prefix. Set to p for /p/{slug}.
admin_prefix blog/admin Path for the built-in Livewire admin.
public_middleware ['web'] Middleware for the public routes.
admin_middleware ['web', 'auth'] Middleware for the admin routes.
admin_gate manage-blog Gate ability the admin requires (in addition to the middleware). null to rely on middleware alone.
register_admin_routes true Register the built-in Livewire admin. Set false if the host supplies its own admin.
layout blog::layouts.app Blade layout the views extend. Point at your own layout, or publish the views.
per_page 10 Posts per page on the index.
brand Blog Title/brand shown in the package's default layout.

This package reads no environment variables directly; configure it via the published config file.

Usage

Routes

Name Path Description
blog.index /{public_prefix} Paginated list of published posts.
blog.show /{post_prefix}/{post:slug} A single published post (404s on drafts/future dates).
blog.admin.posts /{admin_prefix} Built-in Livewire admin (when register_admin_routes is true).

Writing posts

Use the built-in admin, or create posts directly against the model:

use LithiumHosting\Blog\Models\Post;

Post::create([
    'title' => 'White Rim Trail: a 2-day group plan',
    'category' => 'Trails',
    'author_name' => 'Troy',
    'body' => "## Day 1\n\nDrop in at Shafer...",
    'is_published' => true,
    'published_at' => now(),
]);

slug auto-generates from the title (uniquely) when left blank. Post::published() scopes to published posts whose published_at has arrived; $post->rendered_body returns safe rendered HTML; $post->summary() returns the excerpt or a trimmed body.

Theming

Two options:

  1. Use your own layout — set blog.layout to a layout that both @yield('content') (public views) and renders {{ $slot }} (the Livewire admin).

  2. Publish the views and edit them directly:

    php artisan vendor:publish --tag=blog-views
    

    Published views land in resources/views/vendor/blog/ and override the package's. This is the cleanest way to wrap the blog in an existing component layout.

Bring your own admin

Set register_admin_routes => false and build your own admin against LithiumHosting\Blog\Models\Post — for example a Filament resource. The public side and the Post model work the same either way.

Dependencies

  • illuminate/* (^11|^12): framework contracts, database, support.
  • livewire/livewire (^3.0): the optional built-in admin.

Testing

composer test

(Uses Orchestra Testbench. Within a host app, integration is covered by the host's own test suite.)

License

The MIT License (MIT). Please see the License File for more information.

Is it any good?

Yes.

When people first hear about a new product, they frequently ask if it is any good. A Hacker News user remarked:

Note to self: Starting immediately, all raganwald projects will have a "Is it any good?" section in the readme, and the answer shall be "yes.".