Created
January 22, 2020 15:38
-
-
Save chasecmiller/01cb4c0ef72b81aa13e46d1888c8c0bf to your computer and use it in GitHub Desktop.
A Laravel trait to allow any data to be added to a model .
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
<?php | |
// Customize this. | |
namespace Crumbls\ExtendedData\Traits; | |
use Str; | |
use Illuminate\Support\Facades\Cache; | |
use Illuminate\Support\Facades\Schema; | |
use Illuminate\Support\Facades\DB; | |
/** | |
* Trait HasExtendedData | |
* A simple way to add data to a model while it's still being developed. | |
* Just add a MySQL JSON column named extended to the table. | |
* When you're done, it's generally best practice to create actual columns for data in a relational database. | |
* @package Crumbls\ExtendedData\Traits | |
*/ | |
trait HasExtendedData | |
{ | |
/** | |
* Getter for extended column name. | |
* @return string | |
*/ | |
public function getExtendedColumn() | |
{ | |
return 'extended'; | |
} | |
/** | |
* Bootable for our trait. | |
*/ | |
public static function bootHasExtendedData() | |
{ | |
static::saving(function ($model) { | |
self::addExtendedColumn($model); | |
$key = $model->getExtendedColumn(); | |
}); | |
static::updating(function ($model) { | |
self::addExtendedColumn($model); | |
$key = $model->getExtendedColumn(); | |
}); | |
static::retrieved(function ($model) { | |
self::addExtendedColumn($model); | |
}); | |
static::creating(function ($model) { | |
self::addExtendedColumn($model); | |
}); | |
} | |
/** | |
* Get the casts array. | |
* | |
* @return array | |
*/ | |
public function getCasts() | |
{ | |
if (!array_key_exists($this->getExtendedColumn(), $this->casts)) { | |
$name = $this->getExtendedColumn(); | |
$this->casts[$name] = 'array'; | |
} | |
if ($this->getIncrementing()) { | |
return array_merge([$this->getKeyName() => $this->getKeyType()], $this->casts); | |
} | |
return $this->casts; | |
} | |
/** | |
* Setup our extended data. | |
* @param $model | |
* @return bool | |
*/ | |
protected static function addExtendedColumn($model) | |
{ | |
$name = $model->getExtendedColumn(); | |
if (array_key_exists($name, $model->getCasts())) { | |
return $model; | |
} | |
$model->casts[$name] = 'array'; | |
return $model; | |
} | |
/** | |
* Dynamically retrieve attributes on the model. | |
* | |
* @param string $key | |
* @return mixed | |
*/ | |
public function __get($key) | |
{ | |
$ret = parent::getAttribute($key); | |
$name = $this->getExtendedColumn(); | |
if (!$ret && array_key_exists($name, $this->attributes)) { | |
$temp = is_string($this->attributes[$name]) ? (array)@json_decode($this->attributes[$name], true) : $this->attributes[$name]; | |
if (is_null($temp)) { | |
return null; | |
} | |
if (array_key_exists($key, $temp)) { | |
return $temp[$key]; | |
} | |
} | |
return $ret; | |
} | |
/** | |
* Set a given attribute on the model. | |
* Overridden to add our extra data heree. | |
* | |
* @param string $key | |
* @param mixed $value | |
* @return mixed | |
*/ | |
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)) { | |
return $this->setMutatedAttributeValue($key, $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 && $this->isDateAttribute($key)) { | |
$value = $this->fromDateTime($value); | |
} | |
if ($this->isJsonCastable($key) && !is_null($value)) { | |
$value = $this->castAttributeAsJson($key, $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); | |
} | |
// Cached table names | |
$schema = Cache::remember('table-schema-' . $this->getTable(), 60, function () { | |
return $this->getTableColumns(); | |
}); | |
if (in_array($key, $schema)) { | |
$this->attributes[$key] = $value; | |
} else { | |
$this->setExtendedOption($key, $value); | |
} | |
return $this; | |
} | |
/** | |
* We can improve this, but it works for now. | |
* @param string $key | |
* @param $value | |
* @return bool | |
*/ | |
public function setExtendedOption(string $key, $value): bool | |
{ | |
$name = $this->getExtendedColumn(); | |
$temp = $this->$name; | |
$temp[$key] = $value; | |
$this->$name = $temp; | |
return true; | |
} | |
/** | |
* Get a collection of our table columns. | |
* @param null $table | |
* @return mixed | |
*/ | |
public function getTableColumns($table = null) | |
{ | |
if (!$table) { | |
$table = $this->getTable(); | |
} | |
return DB::getSchemaBuilder()->getColumnListing($table); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment