Completed
Push — 19.x ( fa3b72...b3828f )
by Tim
24:29 queued 21:23
created

UrlKeyObserver::process()   B

Complexity

Conditions 5
Paths 8

Size

Total Lines 42

Duplication

Lines 20
Ratio 47.62 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
dl 20
loc 42
ccs 0
cts 32
cp 0
rs 8.9368
c 0
b 0
f 0
cc 5
nc 8
nop 0
crap 30
1
<?php
2
3
/**
4
 * TechDivision\Import\Product\Observers\UrlKeyObserver
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 Zend\Filter\FilterInterface;
24
use TechDivision\Import\Utils\StoreViewCodes;
25
use TechDivision\Import\Product\Utils\MemberNames;
26
use TechDivision\Import\Product\Utils\ColumnKeys;
27
use TechDivision\Import\Utils\Filter\UrlKeyFilterTrait;
28
use TechDivision\Import\Product\Services\ProductBunchProcessorInterface;
29
use TechDivision\Import\Utils\UrlKeyUtilInterface;
30
use TechDivision\Import\Subjects\UrlKeyAwareSubjectInterface;
31
32
/**
33
 * Observer that extracts the URL key from the product name and adds a two new columns
34
 * with the their values.
35
 *
36
 * @author    Tim Wagner <[email protected]>
37
 * @copyright 2016 TechDivision GmbH <[email protected]>
38
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
39
 * @link      https://github.com/techdivision/import-product
40
 * @link      http://www.techdivision.com
41
 */
42
class UrlKeyObserver extends AbstractProductImportObserver
43
{
44
45
    /**
46
     * The trait that provides string => URL key conversion functionality.
47
     *
48
     * @var \TechDivision\Import\Utils\Filter\UrlKeyFilterTrait
49
     */
50
    use UrlKeyFilterTrait;
51
52
    /**
53
     * The URL key utility instance.
54
     *
55
     * @var \TechDivision\Import\Utils\UrlKeyUtilInterface
56
     */
57
    protected $urlKeyUtil;
58
59
    /**
60
     * The product bunch processor instance.
61
     *
62
     * @var \TechDivision\Import\Product\Services\ProductBunchProcessorInterface
63
     */
64
    protected $productBunchProcessor;
65
66
    /**
67
     * Initialize the observer with the passed product bunch processor and filter instance.
68
     *
69
     * @param \TechDivision\Import\Product\Services\ProductBunchProcessorInterface $productBunchProcessor   The product bunch processor instance
70
     * @param \Zend\Filter\FilterInterface                                         $convertLiteralUrlFilter The URL filter instance
71
     * @param \TechDivision\Import\Utils\UrlKeyUtilInterface                       $urlKeyUtil              The URL key utility instance
72
     */
73
    public function __construct(
74
        ProductBunchProcessorInterface $productBunchProcessor,
75
        FilterInterface $convertLiteralUrlFilter,
76
        UrlKeyUtilInterface $urlKeyUtil
77
    ) {
78
        $this->productBunchProcessor = $productBunchProcessor;
79
        $this->convertLiteralUrlFilter = $convertLiteralUrlFilter;
80
        $this->urlKeyUtil = $urlKeyUtil;
81
    }
82
83
    /**
84
     * Return's the product bunch processor instance.
85
     *
86
     * @return \TechDivision\Import\Product\Services\ProductBunchProcessorInterface The product bunch processor instance
87
     */
88
    protected function getProductBunchProcessor()
89
    {
90
        return $this->productBunchProcessor;
91
    }
92
93
    /**
94
     * Process the observer's business logic.
95
     *
96
     * @return void
97
     * @throws \Exception Is thrown, if either column "url_key" or "name" have a value set
98
     */
99
    protected function process()
100
    {
101
102
        // prepare the store view code
103
        $this->getSubject()->prepareStoreViewCode();
104
105
        // set the entity ID for the product with the passed SKU
106
        if ($product = $this->loadProduct($this->getValue(ColumnKeys::SKU))) {
107
            $this->setIds($product);
108
        } else {
109
            $this->setIds(array());
110
        }
111
112
        // query whether or not the URL key column has a value
113 View Code Duplication
        if ($this->hasValue(ColumnKeys::URL_KEY)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
114
            $this->setValue(
115
                ColumnKeys::URL_KEY,
116
                $this->makeUnique(
117
                    $this->getSubject(),
0 ignored issues
show
Documentation introduced by
$this->getSubject() is of type object<TechDivision\Impo...jects\SubjectInterface>, but the function expects a object<TechDivision\Impo...yAwareSubjectInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
118
                    $this->getValue(ColumnKeys::URL_KEY)
119
                )
120
            );
121
            return;
122
        }
123
124
        // query whether or not a product name is available
125 View Code Duplication
        if ($this->hasValue(ColumnKeys::NAME)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
126
            $this->setValue(
127
                ColumnKeys::URL_KEY,
128
                $this->makeUnique(
129
                    $this->getSubject(),
0 ignored issues
show
Documentation introduced by
$this->getSubject() is of type object<TechDivision\Impo...jects\SubjectInterface>, but the function expects a object<TechDivision\Impo...yAwareSubjectInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
130
                    $this->convertNameToUrlKey($this->getValue(ColumnKeys::NAME))
131
                )
132
            );
133
            return;
134
        }
135
136
        // throw an exception, that the URL key can not be initialized and we're in admin store view
137
        if ($this->getSubject()->getStoreViewCode(StoreViewCodes::ADMIN) === StoreViewCodes::ADMIN) {
138
            throw new \Exception('Can\'t initialize the URL key because either columns "url_key" or "name" have a value set for default store view');
139
        }
140
    }
141
142
    /**
143
     * Temporarily persist's the IDs of the passed product.
144
     *
145
     * @param array $product The product to temporarily persist the IDs for
146
     *
147
     * @return void
148
     */
149
    protected function setIds(array $product)
150
    {
151
        $this->setLastEntityId(isset($product[MemberNames::ENTITY_ID]) ? $product[MemberNames::ENTITY_ID] : null);
152
    }
153
154
    /**
155
     * Set's the ID of the product that has been created recently.
156
     *
157
     * @param string $lastEntityId The entity ID
158
     *
159
     * @return void
160
     */
161
    protected function setLastEntityId($lastEntityId)
162
    {
163
        $this->getSubject()->setLastEntityId($lastEntityId);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface TechDivision\Import\Subjects\SubjectInterface as the method setLastEntityId() does only exist in the following implementations of said interface: TechDivision\Import\Plugins\ExportableSubjectImpl, TechDivision\Import\Prod...\AbstractProductSubject, TechDivision\Import\Product\Subjects\BunchSubject, TechDivision\Import\Subjects\ExportableTraitImpl.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
164
    }
165
166
    /**
167
     * Load's and return's the product with the passed SKU.
168
     *
169
     * @param string $sku The SKU of the product to load
170
     *
171
     * @return array The product
172
     */
173
    protected function loadProduct($sku)
174
    {
175
        return $this->getProductBunchProcessor()->loadProduct($sku);
176
    }
177
178
    /**
179
     * Returns the URL key utility instance.
180
     *
181
     * @return \TechDivision\Import\Utils\UrlKeyUtilInterface The URL key utility instance
182
     */
183
    protected function getUrlKeyUtil()
184
    {
185
        return $this->urlKeyUtil;
186
    }
187
188
    /**
189
     * Make's the passed URL key unique by adding the next number to the end.
190
     *
191
     * @param string $urlKey The URL key to make unique
192
     *
193
     * @return string The unique URL key
194
     */
195
    protected function makeUnique(UrlKeyAwareSubjectInterface $subject, $urlKey)
196
    {
197
        return $this->getUrlKeyUtil()->makeUnique($subject, $urlKey);
198
    }
199
}
200