|
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