Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
54.17% covered (warning)
54.17%
26 / 48
0.00% covered (danger)
0.00%
0 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
Moloquent
54.17% covered (warning)
54.17%
26 / 48
0.00% covered (danger)
0.00%
0 / 6
91.09
0.00% covered (danger)
0.00%
0 / 1
 getAttribute
80.00% covered (warning)
80.00%
8 / 10
0.00% covered (danger)
0.00%
0 / 1
7.39
 getAttributeFromArray
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
2.06
 setAttribute
46.67% covered (danger)
46.67%
7 / 15
0.00% covered (danger)
0.00%
0 / 1
11.46
 offsetUnset
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 transformModelValue
42.86% covered (danger)
42.86%
3 / 7
0.00% covered (danger)
0.00%
0 / 1
12.72
 castAttribute
62.50% covered (warning)
62.50%
5 / 8
0.00% covered (danger)
0.00%
0 / 1
3.47
1<?php
2
3namespace App\Http\Models;
4
5use Carbon\CarbonInterface;
6use Illuminate\Support\Arr;
7use MongoDB\Laravel\Eloquent\Model;
8use Illuminate\Support\Str; // Add this line
9
10class Moloquent extends Model
11{
12    /** @inheritdoc */
13    public function getAttribute($key)
14    {
15        if (!$key) {
16            return null;
17        }
18
19        $key = (string) $key;
20
21        // An unset attribute is null or throw an exception.
22        // if (isset($this->unset[$key])) {
23        //     return $this->throwMissingAttributeExceptionIfApplicable($key);
24        // }
25
26        // Dot notation support.
27        if (Str::contains($key, '.') && Arr::has($this->attributes, $key)) {
28            return $this->getAttributeValue($key);
29        }
30
31        // This checks for embedded relation support.
32        if (
33            method_exists($this, $key)
34            && !method_exists(self::class, $key)
35            && !$this->hasAttributeGetMutator($key)
36        ) {
37            return $this->getRelationValue($key);
38        }
39
40        return parent::getAttribute($key);
41    }
42
43    /** @inheritdoc */
44    protected function getAttributeFromArray($key)
45    {
46        $key = (string) $key;
47
48        // Support keys in dot notation.
49        if (Str::contains($key, '.')) {
50            return Arr::get($this->attributes, $key);
51        }
52
53        return parent::getAttributeFromArray($key);
54    }
55
56    /** @inheritdoc */
57    public function setAttribute($key, $value)
58    {
59        $key = (string) $key;
60
61        // Add casts
62        if ($this->hasCast($key)) {
63            if (is_array($value)) {
64                $value = json_encode($value);
65            }
66            $value = $this->castAttribute($key, $value);
67        }
68
69        // Convert _id to ObjectID.
70        if ($key === '_id' && is_string($value)) {
71            $builder = $this->newBaseQueryBuilder();
72
73            $value = $builder->convertKey($value);
74        }
75
76        // Support keys in dot notation.
77        if (Str::contains($key, '.')) {
78            // Store to a temporary key, then move data to the actual key
79            $uniqueKey = uniqid($key);
80
81            parent::setAttribute($uniqueKey, $value);
82
83            Arr::set($this->attributes, $key, $this->attributes[$uniqueKey] ?? null);
84            unset($this->attributes[$uniqueKey]);
85
86            return $this;
87        }
88
89        // Setting an attribute cancels the unset operation.
90        // unset($this->unset[$key]);
91
92        return parent::setAttribute($key, $value);
93    }
94
95    /** @inheritdoc */
96    public function offsetUnset($offset): void
97    {
98        $offset = (string) $offset;
99
100        if (Str::contains($offset, '.')) {
101            // Update the field in the subdocument
102            Arr::forget($this->attributes, $offset);
103        } else {
104            parent::offsetUnset($offset);
105
106            // Force unsetting even if the attribute is not set.
107            // End user can optimize DB calls by checking if the attribute is set before unsetting it.
108            // $this->unset[$offset] = true;
109        }
110    }
111
112    /** @inheritdoc */
113    protected function transformModelValue($key, $value)
114    {
115        $value = parent::transformModelValue($key, $value);
116
117        // Casting attributes to any of date types will convert that attribute
118        // to a Carbon or CarbonImmutable instance.
119        // @see Model::setAttribute()
120        if ($this->hasCast($key) && $value instanceof CarbonInterface) {
121            $value->settings(array_merge($value->getSettings(), ['toStringFormat' => $this->getDateFormat()]));
122
123            // Check if the key is 'scopes' without unnecessary quotes
124            $castType = optional($this->getCasts())[$key] ?? null;
125            if ($this->isCustomDateTimeCast($castType) && is_string($castType) && Str::startsWith($castType, 'date:')) {
126                $value->startOfDay();
127            }
128        }
129
130        return $value;
131    }
132
133    /** @inheritdoc */
134    protected function castAttribute($key, $value)
135    {
136        $castType = $this->getCastType($key);
137        $castTypeString = (string) $this->getCasts()[$key]; // Convert to string
138
139        return match ($castType) {
140            'immutable_custom_datetime', 'immutable_datetime' => is_string($castTypeString) && Str::startsWith($castTypeString, 'immutable_date:') ?
141                $this->asDate($value)->toImmutable() :
142                $this->asDateTime($value)->toImmutable(),
143            default => parent::castAttribute($key, $value)
144        };
145    }
146}