|
| 1 | +# Presenter |
| 2 | + |
| 3 | + |
| 4 | + |
| 5 | +This package provides an easy way to create Presenters that can be used to [decorate](https://en.wikipedia.org/wiki/Decorator_pattern) an Eloquent Model. It is heavily inspired by [Hemp Presenter](https://github.com/davidhemphill/presenter) and [Laravel API Resources](https://laravel.com/docs/6.x/eloquent-resources), however it comes with some key differences: |
| 6 | + - It is a *simplified* version that does not offers Traits and Macros |
| 7 | + - It provides an easy way to handle *paginated* models |
| 8 | + - It does not focus solely in JSON API responses but it serves as a *general purpose* decorator that can be used anywhere in the project |
| 9 | + |
| 10 | +## TL;DR |
| 11 | + |
| 12 | +A **Decorator** is an object that wraps another object in order to add functionality to the wrapped object. It also delegates calls to methods not available on the decorator to the class being decorated. Decorators can be useful when you need to modify the functionality of a class without relying on inheritance. You can use it to add optional features to your objects like logging, access-control, and things of that sort. A **Presenter** is a type of decorator used to present an object to the view of an application, such as a Blade view, or an API response. |
| 13 | + |
| 14 | + |
| 15 | +## Usage |
| 16 | + |
| 17 | +### Customizing Presenter Classes |
| 18 | + |
| 19 | +Presenters are simple classes designed to encapsulate complex or repetitive view logic. For example, here is a simple `UserPresenter` class: |
| 20 | + |
| 21 | +```php |
| 22 | +<?php |
| 23 | + |
| 24 | +namespace App\Presenters; |
| 25 | + |
| 26 | +use CultureGr\Presenter\Presenter; |
| 27 | + |
| 28 | +class UserPresenter extends Presenter |
| 29 | +{ |
| 30 | + |
| 31 | + public function fullname() |
| 32 | + { |
| 33 | + return $this->firstname.' '.$this->lastname; |
| 34 | + } |
| 35 | +} |
| 36 | +``` |
| 37 | + |
| 38 | +Note that the original model's properties (`firstname`, `lastname`) can be directly accessed using the `$this` variable. This is because the `Presenter` class automatically proxies property and method access down to the underlying model. |
| 39 | + |
| 40 | +This class has a custom method `fullname` that can be called wherever an instance of this Presenter is used. |
| 41 | + |
| 42 | +```php |
| 43 | +$presentedUser->firstname; //=> 'John' |
| 44 | +$presentedUser->lastname; //=> 'Doe' |
| 45 | +$presentedUser->fullname() //=> 'John Doe' |
| 46 | +``` |
| 47 | + |
| 48 | +If the presented model needs to be transformed into a JSON structure the Presenter class should define a `toArray` method which returns the array of attributes that should be converted to JSON when sending the response. |
| 49 | + |
| 50 | +```php |
| 51 | +<?php |
| 52 | + |
| 53 | +namespace App\Presenters; |
| 54 | + |
| 55 | +use CultureGr\Presenter\Presenter; |
| 56 | + |
| 57 | +class UserPresenter extends Presenter |
| 58 | +{ |
| 59 | + public function fullname() |
| 60 | + { |
| 61 | + return $this->firstname.' '.$this->lastname; |
| 62 | + } |
| 63 | + |
| 64 | + public function toArray() |
| 65 | + { |
| 66 | + return [ |
| 67 | + 'id' => $this->id, |
| 68 | + 'fullname' => $this->fullname() |
| 69 | + // ... |
| 70 | + ]; |
| 71 | + } |
| 72 | +} |
| 73 | +``` |
| 74 | + |
| 75 | +### Presenting Single Models |
| 76 | + |
| 77 | +Once the presenter is defined, it may be used to create a presented model using the `make` factory method: |
| 78 | + |
| 79 | +```php |
| 80 | +$user = User::first(); |
| 81 | +$presentedUser = UserPresenter::make($user); |
| 82 | +``` |
| 83 | + |
| 84 | +The presented model may be used anywhere in the project. If the model needs to be serialized into a response (like for a view or API response) the overwritten `toArray` method must be explicitly called, or it can be directly returned from a route or controller: |
| 85 | + |
| 86 | +```php |
| 87 | +<?php |
| 88 | + |
| 89 | +namespace App\Http\Controllers; |
| 90 | + |
| 91 | +use App\Models\User; |
| 92 | +use App\Presenters\UserPresenter; |
| 93 | + |
| 94 | +class UserController extends Controller |
| 95 | +{ |
| 96 | + public function show($id) |
| 97 | + { |
| 98 | + return view('users.show', [ |
| 99 | + 'user' => UserPresenter::make(User::find($id))->toArray() |
| 100 | + ]); |
| 101 | + } |
| 102 | +} |
| 103 | +``` |
| 104 | + |
| 105 | +### Presenting Collections |
| 106 | + |
| 107 | +To present a collection of models, the static `collection` method on the Presenter class can be used: |
| 108 | + |
| 109 | +```php |
| 110 | +$users = User::all(); |
| 111 | +$presentedUsers = UserPresenter::collection($users); |
| 112 | +``` |
| 113 | + |
| 114 | +The collection of presented models may be used anywhere in the project. If the collection needs to be serialized into a response (like for a view or API response) the overwritten `toArray` method must be explicitly called, or it can be directly returned from a route or controller: |
| 115 | + |
| 116 | +```php |
| 117 | +<?php |
| 118 | + |
| 119 | +namespace App\Http\Controllers; |
| 120 | + |
| 121 | +use App\Models\User; |
| 122 | +use App\Presenters\UserPresenter; |
| 123 | + |
| 124 | +class UserController extends Controller |
| 125 | +{ |
| 126 | + public function index() |
| 127 | + { |
| 128 | + $users = User::all(); |
| 129 | + |
| 130 | + return UserPresenter::collection($users); |
| 131 | + } |
| 132 | +} |
| 133 | +``` |
| 134 | + |
| 135 | +The output will be something like this: |
| 136 | + |
| 137 | +```json |
| 138 | +[ |
| 139 | + {"id": "1", "fullname": "John Doe"}, |
| 140 | + {"id": "2", "fullname": "Jane Doe"} |
| 141 | +] |
| 142 | +``` |
| 143 | + |
| 144 | +### Presenting Paginated Collections |
| 145 | + |
| 146 | +To present a paginated collection of models, the static `pagination` method on the Presenter class can be used: |
| 147 | + |
| 148 | +```php |
| 149 | +$users = User::paginate(); |
| 150 | +$presentedUsers = UserPresenter::pagination($users); |
| 151 | +``` |
| 152 | + |
| 153 | +The paginated collection of presented models may be used anywhere in the project. If the collection needs to be serialized into a response (like for a view or API response) the overwritten `toArray` method must be explicitly called, or it can be directly returned from a route or controller: |
| 154 | + |
| 155 | +```php |
| 156 | +<?php |
| 157 | + |
| 158 | +namespace App\Http\Controllers; |
| 159 | + |
| 160 | +use App\Models\User; |
| 161 | +use App\Presenters\UserPresenter; |
| 162 | + |
| 163 | +class UserController extends Controller |
| 164 | +{ |
| 165 | + public function index() |
| 166 | + { |
| 167 | + $users = User::paginate(); |
| 168 | + |
| 169 | + return UserPresenter::pagination($users); |
| 170 | + } |
| 171 | +} |
| 172 | +``` |
| 173 | + |
| 174 | +The output will contain the presented models wrapped in a `data` key, as long as `meta` and `links` keys with information about the paginator's state: |
| 175 | + |
| 176 | +```json |
| 177 | +{ |
| 178 | + "data": [ |
| 179 | + {"id": "1", "fullname": "John Doe"}, |
| 180 | + {"id": "2", "fullname": "Jane Doe"} |
| 181 | + ], |
| 182 | + "links":{ |
| 183 | + "first": "http://example.com/pagination?page=1", |
| 184 | + "last": "http://example.com/pagination?page=1", |
| 185 | + "prev": null, |
| 186 | + "next": null |
| 187 | + }, |
| 188 | + "meta":{ |
| 189 | + "current_page": 1, |
| 190 | + "from": 1, |
| 191 | + "last_page": 1, |
| 192 | + "path": "http://example.com/pagination", |
| 193 | + "per_page": 15, |
| 194 | + "to": 10, |
| 195 | + "total": 10 |
| 196 | + } |
| 197 | +} |
| 198 | +``` |
| 199 | + |
| 200 | + |
| 201 | + |
0 commit comments