Skip to content

Instantly share code, notes, and snippets.

@hackel
Created March 8, 2017 18:47
Show Gist options
  • Save hackel/9e2892f8609414741fee58cf7ecaaf44 to your computer and use it in GitHub Desktop.
Save hackel/9e2892f8609414741fee58cf7ecaaf44 to your computer and use it in GitHub Desktop.
Trait for Jenssegers/Mongodb models that will automatically unset any attributes contained in the $clearable (or $fillable) property.
<?php
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Arr;
/**
* Class DropUnsetAttributes will drop any fields contained in the model's $clearable
* array if they are not included in the attributes on mass-assignment (fill).
*
* @package Quirks\ModelTraits
*/
trait DropUnsetAttributes
{
public static function bootDropUnsetAttributes()
{
static::saving([static::class, 'dropEmptyClearables']);
static::saved([static::class, 'dropAttributesToUnset']);
}
/**
* This does *not* currently work for embedded relations
*
* @param static $model
*/
public static function dropEmptyClearables($model)
{
foreach ($model->getClearable() as $key) {
if ($model->isDirty($key) && empty($model->$key)) {
unset($model->$key);
}
}
}
/**
* @param static $model
*/
public static function dropAttributesToUnset($model)
{
if (!$model->getParentRelation()) {
if (count($model->getAttributesToUnset())) {
$model->drop($model->getAttributesToUnset());
}
}
}
/**
* Get the attributes that have been removed since last sync in dot notation.
* Compare all attributes recursively by first converting to dot notation.
*
* @return array
*/
public function getAttributesToUnset()
{
$dirty = [];
foreach (array_dot_assoc($this->original) as $key => $value) {
if (($value !== [] && !array_has($this->getAttributes(), $key))) {
$dirty[] = $key;
}
}
return $dirty;
}
/**
* Get list of clearable attributes, or use Fillable if model does not define $clearable.
*
* @return mixed
*/
public function getClearable()
{
return $this->clearable && count($this->clearable) ? $this->clearable : $this->fillable;
}
/**
* Remove any attributes from the model that are in the $clearable or
* $fillable array, but are not present in $attributes
*
* @param $attributes
*/
public function unsetClearable($attributes)
{
foreach ($this->getClearable() as $field) {
if (isset($this->$field) && !isset($attributes[$field])) {
unset($this->$field);
}
}
}
/**
* Remove one or more fields (immediately).
* Call parent::__unset instead of $this so we don't unset the field twice
*
* @param mixed $columns
* @return int
*/
public function drop($columns)
{
if (!is_array($columns)) {
$columns = [$columns];
}
// Unset attributes
foreach ($columns as $column) {
parent::__unset($column);
}
// Perform unset only on current document
return $this->newQuery()->where($this->getKeyName(), $this->getKey())->unset($columns);
}
/**
* Perform a model update operation. Check attributes to unset so events are fired and timestamps are updated.
*
* @see \Illuminate\Database\Eloquent\Model::performUpdate
* @param Builder $query
* @param array $options
* @return bool
*/
protected function performUpdate(Builder $query, array $options = [])
{
$dirty = array_merge($this->getDirty(), $this->getAttributesToUnset());
if (count($dirty) > 0) {
// If the updating event returns false, we will cancel the update operation so
// developers can hook Validation systems into their models and cancel this
// operation if the model does not pass validation. Otherwise, we update.
if ($this->fireModelEvent('updating') === false) {
return false;
}
// First we need to create a fresh query instance and touch the creation and
// update timestamp on the model which are maintained by us for developer
// convenience. Then we will just continue saving the model instances.
if ($this->timestamps && Arr::get($options, 'timestamps', true)) {
$this->updateTimestamps();
}
// Once we have run the update operation, we will fire the "updated" event for
// this model instance. This will allow developers to hook into these after
// models are updated, giving them a chance to do any special processing.
$dirty = $this->getDirty();
if (count($dirty) > 0) {
$numRows = $this->setKeysForSaveQuery($query)->update($dirty);
$this->fireModelEvent('updated', false);
}
}
return true;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment