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

RonasIT / laravel-helpers / 28081047519

24 Jun 2026 06:57AM UTC coverage: 87.218% (+0.4%) from 86.822%
28081047519

Pull #274

github

web-flow
Merge 6f50e4e3d into 3fc6180f7
Pull Request #274: feat(model-trait): add wasExchanged, wasFilled, wasCleared and origin methods

6 of 13 new or added lines in 1 file covered. (46.15%)

1276 of 1463 relevant lines covered (87.22%)

20.74 hits per line

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

78.87
/src/Traits/ModelTrait.php
1
<?php
2

3
namespace RonasIT\Support\Traits;
4

5
use BadMethodCallException;
6
use Illuminate\Database\Eloquent\Builder;
7
use Illuminate\Database\Eloquent\Relations\Relation;
8
use Illuminate\Support\Arr;
9
use Illuminate\Support\Facades\Schema;
10
use Illuminate\Support\Str;
11

12
trait ModelTrait
13
{
14
    protected bool $disableLazyLoading = true;
15

16
    public static function getFields(): array
17
    {
18
        $model = (new static());
27✔
19

20
        $keyName = $model->getKeyName();
27✔
21
        $guarded = $model->getGuarded();
27✔
22
        $fillable = $model->getFillable();
27✔
23
        $timeStamps = ($model->timestamps) ? ['created_at', 'updated_at'] : [];
27✔
24

25
        array_unshift($fillable, $keyName);
27✔
26

27
        return array_merge($fillable, $guarded, $timeStamps);
27✔
28
    }
29

30
    public function getAllFieldsWithTable(): array
31
    {
32
        $tableName = $this->getTable();
1✔
33
        $fields = Schema::getColumnListing($tableName);
1✔
34

35
        return array_map(fn ($field) => "{$tableName}.{$field}", $fields);
1✔
36
    }
37

38
    /**
39
     * This method was added, because native Laravel's method addSelect
40
     * overwrites existed select clause
41
     */
42
    public function scopeAddFieldsToSelect(Builder $query, array $fields = []): mixed
43
    {
44
        if (is_null($query->getQuery()->columns)) {
5✔
45
            $query->addSelect("{$this->getTable()}.*");
4✔
46
        }
47

48
        if (empty($fields)) {
5✔
49
            return $query;
3✔
50
        }
51

52
        return $query->addSelect($fields);
2✔
53
    }
54

55
    /**
56
     * Add orderBy By related field,
57
     * $manyToManyStrategy is affect oneToMany and ManyToMany Relations make orderBy('id', ASC/DESC)
58
     */
59
    public function scopeOrderByRelated(
60
        Builder $query,
61
        string $relations,
62
        string $desc = 'DESC',
63
        ?string $asField = null,
64
        string $manyToManyStrategy = 'max',
65
    ): mixed {
66
        if (empty($asField)) {
2✔
67
            $asField = str_replace('.', '_', $relations);
2✔
68
        }
69

70
        $relations = $this->prepareRelations($relations);
2✔
71
        $orderField = $this->getOrderedField($relations);
2✔
72

73
        if (!empty($relations)) {
2✔
74
            $queries = $this->getQueriesList($query, $relations);
2✔
75
            $prevQuery = array_shift($queries);
2✔
76
            array_pop($queries);
2✔
77

78
            $this
2✔
79
                ->applyManyToManyStrategy($prevQuery, $manyToManyStrategy)
2✔
80
                ->select($orderField);
2✔
81

82
            foreach ($queries as $queryInCollection) {
2✔
83
                $prevQuery = $this
×
84
                    ->applyManyToManyStrategy($queryInCollection, $manyToManyStrategy)
×
85
                    ->selectSub($prevQuery, $asField);
×
86
            }
87

88
            $query->addFieldsToSelect();
2✔
89
            $query->selectSub($prevQuery, $asField);
2✔
90
        }
91

92
        return $query->orderBy($asField ?? $orderField, $desc);
2✔
93
    }
94

95
    public function wasExchanged(string $fieldName): bool
96
    {
97
        return $this->wasChanged($fieldName)
4✔
98
            && !is_null($this->origin($fieldName))
4✔
99
            && !is_null($this->getAttribute($fieldName));
4✔
100
    }
101

102
    public function wasFilled(string $fieldName): bool
103
    {
104
        return $this->wasChanged($fieldName) && is_null($this->origin($fieldName));
4✔
105
    }
106

107
    public function wasCleared(string $fieldName): bool
108
    {
109
        return $this->wasChanged($fieldName) && is_null($this->getAttribute($fieldName));
4✔
110
    }
111

112
    public function origin(string $fieldName): mixed
113
    {
114
        return Arr::get($this->getPrevious(), $fieldName);
8✔
115
    }
116

117
    protected function getRelationshipFromMethod($method)
118
    {
NEW
119
        if ($this->disableLazyLoading) {
×
NEW
120
            $modelName = static::class;
×
121

NEW
122
            throw new BadMethodCallException(
×
NEW
123
                message: "Attempting to lazy-load relation '{$method}' on model '{$modelName}'. "
×
NEW
124
                . 'See property $disableLazyLoading.',
×
NEW
125
            );
×
126
        }
127

NEW
128
        return parent::getRelationshipFromMethod($method);
×
129
    }
130

131
    protected function getRelationWithoutConstraints(Builder $query, string $relation): Relation
132
    {
133
        return Relation::noConstraints(fn () => call_user_func([$query->getModel(), $relation]));
2✔
134
    }
135

136
    protected function prepareRelations(string $relations): array
137
    {
138
        if (Str::contains($relations, '.')) {
2✔
139
            return explode('.', $relations);
2✔
140
        } else {
141
            return [
×
142
                $relations,
×
143
            ];
×
144
        }
145
    }
146

147
    protected function getOrderedField(&$relations): string
148
    {
149
        if (is_array($relations)) {
2✔
150
            return array_pop($relations);
2✔
151
        }
152

153
        return $relations;
×
154
    }
155

156
    protected function getQueriesList(Builder $query, array $relations): array
157
    {
158
        $requiredColumns = [];
2✔
159
        $queryCollection = [$query];
2✔
160

161
        foreach ($relations as $relationString) {
2✔
162
            $query = Arr::last($queryCollection);
2✔
163

164
            $relation = $this->getRelationWithoutConstraints($query, $relationString);
2✔
165
            $subQuery = $relation->getRelationExistenceQuery(
2✔
166
                $relation->getQuery(),
2✔
167
                $query,
2✔
168
                $requiredColumns,
2✔
169
            );
2✔
170

171
            $queryCollection[] = $subQuery;
2✔
172
        }
173

174
        return array_reverse($queryCollection);
2✔
175
    }
176

177
    protected function applyManyToManyStrategy(Builder $query, string $strategy): Builder
178
    {
179
        if ($strategy === 'max') {
2✔
180
            $query->orderBy('id', 'ASC')->limit(1);
2✔
181
        } else {
182
            $query->orderBy('id', 'DESC')->limit(1);
×
183
        }
184

185
        return $query;
2✔
186
    }
187
}
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

© 2026 Coveralls, Inc