1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* TechDivision\Import\Utils\UrlKeyUtil |
5
|
|
|
* |
6
|
|
|
* NOTICE OF LICENSE |
7
|
|
|
* |
8
|
|
|
* This source file is subject to the Open Software License (OSL 3.0) |
9
|
|
|
* that is available through the world-wide-web at this URL: |
10
|
|
|
* http://opensource.org/licenses/osl-3.0.php |
11
|
|
|
* |
12
|
|
|
* PHP version 5 |
13
|
|
|
* |
14
|
|
|
* @author Tim Wagner <[email protected]> |
15
|
|
|
* @copyright 2020 TechDivision GmbH <[email protected]> |
16
|
|
|
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) |
17
|
|
|
* @link https://github.com/techdivision/import |
18
|
|
|
* @link http://www.techdivision.com |
19
|
|
|
*/ |
20
|
|
|
|
21
|
|
|
namespace TechDivision\Import\Utils; |
22
|
|
|
|
23
|
|
|
use TechDivision\Import\Subjects\UrlKeyAwareSubjectInterface; |
24
|
|
|
use TechDivision\Import\Services\UrlKeyAwareProcessorInterface; |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* Utility class that provides functionality to make URL keys unique. |
28
|
|
|
* |
29
|
|
|
* @author Tim Wagner <[email protected]> |
30
|
|
|
* @copyright 2020 TechDivision GmbH <[email protected]> |
31
|
|
|
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) |
32
|
|
|
* @link https://github.com/techdivision/import |
33
|
|
|
* @link http://www.techdivision.com |
34
|
|
|
*/ |
35
|
|
|
class UrlKeyUtil implements UrlKeyUtilInterface |
36
|
|
|
{ |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* The URL key aware processor instance. |
40
|
|
|
* |
41
|
|
|
* \TechDivision\Import\Services\UrlKeyAwareProcessorInterface |
42
|
|
|
*/ |
43
|
|
|
protected $urlKeyAwareProcessor; |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* The array to temporary handle new URL rewrites until they have |
47
|
|
|
* been persisted in the registry for further processing. |
48
|
|
|
* |
49
|
|
|
* @var array |
50
|
|
|
*/ |
51
|
|
|
protected $urlRewrites = array(); |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* Construct a new instance. |
55
|
|
|
* |
56
|
|
|
* @param \TechDivision\Import\Services\UrlKeyAwareProcessorInterface $urlKeyAwareProcessor The URL key aware processor instance |
57
|
|
|
*/ |
58
|
|
|
public function __construct(UrlKeyAwareProcessorInterface $urlKeyAwareProcessor) |
59
|
|
|
{ |
60
|
|
|
$this->urlKeyAwareProcessor = $urlKeyAwareProcessor; |
61
|
|
|
} |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* Returns the URL key aware processor instance. |
65
|
|
|
* |
66
|
|
|
* @return \TechDivision\Import\Services\UrlKeyAwareProcessorInterface The processor instance |
67
|
|
|
*/ |
68
|
|
|
protected function getUrlKeyAwareProcessor() |
69
|
|
|
{ |
70
|
|
|
return $this->urlKeyAwareProcessor; |
71
|
|
|
} |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* Load's and return's the URL rewrite for the given request path and store ID |
75
|
|
|
* |
76
|
|
|
* @param string $requestPath The request path to load the URL rewrite for |
77
|
|
|
* @param int $storeId The store ID to load the URL rewrite for |
78
|
|
|
* |
79
|
|
|
* @return string|null The URL rewrite found for the given request path and store ID |
80
|
|
|
*/ |
81
|
|
|
protected function loadUrlRewriteByRequestPathAndStoreId(string $requestPath, int $storeId) |
82
|
|
|
{ |
83
|
|
|
return $this->getUrlKeyAwareProcessor()->loadUrlRewriteByRequestPathAndStoreId($requestPath, $storeId); |
84
|
|
|
} |
85
|
|
|
|
86
|
|
|
/** |
87
|
|
|
* Make's the passed URL key unique by adding/raising a number to the end. |
88
|
|
|
* |
89
|
|
|
* @param \TechDivision\Import\Subjects\UrlKeyAwareSubjectInterface $subject The subject to make the URL key unique for |
90
|
|
|
* @param string $urlKey The URL key to make unique |
91
|
|
|
* @param string|null $urlPath The URL path to make unique (only used for categories) |
92
|
|
|
* |
93
|
|
|
* @return string The unique URL key |
94
|
|
|
*/ |
95
|
|
|
protected function doMakeUnique(UrlKeyAwareSubjectInterface $subject, string $urlKey, string $urlPath = null) : string |
96
|
|
|
{ |
97
|
|
|
|
98
|
|
|
// initialize the store view ID, use the default store view if no store view has |
99
|
|
|
// been set, because the default url_key value has been set in default store view |
100
|
|
|
$storeId = $subject->getRowStoreId(); |
101
|
|
|
|
102
|
|
|
// initialize the counter |
103
|
|
|
$counter = 0; |
104
|
|
|
|
105
|
|
|
// initialize the counters |
106
|
|
|
$matchingCounters = array(); |
107
|
|
|
$notMatchingCounters = array(); |
108
|
|
|
|
109
|
|
|
// pre-initialze the URL by concatenating path and/or key to query for |
110
|
|
|
$url = $urlPath ? sprintf('%s/%s', $urlPath, $urlKey) : $urlKey; |
111
|
|
|
|
112
|
|
|
do { |
113
|
|
|
// try to load the attribute |
114
|
|
|
$urlRewrite = $this->loadUrlRewriteByRequestPathAndStoreId($url, $storeId); |
115
|
|
|
|
116
|
|
|
// try to load the entity's URL key |
117
|
|
|
if ($urlRewrite) { |
|
|
|
|
118
|
|
|
// this IS the URL key of the passed entity |
119
|
|
|
if ($subject->isUrlKeyOf($urlRewrite)) { |
120
|
|
|
$matchingCounters[] = $counter; |
121
|
|
|
} else { |
122
|
|
|
$notMatchingCounters[] = $counter; |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
// prepare the next URL key to query for |
126
|
|
|
$url = sprintf('%s-%d', $urlKey, ++$counter); |
127
|
|
|
} |
128
|
|
|
} while ($urlRewrite); |
|
|
|
|
129
|
|
|
|
130
|
|
|
// sort the array ascending according to the counter |
131
|
|
|
asort($matchingCounters); |
132
|
|
|
asort($notMatchingCounters); |
133
|
|
|
|
134
|
|
|
// this IS the URL key of the passed entity => we've an UPDATE |
135
|
|
|
if (sizeof($matchingCounters) > 0) { |
136
|
|
|
// load highest counter |
137
|
|
|
$counter = end($matchingCounters); |
138
|
|
|
// if the counter is > 0, we've to append it to the new URL key |
139
|
|
|
if ($counter > 0) { |
140
|
|
|
$urlKey = sprintf('%s-%d', $urlKey, $counter); |
141
|
|
|
} |
142
|
|
|
} elseif (sizeof($notMatchingCounters) > 0) { |
143
|
|
|
// load the last entry that contains the |
144
|
|
|
// the last NOT matching counter |
145
|
|
|
$newCounter = end($notMatchingCounters); |
146
|
|
|
// create a new URL key by raising the counter |
147
|
|
|
$urlKey = sprintf('%s-%d', $urlKey, ++$newCounter); |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
// return the passed URL key, if NOT |
151
|
|
|
return $urlKey; |
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
/** |
155
|
|
|
* Make's the passed URL key unique by adding the next number to the end. |
156
|
|
|
* |
157
|
|
|
* @param \TechDivision\Import\Subjects\UrlKeyAwareSubjectInterface $subject The subject to make the URL key unique for |
158
|
|
|
* @param string $urlKey The URL key to make unique |
159
|
|
|
* @param array $urlPaths The URL paths to make unique |
160
|
|
|
* |
161
|
|
|
* @return string The unique URL key |
162
|
|
|
*/ |
163
|
|
|
public function makeUnique(UrlKeyAwareSubjectInterface $subject, string $urlKey, array $urlPaths = array()) : string |
164
|
|
|
{ |
165
|
|
|
|
166
|
|
|
// initialize the store view ID, use the default store view if no store view has |
167
|
|
|
// been set, because the default url_key value has been set in default store view |
168
|
|
|
$storeId = $subject->getRowStoreId(); |
169
|
|
|
|
170
|
|
|
// reset the temporary persisted URL |
171
|
|
|
// rewrites because we've a new row |
172
|
|
|
$this->urlRewrites = array(); |
173
|
|
|
|
174
|
|
|
// iterate over the passed URL paths |
175
|
|
|
// and try to find a unique URL key |
176
|
|
|
for ($i = -1; $i < sizeof($urlPaths); $i++) { |
|
|
|
|
177
|
|
|
// pre-initialze the URL by concatenating path and/or key to query for |
178
|
|
|
$url = isset($urlPaths[$i]) ? sprintf('%s/%s', $urlPaths[$i], $urlKey) : $urlKey; |
179
|
|
|
|
180
|
|
|
// we've temporary persist a dummy URL rewrite to keep track of the new URL key, e. g. for |
181
|
|
|
// the case the import contains another product or category that wants to use the same one |
182
|
|
|
$this->urlRewrites[$urlKey][$url][$storeId] = array( |
183
|
|
|
MemberNames::REDIRECT_TYPE => 0, |
184
|
|
|
MemberNames::STORE_ID => $storeId, |
185
|
|
|
MemberNames::ENTITY_ID => $subject->getLastEntityId() |
|
|
|
|
186
|
|
|
); |
187
|
|
|
|
188
|
|
|
// try to make the URL key unique for the given URL path |
189
|
|
|
$proposedUrlKey = $this->doMakeUnique($subject, $urlKey, isset($urlPaths[$i]) ? $urlPaths[$i] : null); |
190
|
|
|
|
191
|
|
|
// if the URL key is NOT the same as the passed one or with the parent URL path |
192
|
|
|
// it can NOT be used, so we've to persist it temporarily and try it again for |
193
|
|
|
// all the other URL paths until we found one that works with every URL path |
194
|
|
|
if ($urlKey !== $proposedUrlKey) { |
195
|
|
|
// temporarily persist the URL key |
196
|
|
|
$urlKey = $proposedUrlKey; |
197
|
|
|
// reset the counter and restart the |
198
|
|
|
// iteration with the first URL path |
199
|
|
|
$i = -2; |
200
|
|
|
} |
201
|
|
|
} |
202
|
|
|
|
203
|
|
|
// do a bulk update with the temporary URL rewrites here, |
204
|
|
|
// because we have to be aware of any changes furthermore |
205
|
|
|
if (isset($this->urlRewrites[$urlKey])) { |
206
|
|
|
$this->getUrlKeyAwareProcessor()->bulkPersistTemporaryUrlRewrites($this->urlRewrites[$urlKey]); |
207
|
|
|
} |
208
|
|
|
|
209
|
|
|
// return the unique URL key |
210
|
|
|
return $urlKey; |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
/** |
214
|
|
|
* Load the url_key if exists |
215
|
|
|
* |
216
|
|
|
* @param \TechDivision\Import\Subjects\UrlKeyAwareSubjectInterface $subject The subject to make the URL key unique for |
217
|
|
|
* @param int $primaryKeyId The ID from category or product |
218
|
|
|
* |
219
|
|
|
* @return string|null The URL key |
220
|
|
|
*/ |
221
|
|
|
public function loadUrlKey(UrlKeyAwareSubjectInterface $subject, $primaryKeyId) |
222
|
|
|
{ |
223
|
|
|
|
224
|
|
|
// initialize the entity type ID |
225
|
|
|
$entityType = $subject->getEntityType(); |
226
|
|
|
$entityTypeId = (integer) $entityType[MemberNames::ENTITY_TYPE_ID]; |
227
|
|
|
|
228
|
|
|
// initialize the store view ID, use the admin store view if no store view has |
229
|
|
|
// been set, because the default url_key value has been set in admin store view |
230
|
|
|
$storeId = $subject->getRowStoreId(StoreViewCodes::ADMIN); |
231
|
|
|
|
232
|
|
|
// try to load the attribute |
233
|
|
|
$attribute = $this->getUrlKeyAwareProcessor() |
234
|
|
|
->loadVarcharAttributeByAttributeCodeAndEntityTypeIdAndStoreIdAndPrimaryKey( |
235
|
|
|
MemberNames::URL_KEY, |
236
|
|
|
$entityTypeId, |
237
|
|
|
$storeId, |
238
|
|
|
$primaryKeyId |
239
|
|
|
); |
240
|
|
|
|
241
|
|
|
// return the attribute value or null, if not available |
242
|
|
|
return $attribute ? $attribute['value'] : null; |
243
|
|
|
} |
244
|
|
|
} |
245
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.