-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit fa0464f
Showing
3 changed files
with
239 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
# Laravel Eloquent custom casts trait | ||
|
||
[![Latest Version on Packagist](https://img.shields.io/packagist/v/slruslan/laravel-eloquent-custom-casts.svg?style=flat-square)](https://packagist.org/packages/slruslan/laravel-eloquent-custom-casts) | ||
![License GPL](http://img.shields.io/badge/license-GPL-blue.svg?style=flat-square) | ||
|
||
Laravel Eloquent provides an Attribute Casting function, that allows you to automatically convert attributes to common data types. | ||
By default, supported cast types are: ```integer, real, float, double, string, boolean, object, array, collection, date, datetime, and timestamp.``` | ||
|
||
As you can see, custom class types are not supported. This trait adds this support, so you automatically serialize and deserialize your custom classes. | ||
|
||
## Installation | ||
|
||
Using Composer: | ||
|
||
``` bash | ||
$ composer require slruslan/laravel-eloquent-custom-casts --dev | ||
``` | ||
|
||
## Usage | ||
|
||
1. Create a field of TEXT type where your data will be stored. | ||
|
||
2. To enable trait, add ```use Slruslan\CustomCasts\CustomCasts;``` line to your Eloquent Model class. | ||
|
||
For example: | ||
|
||
```php | ||
namespace App\Models; | ||
|
||
use Illuminate\Database\Eloquent\Model; | ||
use Slruslan\CustomCasts\CustomCasts; | ||
|
||
class Post extends Model | ||
{ | ||
use CustomCasts; | ||
|
||
``` | ||
|
||
3. Add your fields and class names to be serialized in protected $casts array. | ||
|
||
```php | ||
protected $casts = [ | ||
'geo_location' => \App\Services\CustomLocation::class, | ||
'another_custom_field' => \App\Services\AnotherCustomField::class | ||
]; | ||
``` | ||
|
||
4. You're ready to use your model. Here is a basic example: | ||
|
||
```php | ||
$post = Post::find(1); | ||
|
||
// For example, imagine we have an \App\Services\CustomLocation class, | ||
// that implements any custom logics and for some reasons | ||
// we have to store it in DB as it is. | ||
|
||
// Let's instantinate it. | ||
$location = new \App\Services\CustomLocation(55.9937441, 92.7521816); | ||
|
||
// And call some methods, imagine we have them there. | ||
$location->updatePosition(); | ||
$location->callAPI(); | ||
|
||
// After that, we want to save it in our post model. | ||
// We can just assign the value and call default save() method. | ||
$post->geo_location = $location; | ||
$post->save(); | ||
|
||
// It's saved. Let's get a post again | ||
// and check the field class. | ||
|
||
$post = Post::find(1); | ||
|
||
var_dump($post instanceof \App\Services\CustomLocation); | ||
|
||
// Outputs: | ||
// bool(true) | ||
``` | ||
|
||
## License | ||
|
||
GNU General Public License v3.0 (GPL). Refer to the [LICENSE](LICENSE) file to get more info. | ||
|
||
## Author contacts: | ||
|
||
You can ask me any questions by: | ||
|
||
Email: [email protected] | ||
|
||
VK: [vk.com/slruslan](https://vk.com/slruslan) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
{ | ||
"name": "slruslan/laravel-eloquent-custom-casts", | ||
"description": "Provides Laravel Eloquent trait to enable custom class object casting.", | ||
"version": "1.0", | ||
"license": "GPL", | ||
"homepage": "https://github.com/slruslan/laravel-eloquent-custom-casts", | ||
"authors": [ | ||
{ | ||
"name": "Ruslan Slinkov", | ||
"email": "[email protected]", | ||
"homepage": "http://slinkov.xyz" | ||
} | ||
], | ||
"require": { | ||
"php": ">=5.6", | ||
}, | ||
"autoload": { | ||
"psr-4": {"Slruslan\\CustomCasts\\": "src/"} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
<?php | ||
|
||
namespace Slruslan\CustomCasts; | ||
|
||
use Illuminate\Support\Str; | ||
|
||
trait CustomCasts | ||
{ | ||
/** | ||
* Cast an attribute to a native PHP type. | ||
* | ||
* @param string $key | ||
* @param mixed $value | ||
* @return mixed | ||
*/ | ||
protected function castAttribute($key, $value) | ||
{ | ||
if (is_null($value)) { | ||
return $value; | ||
} | ||
|
||
switch ($this->getCastType($key)) { | ||
case 'int': | ||
case 'integer': | ||
return (int) $value; | ||
case 'real': | ||
case 'float': | ||
case 'double': | ||
return (float) $value; | ||
case 'string': | ||
return (string) $value; | ||
case 'bool': | ||
case 'boolean': | ||
return (bool) $value; | ||
case 'object': | ||
return $this->fromJson($value, true); | ||
case 'array': | ||
case 'json': | ||
return $this->fromJson($value); | ||
case 'collection': | ||
return new BaseCollection($this->fromJson($value)); | ||
case 'date': | ||
case 'datetime': | ||
return $this->asDateTime($value); | ||
case 'timestamp': | ||
return $this->asTimeStamp($value); | ||
default: | ||
return $this->toClass($this->getCastType($key), $value); | ||
} | ||
} | ||
|
||
/** | ||
* Set a given attribute on the model. | ||
* | ||
* @param string $key | ||
* @param mixed $value | ||
* @return $this | ||
*/ | ||
public function setAttribute($key, $value) | ||
{ | ||
// First we will check for the presence of a mutator for the set operation | ||
// which simply lets the developers tweak the attribute as it is set on | ||
// the model, such as "json_encoding" an listing of data for storage. | ||
if ($this->hasSetMutator($key)) { | ||
$method = 'set'.Str::studly($key).'Attribute'; | ||
|
||
return $this->{$method}($value); | ||
} | ||
|
||
// If an attribute is listed as a "date", we'll convert it from a DateTime | ||
// instance into a form proper for storage on the database tables using | ||
// the connection grammar's date format. We will auto set the values. | ||
elseif ($value && (in_array($key, $this->getDates()) || $this->isDateCastable($key))) { | ||
$value = $this->fromDateTime($value); | ||
} | ||
|
||
if ($this->isJsonCastable($key) && ! is_null($value)) { | ||
$value = $this->asJson($value); | ||
} | ||
|
||
// If this attribute contains a JSON ->, we'll set the proper value in the | ||
// attribute's underlying array. This takes care of properly nesting an | ||
// attribute in the array's value in the case of deeply nested items. | ||
if (Str::contains($key, '->')) { | ||
return $this->fillJsonAttribute($key, $value); | ||
} | ||
|
||
// CustomCasts: | ||
// | ||
// If an attribute is listed as a a class name and it's valid, | ||
// we seriaize it to string using php serialize() function | ||
if ($this->isCustomCastable($key) && !is_null($value)) { | ||
$value = serialize($value); | ||
} | ||
|
||
$this->attributes[$key] = $value; | ||
|
||
return $this; | ||
} | ||
|
||
|
||
/** | ||
* Determine whether a value is a valid class name | ||
* and can be custom-casted. | ||
* | ||
* @param string $key | ||
* @return bool | ||
*/ | ||
protected function isCustomCastable($key) | ||
{ | ||
return $this->hasCast($key) && class_exists($this->getCastType($key)); | ||
} | ||
|
||
/** | ||
* Unserialize string value, if it is a valid class | ||
* Otherwise return the original value. | ||
* | ||
* @param string $class | ||
* @param string $value | ||
* @return mixed | ||
*/ | ||
protected function toClass($class, $value) | ||
{ | ||
if(!class_exists($class)) | ||
return $value; | ||
|
||
return unserialize($value); | ||
} | ||
} |