kouks / laravel-casters by kouks

Provides a convenient way to cast and transform your models for API purposes.
144
1
2
Package Data
Maintainer Username: kouks
Maintainer Contact: kouks.koch@gmail.com (Pavel Koch)
Package Create Date: 2016-11-06
Package Last Update: 2016-11-07
Language: PHP
License: MIT
Last Refreshed: 2024-04-26 03:19:25
Package Statistics
Total Downloads: 144
Monthly Downloads: 0
Daily Downloads: 0
Total Stars: 1
Total Watchers: 2
Total Forks: 0
Total Open Issues: 3

Laravel Casters

Scrutinizer Code Quality Latest Version on Packagist Build Status StyleCI

Contents

Installation

Composer

Open your console and cd into your Laravel project. Run:

composer require kouks/laravel-casters

Yeah, you are pretty much ready to go right now...

Usage

Creating casters

You can put you casters pretty much anywhere, but I suppose you put them in a app/Casters directory. Now make a new php class, we are gonna be casting a Post model here. Suppose our model has only id, title, body and active database columns, along with timestamps.

namespace App\Casters;

use Koch\Casters\Caster;

class PostCaster extends Caster
{
    //
}

Good! Now we can put in our first casting rule.

namespace App\Casters;

use Koch\Casters\Caster;

class PostCaster extends Caster
{
    public castRules()
    {
        return [
            'title',
        ];
    }
}

This caster is going to cast only the title column, leaving its contents unchanged. More about making rules in the follwing section.

Making your rules

Let's move on how to make your own rules, there are four ways to achieve that at this moment.

Simple casting/renaming columns

There was an example of a simple cast rule in the previous section. Let us reviews it again and att something up.

namespace App\Casters;

use Koch\Casters\Caster;

class PostCaster extends Caster
{
    public castRules()
    {
        return [
            'title',
            'body' => 'long_text',
        ];
    }
}

This rule will remain the title column unchanged completely, and renaming the body column to long_text, however leaving the contents unchanged.

Using cast queries

Similar to Laravel's validation rules, you can use queries to cast.

namespace App\Casters;

use Koch\Casters\Caster;

class PostCaster extends Caster
{
    public castRules()
    {
        return [
            'active' => '!name:is_active|type:bool',
        ];
    }
}

Note the ! before the cast rule. This says that we want to use a query. This simple query renames the active column to is_active and casts its contents to a boolean. All documentation on cast queries can be found in the Cast queries section.

Casting using a closure

As a value of the cast rules, you can speficy a closure, which will determine what data to return.

namespace App\Casters;

use App\Post;
use Koch\Casters\Caster;

class PostCaster extends Caster
{
    public castRules()
    {
        return [
            'text' => function (Post $post) {
                return str_limit($post->body, 100);
            },
        ];
    }
}

This particular query is going to cast a new column body and as its contents it is going to use whatever is returned from the closure. In this case - the post body limited to 100 characters. Note that you are given an instance of the model that is being cast in the closure arguments.

Casting using a method

You can even use other methods on the caster class to determine what is going to be cast.

namespace App\Casters;

use App\Post;
use Koch\Casters\Caster;

class PostCaster extends Caster
{
    public castRules()
    {
        return [
            'draft' => '@isDraft',
        ];
    }
    
    public function isDraft(Post $post)
    {
        return ! $post->active;
    }
}

Notice the @ sign - it is to determine that we want to use a caster class method to do the casting. This cast is (similarly to the closure one) create a new column draft, which is just a negation of the active column in this example. You are given the model instance in this case, too.

Actual casting

The actual casting gets really simple, there are two ways to cast you data at this moment.

Casting a single model

Let us have a controller action show, which is going to cast our model, and retrun it as json.

...

class PostController extends Controller
{
    public function show(Post $post, PostCaster $caster)
    {
        return $caster->cast($post);
    }
}

Note that you can use DI for your casters, as with anything in Laravel. Then you call a single cast method and provide you model instance in the arguments. If we were to combine all the example casters, the returned json could look following:

{
  "id": 1,
  "title": "Some title",
  "long_text": "Some long text...",
  "text": "Some long text...",
  "active": true,
  "draft": false,
  "updated_at": {
    "date": "2016-11-01 00:38:03.000000",
    "timezone_type": 3,
    "timezone": "UTC"
  },
  "created_at": {
    "date": "2016-08-06 17:58:09.000000",
    "timezone_type": 3,
    "timezone": "UTC"
  }
}

Note that in the example above, the text field is going to be limited to 100 characters.

Casting a collection

You cast collections in the same way that you would cast single models. Only difference is that you are given back array of cast models, instead of a single one.

Casting directly from model instance

This package provides the Koch\Casters\Behavior\Castable trait, which lets you do both casting a single model as follows:

$post->cast($caster);

and a collection directly from you query:

Post::cast($caster);

Also note that if you specify a full class path to the caster in you model, you can omit the caster variable completely.

...
class Post extends Model
{
    use Castable;

    protected $caster = App\Casters\PostCaster::class;
}

Lets you do this fancy stuff:

$post->cast();

Post::cast();

If you follow the convention of storing your casters in the app/Casters directory as well as the naming conventions, this package will try to find related caster in that location - this lets you leave out even the caster property.

Casting relationships

There, obviously, is a way to cast relationships. Suppose there is a related model Comments, wich has a many-one relationship with our Post model. Also suppose that there is a CommentCaster set up. Look at the following code:

namespace App\Casters;

use App\Post;
use Koch\Casters\Caster;

class PostCaster extends Caster
{
    protected $commentCaster;
    
    public function __construct(CommentCaster $commentCaster)
    {
        $this->commentCaster = $commentCaster;
    }
    
    public castRules()
    {
        return [
            'comments' => function (Post $post) {
                return $this->commentCaster->cast($post->comments);
            },
        ];
    }
}

Note that even here you can levarage Laravel's DI. You now create a new column comments, which is then populated by the closure. This closure usis the injected caster to cast the comments relationship. You can do the same with casting via methods.

Also beware of cycling your casts. At the moment, there is no check for that. So if you were to cast your comments relationship on the PostCaster and do the same vice versa, you'd end end with a 500. Feel free to submit a PR correcting this issue.

Cast queries

At this moment, there are only two queries. I am open for suggestions to add more.

  • name:new_name
  • type:new_type - accepts int, string, bool and float

Abstracion

There is a Koch\Casters\Contracts\Caster interface, which you could use for example with the repository pattern, where you have a parent Repository class which deals with all the stuff around any model, including casting it. You might want to inject the caster though a PostRepository constructor, in which case, the parent class would require the contract.

FAQ

Nobody has ever asked me anything about this package so I can't determine the frequency of questions.