Completed
Push — dependabot/npm_and_yarn/vue-se... ( e78fac...845b60 )
by
unknown
358:07 queued 338:52
created

SaveUrlSlugs::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
namespace Thinktomorrow\Chief\Urls\Application;
4
5
use Thinktomorrow\Chief\Urls\ProvidesUrl\ProvidesUrl;
6
use Thinktomorrow\Chief\Urls\UrlRecord;
7
8
class SaveUrlSlugs
9
{
10
    /** @var bool */
11
    private $strict = true;
12
13
    /** @var ProvidesUrl */
14
    private $model;
15
16
    private $existingRecords;
17
18 69
    public function __construct(ProvidesUrl $model)
19
    {
20 69
        $this->model = $model;
21 69
    }
22
23
    /**
24
     * Saving urls slugs in strict mode prevents identical urls to be automatically removed.
25
     * When set to false, this would remove the identical url records.
26
     *
27
     * @param bool $strict
28
     * @return $this
29
     */
30 5
    public function strict(bool $strict = true)
31
    {
32 5
        $this->strict = $strict;
33
34 5
        return $this;
35
    }
36
37 69
    public function handle(array $slugs): void
38
    {
39 69
        $this->existingRecords = UrlRecord::getByModel($this->model);
40
41 69
        foreach ($slugs as $locale => $slug) {
42 68
            if (!$slug) {
43 2
                $this->deleteRecord($locale);
44 2
                continue;
45
            }
46
47 68
            $this->saveRecord($locale, $this->prependBaseUrlSegment($slug, $locale));
48
        }
49 69
    }
50
51 2
    private function deleteRecord(string $locale)
52
    {
53 2
        return $this->saveRecord($locale, null);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->saveRecord($locale, null) targeting Thinktomorrow\Chief\Urls...eUrlSlugs::saveRecord() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
54
    }
55
56 68
    private function saveRecord(string $locale, ?string $slug)
57
    {
58
        // Existing ones for this locale?
59
        $nonRedirectsWithSameLocale = $this->existingRecords->filter(function ($record) use ($locale) {
60
            return (
61 21
                $record->locale == $locale &&
62 21
                !$record->isRedirect()
63
            );
64 68
        });
65
66
        // If slug entry is left empty, all existing records will be deleted
67 68
        if (!$slug) {
68
            $nonRedirectsWithSameLocale->each(function ($existingRecord) {
69 2
                $existingRecord->delete();
70 2
            });
71
72 2
            return;
73
        }
74
75 68
        $this->cleanupExistingRecords($locale, $slug);
76
77
        // If slug entry is left empty, all existing records will be deleted
78 68
        if ($nonRedirectsWithSameLocale->isEmpty()) {
79 66
            $this->createRecord($locale, $slug);
80 66
            return;
81
        }
82
83
        // Only replace the existing records that differ from the current passed slugs
84
        $nonRedirectsWithSameLocale->each(function ($existingRecord) use ($slug) {
85 17
            if ($existingRecord->slug != $slug) {
86 16
                $existingRecord->replaceAndRedirect(['slug' => $slug]);
87
            }
88 17
        });
89 17
    }
90
91 66
    private function createRecord($locale, $slug)
92
    {
93 66
        UrlRecord::create([
94 66
            'locale'              => $locale,
95 66
            'slug'                => $slug,
96 66
            'model_type'          => $this->model->getMorphClass(),
0 ignored issues
show
Bug introduced by
The method getMorphClass() does not exist on Thinktomorrow\Chief\Urls\ProvidesUrl\ProvidesUrl. Since it exists in all sub-types, consider adding an abstract or default implementation to Thinktomorrow\Chief\Urls\ProvidesUrl\ProvidesUrl. ( Ignorable by Annotation )

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

96
            'model_type'          => $this->model->/** @scrutinizer ignore-call */ getMorphClass(),
Loading history...
97 66
            'model_id'            => $this->model->id,
0 ignored issues
show
Bug introduced by
Accessing id on the interface Thinktomorrow\Chief\Urls\ProvidesUrl\ProvidesUrl suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
98
        ]);
99 66
    }
100
101
    /**
102
     * @param string $locale
103
     * @param string|null $slug
104
     */
105 68
    private function cleanupExistingRecords(string $locale, string $slug): void
106
    {
107
        // In the case where we have any redirects that match the given slug, we need to
108
        // remove the redirect record in favour of the newly added one.
109 68
        $this->deleteIdenticalRedirects($this->existingRecords, $locale, $slug);
110
111 68
        $sameExistingRecords = UrlRecord::where('slug', $slug)->where('locale', $locale)->get();
112
113
        // Also delete any redirects that match this locale and slug but are related to another model
114 68
        $this->deleteIdenticalRedirects($sameExistingRecords, $locale, $slug);
115
116
        // Also delete any urls that match this locale and slug but are related to another model
117 68
        $this->deleteIdenticalRecords($sameExistingRecords);
118 68
    }
119
120
    /**
121
     * Remove any redirects owned by this model that equal the new slug.
122
     *
123
     * @param $existingRecords
124
     * @param $locale
125
     * @param $slug
126
     */
127 68
    private function deleteIdenticalRedirects($existingRecords, $locale, $slug): void
128
    {
129
        $existingRecords->filter(function ($record) use ($locale) {
130
            return (
131 19
                $record->locale == $locale &&
132 19
                $record->isRedirect()
133
            );
134
        })->each(function ($existingRecord) use ($slug) {
135 3
            if ($existingRecord->slug == $slug) {
136 2
                $existingRecord->delete();
137
            }
138 68
        });
139 68
    }
140
141 68
    private function deleteIdenticalRecords($existingRecords): void
142
    {
143 68
        if ($this->strict) {
144 66
            return;
145
        }
146
147
        // The old homepage url should be removed since this is no longer in effect.
148
        // In case of any redirect to this old homepage, the last used redirect is now back in effect.
149
        $existingRecords->reject(function ($existingRecord) {
150
            return (
151
                $existingRecord->model_type == $this->model->getMorphClass() &&
152
                $existingRecord->model_id == $this->model->id);
0 ignored issues
show
Bug introduced by
Accessing id on the interface Thinktomorrow\Chief\Urls\ProvidesUrl\ProvidesUrl suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
153
        })->each(function ($existingRecord) {
154
155
            // TODO: if there is a redirect to this page, we'll take this one as the new url
156
            $existingRecord->delete();
157 5
        });
158 5
    }
159
160
    /**
161
     * @param string $slug
162
     * @param $locale
163
     * @return string
164
     */
165 68
    private function prependBaseUrlSegment(string $slug, $locale): string
166
    {
167 68
        $slugWithBaseSegment = $this->model->baseUrlSegment($locale) . '/' . $slug;
168 68
        $slugWithBaseSegment = trim($slugWithBaseSegment, '/');
169
170
        // If slug with base segment is empty string, it means that the passed slug was probably a "/" character.
171
        // so we'll want to return it in case the base segment is not added.
172 68
        return $slugWithBaseSegment ?: '/';
173
    }
174
}
175