-
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 all 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.