-
Notifications
You must be signed in to change notification settings - Fork 0
PHPORM-8 Remove automatic _id conversion, apply Cast to queries #23
base: master
Are you sure you want to change the base?
Changes from 4 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,11 +5,19 @@ | |
use Illuminate\Contracts\Database\Eloquent\CastsAttributes; | ||
use Jenssegers\Mongodb\Eloquent\Model; | ||
use MongoDB\BSON\ObjectId as BSONObjectId; | ||
use MongoDB\Driver\Exception\InvalidArgumentException; | ||
|
||
/** | ||
* Store the value as an ObjectId in the database. This cast should be used for _id fields. | ||
* The value read from the database will not be transformed. | ||
* | ||
* @extends CastsAttributes<BSONObjectId, BSONObjectId> | ||
*/ | ||
class ObjectId implements CastsAttributes | ||
{ | ||
/** | ||
* Cast the given value. | ||
* Nothing will be done here, the value should already be an ObjectId in the database. | ||
* | ||
* @param Model $model | ||
* @param string $key | ||
|
@@ -19,28 +27,38 @@ class ObjectId implements CastsAttributes | |
*/ | ||
public function get($model, string $key, $value, array $attributes) | ||
{ | ||
if (! $value instanceof BSONObjectId) { | ||
return $value; | ||
if ($value instanceof BSONObjectId && $model->getKeyName() === $key && $model->getKeyType() === 'string') { | ||
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. The This impacts queries using the query builder and relations. 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. This must be revisited to support SQL<->MongoDB relations. |
||
return (string) $value; | ||
} | ||
|
||
return (string) $value; | ||
return $value; | ||
} | ||
|
||
/** | ||
* Prepare the given value for storage. | ||
* The value will be converted to an ObjectId. | ||
* | ||
* @param Model $model | ||
* @param string $key | ||
* @param mixed $value | ||
* @param array $attributes | ||
* @return mixed | ||
* | ||
* @throws \RuntimeException when the value is not an ObjectID or a valid ID string. | ||
*/ | ||
public function set($model, string $key, $value, array $attributes) | ||
{ | ||
if ($value instanceof BSONObjectId) { | ||
return $value; | ||
if (! $value instanceof BSONObjectId) { | ||
if (! is_string($value)) { | ||
throw new \RuntimeException(sprintf('Invalid BSON ObjectID provided for %s[%s]. "string" or %s expected, got "%s". Remove the ObjectId cast if you need to store other types of values.', get_class($model), $key, BSONObjectId::class, get_debug_type($value))); | ||
} | ||
try { | ||
$value = new BSONObjectId($value); | ||
} catch (InvalidArgumentException $e) { | ||
throw new \RuntimeException(sprintf('Invalid BSON ObjectID provided for %s[%s]: %s. Remove the ObjectID cast if you need to store string values.', get_class($model), $key, $value), 0, $e); | ||
} | ||
} | ||
|
||
return new BSONObjectId($value); | ||
return [$key => $value]; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
|
||
namespace Jenssegers\Mongodb\Eloquent; | ||
|
||
use MongoDB\BSON\ObjectId; | ||
use function array_key_exists; | ||
use DateTimeInterface; | ||
use function explode; | ||
|
@@ -15,8 +16,6 @@ | |
use Illuminate\Support\Str; | ||
use function in_array; | ||
use Jenssegers\Mongodb\Query\Builder as QueryBuilder; | ||
use MongoDB\BSON\Binary; | ||
use MongoDB\BSON\ObjectID; | ||
use MongoDB\BSON\UTCDateTime; | ||
use function uniqid; | ||
|
||
|
@@ -43,7 +42,7 @@ abstract class Model extends BaseModel | |
* | ||
* @var string | ||
*/ | ||
protected $keyType = 'string'; | ||
protected $keyType = ObjectId::class; | ||
|
||
/** | ||
* The parent relation instance. | ||
|
@@ -52,30 +51,6 @@ abstract class Model extends BaseModel | |
*/ | ||
protected $parentRelation; | ||
|
||
/** | ||
* Custom accessor for the model's id. | ||
* | ||
* @param mixed $value | ||
* @return mixed | ||
*/ | ||
public function getIdAttribute($value = null) | ||
{ | ||
// If we don't have a value for 'id', we will use the MongoDB '_id' value. | ||
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. This is a feature that needs to be kept: Allow to use a custom 'id' field as id. |
||
// This allows us to work with models in a more sql-like way. | ||
if (! $value && array_key_exists('_id', $this->attributes)) { | ||
$value = $this->attributes['_id']; | ||
} | ||
|
||
// Convert ObjectID to string. | ||
if ($value instanceof ObjectID) { | ||
return (string) $value; | ||
} elseif ($value instanceof Binary) { | ||
return (string) $value->getData(); | ||
} | ||
|
||
return $value; | ||
} | ||
|
||
/** | ||
* @inheritdoc | ||
*/ | ||
|
@@ -171,13 +146,24 @@ public function getAttribute($key) | |
return parent::getAttribute($key); | ||
} | ||
|
||
protected function throwMissingAttributeExceptionIfApplicable($key) | ||
{ | ||
// Fallback to "_id" if "id" is not set. | ||
if ($key === 'id') { | ||
// Should be deprecated? | ||
return $this->getAttribute('_id'); | ||
} | ||
|
||
parent::throwMissingAttributeExceptionIfApplicable($key); | ||
} | ||
|
||
/** | ||
* @inheritdoc | ||
*/ | ||
protected function getAttributeFromArray($key) | ||
{ | ||
// Support keys in dot notation. | ||
if (Str::contains($key, '.')) { | ||
if (str_contains($key, '.')) { | ||
return Arr::get($this->attributes, $key); | ||
} | ||
|
||
|
@@ -190,12 +176,7 @@ protected function getAttributeFromArray($key) | |
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 (Str::contains($key, '.')) { | ||
// Store to a temporary key, then move data to the actual key | ||
$uniqueKey = uniqid($key); | ||
parent::setAttribute($uniqueKey, $value); | ||
|
@@ -209,28 +190,6 @@ public function setAttribute($key, $value) | |
return parent::setAttribute($key, $value); | ||
} | ||
|
||
/** | ||
* @inheritdoc | ||
*/ | ||
public function attributesToArray() | ||
{ | ||
$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; | ||
} elseif ($value instanceof Binary) { | ||
$value = (string) $value->getData(); | ||
} | ||
} | ||
|
||
return $attributes; | ||
} | ||
|
||
/** | ||
* @inheritdoc | ||
*/ | ||
|
@@ -291,7 +250,7 @@ public function drop($columns) | |
} | ||
|
||
// Perform unset only on current document | ||
return $this->newQuery()->where($this->getKeyName(), $this->getKey())->unset($columns); | ||
return $this->newQuery()->whereKey($this->getKey())->unset($columns); | ||
} | ||
|
||
/** | ||
|
@@ -502,6 +461,26 @@ protected function isGuardableColumn($key) | |
return true; | ||
} | ||
|
||
/** | ||
* @see \Jenssegers\Mongodb\Query\Builder::whereIn() | ||
* Add a "where in" clause to the query. | ||
* | ||
* @param \Illuminate\Contracts\Database\Query\Expression|string $column | ||
* @param mixed $values | ||
* @param string $boolean | ||
* @param bool $not | ||
* @return $this | ||
*/ | ||
public function whereIn($column, $values, $boolean = 'and', $not = false) | ||
{ | ||
$args = func_get_args(); | ||
if ($column === $this->getKeyName()) { | ||
$args[1] = array_map(fn ($value) => $this->castKeyForDatabase($value), $args[1]); | ||
} | ||
|
||
return parent::__call(__FUNCTION__, $args); | ||
} | ||
|
||
/** | ||
* @inheritdoc | ||
*/ | ||
|
@@ -568,4 +547,31 @@ protected function addCastAttributesToArray(array $attributes, array $mutatedAtt | |
|
||
return $attributes; | ||
} | ||
|
||
/** @internal */ | ||
public function castKeyForDatabase($value) | ||
{ | ||
$key = $this->primaryKey; | ||
|
||
if (! $this->hasCast($key)) { | ||
return $value; | ||
} | ||
|
||
if (! $this->isClassCastable($key)) { | ||
return $value; | ||
} | ||
$caster = $this->resolveCasterClass($key); | ||
$attributes = $this->normalizeCastClassResponse($key, $caster->set($this, $key, $value, [])); | ||
|
||
return $attributes[$key]; | ||
} | ||
|
||
protected function getClassCastableAttributeValue($key, $value) | ||
{ | ||
// The class cast cache does not play nice with database values that | ||
// already are objects, so we need to manually unset it | ||
unset($this->classCastCache[$key]); | ||
|
||
return parent::getClassCastableAttributeValue($key, $value); | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.