From fa0464fa153493dd14f29a0cbe13236975e964f8 Mon Sep 17 00:00:00 2001 From: Ruslan Slinkov Date: Mon, 17 Apr 2017 20:38:35 +0700 Subject: [PATCH] v1.0.0 --- README.md | 90 +++++++++++++++++++++++++++++++ composer.json | 20 +++++++ src/CustomCasts.php | 129 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 239 insertions(+) create mode 100644 README.md create mode 100755 composer.json create mode 100644 src/CustomCasts.php diff --git a/README.md b/README.md new file mode 100644 index 0000000..b9b42cc --- /dev/null +++ b/README.md @@ -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: me@slinkov.xyz + +VK: [vk.com/slruslan](https://vk.com/slruslan) diff --git a/composer.json b/composer.json new file mode 100755 index 0000000..718fc97 --- /dev/null +++ b/composer.json @@ -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": "me@slinkov.xyz", + "homepage": "http://slinkov.xyz" + } + ], + "require": { + "php": ">=5.6", + }, + "autoload": { + "psr-4": {"Slruslan\\CustomCasts\\": "src/"} + } +} diff --git a/src/CustomCasts.php b/src/CustomCasts.php new file mode 100644 index 0000000..76f3c7f --- /dev/null +++ b/src/CustomCasts.php @@ -0,0 +1,129 @@ +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); + } +} \ No newline at end of file