Completed
Push — master ( f098ed...4b87ed )
by Vasyl
03:52
created

SlugMakerTrait::scopeGetBySlugs()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 2
dl 0
loc 3
rs 10
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 SlugMakerTrait
8
{
9
    protected $slugOptions;
10
11
    private $slugField = 'name';
12
13
    private $relatedName = 'slugable';
14
15
    private $slugModel = \Fomvasss\SlugMaker\Models\Slug::class;
16
17
    /**
18
     * Get the options for generating the slug.
19
     */
20
    abstract public function getSlugMakerOptions(): SlugMakerOptions;
21
22
    /**
23
     * The morph relations (has one) for the model.
24
     *
25
     * @return mixed
26
     */
27
    public function slug()
28
    {
29
        $this->setConfig();
30
31
        return $this->morphOne($this->slugModel, $this->relatedName);
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

31
        return $this->/** @scrutinizer ignore-call */ morphOne($this->slugModel, $this->relatedName);
Loading history...
32
    }
33
34
    /**
35
     * The boot trait events in class (model).
36
     */
37
    protected static function bootSlugMakerTrait()
38
    {
39
        static::created(function (Model $model) {
40
            $model->generateSlugOnCreate();
41
        });
42
43
        static::updated(function (Model $model) {
44
            $model->generateSlugOnUpdate();
45
        });
46
47
        static::deleted(function (Model $model) {
48
            $model->removeSlugsOnDeleted();
49
        });
50
    }
51
52
    protected function setConfig()
53
    {
54
        $config = config('slugmaker');
0 ignored issues
show
Bug introduced by
The function config was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

54
        $config = /** @scrutinizer ignore-call */ config('slugmaker');
Loading history...
55
56
        foreach ($config as $name => $value) {
57
            $name = camel_case($name);
58
            $this->$name = $value;
59
        }
60
    }
61
62
    /**
63
     * Handle adding slug on model creation.
64
     */
65 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...
66
    {
67
        $this->slugOptions = $this->getSlugMakerOptions();
68
69
        if (! $this->slugOptions->generateSlugsOnCreate) {
70
            return;
71
        }
72
73
        $slug = $this->getSlug();
74
        $this->storeSlug($slug);
75
    }
76
77
    /**
78
     * Handle adding slug on model update.
79
     */
80 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...
81
    {
82
        $this->slugOptions = $this->getSlugMakerOptions();
83
84
        if (! $this->slugOptions->generateSlugsOnUpdate) {
85
            return;
86
        }
87
88
        $slug = $this->getSlug();
89
        $this->storeSlug($slug);
90
    }
91
92
    /**
93
     * The store (create or update) slug in database.
94
     *
95
     * @param $slug
96
     */
97
    protected function storeSlug($slug)
98
    {
99
        $this->setConfig();
100
101
        if ($this->slug) {
0 ignored issues
show
Bug Best Practice introduced by
The property slug does not exist on Fomvasss\SlugMaker\SlugMakerTrait. Did you maybe forget to declare it?
Loading history...
102
            //$this->slug()->updateOrCreate(['name' => $this->slug->name], [$this->slugField => $slug]);
103
            $this->slug()->update([$this->slugField => $slug]);
104
        } else {
105
            $this->slug()->create([$this->slugField => $slug]);
106
        }
107
    }
108
109
    /**
110
     * Remove the slug in database. Default migration not using softdelete.
111
     */
112
    protected function removeSlugsOnDeleted()
113
    {
114
        $this->slugOptions = $this->getSlugMakerOptions();
115
116
        if ($this->slug && $this->slugOptions->removeSlugsOnDelete) {
0 ignored issues
show
Bug Best Practice introduced by
The property slug does not exist on Fomvasss\SlugMaker\SlugMakerTrait. Did you maybe forget to declare it?
Loading history...
117
            $this->slug()->delete();
118
        }
119
    }
120
121
    /**
122
     * Get the slug with the model.
123
     */
124
    protected function getSlug()
125
    {
126
        $this->guardAgainstInvalidSlugOptions();
127
128
        $slug = $this->generateNonUniqueSlug();
129
130
        if ($this->slugOptions->generateUniqueSlugs) {
131
            $slug = $this->makeSlugUnique($slug);
132
        }
133
134
        return $slug;
135
    }
136
137
    /**
138
     * Generate a non unique slug for this record.
139
     */
140
    protected function generateNonUniqueSlug(): string
141
    {
142
        $this->slugOptions = $this->getSlugMakerOptions();
143
144
        return str_slug($this->getSlugSourceString(), $this->slugOptions->slugSeparator);
145
    }
146
147
    /**
148
     * Get the string that should be used as base for the slug.
149
     */
150
    protected function getSlugSourceString(): string
151
    {
152
        if (is_callable($this->slugOptions->generateSlugFrom)) {
153
            $slugSourceString = call_user_func($this->slugOptions->generateSlugFrom, $this);
154
155
            return substr($slugSourceString, 0, $this->slugOptions->maximumLength);
0 ignored issues
show
Bug Best Practice introduced by
The expression return substr($slugSourc...Options->maximumLength) could return the type false which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
156
        }
157
158
        $slugSourceString = collect($this->slugOptions->generateSlugFrom)
159
            ->map(function (string $fieldName) : string {
160
                return $this->$fieldName ?? '';
161
            })
162
            ->implode('-');
163
164
        return substr($slugSourceString, 0, $this->slugOptions->maximumLength);
0 ignored issues
show
Bug Best Practice introduced by
The expression return substr($slugSourc...Options->maximumLength) could return the type false which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
165
    }
166
167
    /**
168
     * Make the given slug unique.
169
     */
170
    protected function makeSlugUnique(string $slug): string
171
    {
172
        $originalSlug = $slug;
173
        $i = 1;
174
175
        while ($this->otherRecordExistsWithSlug($slug) || $slug === '') {
176
            $slug = $originalSlug.'-'.$i++;
177
        }
178
179
        return $slug;
180
    }
181
182
    /**
183
     * Determine if a record exists with the given slug.
184
     */
185
    protected function otherRecordExistsWithSlug(string $slug): bool
186
    {
187
        return (bool) $this->slugModel::where($this->slugField, $slug)
188
            ->withoutGlobalScopes()
189
            ->first();
190
    }
191
192
    /**
193
     * This function will throw an exception when any of the options is missing or invalid.
194
     */
195
    protected function guardAgainstInvalidSlugOptions()
196
    {
197
        if (! count($this->slugOptions->generateSlugFrom)) {
198
            throw InvalidOption::missingFromField();
199
        }
200
201
        if (! strlen($this->slugField)) {
202
            throw InvalidOption::missingSlugField();
203
        }
204
205
        if ($this->slugOptions->maximumLength <= 0) {
206
            throw InvalidOption::invalidMaximumLength();
207
        }
208
    }
209
210
    /**
211
     * The scope for fet slug name.
212
     */
213
    public function scopeGetSlugName()
214
    {
215
        $this->setConfig();
216
217
        if ($this->slug) {
0 ignored issues
show
Bug Best Practice introduced by
The property slug does not exist on Fomvasss\SlugMaker\SlugMakerTrait. Did you maybe forget to declare it?
Loading history...
218
            return $this->slug->{$this->slugField};
219
        }
220
221
        return '';
222
    }
223
224
    /**
225
     * Models by slugs.
226
     *
227
     * @param $query
228
     * @param string $slug
229
     * @return mixed
230
     */
231
    public function scopeBySlugs($query, $slug)
232
    {
233
        $this->setConfig();
234
235
        $slugs = is_array($slug) ? $slug : [$slug];
236
237
        return $query->whereHas('slug', function ($q) use ($slugs) {
238
            $q->whereIn($this->slugField, $slugs);
239
        });
240
    }
241
242
    /**
243
     * Find first model by slug.
244
     *
245
     * @param $query
246
     * @param string $slug
247
     * @return mixed
248
     */
249
    public function scopeFindBySlug($query, string $slug)
250
    {
251
        return $this->scopeBySlugs($query, $slug)->first();
252
    }
253
254
    /**
255
     * Find first model by slug or throw exciption.
256
     *
257
     * @param $query
258
     * @param string $slug
259
     * @return mixed
260
     */
261
    public function scopeFindOrFailBySlug($query, string $slug)
262
    {
263
        return $this->scopeBySlugs($query, $slug)->firstOrFail();
264
    }
265
266
    /**
267
     * Get models by slugs.
268
     *
269
     * @param $query
270
     * @param $slug
271
     * @return mixed
272
     */
273
    public function scopeGetBySlugs($query, $slug)
274
    {
275
        return $this->scopeBySlugs($query, $slug)->get();
276
    }
277
}
278