Completed
Push — master ( 251911...ba69b6 )
by Lorenzo
01:58
created

HasSlug   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 216
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 0
Metric Value
wmc 43
lcom 1
cbo 3
dl 0
loc 216
rs 8.3157
c 0
b 0
f 0

11 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

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