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

Presenter Laravel Package

hemp/presenter

Hemp Presenter is a lightweight Laravel package for wrapping models or data in dedicated presenter classes, giving you a clean place for display formatting, computed fields, and view-ready output while keeping your models and controllers lean.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install with Composer:

    composer require hemp/presenter
    

    No service provider or config file to publish — the package is trait/class-based only.

  2. Add the trait to the model you want to present:

    use Hemp\Presenter\Presentable;
    
    class User extends Model
    {
        use Presentable;
    }
    
  3. Create a presenter that extends the base class:

    namespace App\Presenters;
    
    use Hemp\Presenter\Presenter;
    
    class UserPresenter extends Presenter
    {
        public function fullName(): string
        {
            return trim("{$this->first_name} {$this->last_name}");
        }
    
        public function initials(): string
        {
            return strtoupper($this->first_name[0] . $this->last_name[0]);
        }
    }
    
  4. Present the model:

    $user = User::find(1);
    
    $user->present()->fullName;   // "David Hemphill"
    

The first place to look is the presenter class itself: any method defined there is a presentation concern; anything it doesn't define is magically proxied to the underlying model. A good first use case is offloading display formatting (full names, currency, status labels, avatar URLs, date formatting) from Blade templates or API resources into a dedicated, testable class.

If the convention-based presenter resolution doesn't match your namespace layout, declare it explicitly on the model:

class User extends Model
{
    use Presentable;

    protected $presenter = UserPresenter::class;
}

Implementation Patterns

Blade formatting

<h1>{{ $user->present()->fullName }}</h1>
<p>Joined {{ $user->present()->joinedOn }}</p>

Replace inline ->format() chains and conditional string-building in views with named presenter methods.

Context-specific presenters. Pass a presenter in explicitly when the same model needs different rendering in different contexts (admin panel vs. public profile vs. API):

$user->present(AdminUserPresenter::class);
$user->present(PublicUserPresenter::class);
$user->present(new InvoiceUserPresenter($user, $currencyService));

This is the cleanest way to inject dependencies the base class doesn't provide.

Status and badge helpers. Put enum/constant-to-display mapping in the presenter so templates stay dumb:

public function statusLabel(): string
{
    return match ($this->status) {
        'a' => 'Active',
        's' => 'Suspended',
        default => 'Unknown',
    };
}

public function statusClass(): string
{
    return $this->status === 'a' ? 'badge-success' : 'badge-danger';
}

Chaining into model methods. Because the presenter forwards unknown calls, you can mix presenter helpers and model methods:

$user->present()->subscription()->renewsAt;

Prefer calling $this->model->... explicitly when the intent matters for readability.

API responses. For resource controllers, present the model and then flatten it, or expose the presenter through an API Resource:

return response()->json([
    'name' => $user->present()->fullName,
    'email' => $user->email,
    'member_since' => $user->present()->joinedOn,
]);

Don't try to make the presenter a full replacement for Eloquent serialization — treat it as a projection layer.

Testing. Presenters are plain classes, so test them directly:

$presenter = new UserPresenter(new User(['first_name' => 'Ada', 'last_name' => 'Lovelace']));
$this->assertSame('Ada Lovelace', $presenter->fullName);

No database needed, which is the main long-term benefit over inline accessors.

Keep presenters read-only. They should never persist, mutate the model, or perform queries directly — treat any I/O as a sign the logic belongs in a service or resource instead.

Gotchas and Tips

  • Silent attribute forwarding is a double-edged sword. $this->firstname (typo) resolves to null rather than throwing. If presenters are critical, write tests for each method or wrap risky access with $this->model->getAttribute().
  • Convention resolution fails late. A wrong namespace or missing $presenter only blows up when present() is called. Use explicit $presenter properties in any model where the default convention is ambiguous.
  • present() builds a new instance each call. Avoid calling it repeatedly inside a loop; assign once:
    $p = $user->present();
    $p->fullName; $p->initials;
    
  • Presenters don't eager load. If a presenter method touches $this->relation->…, ensure the relation is loaded first or you'll reintroduce N+1 queries. A common bug is $user->present()->latestOrderTotal on a paginated list.
  • Casts apply, but nullability doesn't. Magic __get delegates to the model, so casts still work, but a null relation or attribute will happily return null into your string-concatenation. Guard in the presenter rather than in the Blade template.
  • isset() semantics can surprise you when the model defines custom __isset or attributes are computed lazily — test null checks explicitly.
  • Don't override toArray()/jsonSerialize() on a presenter unless you fully intend to shadow the model's serialization; for API endpoints, prefer Laravel's API Resources or an explicit array shape.
  • Model method proxy works both ways. Presenter methods win; unknown methods fall through. This means a presenter method that accidentally shadows a model method (e.g., save, update) can hide surprising behavior — name presenter methods as verbs/nouns that clearly describe presentation (displayName, statusLabel).
  • Dependency injection is limited to explicit construction. The package has no container binding or config; if a presenter needs a service, pass it as a constructor arg via new MyPresenter($user, $service).
  • Extension point: override __get/__call in your own base presenter if you need caching, fallback chains, or logging around attribute access — but keep it thin; the package intentionally has no config surface.
  • Compatibility check before adoption. Last release was 2024, zero dependents, low activity. For new projects, weigh it against native accessors, Attribute casts, and API Resources, which cover the same ground with framework guarantees. The presenter approach shines when formatting logic is large enough to warrant its own tested class and a reusable interface across multiple models.
  • Debugging tip: if a presenter call returns null unexpectedly, add dd($presenter->model) to confirm which model instance was decorated — presenters are commonly mis-bound by passing the wrong class in present().
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.
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core