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...
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.
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.
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.
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.
At this moment, there are only two queries. I am open for suggestions to add more.
name:new_name
type:new_type
- acceptsint
,string
,bool
andfloat
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.
Nobody has ever asked me anything about this package so I can't determine the frequency of questions.