Completed
Push — master ( 174d79...4b253c )
by Vasyl
03:57
created

HasSlugMaker::bootHasSlugMaker()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 6
nc 1
nop 0
dl 0
loc 12
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace Fomvasss\SlugMaker;
4
5
use Illuminate\Database\Eloquent\Model;
0 ignored issues
show
Bug introduced by
The type Illuminate\Database\Eloquent\Model was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
6
7
trait HasSlugMaker
8
{
9
    protected $slugOptions;
10
11
    /**
12
     * Get the options for generating the slug.
13
     */
14
    abstract public function getSlugMakerOptions(): SlugMakerOptions;
15
16
    /**
17
     * The morph relations (has one) for the model.
18
     *
19
     * @return mixed
20
     */
21
    public function slug()
22
    {
23
        return $this->morphOne(\Fomvasss\SlugMaker\Models\Slug::class, 'slugable');
0 ignored issues
show
Bug introduced by
It seems like morphOne() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

23
        return $this->/** @scrutinizer ignore-call */ morphOne(\Fomvasss\SlugMaker\Models\Slug::class, 'slugable');
Loading history...
24
    }
25
26
    /**
27
     * The boot trait events in class (model).
28
     */
29
    protected static function bootHasSlugMaker()
30
    {
31
        static::created(function (Model $model) {
32
            $model->generateSlugOnCreate();
33
        });
34
35
        static::updated(function (Model $model) {
36
            $model->generateSlugOnUpdate();
37
        });
38
39
        static::deleted(function (Model $model) {
40
            $model->removeSlugsOnDeleted();
41
        });
42
    }
43
44
    /**
45
     * Handle adding slug on model creation.
46
     */
47 View Code Duplication
    protected function generateSlugOnCreate()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
48
    {
49
        $this->slugOptions = $this->getSlugMakerOptions();
50
51
        if (! $this->slugOptions->generateSlugsOnCreate) {
52
            return;
53
        }
54
55
        $slug = $this->getSlug();
56
        $this->storeSlug($slug);
57
    }
58
59
    /**
60
     * Handle adding slug on model update.
61
     */
62 View Code Duplication
    protected function generateSlugOnUpdate()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
63
    {
64
        $this->slugOptions = $this->getSlugMakerOptions();
65
66
        if (! $this->slugOptions->generateSlugsOnUpdate) {
67
            return;
68
        }
69
70
        $slug = $this->getSlug();
71
        $this->storeSlug($slug);
72
    }
73
74
    /**
75
     * The store (create or update) slug in database.
76
     *
77
     * @param $slug
78
     */
79
    protected function storeSlug($slug)
80
    {
81
        if ($this->slug) {
0 ignored issues
show
Bug Best Practice introduced by
The property slug does not exist on Fomvasss\SlugMaker\HasSlugMaker. Did you maybe forget to declare it?
Loading history...
82
            //$this->slug()->updateOrCreate(['name' => $this->slug->name], [$this->slugField => $slug]);
83
            $this->slug()->update(['name' => $slug]);
84
        } else {
85
            $this->slug()->create(['name' => $slug]);
86
        }
87
    }
88
89
    /**
90
     * Remove the slug in database. Default migration not using softdelete.
91
     */
92
    protected function removeSlugsOnDeleted()
93
    {
94
        $this->slugOptions = $this->getSlugMakerOptions();
95
96
        if ($this->slug && $this->slugOptions->removeSlugsOnDelete) {
0 ignored issues
show
Bug Best Practice introduced by
The property slug does not exist on Fomvasss\SlugMaker\HasSlugMaker. Did you maybe forget to declare it?
Loading history...
97
            $this->slug()->delete();
98
        }
99
    }
100
101
    /**
102
     * Get the slug with the model.
103
     */
104
    protected function getSlug()
105
    {
106
        $this->guardAgainstInvalidSlugOptions();
107
108
        $slug = $this->generateNonUniqueSlug();
109
110
        if ($this->slugOptions->generateUniqueSlugs) {
111
            $slug = $this->makeSlugUnique($slug);
112
        }
113
114
        return $slug;
115
    }
116
117
    /**
118
     * Generate a non unique slug for this record.
119
     */
120
    protected function generateNonUniqueSlug(): string
121
    {
122
        $this->slugOptions = $this->getSlugMakerOptions();
123
124
        return str_slug($this->getSlugSourceString(), $this->slugOptions->slugSeparator);
125
    }
126
127
    /**
128
     * Get the string that should be used as base for the slug.
129
     */
130
    protected function getSlugSourceString(): string
131
    {
132
        if (is_callable($this->slugOptions->generateSlugFrom)) {
133
            $slugSourceString = call_user_func($this->slugOptions->generateSlugFrom, $this);
134
135
            return $this->getClippedSlugWithPrefixSuffix($slugSourceString);
136
        }
137
138
        $slugSourceString = collect($this->slugOptions->generateSlugFrom)
139
            ->map(function (string $fieldName) : string {
140
                return $this->$fieldName ?? '';
141
            })
142
            ->implode('-');
143
144
        return $this->getClippedSlugWithPrefixSuffix($slugSourceString);
145
    }
146
147
    /**
148
     * Add prefix and suffix to slug. Clipped string.
149
     */
150
    public function getClippedSlugWithPrefixSuffix(string $slugSourceString): string
151
    {
152
        $prefix = $this->slugOptions->slugPrefix;
153
        $suffix = $this->slugOptions->slugSuffix;
154
        $lengthPrefix = strlen($prefix);
155
        $lengthSuffix = strlen($suffix);
156
157
        if ($lengthPrefix + $lengthSuffix) {
158
            $limitWithoutPrefixSuffix = $this->slugOptions->maximumLength - (++$lengthPrefix + ++$lengthSuffix);
159
160
            if ($limitWithoutPrefixSuffix < 1) {
161
                return str_limit($prefix.' '.$suffix, $this->slugOptions->maximumLength);
162
            }
163
164
            return $prefix.' '.str_limit($slugSourceString, $limitWithoutPrefixSuffix, '').' '.$suffix;
165
        }
166
167
        return str_limit($slugSourceString, $this->slugOptions->maximumLength);
168
    }
169
170
    /**
171
     * Make the given slug unique.
172
     */
173
    protected function makeSlugUnique(string $slug): string
174
    {
175
        $originalSlug = $slug;
176
        $i = 1;
177
        while ($this->otherRecordExistsWithSlug($slug) || $slug === '') {
178
            $slug = $originalSlug.'-'.$i++;
179
        }
180
181
        return $slug;
182
    }
183
184
    /**
185
     * Determine if a record exists with the given slug.
186
     */
187
    protected function otherRecordExistsWithSlug(string $slug): bool
188
    {
189
        return (bool) \Fomvasss\SlugMaker\Models\Slug::where('name', $slug)
190
            ->withoutGlobalScopes()
191
            ->first();
192
    }
193
194
    /**
195
     * This function will throw an exception when any of the options is missing or invalid.
196
     */
197
    protected function guardAgainstInvalidSlugOptions()
198
    {
199
        if (! count($this->slugOptions->generateSlugFrom)) {
200
            throw InvalidOption::missingFromField();
201
        }
202
203
        //if (! strlen($this->slugField)) {
204
        //    throw InvalidOption::missingSlugField();
205
        //}
206
207
        if ($this->slugOptions->maximumLength <= 0) {
208
            throw InvalidOption::invalidMaximumLength();
209
        }
210
    }
211
212
    /**
213
     * The scope for fet slug name.
214
     */
215
    public function scopeGetSlugName()
216
    {
217
        if ($this->slug) {
0 ignored issues
show
Bug Best Practice introduced by
The property slug does not exist on Fomvasss\SlugMaker\HasSlugMaker. Did you maybe forget to declare it?
Loading history...
218
            return $this->slug->name;
219
        }
220
        return null;
221
    }
222
223
    /**
224
     * Models by slugs.
225
     *
226
     * @param $query
227
     * @param string $slug
228
     * @return mixed
229
     */
230
    public function scopeBySlugs($query, $slug)
231
    {
232
        $slugs = is_array($slug) ? $slug : [$slug];
233
234
        return $query->whereHas('slug', function ($q) use ($slugs) {
235
            $q->whereIn('name', $slugs);
236
        });
237
    }
238
239
    /**
240
     * Find first model by slug.
241
     *
242
     * @param $query
243
     * @param string $slug
244
     * @return mixed
245
     */
246
    public function scopeFindBySlug($query, string $slug)
247
    {
248
        return $this->scopeBySlugs($query, $slug)->first();
249
    }
250
251
    /**
252
     * Find first model by slug or throw exciption.
253
     *
254
     * @param $query
255
     * @param string $slug
256
     * @return mixed
257
     */
258
    public function scopeFindOrFailBySlug($query, string $slug)
259
    {
260
        return $this->scopeBySlugs($query, $slug)->firstOrFail();
261
    }
262
263
    /**
264
     * Get models by slugs.
265
     *
266
     * @param $query
267
     * @param $slugs
268
     * @return mixed
269
     */
270
    public function scopeGetBySlugs($query, $slugs)
271
    {
272
        return $this->scopeBySlugs($query, $slugs)->get();
273
    }
274
275
    /**
276
     * Get array ids by slugs.
277
     *
278
     * @param $query
279
     * @param array $slugs
280
     * @return array
281
     */
282
    public function scopeGetArrayIdsBySlugs($query, array $slugs): array
283
    {
284
        return $this->scopeBySlugs($query, $slugs)->pluck('id')->toArray();
0 ignored issues
show
Bug introduced by
$slugs of type array is incompatible with the type string expected by parameter $slug of Fomvasss\SlugMaker\HasSlugMaker::scopeBySlugs(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

284
        return $this->scopeBySlugs($query, /** @scrutinizer ignore-type */ $slugs)->pluck('id')->toArray();
Loading history...
285
    }
286
}
287