Completed
Pull Request — master (#48)
by Tim
03:23
created

UrlRewriteObserver::persistUrlRewrite()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
crap 1
1
<?php
2
3
/**
4
 * TechDivision\Import\Product\Observers\UrlRewriteObserver
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 2016 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-product
18
 * @link      http://www.techdivision.com
19
 */
20
21
namespace TechDivision\Import\Product\Observers;
22
23
use TechDivision\Import\Product\Utils\ColumnKeys;
24
use TechDivision\Import\Product\Utils\MemberNames;
25
use TechDivision\Import\Utils\Filter\ConvertLiteralUrl;
26
use TechDivision\Import\Product\Observers\AbstractProductImportObserver;
27
28
/**
29
 * Observer that creates/updates the product's URL rewrites.
30
 *
31
 * @author    Tim Wagner <[email protected]>
32
 * @copyright 2016 TechDivision GmbH <[email protected]>
33
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
34
 * @link      https://github.com/techdivision/import-product
35
 * @link      http://www.techdivision.com
36
 */
37
class UrlRewriteObserver extends AbstractProductImportObserver
38
{
39
40
    /**
41
     * The entity type to load the URL rewrites for.
42
     *
43
     * @var string
44
     */
45
    const ENTITY_TYPE = 'product';
46
47
    /**
48
     * The URL key from the CSV file column that has to be processed by the observer.
49
     *
50
     * @var string
51
     */
52
    protected $urlKey;
53
54
    /**
55
     * The actual category ID to process.
56
     *
57
     * @var integer
58
     */
59
    protected $categoryId;
60
61
    /**
62
     * The actual entity ID to process.
63
     *
64
     * @var integer
65
     */
66
    protected $entityId;
67
68
    /**
69
     * The array with the URL rewrites that has to be created.
70
     *
71
     * @var array
72
     */
73
    protected $urlRewrites = array();
74
75
    /**
76
     * Process the observer's business logic.
77
     *
78
     * @return void
79
     */
80 3
    protected function process()
81
    {
82
83
        // query whether or not, we've found a new SKU => means we've found a new product
84 3
        if ($this->hasBeenProcessed($this->getValue(ColumnKeys::SKU))) {
85
            return;
86
        }
87
88
        // try to load the URL key, return immediately if not possible
89 3
        if ($this->hasValue(ColumnKeys::URL_KEY)) {
90 3
            $this->urlKey = $this->getValue(ColumnKeys::URL_KEY);
91
        } else {
92
            return;
93
        }
94
95
        // initialize the store view code
96 3
        $this->prepareStoreViewCode();
97
98
        // prepare the URL rewrites
99 3
        $this->prepareUrlRewrites();
100
101
        // iterate over the categories and create the URL rewrites
102 3
        foreach ($this->urlRewrites as $categoryId => $urlRewrite) {
103
            // initialize and persist the URL rewrite
104 3
            if ($urlRewrite = $this->initializeUrlRewrite($urlRewrite)) {
105
                // initialize URL rewrite and catagory ID
106 3
                $this->categoryId = $categoryId;
0 ignored issues
show
Documentation Bug introduced by
It seems like $categoryId can also be of type string. However, the property $categoryId is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
107 3
                $this->entityId = $urlRewrite[MemberNames::ENTITY_ID];
108 3
                $this->urlRewriteId = $this->persistUrlRewrite($urlRewrite);
0 ignored issues
show
Bug introduced by
The property urlRewriteId does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
109
110
                // initialize and persist the URL rewrite product => category relation
111 3
                $urlRewriteProductCategory = $this->initializeUrlRewriteProductCategory(
112 3
                    $this->prepareUrlRewriteProductCategoryAttributes()
113
                );
114
115
                // persist the URL rewrite product category relation
116 3
                $this->persistUrlRewriteProductCategory($urlRewriteProductCategory);
117
            }
118
        }
119 3
    }
120
121
    /**
122
     * Initialize the category product with the passed attributes and returns an instance.
123
     *
124
     * @param array $attr The category product attributes
125
     *
126
     * @return array The initialized category product
127
     */
128 2
    protected function initializeUrlRewrite(array $attr)
129
    {
130 2
        return $attr;
131
    }
132
133
    /**
134
     * Initialize the URL rewrite product => category relation with the passed attributes
135
     * and returns an instance.
136
     *
137
     * @param array $attr The URL rewrite product => category relation attributes
138
     *
139
     * @return array The initialized URL rewrite product => category relation
140
     */
141 2
    protected function initializeUrlRewriteProductCategory($attr)
142
    {
143 2
        return $attr;
144
    }
145
146
    /**
147
     * Prepare's the URL rewrites that has to be created/updated.
148
     *
149
     * @return void
150
     */
151 3
    protected function prepareUrlRewrites()
152
    {
153
154
        // (re-)initialize the array for the URL rewrites
155 3
        $this->urlRewrites = array();
156
157
        // load the root category, because we need that to create the default product URL rewrite
158 3
        $rootCategory = $this->getRootCategory();
159
160
        // add the root category ID to the category => product relations
161 3
        $productCategoryIds = $this->getProductCategoryIds();
162 3
        $productCategoryIds[$rootCategory[MemberNames::ENTITY_ID]] = $this->getLastEntityId();
163
164
        // prepare the URL rewrites
165 3
        foreach ($productCategoryIds as $categoryId => $entityId) {
166
            // set category/entity ID
167 3
            $this->categoryId = $categoryId;
168 3
            $this->entityId = $entityId;
0 ignored issues
show
Documentation Bug introduced by
The property $entityId was declared of type integer, but $entityId is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
169
170
            // prepare the attributes for each URL rewrite
171 3
            $this->urlRewrites[$categoryId] = $this->prepareAttributes();
172
        }
173 3
    }
174
175
    /**
176
     * Prepare the attributes of the entity that has to be persisted.
177
     *
178
     * @return array The prepared attributes
179
     */
180 3
    protected function prepareAttributes()
181
    {
182
183
        // load the store ID to use
184 3
        $storeId = $this->getRowStoreId();
185
186
        // load the category to create the URL rewrite for
187 3
        $category = $this->getCategory($this->categoryId);
188
189
        // initialize the values
190 3
        $requestPath = $this->prepareRequestPath($category);
191 3
        $targetPath = $this->prepareTargetPath($category);
192 3
        $metadata = serialize($this->prepareMetadata($category));
193
194
        // return the prepared URL rewrite
195 3
        return $this->initializeEntity(
196
            array(
197 3
                MemberNames::ENTITY_TYPE      => UrlRewriteObserver::ENTITY_TYPE,
198 3
                MemberNames::ENTITY_ID        => $this->entityId,
199 3
                MemberNames::REQUEST_PATH     => $requestPath,
200 3
                MemberNames::TARGET_PATH      => $targetPath,
201 3
                MemberNames::REDIRECT_TYPE    => 0,
202 3
                MemberNames::STORE_ID         => $storeId,
203 3
                MemberNames::DESCRIPTION      => null,
204 3
                MemberNames::IS_AUTOGENERATED => 1,
205 3
                MemberNames::METADATA         => $metadata
206
            )
207
        );
208
    }
209
210
    /**
211
     * Prepare's the URL rewrite product => category relation attributes.
212
     *
213
     * @return arry The prepared attributes
214
     */
215 3
    protected function prepareUrlRewriteProductCategoryAttributes()
216
    {
217
218
        // return the prepared product
219 3
        return $this->initializeEntity(
220
            array(
221 3
                MemberNames::PRODUCT_ID => $this->entityId,
222 3
                MemberNames::CATEGORY_ID => $this->categoryId,
223 3
                MemberNames::URL_REWRITE_ID => $this->urlRewriteId
224
            )
225
        );
226
    }
227
228
    /**
229
     * Prepare's the target path for a URL rewrite.
230
     *
231
     * @param array $category The categroy with the URL path
232
     *
233
     * @return string The target path
234
     */
235 3
    protected function prepareTargetPath(array $category)
236
    {
237
238
        // load the actual entity ID
239 3
        $lastEntityId = $this->getPrimaryKey();
240
241
        // query whether or not, the category is the root category
242 3
        if ($this->isRootCategory($category)) {
243 3
            $targetPath = sprintf('catalog/product/view/id/%d', $lastEntityId);
244
        } else {
245 2
            $targetPath = sprintf('catalog/product/view/id/%d/category/%d', $lastEntityId, $category[MemberNames::ENTITY_ID]);
246
        }
247
248
        // return the target path
249 3
        return $targetPath;
250
    }
251
252
    /**
253
     * Prepare's the request path for a URL rewrite or the target path for a 301 redirect.
254
     *
255
     * @param array $category The categroy with the URL path
256
     *
257
     * @return string The request path
258
     */
259 3
    protected function prepareRequestPath(array $category)
260
    {
261
262
        // query whether or not, the category is the root category
263 3
        if ($this->isRootCategory($category)) {
264 3
            $requestPath = sprintf('%s.html', $this->urlKey);
265
        } else {
266 2
            $requestPath = sprintf('%s/%s.html', $category[MemberNames::URL_PATH], $this->urlKey);
267
        }
268
269
        // return the request path
270 3
        return $requestPath;
271
    }
272
273
    /**
274
     * Prepare's the URL rewrite's metadata with the passed category values.
275
     *
276
     * @param array $category The category used for preparation
277
     *
278
     * @return array The metadata
279
     */
280 3
    protected function prepareMetadata(array $category)
281
    {
282
283
        // initialize the metadata
284 3
        $metadata = array();
285
286
        // query whether or not, the passed category IS the root category
287 3
        if ($this->isRootCategory($category)) {
288 3
            return $metadata;
289
        }
290
291
        // if not, set the category ID in the metadata
292 2
        $metadata['category_id'] = $category[MemberNames::ENTITY_ID];
293
294
        // return the metadata
295 2
        return $metadata;
296
    }
297
298
    /**
299
     * Initialize's and return's the URL key filter.
300
     *
301
     * @return \TechDivision\Import\Utils\ConvertLiteralUrl The URL key filter
302
     */
303
    protected function getUrlKeyFilter()
304
    {
305
        return new ConvertLiteralUrl();
306
    }
307
308
    /**
309
     * Convert's the passed string into a valid URL key.
310
     *
311
     * @param string $string The string to be converted, e. g. the product name
312
     *
313
     * @return string The converted string as valid URL key
314
     */
315
    protected function convertNameToUrlKey($string)
316
    {
317
        return $this->getUrlKeyFilter()->filter($string);
318
    }
319
320
    /**
321
     * Return's the root category for the actual view store.
322
     *
323
     * @return array The store's root category
324
     * @throws \Exception Is thrown if the root category for the passed store code is NOT available
325
     */
326 3
    protected function getRootCategory()
327
    {
328 3
        return $this->getSubject()->getRootCategory();
329
    }
330
331
    /**
332
     * Return's TRUE if the passed category IS the root category, else FALSE.
333
     *
334
     * @param array $category The category to query
335
     *
336
     * @return boolean TRUE if the passed category IS the root category
337
     */
338 3
    protected function isRootCategory(array $category)
339
    {
340
341
        // load the root category
342 3
        $rootCategory = $this->getRootCategory();
343
344
        // compare the entity IDs and return the result
345 3
        return $rootCategory[MemberNames::ENTITY_ID] === $category[MemberNames::ENTITY_ID];
346
    }
347
348
    /**
349
     * Return's the store ID of the actual row, or of the default store
350
     * if no store view code is set in the CSV file.
351
     *
352
     * @param string|null $default The default store view code to use, if no store view code is set in the CSV file
353
     *
354
     * @return integer The ID of the actual store
355
     * @throws \Exception Is thrown, if the store with the actual code is not available
356
     */
357 3
    protected function getRowStoreId($default = null)
358
    {
359 3
        return $this->getSubject()->getRowStoreId($default);
360
    }
361
362
    /**
363
     * Return's the list with category IDs the product is related with.
364
     *
365
     * @return array The product's category IDs
366
     */
367 3
    protected function getProductCategoryIds()
368
    {
369 3
        return $this->getSubject()->getProductCategoryIds();
370
    }
371
372
    /**
373
     * Return's the category with the passed ID.
374
     *
375
     * @param integer $categoryId The ID of the category to return
376
     *
377
     * @return array The category data
378
     */
379 3
    protected function getCategory($categoryId)
380
    {
381 3
        return $this->getSubject()->getCategory($categoryId);
382
    }
383
384
    /**
385
     * Persist's the URL rewrite with the passed data.
386
     *
387
     * @param array $row The URL rewrite to persist
388
     *
389
     * @return string The ID of the persisted entity
390
     */
391 3
    protected function persistUrlRewrite($row)
392
    {
393 3
        return $this->getSubject()->persistUrlRewrite($row);
394
    }
395
396
    /**
397
     * Persist's the URL rewrite product => category relation with the passed data.
398
     *
399
     * @param array $row The URL rewrite product => category relation to persist
400
     *
401
     * @return void
402
     */
403 3
    protected function persistUrlRewriteProductCategory($row)
404
    {
405 3
        return $this->getSubject()->persistUrlRewriteProductCategory($row);
406
    }
407
408
    /**
409
     * Return's the PK to create the product => attribute relation.
410
     *
411
     * @return integer The PK to create the relation with
412
     */
413 3
    protected function getPrimaryKey()
414
    {
415 3
        return $this->getLastEntityId();
416
    }
417
}
418