Completed
Push — master ( 993f12...d9bb9d )
by Tim
31:28 queued 16:46
created

UrlRewriteObserver::getPrimaryKey()   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
c 0
b 0
f 0
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
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\Product\Utils\CoreConfigDataKeys;
26
use TechDivision\Import\Utils\Filter\ConvertLiteralUrl;
27
use TechDivision\Import\Product\Observers\AbstractProductImportObserver;
28
29
/**
30
 * Observer that creates/updates the product's URL rewrites.
31
 *
32
 * @author    Tim Wagner <[email protected]>
33
 * @copyright 2016 TechDivision GmbH <[email protected]>
34
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
35
 * @link      https://github.com/techdivision/import-product
36
 * @link      http://www.techdivision.com
37
 */
38
class UrlRewriteObserver extends AbstractProductImportObserver
39
{
40
41
    /**
42
     * The entity type to load the URL rewrites for.
43
     *
44
     * @var string
45
     */
46
    const ENTITY_TYPE = 'product';
47
48
    /**
49
     * The URL key from the CSV file column that has to be processed by the observer.
50
     *
51
     * @var string
52
     */
53
    protected $urlKey;
54
55
    /**
56
     * The actual category ID to process.
57
     *
58
     * @var integer
59
     */
60
    protected $categoryId;
61
62
    /**
63
     * The actual entity ID to process.
64
     *
65
     * @var integer
66
     */
67
    protected $entityId;
68
69
    /**
70
     * The array with the URL rewrites that has to be created.
71
     *
72
     * @var array
73
     */
74
    protected $urlRewrites = array();
75
76
    /**
77
     * Process the observer's business logic.
78
     *
79
     * @return void
80
     */
81 3
    protected function process()
82
    {
83
84
        // query whether or not, we've found a new SKU => means we've found a new product
85 3
        if ($this->hasBeenProcessed($this->getValue(ColumnKeys::SKU))) {
86
            return;
87
        }
88
89
        // try to load the URL key, return immediately if not possible
90 3
        if ($this->hasValue(ColumnKeys::URL_KEY)) {
91 3
            $this->urlKey = $this->getValue(ColumnKeys::URL_KEY);
92 3
        } else {
93
            return;
94
        }
95
96
        // initialize the store view code
97 3
        $this->prepareStoreViewCode();
98
99
        // prepare the URL rewrites
100 3
        $this->prepareUrlRewrites();
101
102
        // iterate over the categories and create the URL rewrites
103 3
        foreach ($this->urlRewrites as $categoryId => $urlRewrite) {
104
            // initialize and persist the URL rewrite
105 3
            if ($urlRewrite = $this->initializeUrlRewrite($urlRewrite)) {
106
                // initialize URL rewrite and catagory ID
107 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...
108 3
                $this->entityId = $urlRewrite[MemberNames::ENTITY_ID];
109 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...
110
111
                // initialize and persist the URL rewrite product => category relation
112 3
                $urlRewriteProductCategory = $this->initializeUrlRewriteProductCategory(
113 3
                    $this->prepareUrlRewriteProductCategoryAttributes()
114 3
                );
115
116
                // persist the URL rewrite product category relation
117 3
                $this->persistUrlRewriteProductCategory($urlRewriteProductCategory);
118 3
            }
119 3
        }
120 3
    }
121
122
    /**
123
     * Initialize the category product with the passed attributes and returns an instance.
124
     *
125
     * @param array $attr The category product attributes
126
     *
127
     * @return array The initialized category product
128
     */
129 2
    protected function initializeUrlRewrite(array $attr)
130
    {
131 2
        return $attr;
132
    }
133
134
    /**
135
     * Initialize the URL rewrite product => category relation with the passed attributes
136
     * and returns an instance.
137
     *
138
     * @param array $attr The URL rewrite product => category relation attributes
139
     *
140
     * @return array The initialized URL rewrite product => category relation
141
     */
142 2
    protected function initializeUrlRewriteProductCategory($attr)
143
    {
144 2
        return $attr;
145
    }
146
147
    /**
148
     * Prepare's the URL rewrites that has to be created/updated.
149
     *
150
     * @return void
151
     */
152 3
    protected function prepareUrlRewrites()
153
    {
154
155
        // (re-)initialize the array for the URL rewrites
156 3
        $this->urlRewrites = array();
157
158
        // load the root category, because we need that to create the default product URL rewrite
159 3
        $rootCategory = $this->getRootCategory();
160
161
        // query whether or not categories has to be used as product URL suffix
162 3
        $productCategoryIds = array();
163 3
        if ($this->getCoreConfigData(CoreConfigDataKeys::CATALOG_SEO_PRODUCT_USE_CATEGORIES, false)) {
164
            // if yes, add the category IDs of the products
165 3
            $productCategoryIds = $this->getProductCategoryIds();
166 3
        }
167
168
        // at least, add the root category ID to the category => product relations
169 3
        $productCategoryIds[$rootCategory[MemberNames::ENTITY_ID]] = $this->getLastEntityId();
170
171
        // prepare the URL rewrites
172 3
        foreach ($productCategoryIds as $categoryId => $entityId) {
173
            // set category/entity ID
174 3
            $this->categoryId = $categoryId;
175 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...
176
177
            // prepare the attributes for each URL rewrite
178 3
            $this->urlRewrites[$categoryId] = $this->prepareAttributes();
179 3
        }
180 3
    }
181
182
    /**
183
     * Prepare the attributes of the entity that has to be persisted.
184
     *
185
     * @return array The prepared attributes
186
     */
187 3
    protected function prepareAttributes()
188
    {
189
190
        // load the store ID to use
191 3
        $storeId = $this->getRowStoreId();
192
193
        // load the category to create the URL rewrite for
194 3
        $category = $this->getCategory($this->categoryId);
195
196
        // initialize the values
197 3
        $requestPath = $this->prepareRequestPath($category);
198 3
        $targetPath = $this->prepareTargetPath($category);
199 3
        $metadata = serialize($this->prepareMetadata($category));
200
201
        // return the prepared URL rewrite
202 3
        return $this->initializeEntity(
203
            array(
204 3
                MemberNames::ENTITY_TYPE      => UrlRewriteObserver::ENTITY_TYPE,
205 3
                MemberNames::ENTITY_ID        => $this->entityId,
206 3
                MemberNames::REQUEST_PATH     => $requestPath,
207 3
                MemberNames::TARGET_PATH      => $targetPath,
208 3
                MemberNames::REDIRECT_TYPE    => 0,
209 3
                MemberNames::STORE_ID         => $storeId,
210 3
                MemberNames::DESCRIPTION      => null,
211 3
                MemberNames::IS_AUTOGENERATED => 1,
212 3
                MemberNames::METADATA         => $metadata
213 3
            )
214 3
        );
215
    }
216
217
    /**
218
     * Prepare's the URL rewrite product => category relation attributes.
219
     *
220
     * @return arry The prepared attributes
221
     */
222 3
    protected function prepareUrlRewriteProductCategoryAttributes()
223
    {
224
225
        // return the prepared product
226 3
        return $this->initializeEntity(
227
            array(
228 3
                MemberNames::PRODUCT_ID => $this->entityId,
229 3
                MemberNames::CATEGORY_ID => $this->categoryId,
230 3
                MemberNames::URL_REWRITE_ID => $this->urlRewriteId
231 3
            )
232 3
        );
233
    }
234
235
    /**
236
     * Prepare's the target path for a URL rewrite.
237
     *
238
     * @param array $category The categroy with the URL path
239
     *
240
     * @return string The target path
241
     */
242 3
    protected function prepareTargetPath(array $category)
243
    {
244
245
        // load the actual entity ID
246 3
        $lastEntityId = $this->getPrimaryKey();
247
248
        // query whether or not, the category is the root category
249 3
        if ($this->isRootCategory($category)) {
250 3
            $targetPath = sprintf('catalog/product/view/id/%d', $lastEntityId);
251 3
        } else {
252 2
            $targetPath = sprintf('catalog/product/view/id/%d/category/%d', $lastEntityId, $category[MemberNames::ENTITY_ID]);
253
        }
254
255
        // return the target path
256 3
        return $targetPath;
257
    }
258
259
    /**
260
     * Prepare's the request path for a URL rewrite or the target path for a 301 redirect.
261
     *
262
     * @param array $category The categroy with the URL path
263
     *
264
     * @return string The request path
265
     */
266 3
    protected function prepareRequestPath(array $category)
267
    {
268
269
        // load the product URL suffix to use
270 3
        $urlSuffix = $this->getCoreConfigData(CoreConfigDataKeys::CATALOG_SEO_PRODUCT_URL_SUFFIX, 'html');
271
272
        // query whether or not, the category is the root category
273 3
        if ($this->isRootCategory($category)) {
274 3
            $requestPath = sprintf('%s.%s', $this->urlKey, $urlSuffix);
275 3
        } else {
276 2
            $requestPath = sprintf('%s/%s.%s', $category[MemberNames::URL_PATH], $this->urlKey, $urlSuffix);
277
        }
278
279
        // return the request path
280 3
        return $requestPath;
281
    }
282
283
    /**
284
     * Prepare's the URL rewrite's metadata with the passed category values.
285
     *
286
     * @param array $category The category used for preparation
287
     *
288
     * @return array The metadata
289
     */
290 3
    protected function prepareMetadata(array $category)
291
    {
292
293
        // initialize the metadata
294 3
        $metadata = array();
295
296
        // query whether or not, the passed category IS the root category
297 3
        if ($this->isRootCategory($category)) {
298 3
            return $metadata;
299
        }
300
301
        // if not, set the category ID in the metadata
302 2
        $metadata['category_id'] = $category[MemberNames::ENTITY_ID];
303
304
        // return the metadata
305 2
        return $metadata;
306
    }
307
308
    /**
309
     * Initialize's and return's the URL key filter.
310
     *
311
     * @return \TechDivision\Import\Utils\ConvertLiteralUrl The URL key filter
312
     */
313
    protected function getUrlKeyFilter()
314
    {
315
        return new ConvertLiteralUrl();
316
    }
317
318
    /**
319
     * Convert's the passed string into a valid URL key.
320
     *
321
     * @param string $string The string to be converted, e. g. the product name
322
     *
323
     * @return string The converted string as valid URL key
324
     */
325
    protected function convertNameToUrlKey($string)
326
    {
327
        return $this->getUrlKeyFilter()->filter($string);
328
    }
329
330
    /**
331
     * Return's the root category for the actual view store.
332
     *
333
     * @return array The store's root category
334
     * @throws \Exception Is thrown if the root category for the passed store code is NOT available
335
     */
336 3
    protected function getRootCategory()
337
    {
338 3
        return $this->getSubject()->getRootCategory();
339
    }
340
341
    /**
342
     * Return's TRUE if the passed category IS the root category, else FALSE.
343
     *
344
     * @param array $category The category to query
345
     *
346
     * @return boolean TRUE if the passed category IS the root category
347
     */
348 3
    protected function isRootCategory(array $category)
349
    {
350
351
        // load the root category
352 3
        $rootCategory = $this->getRootCategory();
353
354
        // compare the entity IDs and return the result
355 3
        return $rootCategory[MemberNames::ENTITY_ID] === $category[MemberNames::ENTITY_ID];
356
    }
357
358
    /**
359
     * Return's the store ID of the actual row, or of the default store
360
     * if no store view code is set in the CSV file.
361
     *
362
     * @param string|null $default The default store view code to use, if no store view code is set in the CSV file
363
     *
364
     * @return integer The ID of the actual store
365
     * @throws \Exception Is thrown, if the store with the actual code is not available
366
     */
367 3
    protected function getRowStoreId($default = null)
368
    {
369 3
        return $this->getSubject()->getRowStoreId($default);
370
    }
371
372
    /**
373
     * Return's the list with category IDs the product is related with.
374
     *
375
     * @return array The product's category IDs
376
     */
377 3
    protected function getProductCategoryIds()
378
    {
379 3
        return $this->getSubject()->getProductCategoryIds();
380
    }
381
382
    /**
383
     * Return's the category with the passed ID.
384
     *
385
     * @param integer $categoryId The ID of the category to return
386
     *
387
     * @return array The category data
388
     */
389 2
    protected function getCategory($categoryId)
390
    {
391 2
        return $this->getSubject()->getCategory($categoryId);
392
    }
393
394
    /**
395
     * Persist's the URL rewrite with the passed data.
396
     *
397
     * @param array $row The URL rewrite to persist
398
     *
399
     * @return string The ID of the persisted entity
400
     */
401 3
    protected function persistUrlRewrite($row)
402
    {
403 3
        return $this->getSubject()->persistUrlRewrite($row);
404
    }
405
406
    /**
407
     * Persist's the URL rewrite product => category relation with the passed data.
408
     *
409
     * @param array $row The URL rewrite product => category relation to persist
410
     *
411
     * @return void
412
     */
413 3
    protected function persistUrlRewriteProductCategory($row)
414
    {
415 3
        return $this->getSubject()->persistUrlRewriteProductCategory($row);
416
    }
417
418
    /**
419
     * Return's the PK to create the product => attribute relation.
420
     *
421
     * @return integer The PK to create the relation with
422
     */
423 3
    protected function getPrimaryKey()
424
    {
425 3
        return $this->getLastEntityId();
426
    }
427
}
428