Passed
Pull Request — master (#324)
by Philippe
54:56 queued 21:29
created

SaveUrlSlugs   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 160
Duplicated Lines 0 %

Test Coverage

Coverage 95.16%

Importance

Changes 0
Metric Value
wmc 20
eloc 54
dl 0
loc 160
ccs 59
cts 62
cp 0.9516
rs 10
c 0
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
A saveRecord() 0 31 5
A prependBaseUrlSegment() 0 3 1
A handle() 0 11 3
A deleteIdenticalRedirects() 0 10 3
A createRecord() 0 7 1
A strict() 0 5 1
A cleanupExistingRecords() 0 13 1
A deleteRecord() 0 3 1
A __construct() 0 3 1
A deleteIdenticalRecords() 0 16 3
1
<?php
2
3
namespace Thinktomorrow\Chief\Urls\Application;
4
5
use Thinktomorrow\Chief\Urls\ProvidesUrl\ProvidesUrl;
6
use Thinktomorrow\Chief\Urls\UrlRecord;
7
use Thinktomorrow\Chief\Urls\ProvidesUrl\BaseUrlSegment;
8
9
class SaveUrlSlugs
10
{
11
    /** @var bool */
12
    private $strict = true;
13
14
    /** @var ProvidesUrl */
15
    private $model;
16
17
    private $existingRecords;
18
19 73
    public function __construct(ProvidesUrl $model)
20
    {
21 73
        $this->model = $model;
22 73
    }
23
24
    /**
25
     * Saving urls slugs in strict mode prevents identical urls to be automatically removed.
26
     * When set to false, this would remove the identical url records.
27
     *
28
     * @param bool $strict
29
     * @return $this
30
     */
31 5
    public function strict(bool $strict = true)
32
    {
33 5
        $this->strict = $strict;
34
35 5
        return $this;
36
    }
37
38 73
    public function handle(array $slugs): void
39
    {
40 73
        $this->existingRecords = UrlRecord::getByModel($this->model);
41
42 73
        foreach ($slugs as $locale => $slug) {
43 72
            if (!$slug) {
44 2
                $this->deleteRecord($locale);
45 2
                continue;
46
            }
47
48 72
            $this->saveRecord($locale, $this->prependBaseUrlSegment($slug, $locale));
49
        }
50 73
    }
51
52 2
    private function deleteRecord(string $locale)
53
    {
54 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...
55
    }
56
57 72
    private function saveRecord(string $locale, ?string $slug)
58
    {
59
        // Existing ones for this locale?
60
        $nonRedirectsWithSameLocale = $this->existingRecords->filter(function ($record) use ($locale) {
61
            return (
62 23
                $record->locale == $locale &&
63 23
                !$record->isRedirect()
64
            );
65 72
        });
66
67
        // If slug entry is left empty, all existing records will be deleted
68 72
        if (!$slug) {
69
            $nonRedirectsWithSameLocale->each(function ($existingRecord) {
70 2
                $existingRecord->delete();
71 2
            });
72
73 2
            return;
74
        }
75
76 72
        $this->cleanupExistingRecords($locale, $slug);
77
78
        // If slug entry is left empty, all existing records will be deleted
79 72
        if ($nonRedirectsWithSameLocale->isEmpty()) {
80 70
            $this->createRecord($locale, $slug);
81 70
            return;
82
        }
83
84
        // Only replace the existing records that differ from the current passed slugs
85
        $nonRedirectsWithSameLocale->each(function ($existingRecord) use ($slug) {
86 19
            if ($existingRecord->slug != $slug) {
87 18
                $existingRecord->replaceAndRedirect(['slug' => $slug]);
88
            }
89 19
        });
90 19
    }
91
92 70
    private function createRecord($locale, $slug)
93
    {
94 70
        UrlRecord::create([
95 70
            'locale'              => $locale,
96 70
            'slug'                => $slug,
97 70
            '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

97
            'model_type'          => $this->model->/** @scrutinizer ignore-call */ getMorphClass(),
Loading history...
98 70
            '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...
99
        ]);
100 70
    }
101
102
    /**
103
     * @param string $locale
104
     * @param string|null $slug
105
     */
106 72
    private function cleanupExistingRecords(string $locale, string $slug): void
107
    {
108
        // In the case where we have any redirects that match the given slug, we need to
109
        // remove the redirect record in favour of the newly added one.
110 72
        $this->deleteIdenticalRedirects($this->existingRecords, $locale, $slug);
111
112 72
        $sameExistingRecords = UrlRecord::where('slug', $slug)->where('locale', $locale)->get();
113
114
        // Also delete any redirects that match this locale and slug but are related to another model
115 72
        $this->deleteIdenticalRedirects($sameExistingRecords, $locale, $slug);
116
117
        // Also delete any urls that match this locale and slug but are related to another model
118 72
        $this->deleteIdenticalRecords($sameExistingRecords);
119 72
    }
120
121
    /**
122
     * Remove any redirects owned by this model that equal the new slug.
123
     *
124
     * @param $existingRecords
125
     * @param $locale
126
     * @param $slug
127
     */
128 72
    private function deleteIdenticalRedirects($existingRecords, $locale, $slug): void
129
    {
130
        $existingRecords->filter(function ($record) use ($locale) {
131
            return (
132 21
                $record->locale == $locale &&
133 21
                $record->isRedirect()
134
            );
135
        })->each(function ($existingRecord) use ($slug) {
136 3
            if ($existingRecord->slug == $slug) {
137 2
                $existingRecord->delete();
138
            }
139 72
        });
140 72
    }
141
142 72
    private function deleteIdenticalRecords($existingRecords): void
143
    {
144 72
        if ($this->strict) {
145 70
            return;
146
        }
147
148
        // The old homepage url should be removed since this is no longer in effect.
149
        // In case of any redirect to this old homepage, the last used redirect is now back in effect.
150
        $existingRecords->reject(function ($existingRecord) {
151
            return (
152
                $existingRecord->model_type == $this->model->getMorphClass() &&
153
                $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...
154
        })->each(function ($existingRecord) {
155
156
            // TODO: if there is a redirect to this page, we'll take this one as the new url
157
            $existingRecord->delete();
158 5
        });
159 5
    }
160
161
    /**
162
     * @param string $slug
163
     * @param $locale
164
     * @return string
165
     */
166 72
    private function prependBaseUrlSegment(string $slug, $locale): string
167
    {
168 72
        return BaseUrlSegment::prepend($this->model, $slug, $locale);
169
    }
170
}
171