• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

orchestral / sidekick / 14897684102

08 May 2025 03:03AM UTC coverage: 93.913% (+0.05%) from 93.86%
14897684102

Pull #30

github

web-flow
Merge 6ca86c416 into 976186051
Pull Request #30: Add `Orchestra\Sidekick\Eloquent\summarize_changes()` function

12 of 12 new or added lines in 1 file covered. (100.0%)

3 existing lines in 1 file now uncovered.

108 of 115 relevant lines covered (93.91%)

2.92 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.83
/src/Eloquent/functions.php
1
<?php
2

3
namespace Orchestra\Sidekick\Eloquent;
4

5
use BackedEnum;
6
use Illuminate\Database\Eloquent\Concerns\HasUlids;
7
use Illuminate\Database\Eloquent\Concerns\HasUuids;
8
use Illuminate\Database\Eloquent\Model;
9
use Illuminate\Database\Eloquent\Relations\Concerns\AsPivot;
10
use Illuminate\Database\Eloquent\Relations\Pivot;
11
use InvalidArgumentException;
12
use JsonSerializable;
13
use Orchestra\Sidekick\SensitiveValue;
14
use Stringable;
15
use Throwable;
16

17
if (! \function_exists('Orchestra\Sidekick\Eloquent\column_name')) {
18
    /**
19
     * Get qualify column name from Eloquent model.
20
     *
21
     * @api
22
     *
23
     * @param  \Illuminate\Database\Eloquent\Model|class-string<\Illuminate\Database\Eloquent\Model>  $model
24
     *
25
     * @throws \InvalidArgumentException
26
     */
27
    function column_name(Model|string $model, string $attribute): string
28
    {
29
        if (\is_string($model)) {
3✔
30
            $model = new $model;
2✔
31
        }
32

33
        if (! $model instanceof Model) {
3✔
34
            throw new InvalidArgumentException(\sprintf('Given $model is not an instance of [%s].', Model::class));
1✔
35
        }
36

37
        return $model->qualifyColumn($attribute);
2✔
38
    }
39
}
40

41
if (! \function_exists('Orchestra\Sidekick\Eloquent\is_pivot_model')) {
42
    /**
43
     * Determine if the given model is a pivot model.
44
     *
45
     * @api
46
     *
47
     * @template TPivotModel of (\Illuminate\Database\Eloquent\Model&\Illuminate\Database\Eloquent\Relations\Concerns\AsPivot)|\Illuminate\Database\Eloquent\Relations\Pivot
48
     *
49
     * @param  TPivotModel|class-string<TPivotModel>  $model
50
     *
51
     * @throws \InvalidArgumentException
52
     */
53
    function is_pivot_model(Pivot|Model|string $model): bool
54
    {
55
        if (\is_string($model)) {
4✔
56
            $model = new $model;
1✔
57
        }
58

59
        if (! $model instanceof Model) {
4✔
60
            throw new InvalidArgumentException(\sprintf('Given $model is not an instance of [%s|%s].', Model::class, Pivot::class));
1✔
61
        }
62

63
        if ($model instanceof Pivot) {
3✔
64
            return true;
1✔
65
        }
66

67
        return \in_array(AsPivot::class, class_uses_recursive($model), true);
2✔
68
    }
69
}
70

71
if (! \function_exists('Orchestra\Sidekick\Eloquent\model_exists')) {
72
    /**
73
     * Check whether given $model exists.
74
     *
75
     * @api
76
     *
77
     * @param  \Illuminate\Database\Eloquent\Model|mixed  $model
78
     */
79
    function model_exists(mixed $model): bool
80
    {
81
        return $model instanceof Model && $model->exists === true;
5✔
82
    }
83
}
84

85
if (! \function_exists('Orchestra\Sidekick\Eloquent\model_key_type')) {
86
    /**
87
     * Check whether given $model key type.
88
     *
89
     * @api
90
     *
91
     * @param  \Illuminate\Database\Eloquent\Model|class-string<\Illuminate\Database\Eloquent\Model>  $model
92
     *
93
     * @throws \InvalidArgumentException
94
     */
95
    function model_key_type(Model|string $model): string
96
    {
97
        if (\is_string($model)) {
6✔
98
            $model = new $model;
2✔
99
        }
100

101
        if (! $model instanceof Model) {
6✔
102
            throw new InvalidArgumentException(\sprintf('Given $model is not an instance of [%s].', Model::class));
1✔
103
        }
104

105
        $uses = class_uses_recursive($model);
5✔
106

107
        if (\in_array(HasUlids::class, $uses, true)) {
5✔
108
            return 'ulid';
1✔
109
        } elseif (\in_array(HasUuids::class, $uses, true)) {
4✔
110
            return 'uuid';
1✔
111
        }
112

113
        return $model->getKeyType();
3✔
114
    }
115
}
116

117
if (! \function_exists('Orchestra\Sidekick\Eloquent\model_state')) {
118
    /**
119
     * Get attributes original and changed state from a model.
120
     *
121
     * @api
122
     *
123
     * @return array{0: array<string, mixed>|null, 1: array<string, mixed>}
124
     */
125
    function model_state(Model $model): array
126
    {
127
        $copy = clone $model;
3✔
128
        $hiddens = $model->getHidden();
3✔
129

130
        $copy->setHidden([]);
3✔
131

132
        if (! model_exists($model) || $model->wasRecentlyCreated == true) {
3✔
133
            $original = null;
2✔
134
            $changes = summarize_changes($copy->attributesToArray(), hiddens: $hiddens);
2✔
135

136
            return [$original, $changes];
2✔
137
        }
138

139
        $changes = summarize_changes($copy->getDirty(), hiddens: $hiddens);
1✔
140
        $original = summarize_changes(
1✔
141
            array_intersect_key($model->newInstance()->setRawAttributes($model->getRawOriginal())->attributesToArray(), $changes),
1✔
142
            hiddens: $hiddens
1✔
143
        );
1✔
144

145
        return [$original, $changes];
1✔
146
    }
147
}
148

149
if (! \function_exists('Orchestra\Sidekick\Eloquent\normalize_value')) {
150
    /**
151
     * Normalize the given value to be store to database as scalar.
152
     *
153
     * @api
154
     *
155
     * @return scalar
156
     */
157
    function normalize_value(mixed $value): mixed
158
    {
159
        if ($value instanceof JsonSerializable) {
11✔
160
            $value = $value->jsonSerialize();
3✔
161
        }
162

163
        if ($value instanceof BackedEnum) {
11✔
UNCOV
164
            return $value->value;
×
165
        } elseif (\is_object($value) && $value instanceof Stringable) {
11✔
166
            return (string) $value;
1✔
167
        }
168

169
        if (\is_object($value) || \is_array($value)) {
10✔
170
            try {
171
                return json_encode($value);
4✔
UNCOV
172
            } catch (Throwable $e) { // @phpstan-ignore catch.neverThrown
×
UNCOV
173
                return $value;
×
174
            }
175
        }
176

177
        return $value;
6✔
178
    }
179
}
180

181
if (! \function_exists('Orchestra\Sidekick\Eloquent\summarize_changes')) {
182
    /**
183
     * Get table name from Eloquent model.
184
     *
185
     * @api
186
     *
187
     * @param  array<string, mixed>  $changes
188
     * @param  array<int, string>  $hiddens
189
     * @return array<string, \Orchestra\Sidekick\SensitiveValue|scalar>
190
     */
191
    function summarize_changes(array $changes, array $hiddens = []): array
192
    {
193
        $summaries = [];
4✔
194

195
        foreach ($changes as $attribute => $value) {
4✔
196
            $summaries[$attribute] = \in_array($attribute, $hiddens, true)
4✔
197
                ? new SensitiveValue($value)
3✔
198
                : normalize_value($value);
4✔
199
        }
200

201
        return $summaries;
4✔
202
    }
203
}
204

205
if (! \function_exists('Orchestra\Sidekick\Eloquent\table_name')) {
206
    /**
207
     * Get table name from Eloquent model.
208
     *
209
     * @api
210
     *
211
     * @param  \Illuminate\Database\Eloquent\Model|class-string<\Illuminate\Database\Eloquent\Model>  $model
212
     *
213
     * @throws \InvalidArgumentException
214
     */
215
    function table_name(Model|string $model): string
216
    {
217
        if (\is_string($model)) {
3✔
218
            $model = new $model;
2✔
219
        }
220

221
        if (! $model instanceof Model) {
3✔
222
            throw new InvalidArgumentException(\sprintf('Given $model is not an instance of [%s].', Model::class));
1✔
223
        }
224

225
        return $model->getTable();
2✔
226
    }
227
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc