Skip to content

Commit

Permalink
v1.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
slruslan committed Apr 17, 2017
0 parents commit fa0464f
Show file tree
Hide file tree
Showing 3 changed files with 239 additions and 0 deletions.
90 changes: 90 additions & 0 deletions README.md
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)
20 changes: 20 additions & 0 deletions composer.json
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/"}
}
}
129 changes: 129 additions & 0 deletions src/CustomCasts.php
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);
}
}

0 comments on commit fa0464f

Please sign in to comment.