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

Laravel Mutable Content Laravel Package

amarenkov/laravel-mutable-content

A Laravel package for adding mutable content to Eloquent models. Attach editable content records to any model and manage them through a simple API, making it easier to build CMS-style features without custom content tables. Lightweight and easy to integrate into existing Laravel apps.

View on GitHub
Deep Wiki
Context7

Getting Started

This is an audit/change-log package: it records model changes as Entry rows and exposes two read-path helpers that turn raw attribute diffs into human-readable strings — Entry::describe() (per-entry) and LogHelper::describeChanges() (batch, e.g. all entries for a record or request).

  1. composer require the package, then publish and run its migrations (php artisan vendor:publish --tag=...; check the package's README/composer.json extra.laravel block for the exact tag) and php artisan migrate. The entries table is the single source of truth — everything else is derived.
  2. First place to look: Entry::describe() and LogHelper::describeChanges(). They are the contract you'll build UI and notifications on top of, so skim them before wiring up logging.
  3. First use case: attach the package's trait/observer to one model, make a couple of updates, and dump Entry::latest()->first()->describe() in tinker. That confirms the field-type formatting path (including money) is what you expect before you build a history tab around it.

Implementation Patterns

  • Per-record history tab. Query entries for a model instance, order by created_at, map through describe(), and render as a timeline. Keep the raw changes JSON alongside the description if you need a "show raw diff" toggle.
  • Batch change summaries. Use LogHelper::describeChanges() when one logical operation touches multiple entries (a bulk import, a multi-model transaction). It returns the aggregated, formatted description set — use it directly in flash messages, Slack/webhook payloads, or email digests rather than re-implementing string building.
  • Formatting is type-driven. Descriptions come from field-type formatters. money fields render as 10.50 $ (decimals + currency from the field definition) — see the v0.7.8 note below. Treat other types (dates, booleans, enums) the same way: define the field correctly and let the package format it, instead of hand-formatting in Blade.
  • Separate storage from presentation. Persist entries only; build all human-readable text at render time via describe(). That way formatter fixes (like this release) apply retroactively to old entries without a migration.
  • Testing. Assert on describe() output in feature tests where the wording is part of your UX. For the data, assert the changes array/isDirty payload instead — that's the stable part.

Gotchas and Tips

  • v0.7.8 changes rendered output. money values in LogHelper::describeChanges() and Entry::describe() now show as formatted amounts with the field's decimals (10.50 $) instead of the raw JSON value ("10.5"). If you snapshot-test description strings, inline-asserted text, or have cached/exported descriptions (CSV, PDFs, sent emails stored in DB), they will change. Update assertions and consider re-rendering cached output.
  • Display-only fix. Stored changes JSON is untouched — this only affects the read path. Don't write migrations to "fix" existing entries.
  • Decimals come from the field definition, not the stored value. A money field declared with different precision than the underlying data will produce surprising rounding in descriptions; keep the field definition and DB column precision aligned.
  • Debugging mismatched output: dump $entry->changes (raw) next to $entry->describe() and compare. A wrong type declaration on the field is the usual cause of JSON leaking into the description.
  • Performance: describing many entries in a loop is cheap per-call but add up; eager-load any relations your field formatters touch, and paginate history views rather than describing an unbounded set.
  • Extension point: custom field types are handled by registering your own formatter — do that instead of str_replace-ing output from describe(), which will break on the next formatting change like this one.
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.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager