Weave Code
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

laracasts/presenter

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require laracasts/presenter
    

    No additional configuration is required—just use it.

  2. First Use Case: Create a presenter for a model (e.g., User). Place it in app/Presenters/UserPresenter.php:

    namespace App\Presenters;
    
    use App\Models\User;
    use Laracasts\Presenter\Presenter;
    
    class UserPresenter extends Presenter
    {
        public function fullName()
        {
            return "{$this->first_name} {$this->last_name}";
        }
    
        public function avatarUrl()
        {
            return $this->avatar ? "/uploads/{$this->avatar}" : '/default-avatar.png';
        }
    }
    
  3. Enable Presenters: In your AppServiceProvider (or a model's boot() method), register the presenter:

    use App\Models\User;
    use App\Presenters\UserPresenter;
    
    public function boot()
    {
        User::addPresenter(UserPresenter::class);
    }
    
  4. Usage in Blade:

    {{ $user->fullName() }} <!-- Outputs: "John Doe" -->
    {{ $user->avatarUrl() }} <!-- Outputs: "/uploads/john-doe.jpg" -->
    

Implementation Patterns

Common Workflows

  1. Model-Specific Presenters:

    • Use presenters to encapsulate view logic for a single model (e.g., PostPresenter, ProductPresenter).
    • Example:
      class PostPresenter extends Presenter
      {
          public function formattedDate()
          {
              return $this->created_at->format('M d, Y');
          }
      
          public function excerpt()
          {
              return Str::limit($this->body, 200);
          }
      }
      
  2. Collection Presenters:

    • Apply presenters to collections for consistent formatting across items:
      $posts = Post::all(); // Each Post now has presenter methods
      foreach ($posts as $post) {
          echo $post->formattedDate(); // Works on collection items
      }
      
  3. Dynamic Presenters:

    • Useful for polymorphic relationships or conditional logic:
      class MediaPresenter extends Presenter
      {
          public function url()
          {
              return match ($this->type) {
                  'image' => "/images/{$this->path}",
                  'video' => "/videos/{$this->path}",
                  default => "#",
              };
          }
      }
      
  4. Presenter Inheritance:

    • Extend base presenters for shared functionality:
      class BaseUserPresenter extends Presenter
      {
          public function gravatar()
          {
              return "https://www.gravatar.com/avatar/{$this->email}?d=mp";
          }
      }
      
      class AdminUserPresenter extends BaseUserPresenter
      {
          public function roleBadge()
          {
              return '<span class="badge">Admin</span>';
          }
      }
      
  5. Presenter in Controllers:

    • Avoid logic in controllers; delegate to presenters:
      public function show(User $user)
      {
          return view('user.profile', [
              'user' => $user, // $user->fullName() works in Blade
          ]);
      }
      
  6. Presenter in API Responses:

    • Transform model data for APIs:
      class ApiUserPresenter extends Presenter
      {
          public function toArray()
          {
              return [
                  'id' => $this->id,
                  'name' => $this->fullName(),
                  'avatar' => $this->avatarUrl(),
              ];
          }
      }
      

Integration Tips

  1. Blade Directives: Create a custom Blade directive for reusable presenter logic:

    Blade::directive('present', function ($expression) {
        return "<?php echo {$expression}->{$expression}(); ?>";
    });
    

    Usage:

    @present($user, 'fullName')
    
  2. Presenter for Eloquent Relationships: Extend presenters to handle relationships:

    class CommentPresenter extends Presenter
    {
        public function authorName()
        {
            return $this->user->fullName(); // Calls UserPresenter
        }
    }
    
  3. Presenter Caching: Cache presenter output for performance-critical views:

    public function fullName()
    {
        return Cache::remember("user_{$this->id}_name", now()->addHours(1), function () {
            return "{$this->first_name} {$this->last_name}";
        });
    }
    
  4. Presenter for Non-Model Data: Use presenters with arrays or DTOs:

    $data = ['name' => 'John', 'age' => 30];
    $presenter = new class($data) extends Presenter {
        public function greeting()
        {
            return "Hi, I'm {$this->name} and I'm {$this->age} years old.";
        }
    };
    

Gotchas and Tips

Common Pitfalls

  1. Presenter Not Registered:

    • Error: Call to undefined method App\Models\User::fullName().
    • Fix: Ensure the presenter is registered via addPresenter() in a model's boot() method or AppServiceProvider.
  2. Presenter Overwriting Model Methods:

    • Issue: Presenters can shadow model methods (e.g., toArray()).
    • Fix: Prefix presenter methods with presenter_ or use a naming convention like format*():
      public function formatDate() { ... }
      
  3. Circular Dependencies:

    • Issue: Presenters calling other presenters in a loop (e.g., UserPresenter calls PostPresenter, which calls UserPresenter).
    • Fix: Avoid deep presenter chaining or use lazy-loading (e.g., if ($this->post) { ... }).
  4. Presenter in Livewire/Alpine:

    • Issue: Presenters may not work as expected in reactive components.
    • Fix: Use @once directives in Blade or compute properties in Alpine:
      <div x-data="{ name: @js($user->fullName()) }">...</div>
      
  5. Presenter Serialization:

    • Issue: Presenters may interfere with JSON serialization (e.g., in APIs).
    • Fix: Exclude presenters from toArray() or toJson():
      public function toArray()
      {
          return array_merge(parent::toArray(), [
              'presenter_data' => [
                  'full_name' => $this->fullName(),
              ],
          ]);
      }
      

Debugging Tips

  1. Check Registered Presenters: Use Tinker to verify presenters are loaded:

    php artisan tinker
    >>> $user = App\Models\User::first();
    >>> method_exists($user, 'fullName')
    
  2. Presenter Method Introspection: Dump presenter methods for a model:

    dd(get_class_methods($user)); // Look for presenter methods
    
  3. Presenter Isolation: Test presenters in isolation:

    $user = new \App\Models\User();
    $user->first_name = 'John';
    $user->last_name = 'Doe';
    $presenter = new \App\Presenters\UserPresenter($user);
    dd($presenter->fullName()); // Debug without DB
    

Extension Points

  1. Custom Presenter Events: Trigger events when presenters are called:

    class UserPresenter extends Presenter
    {
        public function fullName()
        {
            event(new PresenterCalled($this, 'fullName'));
            return "{$this->first_name} {$this->last_name}";
        }
    }
    
  2. Presenter Middleware: Add middleware to presenters (e.g., logging, caching):

    class UserPresenter extends Presenter
    {
        public function __call($method, $args)
        {
            if (str_starts_with($method, 'presenter_')) {
                Log::info("Presenter method called: {$method}");
                return $this->{$method}(...$args);
            }
        }
    }
    
  3. Dynamic Presenter Resolution: Resolve presenters dynamically based on context:

    User::addPresenter(function ($model, $context) {
        return match ($context) {
            'api' => new ApiUserPresenter($model),
            default => new UserPresenter($model),
        };
    });
    
  4. Presenter for API Resources: Integrate with Laravel's API Resources:

    class UserResource extends JsonResource
    {
        public function toArray($request)
        {
            return [
                'name' => $this->resource->fullName(),
                'avatar' => $this->resource->avatarUrl(),
            ];
        }
    }
    
  5. Presenter Testing: Mock presenters in tests:

    $user = new User();
    $presenter = Mockery::mock(User
    
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.
milito/query-filter
apiboxsym/user-bundle
apiboxsym/health-check-bundle
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours