-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Added support for ObjectIds in fields other than _id & improved date casting #1523
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
1ae0d19
added objectid to the casts array
Flambe e86e2f5
documentation
Flambe 8c5fbaf
removed unnecessary space
Flambe ac77d91
added handling for arrays of objectids
Flambe 7a7a31e
improved docs example
Flambe 6b25b17
Ensure $casts are obeyed for pushing values
ScottSpittle 204472d
Ensure $casts are obeyed for where query building
ScottSpittle fd027b3
added support for full date casting, push(), arrays, date casting
Flambe 4f7fd0f
styleci fixes
Flambe 4e67ac4
pulled changes from upstream
Flambe 37e918d
fixed closures in where failing
Flambe d56d326
Merge branch 'master' into casting
Flambe File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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 |
---|---|---|
@@ -1,3 +1,4 @@ | ||
|
||
ARG PHP_VERSION=7.2 | ||
|
||
FROM php:${PHP_VERSION}-cli | ||
|
This file contains hidden or 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
This file contains hidden or 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 |
---|---|---|
|
@@ -97,6 +97,10 @@ protected function asDateTime($value) | |
return Carbon::createFromTimestamp($value->toDateTime()->getTimestamp()); | ||
} | ||
|
||
if ($value instanceof Carbon) { | ||
return $value; | ||
} | ||
|
||
return parent::asDateTime($value); | ||
} | ||
|
||
|
@@ -159,53 +163,144 @@ protected function getAttributeFromArray($key) | |
return parent::getAttributeFromArray($key); | ||
} | ||
|
||
/** | ||
* Is the given array an associative array | ||
* | ||
* @param array $arr | ||
* | ||
* @return bool | ||
*/ | ||
private function isAssoc(array $arr) | ||
{ | ||
// if it's empty, we presume that it's sequential | ||
if ([] === $arr) { | ||
return false; | ||
} | ||
|
||
// if $arr[0] does not exist, it must to be a associative array | ||
if (!array_key_exists(0, $arr)) { | ||
return true; | ||
} | ||
|
||
// strict compare the array keys against a range of the same length | ||
return array_keys($arr) !== range(0, count($arr) - 1); | ||
} | ||
|
||
/** | ||
* Flatten an associative array of attributes to dot notation | ||
* | ||
* @param string $key | ||
* @param $value | ||
* | ||
* @return \Illuminate\Support\Collection | ||
*/ | ||
private function flattenAttributes($key, $value) | ||
{ | ||
$values = collect(); | ||
|
||
if (is_array($value) && $this->isAssoc($value)) { | ||
foreach ($value as $k => $value) { | ||
$values = $values->merge($this->flattenAttributes("$key.$k", $value)); | ||
} | ||
} else { | ||
$values->put($key, $value); | ||
} | ||
|
||
return $values; | ||
} | ||
|
||
/** | ||
* @inheritdoc | ||
*/ | ||
public function setAttribute($key, $value) | ||
{ | ||
// Convert _id to ObjectID. | ||
if ($key == '_id' && is_string($value)) { | ||
$builder = $this->newBaseQueryBuilder(); | ||
|
||
$value = $builder->convertKey($value); | ||
} // Support keys in dot notation. | ||
elseif (Str::contains($key, '.')) { | ||
if (in_array($key, $this->getDates()) && $value) { | ||
$value = $this->fromDateTime($value); | ||
$values = $this->flattenAttributes($key, $value); | ||
|
||
$values->each(function ($val, $k) { | ||
// Convert to ObjectID. | ||
if ($this->isCastableToObjectId($k)) { | ||
$builder = $this->newBaseQueryBuilder(); | ||
|
||
if (is_array($val)) { | ||
foreach ($val as &$v) { | ||
$v = $builder->convertKey($v); | ||
} | ||
} else { | ||
$val = $builder->convertKey($val); | ||
} | ||
} // Convert to UTCDateTime | ||
else { | ||
if (in_array($k, $this->getDates()) && $val) { | ||
if (is_array($val)) { | ||
foreach ($val as &$v) { | ||
$v = $this->fromDateTime($v); | ||
} | ||
} else { | ||
$val = $this->fromDateTime($val); | ||
} | ||
} | ||
} | ||
|
||
Arr::set($this->attributes, $key, $value); | ||
// Support keys in dot notation. | ||
if (Str::contains($k, '.')) { | ||
Arr::set($this->attributes, $k, $val); | ||
|
||
return; | ||
} | ||
return; | ||
} | ||
|
||
parent::setAttribute($k, $val); | ||
}); | ||
|
||
return parent::setAttribute($key, $value); | ||
return $this; | ||
} | ||
|
||
/** | ||
* @inheritdoc | ||
*/ | ||
public function attributesToArray() | ||
{ | ||
// Convert dates before parent method call so that all dates have the same format | ||
foreach ($this->getDates() as $key) { | ||
if (!Arr::has($this->attributes, $key)) { | ||
continue; | ||
} | ||
|
||
$val = Arr::get($this->attributes, $key); | ||
|
||
if (is_array($val)) { | ||
foreach ($val as &$v) { | ||
$v = $this->asDateTime($v)->format('Y-m-d H:i:s.v'); | ||
} | ||
} elseif ($val) { | ||
$val = $this->asDateTime($val)->format('Y-m-d H:i:s.v'); | ||
} | ||
|
||
Arr::set($this->attributes, $key, $val); | ||
} | ||
|
||
$attributes = parent::attributesToArray(); | ||
|
||
// Because the original Eloquent never returns objects, we convert | ||
// MongoDB related objects to a string representation. This kind | ||
// of mimics the SQL behaviour so that dates are formatted | ||
// nicely when your models are converted to JSON. | ||
foreach ($attributes as $key => &$value) { | ||
if ($value instanceof ObjectID) { | ||
$value = (string) $value; | ||
$this->getObjectIds()->each(function ($key) use (&$attributes) { | ||
if (!Arr::has($attributes, $key)) { | ||
return; | ||
} | ||
} | ||
|
||
// Convert dot-notation dates. | ||
foreach ($this->getDates() as $key) { | ||
if (Str::contains($key, '.') && Arr::has($attributes, $key)) { | ||
Arr::set($attributes, $key, (string) $this->asDateTime(Arr::get($attributes, $key))); | ||
$value = Arr::get($attributes, $key); | ||
|
||
if (is_array($value)) { | ||
foreach ($value as &$val) { | ||
$val = (string) $val; | ||
} | ||
} else { | ||
$value = (string) $value; | ||
} | ||
} | ||
|
||
Arr::set($attributes, $key, $value); | ||
}); | ||
|
||
return $attributes; | ||
} | ||
|
@@ -218,6 +313,18 @@ public function getCasts() | |
return $this->casts; | ||
} | ||
|
||
/** | ||
* Get a list of the castable ObjectId fields | ||
* | ||
* @return \Illuminate\Support\Collection | ||
*/ | ||
public function getObjectIds() | ||
{ | ||
return collect($this->getCasts())->filter(function ($val) { | ||
return 'objectid' === $val; | ||
})->keys()->push('_id'); | ||
} | ||
|
||
/** | ||
* @inheritdoc | ||
*/ | ||
|
@@ -293,6 +400,22 @@ public function push() | |
|
||
$this->pushAttributeValues($column, $values, $unique); | ||
|
||
// Convert $casts on push() | ||
if ($this->isCastableToObjectId($column)) { | ||
foreach ($values as &$value) { | ||
if (is_string($value)) { | ||
$value = new ObjectId($value); | ||
} | ||
} | ||
} | ||
|
||
// Convert dates on push() | ||
if (in_array($column, $this->getDates())) { | ||
foreach ($values as &$value) { | ||
$value = $this->fromDateTime($value); | ||
} | ||
} | ||
|
||
return $query->push($column, $values, $unique); | ||
} | ||
|
||
|
@@ -338,11 +461,94 @@ protected function pushAttributeValues($column, array $values, $unique = false) | |
$current[] = $value; | ||
} | ||
|
||
$this->attributes[$column] = $current; | ||
// Support for dot notation keys | ||
if (Str::contains($column, '.')) { | ||
Arr::set($this->attributes, $column, $current); | ||
} else { | ||
$this->attributes[$column] = $current; | ||
} | ||
|
||
$this->syncOriginalAttribute($column); | ||
} | ||
|
||
/** | ||
* @inheritdoc | ||
*/ | ||
public function syncOriginalAttribute($attribute) | ||
{ | ||
// Support for dot notation keys | ||
if (Str::contains($attribute, '.')) { | ||
$value = Arr::get($this->attributes, $attribute); | ||
|
||
Arr::set($this->original, $attribute, $value); | ||
} else { | ||
$this->original[$attribute] = $this->attributes[$attribute]; | ||
} | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* @inheritdoc | ||
*/ | ||
protected function addDateAttributesToArray(array $attributes) | ||
{ | ||
foreach ($this->getDates() as $key) { | ||
if (!isset($attributes[$key])) { | ||
continue; | ||
} | ||
|
||
// Support for array of dates | ||
if (is_array($attributes[$key])) { | ||
foreach ($attributes[$key] as &$value) { | ||
$value = $this->serializeDate($this->asDateTime($value)); | ||
} | ||
} else { | ||
$attributes[$key] = $this->serializeDate($this->asDateTime($attributes[$key])); | ||
} | ||
} | ||
|
||
return $attributes; | ||
} | ||
|
||
/** | ||
* @inheritdoc | ||
*/ | ||
public function getAttributeValue($key) | ||
{ | ||
$value = $this->getAttributeFromArray($key); | ||
|
||
// If the attribute is listed as a date, we will convert it to a DateTime | ||
// instance on retrieval, which makes it quite convenient to work with | ||
// date fields without having to create a mutator for each property. | ||
if (in_array($key, $this->getDates()) && !is_null($value)) { | ||
if (is_array($value)) { | ||
foreach ($value as &$val) { | ||
$val = $this->asDateTime($val); | ||
} | ||
} else { | ||
$value = $this->asDateTime($value); | ||
} | ||
|
||
return $value; | ||
} | ||
|
||
// Also convert objectIds to strings | ||
if ($this->getObjectIds()->search($key) !== false && !is_null($value)) { | ||
if (is_array($value)) { | ||
foreach ($value as &$val) { | ||
$val = (string) $val; | ||
} | ||
} else { | ||
$value = (string) $value; | ||
} | ||
|
||
return $value; | ||
} | ||
|
||
return parent::getAttributeFromArray($key); | ||
} | ||
|
||
/** | ||
* Remove one or more values to the underlying attribute value and sync with original. | ||
* | ||
|
@@ -411,7 +617,7 @@ protected function newBaseQueryBuilder() | |
{ | ||
$connection = $this->getConnection(); | ||
|
||
return new QueryBuilder($connection, $connection->getPostProcessor()); | ||
return (new QueryBuilder($connection, $connection->getPostProcessor()))->casts($this->casts); | ||
} | ||
|
||
/** | ||
|
@@ -480,4 +686,39 @@ public function __call($method, $parameters) | |
|
||
return parent::__call($method, $parameters); | ||
} | ||
|
||
/** | ||
* @inheritdoc | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fill params please in dock block |
||
*/ | ||
protected function castAttribute($key, $value) | ||
{ | ||
if (is_null($value)) { | ||
return $value; | ||
} | ||
|
||
if ($this->getCastType($key) === 'objectid') { | ||
if (is_array($value)) { | ||
foreach ($value as &$val) { | ||
$val = (string) $val; | ||
} | ||
|
||
return $value; | ||
} | ||
|
||
return (string) $value; | ||
} | ||
|
||
return parent::castAttribute($key, $value); | ||
} | ||
|
||
/** | ||
* Is the field an ObjectId | ||
* | ||
* @param string $key | ||
* @return bool | ||
*/ | ||
protected function isCastableToObjectId($key) | ||
{ | ||
return '_id' === $key || $this->hasCast($key, ['objectid']); | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add dock block for
$key