Completed
Push — master ( 4f2ee5...fe1e1a )
by Lorenzo
01:59
created

HasSlug   B

Complexity

Total Complexity 46

Size/Duplication

Total Lines 261
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
wmc 46
lcom 1
cbo 4
dl 0
loc 261
rs 8.3999
c 0
b 0
f 0

14 Methods

Rating   Name   Duplication   Size   Complexity  
A bootHasSlug() 0 10 1
A addSlug() 0 16 2
A getSlugOptionsOrDefault() 0 8 2
A generateNonUniqueSlug() 0 8 2
A hasCustomSlugBeenUsed() 0 9 3
B getSlugSourceString() 0 21 7
C getSlugFrom() 0 38 16
A makeSlugUnique() 0 11 3
A otherRecordExistsWithSlug() 0 6 1
A guardAgainstInvalidSlugOptions() 0 14 4
A getImplodeSourceString() 0 12 2
A scopeWhereSlug() 0 5 1
A findBySlug() 0 4 1
A findBySlugOrFail() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like HasSlug often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use HasSlug, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Padosoft\Sluggable;
4
5
use Illuminate\Database\Eloquent\Model;
6
7
trait HasSlug
8
{
9
    /** @var \Padosoft\Sluggable\SlugOptions */
10
    protected $slugOptions;
11
12
    /**
13
     * Boot the trait.
14
     */
15
    protected static function bootHasSlug()
16
    {
17
        static::creating(function (Model $model) {
18
            $model->addSlug();
19
        });
20
21
        static::updating(function (Model $model) {
22
            $model->addSlug();
23
        });
24
    }
25
26
    /**
27
     * Add the slug to the model.
28
     */
29
    protected function addSlug()
30
    {
31
        $this->slugOptions = $this->getSlugOptionsOrDefault();
32
33
        $this->guardAgainstInvalidSlugOptions();
34
35
        $slug = $this->generateNonUniqueSlug();
36
37
        if ($this->slugOptions->generateUniqueSlugs) {
38
            $slug = $this->makeSlugUnique($slug);
39
        }
40
41
        $slugField = $this->slugOptions->slugField;
42
43
        $this->$slugField = $slug;
44
    }
45
46
    /**
47
     * Retrive a specifice SlugOptions for this model, or return default SlugOptions
48
     */
49
    protected function getSlugOptionsOrDefault()
50
    {
51
        if (method_exists($this, 'getSlugOptions')) {
52
            return $this->getSlugOptions();
53
        } else {
54
            return SlugOptions::create()->getSlugOptionsDefault();
55
        }
56
    }
57
58
    /**
59
     * Generate a non unique slug for this record.
60
     * @return string
61
     * @throws InvalidOption
62
     */
63
    protected function generateNonUniqueSlug(): string
64
    {
65
        if ($this->hasCustomSlugBeenUsed()) {
66
            $slugField = $this->slugOptions->slugField;
67
            return $this->$slugField ?? '';
68
        }
69
        return str_slug($this->getSlugSourceString(), $this->slugOptions->separator);
70
    }
71
72
    /**
73
     * Determine if a custom slug has been saved.
74
     * @return bool
75
     */
76
    protected function hasCustomSlugBeenUsed(): bool
77
    {
78
        $slugField = $this->slugOptions->slugField;
79
80
        if(!$this->$slugField || trim($this->$slugField)==''){
81
            return false;
82
        }
83
        return $this->getOriginal($slugField) != $this->$slugField;
84
    }
85
86
    /**
87
     * Get the string that should be used as base for the slug.
88
     * @return string
89
     * @throws InvalidOption
90
     */
91
    protected function getSlugSourceString(): string
92
    {
93
        if (is_callable($this->slugOptions->generateSlugFrom)) {
94
            $slugSourceString = call_user_func($this->slugOptions->generateSlugFrom, $this);
95
            return substr($slugSourceString, 0, $this->slugOptions->maximumLength);
96
        }
97
98
        $slugFrom = $this->getSlugFrom($this->slugOptions->generateSlugFrom);
99
100
        if(is_null($slugFrom) || (!is_array($slugFrom) && trim($slugFrom)=='')){
101
            if(!$this->slugOptions->generateSlugIfAllSourceFieldsEmpty){
102
                throw InvalidOption::missingFromField();
103
            }
104
105
            return str_random($this->slugOptions->maximumLength > $this->slugOptions->randomUrlLen ? $this->slugOptions->randomUrlLen : $this->slugOptions->maximumLength);
106
        }
107
108
        $slugSourceString = $this->getImplodeSourceString($slugFrom, $this->slugOptions->separator);
109
110
        return substr($slugSourceString, 0, $this->slugOptions->maximumLength);
111
    }
112
113
    /**
114
     * Get the correct field(s) from to generate slug
115
     * @param string|array|callable $fieldName
116
     * @return string|array
117
     */
118
    protected function getSlugFrom($fieldName)
119
    {
120
        if(!is_callable($fieldName) && !is_array($fieldName) && trim($fieldName)==''){
121
            return '';
122
        }
123
124
        if(!is_callable($fieldName) && !is_array($fieldName) && (!data_get($this, $fieldName))){
125
            return '';
126
        }elseif (!is_array($fieldName)){
127
            return $fieldName;
128
        }
129
130
        $slugSourceString = '';
131
        $countFieldName = count($fieldName);
132
        for($i=0;$i<$countFieldName;$i++){
133
134
            $currFieldName = $fieldName[$i];
135
            if(!is_array($currFieldName) && trim($currFieldName)==''){
136
                continue;
137
            }
138
            if (!is_array($currFieldName) && (!data_get($this, $currFieldName))){
139
                continue;
140
            }
141
            if (!is_array($currFieldName) && data_get($this, $currFieldName)){
142
                $slugSourceString = $currFieldName;
143
                break;
144
            }
145
146
            $slugSourceString = $this->getImplodeSourceString($currFieldName, '');
147
148
            if($slugSourceString!=''){
149
                $slugSourceString = $currFieldName;
150
                break;
151
            }
152
        }
153
154
        return $slugSourceString;
155
    }
156
157
    /**
158
     * Make the given slug unique.
159
     * @param string $slug
160
     * @return string
161
     */
162
    protected function makeSlugUnique(string $slug): string
163
    {
164
        $originalSlug = $slug;
165
        $i = 1;
166
167
        while ($this->otherRecordExistsWithSlug($slug) || $slug === '') {
168
            $slug = $originalSlug.$this->slugOptions->separator.$i++;
169
        }
170
171
        return $slug;
172
    }
173
174
    /**
175
     * Determine if a record exists with the given slug.
176
     * @param string $slug
177
     * @return bool
178
     */
179
    protected function otherRecordExistsWithSlug(string $slug): bool
180
    {
181
        return (bool) static::where($this->slugOptions->slugField, $slug)
182
            ->where($this->getKeyName(), '!=', $this->getKey() ?? '0')
183
            ->first();
184
    }
185
186
    /**
187
     * This function will throw an exception when any of the options is missing or invalid.
188
     * @throws InvalidOption
189
     */
190
    protected function guardAgainstInvalidSlugOptions()
191
    {
192
        if (!count($this->slugOptions->generateSlugFrom)) {
193
            throw InvalidOption::missingFromField();
194
        }
195
196
        if (!strlen($this->slugOptions->slugField)) {
197
            throw InvalidOption::missingSlugField();
198
        }
199
200
        if ($this->slugOptions->maximumLength <= 0) {
201
            throw InvalidOption::invalidMaximumLength();
202
        }
203
    }
204
205
    /**
206
     * @param $slugFrom
207
     * @param string $separator
208
     * @return string
209
     */
210
    protected function getImplodeSourceString($slugFrom, string $separator) : string
211
    {
212
        $slugSourceString = collect($slugFrom)
213
            ->map(function (string $fieldName) : string {
214
                if ($fieldName == '') {
215
                    return '';
216
                }
217
                return data_get($this, $fieldName) ?? '';
218
            })
219
            ->implode($separator);
220
        return $slugSourceString;
221
    }
222
223
    /**
224
     *
225
     * SCOPE HELPERS
226
     *
227
     */
228
229
    /**
230
     * Query scope for finding a model by its slug field.
231
     *
232
     * @param \Illuminate\Database\Eloquent\Builder $scope
233
     * @param string $slug
234
     * @return \Illuminate\Database\Eloquent\Builder
235
     */
236
    public function scopeWhereSlug($scope, $slug)
237
    {
238
        $this->slugOptions = $this->getSlugOptionsOrDefault();
239
        return $scope->where($this->slugOptions->slugField, $slug);
240
    }
241
242
    /**
243
     * Find a model by its slug field.
244
     *
245
     * @param string $slug
246
     * @param array $columns
247
     * @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection|static[]|static|null
248
     */
249
    public static function findBySlug($slug, array $columns = ['*'])
250
    {
251
        return static::whereSlug($slug)->first($columns);
1 ignored issue
show
Bug introduced by
The method whereSlug() does not exist on Padosoft\Sluggable\HasSlug. Did you maybe mean scopeWhereSlug()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
252
    }
253
254
    /**
255
     * Find a model by its slug field or throw an exception.
256
     *
257
     * @param string $slug
258
     * @param array $columns
259
     * @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection
260
     *
261
     * @throws \Illuminate\Database\Eloquent\ModelNotFoundException
262
     */
263
    public static function findBySlugOrFail($slug, array $columns = ['*'])
264
    {
265
        return static::whereSlug($slug)->firstOrFail($columns);
1 ignored issue
show
Bug introduced by
The method whereSlug() does not exist on Padosoft\Sluggable\HasSlug. Did you maybe mean scopeWhereSlug()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
266
    }
267
}
268