Completed
Push — master ( fe1e1a...30a91f )
by Lorenzo
03:40
created

HasSlug::hasCustomSlugBeenUsed()   B

Complexity

Conditions 5
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 3
Ratio 37.5 %

Importance

Changes 0
Metric Value
dl 3
loc 8
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 5
nc 2
nop 0
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
        if ($this->slugOptions->generateUniqueSlugs) {
37
            $slug = $this->makeSlugUnique($slug);
38
        }
39
        $slugField = $this->slugOptions->slugField;
40
41
        $this->$slugField = $slug;
42
    }
43
44
    /**
45
     * Retrive a specifice SlugOptions for this model, or return default SlugOptions
46
     */
47
    protected function getSlugOptionsOrDefault()
48
    {
49
        if (method_exists($this, 'getSlugOptions')) {
50
            return $this->getSlugOptions();
51
        } else {
52
            return SlugOptions::create()->getSlugOptionsDefault();
53
        }
54
    }
55
56
    /**
57
     * Generate a non unique slug for this record.
58
     * @return string
59
     * @throws InvalidOption
60
     */
61
    protected function generateNonUniqueSlug(): string
62
    {
63
        if ($this->hasCustomSlugBeenUsed()) {
64
            $slugField = $this->slugOptions->slugCustomField;
65
            if(!$this->$slugField){
66
                return '';
67
            }
68
            return str_slug($this->$slugField, $this->slugOptions->separator);
69
        }
70
        if ($this->hasSlugBeenUsed()) {
71
            $slugField = $this->slugOptions->slugField;
72
            return $this->$slugField ?? '';
73
        }
74
        return str_slug($this->getSlugSourceString(), $this->slugOptions->separator);
75
    }
76
77
    /**
78
     * Determine if a custom slug has been saved.
79
     * @return bool
80
     */
81
    protected function hasCustomSlugBeenUsed(): bool
82
    {
83
        $slugField = $this->slugOptions->slugCustomField;
84 View Code Duplication
        if(!$slugField || trim($slugField)==''|| !$this->$slugField || trim($this->$slugField)==''){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
85
            return false;
86
        }
87
        return true;
88
    }
89
90
    /**
91
     * Determine if a custom slug has been saved.
92
     * @return bool
93
     */
94
    protected function hasSlugBeenUsed(): bool
95
    {
96
        $slugField = $this->slugOptions->slugField;
97
98 View Code Duplication
        if(!$slugField || trim($slugField)==''|| !$this->$slugField || trim($this->$slugField)==''){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
99
            return false;
100
        }
101
        return $this->getOriginal($slugField) != $this->$slugField;
102
    }
103
104
    /**
105
     * Get the string that should be used as base for the slug.
106
     * @return string
107
     * @throws InvalidOption
108
     */
109
    protected function getSlugSourceString(): string
110
    {
111
        if (is_callable($this->slugOptions->generateSlugFrom)) {
112
            $slugSourceString = call_user_func($this->slugOptions->generateSlugFrom, $this);
113
            return substr($slugSourceString, 0, $this->slugOptions->maximumLength);
114
        }
115
116
        $slugFrom = $this->getSlugFrom($this->slugOptions->generateSlugFrom);
117
118
        if(is_null($slugFrom) || (!is_array($slugFrom) && trim($slugFrom)=='')){
119
            if(!$this->slugOptions->generateSlugIfAllSourceFieldsEmpty){
120
                throw InvalidOption::missingFromField();
121
            }
122
123
            return str_random($this->slugOptions->maximumLength > $this->slugOptions->randomUrlLen ? $this->slugOptions->randomUrlLen : $this->slugOptions->maximumLength);
124
        }
125
126
        $slugSourceString = $this->getImplodeSourceString($slugFrom, $this->slugOptions->separator);
127
128
        return substr($slugSourceString, 0, $this->slugOptions->maximumLength);
129
    }
130
131
    /**
132
     * Get the correct field(s) from to generate slug
133
     * @param string|array|callable $fieldName
134
     * @return string|array
135
     */
136
    protected function getSlugFrom($fieldName)
137
    {
138
        if(!is_callable($fieldName) && !is_array($fieldName) && trim($fieldName)==''){
139
            return '';
140
        }
141
142
        if(!is_callable($fieldName) && !is_array($fieldName) && (!data_get($this, $fieldName))){
143
            return '';
144
        }elseif (!is_array($fieldName)){
145
            return $fieldName;
146
        }
147
148
        $slugSourceString = '';
149
        $countFieldName = count($fieldName);
150
        for($i=0;$i<$countFieldName;$i++){
151
152
            $currFieldName = $fieldName[$i];
153
            if(!is_array($currFieldName) && trim($currFieldName)==''){
154
                continue;
155
            }
156
            if (!is_array($currFieldName) && (!data_get($this, $currFieldName))){
157
                continue;
158
            }
159
            if (!is_array($currFieldName) && data_get($this, $currFieldName)){
160
                $slugSourceString = $currFieldName;
161
                break;
162
            }
163
164
            $slugSourceString = $this->getImplodeSourceString($currFieldName, '');
165
166
            if($slugSourceString!=''){
167
                $slugSourceString = $currFieldName;
168
                break;
169
            }
170
        }
171
172
        return $slugSourceString;
173
    }
174
175
    /**
176
     * Make the given slug unique.
177
     * @param string $slug
178
     * @return string
179
     */
180
    protected function makeSlugUnique(string $slug): string
181
    {
182
        $originalSlug = $slug;
183
        $i = 1;
184
185
        while ($this->otherRecordExistsWithSlug($slug) || $slug === '') {
186
            $slug = $originalSlug.$this->slugOptions->separator.$i++;
187
        }
188
189
        return $slug;
190
    }
191
192
    /**
193
     * Determine if a record exists with the given slug.
194
     * @param string $slug
195
     * @return bool
196
     */
197
    protected function otherRecordExistsWithSlug(string $slug): bool
198
    {
199
        return (bool) static::where($this->slugOptions->slugField, $slug)
200
            ->where($this->getKeyName(), '!=', $this->getKey() ?? '0')
201
            ->first();
202
    }
203
204
    /**
205
     * This function will throw an exception when any of the options is missing or invalid.
206
     * @throws InvalidOption
207
     */
208
    protected function guardAgainstInvalidSlugOptions()
209
    {
210
        if (!count($this->slugOptions->generateSlugFrom)) {
211
            throw InvalidOption::missingFromField();
212
        }
213
214
        if (!strlen($this->slugOptions->slugField)) {
215
            throw InvalidOption::missingSlugField();
216
        }
217
218
        if ($this->slugOptions->maximumLength <= 0) {
219
            throw InvalidOption::invalidMaximumLength();
220
        }
221
    }
222
223
    /**
224
     * @param $slugFrom
225
     * @param string $separator
226
     * @return string
227
     */
228
    protected function getImplodeSourceString($slugFrom, string $separator) : string
229
    {
230
        $slugSourceString = collect($slugFrom)
231
            ->map(function (string $fieldName) : string {
232
                if ($fieldName == '') {
233
                    return '';
234
                }
235
                return data_get($this, $fieldName) ?? '';
236
            })
237
            ->implode($separator);
238
        return $slugSourceString;
239
    }
240
241
    /**
242
     *
243
     * SCOPE HELPERS
244
     *
245
     */
246
247
    /**
248
     * Query scope for finding a model by its slug field.
249
     *
250
     * @param \Illuminate\Database\Eloquent\Builder $scope
251
     * @param string $slug
252
     * @return \Illuminate\Database\Eloquent\Builder
253
     */
254
    public function scopeWhereSlug(\Illuminate\Database\Eloquent\Builder $scope, $slug)
255
    {
256
        $this->slugOptions = $this->getSlugOptionsOrDefault();
257
        return $scope->where($this->slugOptions->slugField, $slug);
258
    }
259
260
    /**
261
     * Find a model by its slug field.
262
     *
263
     * @param string $slug
264
     * @param array $columns
265
     * @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection|static[]|static|null
266
     */
267
    public static function findBySlug($slug, array $columns = ['*'])
268
    {
269
        return static::whereSlug($slug)->first($columns);
270
    }
271
272
    /**
273
     * Find a model by its slug field or throw an exception.
274
     *
275
     * @param string $slug
276
     * @param array $columns
277
     * @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection
278
     *
279
     * @throws \Illuminate\Database\Eloquent\ModelNotFoundException
280
     */
281
    public static function findBySlugOrFail($slug, array $columns = ['*'])
282
    {
283
        return static::whereSlug($slug)->firstOrFail($columns);
284
    }
285
}
286