roundly-consulting / posts-for-laravel
A modern, multilingual, SEO-ready blog/posts engine for Laravel with translatable content, a publishing lifecycle, categories, and tags.
Package info
github.com/roundly-consulting/posts-for-laravel
pkg:composer/roundly-consulting/posts-for-laravel
Fund package maintenance!
Requires
- php: ^8.4
- illuminate/contracts: ^12.0|^13.0
- roundly-consulting/approvals-for-laravel: ^1.0
- roundly-consulting/enums-for-laravel: ^1.0
- roundly-consulting/likes-for-laravel: ^1.0
- roundly-consulting/media-library-for-laravel: ^1.0
- roundly-consulting/package-toolkit-for-laravel: ^1.0
- roundly-consulting/reports-for-laravel: ^1.0
- roundly-consulting/sluggable-for-laravel: ^1.0
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.0
- nunomaduro/collision: ^8.0
- orchestra/testbench: ^10.0|^11.0
- pestphp/pest: ^4.0
- pestphp/pest-plugin-arch: ^4.0
- pestphp/pest-plugin-laravel: ^4.0
- phpstan/extension-installer: ^1.4
- roundly-consulting/testing-for-laravel: ^1.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Posts for Laravel
A multilingual, SEO-ready blog engine for Laravel: translatable posts with a real publishing lifecycle (draft, scheduled, published, archived), nested categories and tags, per-locale slugs, SEO meta and schema.org structured data, and a polymorphic author of any key type. Featured images, likes and reports come built in through our own media, likes and reports packages.
Installation
Requires PHP 8.4 and Laravel 12 or 13.
composer require roundly-consulting/posts-for-laravel php artisan vendor:publish --tag="posts-migrations" php artisan vendor:publish --tag="media-migrations" php artisan vendor:publish --tag="likes-migrations" php artisan vendor:publish --tag="reports-migrations" php artisan vendor:publish --tag="approvals-migrations" php artisan migrate
If your author models are UUID/ULID-keyed, set POSTS_KEY_TYPE (and POSTS_PRIMARY_KEY_TYPE for
the posts' own ids) before migrating. Scheduled posts go live through
Schedule::command('posts:publish-scheduled')->everyMinute();.
Usage
Write a post in two languages and publish it (or ->save() a draft, ->schedule($at) it):
use RoundlyConsulting\Posts\DataTransferObjects\SeoData; use RoundlyConsulting\Posts\Facades\Posts; $post = Posts::draft() ->title('en', 'Hello world')->title('sk', 'Ahoj svet') ->perex('en', 'A short intro') ->content('en', '<p>…</p>') ->by($user) // any author model, bigint/uuid/ulid keyed ->tags(['laravel', 'php']) // found or created by name ->seo(new SeoData(canonical: 'https://example.test/hello-world')) ->publish(); $post->getTranslation('title', 'sk'); // 'Ahoj svet'
Read it back — slugs are generated per locale, and {post} route parameters bind by them:
use RoundlyConsulting\Posts\Models\Post; Posts::findBySlug('hello-world'); // current locale → fallback → any locale Posts::published()->latest('published_at')->paginate(); Posts::archive($post); // or unpublish() back to draft Route::get('/posts/{post}', fn (Post $post) => view('posts.show', compact('post')))->name('posts.show');
And render it with its SEO tags and JSON-LD:
{!! $post->renderMetaTags() !!} {{-- <title>, description, canonical, og:*, twitter:*, robots --}} {!! $post->renderJsonLd() !!} {{-- schema.org BlogPosting --}} <h1>{{ $post->title }}</h1> {{-- current locale, with fallback --}} {!! $post->renderContent() !!} {{-- raw HTML: trusted or sanitized content only --}}
Documentation
The full documentation — configuration, every feature and its API, and testing — lives on our website: roundly-consulting.com/open-source/docs/posts-for-laravel
Release notes are in CHANGELOG.md. To contribute, see the contributing guide.
Support our work
This package is free and open source, built and maintained by Roundly Consulting. If it saves you time, please consider supporting our open-source work — a one-time donation, a monthly pledge on Patreon or a crypto donation helps fund maintenance, new features and new packages.
License
The MIT License (MIT). See LICENSE.md.