A system for auto-decorating models with presenters

Overview

Laravel Auto Presenter 7

This package automatically decorates objects bound to views during the view render process.

Banner

Build Status StyleCI Status Software License Packagist Downloads Latest Version

Features

  • Automatically decorate objects bound to views
  • Automatically decorate objects within paginator instances
  • Automatically decorate objects within arrays and collections

Installing

Laravel Auto Presenter requires PHP 7.1-8.1 and supports Laravel 5.5-9. If you need support for older Laravel or PHP versions, please choose an older version of Laravel Auto Presenter.

Auto Presenter L5.1 L5.2 L5.3 L5.4 L5.5 L5.6 L5.7 L5.8 L6 L7 L8 L9
4.3
5.0
6.2
7.7

To get the latest version, simply require the project using Composer:

$ composer require mccool/laravel-auto-presenter

Once installed, if you are not using automatic package discovery, then you need to register the McCool\LaravelAutoPresenter\AutoPresenterServiceProvider service provider in your config/app.php.

You can also optionally alias our facade:

        'AutoPresenter' => McCool\LaravelAutoPresenter\Facades\AutoPresenter::class,

Upgrading

Version 6 to 7

In Laravel Auto Presenter 7, note that:

  • Laravel 5.5-8 are supported now. Use V6 if you need L5.1-5.4.
  • Our new minimum PHP version requirement is 7.1.3, up from 7.0.0.

Version 5 to 6

Going from Laravel Auto Presenter 5, to 6, note that:

  • We have a new Decoratable interface to determine if relations can be decorated. While this is not a BC break, since HasPresenter extends it, it is definitely worth noting.
  • v6 supports Laravel 5.5 now, without dropping support for 5.1+.
  • Our new minimum PHP version requirement is 7.0.0, up from 5.5.9.

Version 4 to 5

If you're upgrading from Laravel Auto Presenter 4, to 5, note that:

  • The BasePresenter no longer has a constructor, so you cannot call parent::__construct($resource).
  • The model is now injected using the setWrappedObject method, inherited from the BasePresenter.
  • V5 now supports Laravel 5.4 as well as 5.1, 5.2, and 5.3.

Usage

To show how it's used, we'll pretend that we have an Eloquent Post model. It doesn't have to be Eloquent, it could be any kind of class. But, this is a normal situation. The Post model represents a blog post.

I'm using really basic code examples here, so just focus on how the auto-presenter is used and ignore the rest.

use Example\Accounts\User;
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    protected $table = 'posts';
    protected $fillable = ['author_id', 'title', 'content', 'published_at'];

    public function author()
    {
        return $this->belongsTo(User::class, 'author_id');
    }
}

Also, we'll need a controller..

use Example\Accounts\Post;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\View;

class PostsController extends Controller
{
    public function getIndex()
    {
        $posts = Post::all();
        return View::make('posts.index', compact('posts'));
    }
}

and a view...

@foreach($posts as $post)
    <li>{{ $post->title }} - {{ $post->published_at }}</li>
@endforeach

In this example the published_at attribute is likely to be in the format: "Y-m-d H:i:s" or "2013-08-10 10:20:13". In the real world this is not what we want in our view. So, let's make a presenter that lets us change how the data from the Post class is rendered within the view.

use Carbon\Carbon;
use Example\Accounts\Post;
use McCool\LaravelAutoPresenter\BasePresenter;

class PostPresenter extends BasePresenter
{
    public function published_at()
    {
        $published = $this->wrappedObject->published_at;

        return Carbon::createFromFormat('Y-m-d H:i:s', $published)
            ->toFormattedDateString();
    }
}

Note that the model is injected by calling the setWrappedObject method, inherited from BasePresenter.

We need the post class to implement the interface.

use Example\Accounts\User;
use Example\Blog\PostPresenter;
use McCool\LaravelAutoPresenter\HasPresenter;
use Illuminate\Database\Eloquent\Model;

class Post extends Model implements HasPresenter
{
    protected $table = 'posts';
    protected $fillable = ['author_id', 'title', 'content', 'published_at'];

    public function author()
    {
        return $this->belongsTo(User::class, 'author_id');
    }

    public function getPresenterClass()
    {
        return PostPresenter::class;
    }
}

Now, with no additional changes our view will show the date in the desired format.

The Decoratable interface is used to allow the model's relations to be decorated, and the HasPresenter interface (which extends that one) is used to have the model itself decorated.

Troubleshooting

If an object isn't being decorated correctly in the view then there's a good chance that it's simply not in existence when the view begins to render. For example, lazily-loaded relationships won't be decorated. You can fix this by eager-loading them instead. Auth::user() will never be decorated. I prefer to bind $currentUser to my views, anyway.

If an object is a relation of another object and it isn't being decorated in the view, you might not have added the Decoratable interface to the other object. To fix this, add the Decoratable interface to the other object.

Security

If you discover a security vulnerability within this package, please send an email to [email protected]. All security vulnerabilities will be promptly addressed. You may view our full security policy here.

License

Laravel Auto Presenter is licensed under The MIT License (MIT).

For Enterprise

Available as part of the Tidelift Subscription

The maintainers of mccool/laravel-auto-presenter and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. Learn more.

Comments
  • [3.0] API support, field accessibility, test fixes and resolution

    [3.0] API support, field accessibility, test fixes and resolution

    As part of a new project, we require the need to use presenters that respond well to APIs, wiht some interesting requirements, that being - the ability to change the fields that are actually presented as part of the response.

    For an example - a user may be logged in as an admin, and when fetching forums via the api, should have more information available to them than a regular forum user. These updates allow a presenter to specify the fields available upon the presenter.

    In addition, presenters can now be used as part of a JSON response payload, by returning the presenter itself from the controller action.

    What's really cool about the current functionality, is that because the getPresenter is now a method - logic to define which presenter to be returned based on user level is easily supported.

    opened by kirkbushell 13
  • Decorate Pagination issue

    Decorate Pagination issue

        ```
        use McCool\LaravelAutoPresenter\Facades\AutoPresenter 
        $model = MyModel::where('status', '1')->paginate();
        AutoPresenter::decorate($model)->toArray();
    
        ```
    

    result:

    {
        total: 24,
        per_page: 50,
        current_page: 1,
        last_page: 1,
        next_page_url: null,
        prev_page_url: null,
        from: 1,
        to: 24,
        data: [
            { },
            { },
            { },
            { },
            { },
            { },
            { },
            { },
            { },
            { },
            { },
            { },
            { },
            { },
            { },
            { },
            { },
            { },
            { },
            { },
            { },
            { },
            { },
            { }
        ]
    }
    
    opened by k1ng440 11
  • [L5] Method does not exist

    [L5] Method does not exist

    I'm getting UserPresenter::username method does not exist when trying to use v3...

    The property_exists() in the __get() magic method returns false even when the key exists as an attribute on the model.

    opened by devonzara 10
  • Accessing undefined variables/methods causes problems in Twig

    Accessing undefined variables/methods causes problems in Twig

    I am using twig instead of blade as template engine and accessing undefined variables causes problems in Twig. If you try to output a variable that is not there with twig the default behavior is to output nothing. However since your Base Presenter throws an exception if a variable/method does not exist this breaks things!

    twig will translate {{ content.subpages }} to $content->subpages and if this does not exist it will try $content->subpages(); and if this is not found either it wont complain and simply wont output anything

    Unless there is a specific reason you are trowing an exception you should remove the below line to make this compatible with twig. i believe this will be a problem in Blade as well? not sure if blade haves the same way.

    in BasePresenter.php 
    public function __call($key, $args)
    the following line should be removed.
            throw new ResourceMethodNotFoundException('Presenter: '.get_called_class().'::'.$key.' method does not exist');
    
    opened by unitedworx 8
  • Infinite recursion in child-parent relations

    Infinite recursion in child-parent relations

    I'm seeing the following problem whenever I try to render a view with a form with an embedded document (using kristijanhusak/laravel-form-builder, but I don't think this is the cause). I'm not using laravel-auto-presenter for this class at all, so I don't understand why it's even getting involved. It seems to be confused/conflicting with my (jenssegers) MongoDB collection that implements an "embedsMany" relation that isn't a standard part of Eloquent. The error is only triggered when an embedded document exists.

    I have an Article model that contains an embedsMany relation to Notes. It is trying to decorate the Article model (which I never told it to do), and then decorate the related Note model. However, Note has a hasOne relation to the original article, which it then also tries to decorate (again), hence entering the infinite loop.

    Shouldn't laravel-auto-presenter only be trying to decorate models that implement HasPresenter? I tried adding this check if ($value instanceof HasPresenter) inside the foreach of LaravelAutoPresenterServiceProvider::setupEventListening() on line 69 and it solves the problem of calling the decorator when I haven't told it to. This is fine for the time being, but what if I want to actually use the decorator at some point for this model?

    FatalErrorException in Model.php line 3062: Maximum function nesting level of '200' reached, aborting!
        in Model.php line 3062
        at FatalErrorException->__construct() in HandleExceptions.php line 131
        at HandleExceptions->fatalExceptionFromError() in HandleExceptions.php line 116
        at HandleExceptions->handleShutdown() in HandleExceptions.php line 0
        ...
        at AtomDecorator->decorateRelations() in AtomDecorator.php line 49
        at AtomDecorator->decorate() in AtomDecorator.php line 79
        at AtomDecorator->decorateRelations() in AtomDecorator.php line 49
        at AtomDecorator->decorate() in CollectionDecorator.php line 46
        at CollectionDecorator->decorate() in AtomDecorator.php line 76
       ...
        at AtomDecorator->decorateRelations() in AtomDecorator.php line 49
        at AtomDecorator->decorate() in AtomDecorator.php line 79
        at AtomDecorator->decorateRelations() in AtomDecorator.php line 49
        at AtomDecorator->decorate() in CollectionDecorator.php line 46
        at CollectionDecorator->decorate() in AtomDecorator.php line 76
    *** The above 5 lines repeat until it gets cut off) ***
    
        at AtomDecorator->decorateRelations() in AtomDecorator.php line 49
        at AtomDecorator->decorate() in PresenterDecorator.php line 58
        at PresenterDecorator->decorate() in LaravelAutoPresenterServiceProvider.php line 69
        at LaravelAutoPresenterServiceProvider->McCool\LaravelAutoPresenter\{closure}() in Dispatcher.php line 218
        at call_user_func_array:{vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php:218}() in Dispatcher.php line 218
        at Dispatcher->fire() in LaravelAutoPresenterServiceProvider.php line 49
        at LaravelAutoPresenterServiceProvider->McCool\LaravelAutoPresenter\{closure}() in Dispatcher.php line 218
        at call_user_func_array:{vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php:218}() in Dispatcher.php line 218
        at Dispatcher->fire() in Factory.php line 513
        at Factory->callComposer() in View.php line 102
        at View->renderContents() in View.php line 78
        at View->render() in Form.php line 548
        at Form->render() in Form.php line 199
        at Form->renderRest() in helpers.php line 28
        at form_end() in b91f5562b3cccb29edcccc11751454df line 15
        in PhpEngine.php line 39
        at PhpEngine->evaluatePath() in CompilerEngine.php line 57
        at CompilerEngine->get() in View.php line 136
        at View->getContents() in View.php line 104
        at View->renderContents() in View.php line 78
        at View->render() in View.php line 152
        at View->gatherData() in View.php line 136
        at View->getContents() in View.php line 104
        at View->renderContents() in View.php line 78
        at View->render() in Response.php line 44
        at Response->setContent() in Response.php line 202
        at Response->__construct() in Router.php line 1198
        at Router->prepareResponse() in Router.php line 702
        at Router->Illuminate\Routing\{closure}() in Pipeline.php line 141
        at call_user_func:{vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:141}() in Pipeline.php line 141
        at Pipeline->Illuminate\Pipeline\{closure}() in Auth.php line 44
        at Auth->handle() in Pipeline.php line 125
        at Pipeline->Illuminate\Pipeline\{closure}() in Pipeline.php line 101
        at call_user_func:{vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:101}() in Pipeline.php line 101
        at Pipeline->then() in Router.php line 703
        at Router->runRouteWithinStack() in Router.php line 670
        at Router->dispatchToRoute() in Router.php line 628
        at Router->dispatch() in Kernel.php line 214
        at Kernel->Illuminate\Foundation\Http\{closure}() in Pipeline.php line 141
        at call_user_func:{vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:141}() in Pipeline.php line 141
        at Pipeline->Illuminate\Pipeline\{closure}() in VerifyCsrfToken.php line 43
        at VerifyCsrfToken->handle() in VerifyCsrfToken.php line 18
        at VerifyCsrfToken->handle() in Pipeline.php line 125
        at Pipeline->Illuminate\Pipeline\{closure}() in ShareErrorsFromSession.php line 55
        at ShareErrorsFromSession->handle() in Pipeline.php line 125
        at Pipeline->Illuminate\Pipeline\{closure}() in StartSession.php line 61
        at StartSession->handle() in Pipeline.php line 125
        at Pipeline->Illuminate\Pipeline\{closure}() in AddQueuedCookiesToResponse.php line 36
        at AddQueuedCookiesToResponse->handle() in Pipeline.php line 125
        at Pipeline->Illuminate\Pipeline\{closure}() in EncryptCookies.php line 40
        at EncryptCookies->handle() in Pipeline.php line 125
        at Pipeline->Illuminate\Pipeline\{closure}() in CheckForMaintenanceMode.php line 42
        at CheckForMaintenanceMode->handle() in Pipeline.php line 125
        at Pipeline->Illuminate\Pipeline\{closure}() in Pipeline.php line 101
        at call_user_func:{vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:101}() in Pipeline.php line 101
        at Pipeline->then() in Kernel.php line 115
        at Kernel->sendRequestThroughRouter() in Kernel.php line 84
        at Kernel->handle() in index.php line 53
        at {main}() in index.php line 0
    
    opened by hackel 7
  • Does the constructor argument name matter?

    Does the constructor argument name matter?

    Hi,

    I'm using 3.0.0-beta7 on the latest L5 commit.

    It seems to me that the argument name you give to the constructor of your Presenter class does really matter. It should be named $resource, if you call it $post (like the readme says) or $item or $whatever, it won't work. This is how it works for me:

    class PostPresenter extends BasePresenter
    {
        public function __construct(Post $resource)
        {
            $this->wrappedObject = $resource;
        }
    

    Is this by design, is this a bug, or is this me not using it correctly? The readme doesn't mention this.

    Anyway, thanks for the awesome package!

    opened by webcraft 7
  • Is it possible to turn off presenter

    Is it possible to turn off presenter

    The issue here is that I defined presenter, everything works like charm when I actually "want" to present data, but what about when I want to grab different data not just "presented".

    Here an example:

    I set presenter for eager loaded $user->with('comments'); for a view A, now I need different data (id) for view B, but when I try inside my view $user->comments->lists('id') I get error because presenter made it a string already. I need array of IDs for multiselect.

    Is there any way to tell presenter "do not present this time".

    opened by Kyslik 6
  • Allow subjects to be decorated within basic arrays in view data

    Allow subjects to be decorated within basic arrays in view data

    Currently the AutoPresenter only works for simple variables in the view data. This commit allows the Presenter to also decorate simple array structures, for example an array containing 2 collections of Models.

    opened by squigg 6
  • Presenter methods will never be hit

    Presenter methods will never be hit

    Reference line 43 of the BasePresnter class.

    The conditional checks if the entity has $key set before calling its presenter. If $key is a column in the database, it will be "set" by Eloquent's magic method. Hence, the presenter will never be hit.

    opened by a7y 6
  • Ensure that properties available through __get and __isset magic methods

    Ensure that properties available through __get and __isset magic methods

    This is a fix for #39. I had to make an executive decision to use isset() – for Eloquent models this will be fine. Other classes will need to define the corresponding magic methods, but this is a fringe case.

    Tests included.

    opened by jamierumbelow 6
  • Error presenting view (Help needed (?) )

    Error presenting view (Help needed (?) )

    I am getting a very weird error when I want to render a view:

    McCool \ LaravelAutoPresenter \ PresenterNotFoundException
    {"id":"1","username":"admin","nickname":"","email":"[email protected]","confirmation_code":"254468d9292ca8461029d465db50633b","confirmed":"1","about":"","created_at":"2014-06-08 16:51:14","updated_at":"2014-06-08 16:51:14","deleted_at":null,"use_gravatar":"1","avatar_file_name":null,"avatar_file_size":null,"avatar_content_type":null,"avatar_updated_at":null,"remember_token":null}
    

    I definitely know that I am eager-loading my objects, and I don't see any code where I add a raw string to the view, is this is a bug in this library or definitely in my code? I looked around and only saw things like $view->with('currentUser', Confide::user()) (as suggested in the README) and eager loaded eloquents (Post::with('comments', 'author')->get($id)).

    opened by spaceemotion 6
  • Access and format attribute of the pivot table

    Access and format attribute of the pivot table

    Hey,

    I have the following question, how can I use this package if I have many to many relationships between the model "worklist" and "task", the pivot table has attributes "interval" that I want to access and format it differently with the presenter.

    My situation: This is my Database: grafik

    My models: grafik

    opened by st-kovalenko 0
Releases(7.7.0)
Owner
Laravel Auto Presenter
Laravel Auto Presenter
Decorate Your Models and Write Clean/Reusable Code with Presenters.

Laravel Presenter A clean way to present your model attributes without putting them in the wrong file. Installation You can install the package via co

Coderflex 14 Dec 19, 2022
Use auto generated UUID slugs to identify and retrieve your Eloquent models.

Laravel Eloquent UUID slug Summary About Features Requirements Installation Examples Compatibility table Alternatives Tests About By default, when get

Khalyomede 25 Dec 14, 2022
cybercog 996 Dec 28, 2022
Auto generate routes for Laravel Livewire components

livewire-auto-routes Auto generate routes for Laravel Livewire Components. Requirements Livewire 2 Laravel 8 php 8 Installation composer require tanth

Tina Hammar 19 May 11, 2022
Auto Generate laravel api Documentation

Laravel Dark Documentation Generator ?? You can create Documentation for your api easily by using this library Installation: Require this package with

HusseinAlaa 6 May 8, 2022
Auto-generated Interface and Repository file via Repository pattern in Laravel

Auto-generated Repository Pattern in Laravel A repository is a separation between a domain and a persistent layer. The repository provides a collectio

Ngo Dinh Cuong 11 Aug 15, 2022
Laravel and Lumen Auto Hard Deleter

Laravel Auto Hard Deleter This package deletes soft deleted rows automatically after a time interval that you define. For Laravel and Lumen 6, 7, 8 In

Siavash Bamshadnia 38 Dec 28, 2022
PHP Simple Response, XML, JSON,... auto response with accept in request's header

simple-response Simple package to handle response properly in your API. This package does not include any dependency. Install Via Composer $ composer

Phuong Danh 3 Dec 8, 2021
An Eloquent Way To Filter Laravel Models And Their Relationships

Eloquent Filter An Eloquent way to filter Eloquent Models and their relationships Introduction Lets say we want to return a list of users filtered by

Eric Tucker 1.5k Jan 7, 2023
Easy creation of slugs for your Eloquent models in Laravel

Eloquent-Sluggable Easy creation of slugs for your Eloquent models in Laravel. NOTE: These instructions are for the latest version of Laravel. If you

Colin Viebrock 3.6k Dec 30, 2022
Sortable behaviour for Eloquent models

Sortable behaviour for Eloquent models This package provides a trait that adds sortable behaviour to an Eloquent model. The value of the order column

Spatie 1.2k Dec 22, 2022
Record the change log from models in Laravel

This package will help you understand changes in your Eloquent models, by providing information about possible discrepancies and anomalies that could

Owen IT Services 2.5k Dec 30, 2022
This package gives Eloquent models the ability to manage their friendships.

Laravel 5 Friendships This package gives Eloquent models the ability to manage their friendships. You can easily design a Facebook like Friend System.

Alex Kyriakidis 690 Nov 27, 2022
Automatically validating Eloquent models for Laravel

Validating, a validation trait for Laravel Validating is a trait for Laravel Eloquent models which ensures that models meet their validation criteria

Dwight Watson 955 Dec 25, 2022
Laravel Ban simplify blocking and banning Eloquent models.

Laravel Ban Introduction Laravel Ban simplify management of Eloquent model's ban. Make any model bannable in a minutes! Use case is not limited to Use

cybercog 879 Dec 30, 2022
A small package for adding UUIDs to Eloquent models.

A small package for adding UUIDs to Eloquent models. Installation You can install the package via composer: composer require ryangjchandler/laravel-uu

Ryan Chandler 40 Jun 5, 2022
The package lets you generate TypeScript interfaces from your Laravel models.

Laravel TypeScript The package lets you generate TypeScript interfaces from your Laravel models. Introduction Say you have a model which has several p

Boris Lepikhin 296 Dec 24, 2022
A Laravel package for multilingual models

Introduction If you want to store translations of your models into the database, this package is for you. This is a Laravel package for translatable m

Astrotomic 974 Dec 23, 2022
A Laravel package for attachment files to models

Laravel attachmentable package A package for attachment files to models Installation Run the command below to add this package: composer require larav

Laravel Iran Community 4 Jan 18, 2022