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

Presentable Laravel Package

terranet/presentable

Terranet Presentable is a Laravel package that adds reusable presenters to Eloquent models and data objects. It transforms raw attributes into view-ready output, decoupling presentation logic from controllers, APIs, and Blade templates so your application stays clean, consistent, and easy to maintai...

View on GitHub
Deep Wiki
Context7

Getting Started

Start by reading the package source, not the README — at ~1 star and a 2016 release there is essentially no documentation surface, and the entire package is likely a single trait plus a small presenter base class.

  1. Install it:

    composer require terranet/presentable
    

    Check composer.json for the actual PSR-4 namespace (likely Terranet\Presentable\) and whether a service provider is auto-discovered. On modern Laravel you may need to register it manually in config/app.php, and you should confirm the package's illuminate/support constraint is compatible with your framework version before committing.

  2. Identify the entry point. Look for a PresentableTrait (or similarly named trait) exposing a present() method, and a base Presenter class that wraps an underlying model. Confirm the convention it uses to resolve a presenter: usually a $presenter property on the model, or a namespace/class-name convention.

  3. First use case — the canonical one:

    class User extends Model
    {
        use PresentableTrait;
    
        protected $presenter = \App\Presenters\UserPresenter::class;
    }
    
    class UserPresenter
    {
        public function __construct(protected User $user) {}
    
        public function fullName(): string
        {
            return trim("{$this->user->first_name} {$this->user->last_name}");
        }
    
        public function __get($key)
        {
            return $this->user->{$key};
        }
    }
    

    Then in a controller or Blade view: $user->present()->fullName.

If the trait resolves presenters by string convention rather than a class constant, adjust the $presenter value to whatever the source expects (a short name, a namespaced class, or a config-driven namespace).

Implementation Patterns

One presenter per model, formatting only. Keep presenters thin: currency formatting, date presentation, status badges, derived display strings, and composed labels. Business logic belongs in the model or a service; presenters should be pure and side-effect free so they are trivially unit-testable.

Delegation via __get/__call. The usual shape is a presenter that forwards unknown properties and methods to the wrapped model. This lets views call $user->present()->email without you re-declaring every attribute. Implement __get, __isset, and optionally __call so that isset() and relation access behave predictably.

Blade integration. Call present() at the point of display rather than storing presenters on the model instance. If you find yourself writing $user->present()->... repeatedly, assign it once in the view: @php($p = $user->present()).

Testing. Treat a presenter as a plain class: instantiate it with a model (or a factory-built model) and assert on returned strings. No HTTP layer required, which is the main payoff of this pattern.

Escape hatch for modern Laravel. Before adopting this, evaluate whether native accessors (Attribute casts, getFullNameAttribute) or API Resources cover the same need. Presenters still earn their place when you have multiple output contexts (HTML, CSV, PDF) that need different renderings of the same model, because you can define HtmlUserPresenter, CsvUserPresenter, etc., and inject whichever the context requires.

Composition with resources. If you later add JSON APIs, do not reuse presenters there — use JsonResource so you retain control over conditional relationships and pagination metadata.

Gotchas and Tips

  • Maintenance risk is the headline gotcha. Last release August 2016, zero dependents, one star. Assume it targets Laravel 5.x. Verify PHP 8 compatibility (dynamic properties, __get return types, Illuminate contract signatures) before relying on it in production. If it breaks, the fallback is a ~30-line trait of your own — this package is not load-bearing enough to be worth a fork.
  • Attribute collisions. If a presenter method shares a name with a model attribute, the presenter's __get ordering decides which wins. Be explicit: define the presenter method and document that it shadows the column.
  • N+1 queries. Delegated relation access ($user->present()->posts) triggers lazy loading exactly as the model would. Eager-load on the query, not in the presenter — the presenter cannot fix an N+1 it did not cause.
  • Recursion. A __get that falls through to $this->user->{$key} is safe, but a presenter that returns $this for unknown keys, or a model that itself calls present(), can loop. Guard with isset($this->user->{$key}) or throw on miss.
  • Not serialized. Presenters are view objects. toArray()/toJson() on the model will not include presenter output; do not expect $user->present()->fullName to appear in an API response.
  • Resolution failures are opaque. Convention-based lookup typically fails with a generic "class not found" or a null presenter. If present() returns null, dump get_class($model) and trace the trait's resolution method to see the exact class string it builds.
  • Extension points. Override present() in your model to inject a specific presenter, or bind a presenter factory in the container if the package supports it. If it does not, wrapping the trait method in your own base model is the cleanest customization seam.
  • Debugging tip. dd($user->present()) shows only the presenter's own public properties; use get_object_vars() plus the underlying model dump to see the full picture, and check method_exists() before assuming a delegated call resolves.
  • Pin the version. With a single 2016 release, composer require without an explicit constraint risks surprises if anything is ever tagged. Commit composer.lock.
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