Completed
Push — pac-264--pdo-exception ( 68f56d...d24c6a )
by Tim
08:44
created

UrlKeyUtil::makeUnique()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 23
rs 9.552
c 0
b 0
f 0
cc 4
nc 3
nop 3
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
     * Construct a new instance.
47
     *
48
     * @param \TechDivision\Import\Services\UrlKeyAwareProcessorInterface $urlKeyAwareProcessor The URL key aware processor instance
49
     */
50
    public function __construct(UrlKeyAwareProcessorInterface $urlKeyAwareProcessor)
51
    {
52
        $this->urlKeyAwareProcessor = $urlKeyAwareProcessor;
53
    }
54
55
    /**
56
     * Returns the URL key aware processor instance.
57
     *
58
     * @return \TechDivision\Import\Services\UrlKeyAwareProcessorInterface The processor instance
59
     */
60
    protected function getUrlKeyAwareProcessor()
61
    {
62
        return $this->urlKeyAwareProcessor;
63
    }
64
65
    /**
66
     * Load's and return's the URL rewrite for the given request path and store ID
67
     *
68
     * @param string $requestPath The request path to load the URL rewrite for
69
     * @param int    $storeId     The store ID to load the URL rewrite for
70
     *
71
     * @return string|null The URL rewrite found for the given request path and store ID
72
     */
73
    protected function loadUrlRewriteByRequestPathAndStoreId(string $requestPath, int $storeId)
74
    {
75
        return $this->getUrlKeyAwareProcessor()->loadUrlRewriteByRequestPathAndStoreId($requestPath, $storeId);
76
    }
77
78
    /**
79
     * Make's the passed URL key unique by adding/raising a number to the end.
80
     *
81
     * @param \TechDivision\Import\Subjects\UrlKeyAwareSubjectInterface $subject The subject to make the URL key unique for
82
     * @param string                                                    $urlKey  The URL key to make unique
83
     * @param string|null                                               $urlPath The URL path to make unique (only used for categories)
84
     *
85
     * @return string The unique URL key
86
     */
87
    protected function doMakeUnique(UrlKeyAwareSubjectInterface $subject, string $urlKey, string $urlPath = null) : string
88
    {
89
90
        // initialize the store view ID, use the default store view if no store view has
91
        // been set, because the default url_key value has been set in default store view
92
        $storeId = $subject->getRowStoreId();
93
94
        // initialize the counter
95
        $counter = 0;
96
97
        // initialize the counters
98
        $matchingCounters = array();
99
        $notMatchingCounters = array();
100
101
        // pre-initialze the URL by concatenating path and/or key to query for
102
        $url = $urlPath ? sprintf('%s/%s', $urlPath, $urlKey) : $urlKey;
103
104
        do {
105
            // try to load the attribute
106
            $urlRewrite = $this->loadUrlRewriteByRequestPathAndStoreId($url, $storeId);
107
108
            // try to load the entity's URL key
109
            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...
110
                // this IS the URL key of the passed entity
111
                if ($subject->isUrlKeyOf($urlRewrite)) {
112
                    $matchingCounters[] = $counter;
113
                } else {
114
                    $notMatchingCounters[] = $counter;
115
                }
116
117
                // prepare the next URL key to query for
118
                $url = sprintf('%s-%d', $urlKey, ++$counter);
119
            }
120
        } 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...
121
122
        // sort the array ascending according to the counter
123
        asort($matchingCounters);
124
        asort($notMatchingCounters);
125
126
        // this IS the URL key of the passed entity => we've an UPDATE
127
        if (sizeof($matchingCounters) > 0) {
128
            // load highest counter
129
            $counter = end($matchingCounters);
130
            // if the counter is > 0, we've to append it to the new URL key
131
            if ($counter > 0) {
132
                $urlKey = sprintf('%s-%d', $urlKey, $counter);
133
            }
134
        } elseif (sizeof($notMatchingCounters) > 0) {
135
            // create a new URL key by raising the counter
136
            $newCounter = end($notMatchingCounters);
137
            $urlKey = sprintf('%s-%d', $urlKey, ++$newCounter);
138
        }
139
140
        // return the passed URL key, if NOT
141
        return $urlKey;
142
    }
143
144
    /**
145
     * Make's the passed URL key unique by adding the next number to the end.
146
     *
147
     * @param \TechDivision\Import\Subjects\UrlKeyAwareSubjectInterface $subject  The subject to make the URL key unique for
148
     * @param string                                                    $urlKey   The URL key to make unique
149
     * @param array                                                     $urlPaths The URL paths to make unique
150
     *
151
     * @return string The unique URL key
152
     */
153
    public function makeUnique(UrlKeyAwareSubjectInterface $subject, string $urlKey, array $urlPaths = array()) : string
154
    {
155
156
        // iterate over the passed URL paths
157
        // and try to find a unique URL key
158
        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...
159
            // try to make the URL key unique for the given URL path
160
            $proposedUrlKey = $this->doMakeUnique($subject, $urlKey, isset($urlPaths[$i]) ? $urlPaths[$i] : null);
161
            // if the URL key is NOT the same as the passed one or with the parent URL path
162
            // it can NOT be used, so we've to persist it temporarily and try it again for
163
            // all the other URL paths until we found one that works with every URL path
164
            if ($urlKey !== $proposedUrlKey) {
165
                // temporarily persist the URL key
166
                $urlKey = $proposedUrlKey;
167
                // reset the counter and restart the
168
                // iteration with the first URL path
169
                $i = 0;
170
            }
171
        }
172
173
        // return the unique URL key
174
        return $urlKey;
175
    }
176
177
    /**
178
     * Load the url_key if exists
179
     *
180
     * @param \TechDivision\Import\Subjects\UrlKeyAwareSubjectInterface $subject      The subject to make the URL key unique for
181
     * @param int                                                       $primaryKeyId The ID from category or product
182
     *
183
     * @return string|null The URL key
184
     */
185
    public function loadUrlKey(UrlKeyAwareSubjectInterface $subject, $primaryKeyId)
186
    {
187
188
        // initialize the entity type ID
189
        $entityType = $subject->getEntityType();
190
        $entityTypeId = (integer) $entityType[MemberNames::ENTITY_TYPE_ID];
191
192
        // initialize the store view ID, use the admin store view if no store view has
193
        // been set, because the default url_key value has been set in admin store view
194
        $storeId = $subject->getRowStoreId(StoreViewCodes::ADMIN);
195
196
        // try to load the attribute
197
        $attribute = $this->getUrlKeyAwareProcessor()
198
            ->loadVarcharAttributeByAttributeCodeAndEntityTypeIdAndStoreIdAndPrimaryKey(
199
                MemberNames::URL_KEY,
200
                $entityTypeId,
201
                $storeId,
202
                $primaryKeyId
203
            );
204
205
        // return the attribute value or null, if not available
206
        return $attribute ? $attribute['value'] : null;
207
    }
208
}
209