|
4 | 4 |
|
5 | 5 | namespace MongoDB\Laravel\Eloquent; |
6 | 6 |
|
7 | | -use Brick\Math\BigDecimal; |
8 | | -use Brick\Math\Exception\MathException as BrickMathException; |
9 | | -use Brick\Math\RoundingMode; |
10 | 7 | use Carbon\CarbonInterface; |
11 | 8 | use DateTimeInterface; |
12 | 9 | use Illuminate\Contracts\Queue\QueueableCollection; |
13 | 10 | use Illuminate\Contracts\Queue\QueueableEntity; |
14 | 11 | use Illuminate\Contracts\Support\Arrayable; |
15 | | -use Illuminate\Database\Eloquent\Casts\Json; |
16 | 12 | use Illuminate\Database\Eloquent\Model as BaseModel; |
17 | 13 | use Illuminate\Database\Eloquent\Relations\Relation; |
18 | 14 | use Illuminate\Support\Arr; |
|
22 | 18 | use MongoDB\BSON\Binary; |
23 | 19 | use MongoDB\BSON\Decimal128; |
24 | 20 | use MongoDB\BSON\ObjectID; |
| 21 | +use MongoDB\BSON\Type; |
25 | 22 | use MongoDB\BSON\UTCDateTime; |
26 | 23 | use MongoDB\Laravel\Query\Builder as QueryBuilder; |
| 24 | +use Stringable; |
27 | 25 |
|
28 | | -use function abs; |
29 | 26 | use function array_key_exists; |
30 | 27 | use function array_keys; |
31 | 28 | use function array_merge; |
|
41 | 38 | use function is_string; |
42 | 39 | use function ltrim; |
43 | 40 | use function method_exists; |
44 | | -use function sprintf; |
45 | 41 | use function str_contains; |
46 | 42 | use function str_starts_with; |
47 | 43 | use function strcmp; |
@@ -139,15 +135,9 @@ public function fromDateTime($value) |
139 | 135 | /** @inheritdoc */ |
140 | 136 | protected function asDateTime($value) |
141 | 137 | { |
142 | | - // Convert UTCDateTime instances. |
| 138 | + // Convert UTCDateTime instances to Carbon. |
143 | 139 | if ($value instanceof UTCDateTime) { |
144 | | - $date = $value->toDateTime(); |
145 | | - |
146 | | - $seconds = $date->format('U'); |
147 | | - $milliseconds = abs((int) $date->format('v')); |
148 | | - $timestampMs = sprintf('%d%03d', $seconds, $milliseconds); |
149 | | - |
150 | | - return Date::createFromTimestampMs($timestampMs); |
| 140 | + return Date::instance($value->toDateTime()); |
151 | 141 | } |
152 | 142 |
|
153 | 143 | return parent::asDateTime($value); |
@@ -250,9 +240,16 @@ public function setAttribute($key, $value) |
250 | 240 | { |
251 | 241 | $key = (string) $key; |
252 | 242 |
|
253 | | - // Add casts |
254 | | - if ($this->hasCast($key)) { |
255 | | - $value = $this->castAttribute($key, $value); |
| 243 | + $casts = $this->getCasts(); |
| 244 | + if (array_key_exists($key, $casts)) { |
| 245 | + $castType = $this->getCastType($key); |
| 246 | + $castOptions = Str::after($casts[$key], ':'); |
| 247 | + |
| 248 | + // Can add more native mongo type casts here. |
| 249 | + $value = match ($castType) { |
| 250 | + 'decimal' => $this->fromDecimal($value, $castOptions), |
| 251 | + default => $value, |
| 252 | + }; |
256 | 253 | } |
257 | 254 |
|
258 | 255 | // Convert _id to ObjectID. |
@@ -281,26 +278,38 @@ public function setAttribute($key, $value) |
281 | 278 | return parent::setAttribute($key, $value); |
282 | 279 | } |
283 | 280 |
|
284 | | - /** @inheritdoc */ |
| 281 | + /** |
| 282 | + * @param mixed $value |
| 283 | + * |
| 284 | + * @inheritdoc |
| 285 | + */ |
285 | 286 | protected function asDecimal($value, $decimals) |
286 | 287 | { |
287 | | - try { |
288 | | - $value = (string) BigDecimal::of((string) $value)->toScale((int) $decimals, RoundingMode::HALF_UP); |
289 | | - |
290 | | - return new Decimal128($value); |
291 | | - } catch (BrickMathException $e) { |
292 | | - throw new MathException('Unable to cast value to a decimal.', previous: $e); |
| 288 | + // Convert BSON to string. |
| 289 | + if ($this->isBSON($value)) { |
| 290 | + if ($value instanceof Binary) { |
| 291 | + $value = $value->getData(); |
| 292 | + } elseif ($value instanceof Stringable) { |
| 293 | + $value = (string) $value; |
| 294 | + } else { |
| 295 | + throw new MathException('BSON type ' . $value::class . ' cannot be converted to string'); |
| 296 | + } |
293 | 297 | } |
| 298 | + |
| 299 | + return parent::asDecimal($value, $decimals); |
294 | 300 | } |
295 | 301 |
|
296 | | - /** @inheritdoc */ |
297 | | - public function fromJson($value, $asObject = false) |
| 302 | + /** |
| 303 | + * Change to mongo native for decimal cast. |
| 304 | + * |
| 305 | + * @param mixed $value |
| 306 | + * @param int $decimals |
| 307 | + * |
| 308 | + * @return Decimal128 |
| 309 | + */ |
| 310 | + protected function fromDecimal($value, $decimals) |
298 | 311 | { |
299 | | - if (! is_string($value)) { |
300 | | - $value = Json::encode($value); |
301 | | - } |
302 | | - |
303 | | - return Json::decode($value, ! $asObject); |
| 312 | + return new Decimal128($this->asDecimal($value, $decimals)); |
304 | 313 | } |
305 | 314 |
|
306 | 315 | /** @inheritdoc */ |
@@ -707,4 +716,16 @@ protected function addCastAttributesToArray(array $attributes, array $mutatedAtt |
707 | 716 |
|
708 | 717 | return $attributes; |
709 | 718 | } |
| 719 | + |
| 720 | + /** |
| 721 | + * Is a value a BSON type? |
| 722 | + * |
| 723 | + * @param mixed $value |
| 724 | + * |
| 725 | + * @return bool |
| 726 | + */ |
| 727 | + protected function isBSON(mixed $value): bool |
| 728 | + { |
| 729 | + return $value instanceof Type; |
| 730 | + } |
710 | 731 | } |
0 commit comments