Completed
Push — pac-264--pdo-exception ( d24c6a...c69e59 )
by Tim
05:45
created

UrlKeyUtil::doMakeUnique()   B

Complexity

Conditions 8
Paths 24

Size

Total Lines 58

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 58
c 0
b 0
f 0
rs 7.6719
cc 8
nc 24
nop 3

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $urlRewrite of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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.

Loading history...
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);
0 ignored issues
show
Bug Best Practice introduced by
The expression $urlRewrite of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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.

Loading history...
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++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function sizeof() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
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()
0 ignored issues
show
Bug introduced by
The method getLastEntityId() does not seem to exist on object<TechDivision\Impo...yAwareSubjectInterface>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
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