Weave 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

Posts Module Laravel Package

anomaly/posts-module

A posts management module for the Anomaly/Streams platform, providing post types, fields, and CRUD workflows for creating, editing, and publishing blog or article content with Laravel-powered flexibility.

View on GitHub
Deep Wiki
Context7

Getting Started

anomaly/posts-module is not a standalone blog package for a vanilla Laravel app. It is a module for the Streams Platform (the engine behind PyroCMS 3/4), so your starting point is an existing PyroCMS or Streams-powered project — not a fresh laravel new install.

Prerequisites

  • Laravel 8–9 with anomaly/streams-platform (v4.x of this module) or Laravel 5.7–8 with Streams 1.3 (v3.x of this module).
  • Pin the module version to the major version of Streams/PyroCMS you're running. There is almost no community activity (3 stars, 0 dependents), so composer update on this package without a ^ constraint is a recipe for a broken build.

Install

composer require anomaly/posts-module:^4.0
php artisan module:install posts

The module ships as a Laravel package with a Streams service provider (PostsModuleServiceProvider), auto-discovery under package discovery. You usually don't need to add it to config/app.php on PyroCMS — the installer registers core modules for you. In a custom Streams project you'll need to register the module in config/streams.php (or via php artisan addon:install).

Where to look first

  • src/Post/PostRepository.php and PostModel.php — the entry point for querying.
  • src/Post/PostPresenter.php — output formatting (URLs, summaries, etc.).
  • src/Post/PostPlugin.php — the Twig-facing plugin used in themes.
  • migrations/ — the Streams stream definition (fields, assignments). This is where you learn what fields exist out of the box (title, slug, summary, body, meta_title, meta_description, category, tags, enabled).
  • Admin URL: /admin/posts (and /admin/posts/categories).

First use case — create and render a post

  1. Go to /admin/posts/create, fill in title, summary, body; save. A posts stream entry is created and a slug is derived.
  2. To make it publicly reachable, assign the posts stream to a Page in /admin/pages (Streams modules rely on the Pages module for routing) — or query entries directly in a controller/view:
use Anomaly\PostsModule\Post\Contract\PostRepositoryInterface;

$posts = app(PostRepositoryInterface::class)
    ->where('enabled', true)
    ->orderBy('publish_at', 'DESC')
    ->paginate(10);

Or via the Streams API:

$entries = \Streams::entries('posts', 'posts')->where('enabled', true)->get();

Implementation Patterns

Repository-first queries. Prefer the contract (PostRepositoryInterface) over the concrete class so you can bind a custom implementation later. The repository extends Streams' EntryRepository, so you get the full Eloquent/Streams query builder: where, orderBy, paginate, findBySlug, findByStrId, etc.

Twig plugin (theme layer). In PyroCMS themes, the module registers a posts plugin (PostPlugin) so you can do {{ posts().get() }} or {{ posts().categories() }} in Twig. Verify method names in src/Post/PostPlugin.php — the surface is small and undocumented.

Extending the post stream. Don't fork the module to add fields. Use the Streams admin UI (Content → Streams → posts → Assignments) to add text, integer, relationship, or custom field types. The PostModel reads through Streams' dynamic attributes, so $post->custom_field just works. For version-controlled fields, create a tiny add-on module with a migrations/ step that calls Streams::make('posts', 'posts')->addField(...).

Custom Post model. If you need domain methods, bind your own model class implementing PostInterface and extending EntryModel:

$this->app->bind(
    \Anomaly\PostsModule\Post\Contract\PostInterface::class,
    \App\Posts\CustomPostModel::class
);

View overrides. All admin views use the anomaly.posts_module::* namespace. Override either by publishing:

php artisan vendor:publish --tag=posts-module

or by creating files in resources/views/anomaly/posts_module/... — the module's ViewServiceProvider checks the app first.

Frontend rendering options. Three patterns dominate:

  • Page + Streams rendering — assign the posts stream to a Page; the Streams theme plugin renders a list view and detail view automatically. Lowest-code path.
  • Custom controller — hit the repository in a controller and render Blade. Use this when you need caching or bespoke pagination.
  • Theme plugin overrides — extend PostPlugin in a theme add-on to expose additional query methods without touching the vendor code.

Categories and tags. A PostCategoryRepository is exposed alongside posts. Categories are a separate stream the module defines; use it in your UI rather than stuffing a string field into posts.

Gotchas and Tips

Coupling to Streams is total. You cannot install this in a plain Laravel app and expect Post::all() to behave like an Eloquent model. Everything flows through Streams::entries(), and without the platform service providers, migrations won't run. If you want a blog without Streams, look at spatie/laravel-tags + a hand-rolled model — this module is the wrong tool.

Stream migrations are painful to debug. The migrations/ files use Streams::make(...) which fails silently in odd ways if the posts namespace isn't registered yet. Run php artisan streams:compile if a field or assignment doesn't show up in the admin after a deploy.

Caching is aggressive. Streams caches entries, stream definitions, and field assignments. After changing fields or migrations, run:

php artisan cache:clear && php artisan view:clear && php artisan streams:compile

Otherwise you'll stare at "field not found" errors that are actually stale cache hits.

Slug uniqueness and locale. Slugs are unique per locale in a translatable stream. Creating posts programmatically without a slug field will auto-generate one, but you will hit collisions if you batch-import. Set slugs manually or catch QueryException during imports.

Route collisions with Pages module. If you assign the posts stream to a Page, make sure the page slug (blog, news, etc.) doesn't clash with a manually created route. The Pages module wins over custom routes — debug order with php artisan route:list.

The admin URL is /admin/posts, not /admin/post. Easy to miss; both work in some versions, but the router registers the plural.

Zero dependents means zero community answers. README is thin, issues are sparse. Budget time to read src/Post/PostRepository.php and migrations/ before you design anything. The MIT license is friendly for forking — the codebase is small enough that a private fork is often cleaner than trying to extend via bindings.

Don't edit vendor files. Because there are no community PRs landing, your local changes will vanish on the next composer install. Use the PostInterface binding, view overrides, or a small add-on module for anything non-trivial.

Version matrix matters. v3.x and v4.x differ in service provider registrations and admin UI conventions. Pin one and stick to it; cross-version upgrades on a 3-star package are a project, not a composer update.

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.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console