Skip to content

Instantly share code, notes, and snippets.

@jarektkaczyk
Created January 27, 2017 10:12
Show Gist options
  • Save jarektkaczyk/1d38b3af8c3f60cbd6dec74c3be46371 to your computer and use it in GitHub Desktop.
Save jarektkaczyk/1d38b3af8c3f60cbd6dec74c3be46371 to your computer and use it in GitHub Desktop.
presenter for eloquent
<?php
namespace App\Presenters;
use ArrayAccess;
use Illuminate\Support\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Contracts\Pagination\Paginator;
class Decorator
{
/** @var string[] Namepsaces checked for proprietary presenters */
protected $proprietary_namespaces = ['App\\Presenters\\'];
/** @var string Generic presenter, that will be used if no proprietary presenter is found */
protected $generic_presenter = Presenter::class;
/**
* Set generic presenter class.
*
* @param string $class
*/
public function setGenericPresenter($class)
{
$this->generic_presenter = $class;
}
/**
* Add namespace for proprietary presenter lookup.
*
* @param string $namespace
*/
public function addNamespace($namespace)
{
array_unshift($this->proprietary_namespaces, $namespace);
}
/**
* Decorate model with presenter class.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @return \App\Presenters\Presenter
*/
public function decorate($model)
{
if ($model instanceof Collection || is_array($model) || $model instanceof Paginator) {
return $this->decorateMany($model);
}
if (!$model instanceof Presentable) {
return $model;
}
$presenter = $this->generic_presenter;
foreach ($this->proprietary_namespaces as $ns) {
if (class_exists($proprietary = $ns.class_basename($model))) {
$presenter = $proprietary;
break;
}
}
return new $presenter($model, $this);
}
public function decorateMany($models)
{
foreach ($models as $key => $model) {
if ($model instanceof Presentable) {
$models[$key] = $this->decorate($model);
}
}
return $models;
}
}
<?php
namespace App\Presenters;
use Illuminate\Database\Eloquent\Model;
use App\Traits\TranslatableFields;
class LanguageAwarePresenter extends Presenter
{
public function present($key)
{
$value = in_array(TranslatableFields::class, class_uses_recursive(get_class($this->model)))
? $this->translate($key)
: $this->model->{$key};
return ($method = $this->getMethod($key)) ? $this->{$method}($value) : $value;
}
protected function translate($key)
{
return $this->model->getTranslation($key);
}
}
<?php
namespace App\Presenters;
interface Presentable
{
//
}
<?php
namespace App\Presenters;
use Illuminate\Support\Str;
use Illuminate\Support\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Contracts\Routing\UrlRoutable;
class Presenter implements UrlRoutable
{
protected $model;
protected $decorator;
public function __construct(Model $model, Decorator $decorator)
{
$this->model = $model;
$this->decorator = $decorator;
}
public function getModel()
{
return $this->model;
}
public function __get($key)
{
$value = $this->model->{$key};
if ($value instanceof Model || $value instanceof Collection || is_array($value)) {
return $this->decorator->decorate($this->model->{$key});
}
return $this->present($key);
}
protected function present($key)
{
return ($method = $this->getMethod($key))
? $this->{$method}($this->model->{$key})
: $this->model->{$key};
}
protected function getMethod($key)
{
if (method_exists($this, $method = $key)
|| method_exists($this, $method = Str::snake($key))
|| method_exists($this, $method = Str::camel($key))
) {
return $method;
}
}
public function __call($method, $params)
{
return call_user_func_array([$this->model, $method], $params);
}
public function __isset($key)
{
return $this->model->__isset($key);
}
public function toArray()
{
$attributes = collect($this->model->toArray());
return $attributes->map(function ($_, $key) {
return $this->present($key);
})->all();
}
public function __toString()
{
return json_encode($this->toArray());
}
/**
* Get the value of the model's route key.
*
* @return mixed
*/
public function getRouteKey()
{
return $this->model->getRouteKey();
}
/**
* Get the route key for the model.
*
* @return string
*/
public function getRouteKeyName()
{
return $this->model->getRouteKeyName();
}
}
<?php
namespace App\Providers;
use App\Presenters\Decorator;
use App\Presenters\Presenter;
use Illuminate\Contracts\View\Factory;
use Illuminate\Support\ServiceProvider;
/**
* This Provider registers a View Composer that will automatically
* decorate Presentable models with appropriate Presenter class.
*/
class PresentersServiceProvider extends ServiceProvider
{
public function boot(Factory $view)
{
$view->composer('*', function ($view) {
$data = array_merge($view->getFactory()->getShared(), $view->getData());
foreach ($data as $var => $model) {
$view[$var] = $this->app['presenters']->decorate($model);
}
}, 999);
}
public function register()
{
$this->app->singleton('presenters', function () {
return new Decorator;
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment