Completed
Push — 10.x ( 0c1414 )
by Tim
08:49
created

LinkObserver::initializeProductLink()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 16

Duplication

Lines 16
Ratio 100 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 16
loc 16
ccs 0
cts 10
cp 0
rs 9.7333
c 0
b 0
f 0
cc 2
nc 2
nop 1
crap 6
1
<?php
2
3
/**
4
 * TechDivision\Import\Product\Link\Observers\LinkObserver
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-link
18
 * @link      http://www.techdivision.com
19
 */
20
21
namespace TechDivision\Import\Product\Link\Observers;
22
23
use TechDivision\Import\Product\Link\Utils\ColumnKeys;
24
use TechDivision\Import\Product\Link\Utils\MemberNames;
25
use TechDivision\Import\Product\Observers\AbstractProductImportObserver;
26
use TechDivision\Import\Product\Link\Services\ProductLinkProcessorInterface;
27
28
/**
29
 * Oberserver that provides functionality for the product link replace operation.
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-link
35
 * @link      http://www.techdivision.com
36
 */
37
class LinkObserver extends AbstractProductImportObserver
38
{
39
40
    /**
41
     * The product link processor instance.
42
     *
43
     * @var \TechDivision\Import\Product\Link\Services\ProductLinkProcessorInterface
44
     */
45
    protected $productLinkProcessor;
46
47
    /**
48
     * Initialize the observer with the passed product link processor instance.
49
     *
50
     * @param \TechDivision\Import\Product\Link\Services\ProductLinkProcessorInterface $productLinkProcessor The product link processor instance
51
     */
52
    public function __construct(ProductLinkProcessorInterface $productLinkProcessor)
53
    {
54
        $this->productLinkProcessor= $productLinkProcessor;
55
    }
56
57
    /**
58
     * Return's the product link processor instance.
59
     *
60
     * @return \TechDivision\Import\Product\Link\Services\ProductLinkProcessorInterface The product link processor instance
61
     */
62
    protected function getProductLinkProcessor()
63
    {
64
        return $this->productLinkProcessor;
65
    }
66
67
    /**
68
     * Process the observer's business logic.
69
     *
70
     * @return array The processed row
71
     */
72
    protected function process()
73
    {
74
75
        // prepare the product link entity
76
        if ($attr = $this->prepareAttributes()) {
77
            // initialize the product link entity
78
            $productLink = $this->initializeProductLink($attr);
79
80
            // persist the product link entity and store the link ID
81
            $this->setLastLinkId($this->persistProductLink($productLink));
82
        }
83
    }
84
85
    /**
86
     * Prepare the attributes of the entity that has to be persisted.
87
     *
88
     * @return array The prepared attributes
89
     */
90
    protected function prepareAttributes()
91
    {
92
93
        try {
94
            // extract the parent ID from the row
95
            $parentId = $this->mapSku($this->getValue(ColumnKeys::LINK_PARENT_SKU));
96
        } catch (\Exception $e) {
97
            throw $this->wrapException(array(ColumnKeys::LINK_PARENT_SKU), $e);
0 ignored issues
show
Documentation introduced by
array(\TechDivision\Impo...nKeys::LINK_PARENT_SKU) is of type array<integer,?>, but the function expects a string.

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...
Deprecated Code introduced by
The method TechDivision\Import\Obse...server::wrapException() has been deprecated with message: Will be removed with version 1.0.0, use subject method instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
98
        }
99
100
        try {
101
            // extract the child ID from the row
102
            $childId = $this->mapSkuToEntityId($this->getValue(ColumnKeys::LINK_CHILD_SKU));
103
        } catch (\Exception $e) {
104
            throw $this->wrapException(array(ColumnKeys::LINK_CHILD_SKU), $e);
0 ignored issues
show
Documentation introduced by
array(\TechDivision\Impo...mnKeys::LINK_CHILD_SKU) is of type array<integer,?>, but the function expects a string.

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...
Deprecated Code introduced by
The method TechDivision\Import\Obse...server::wrapException() has been deprecated with message: Will be removed with version 1.0.0, use subject method instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
105
        }
106
107
        try {
108
            // extract the link type code from the row
109
            $linkTypeId = $this->mapLinkTypeCodeToLinkTypeId($this->getValue(ColumnKeys::LINK_TYPE_CODE));
110
        } catch (\Exception $e) {
111
            // query whether or not, debug mode is enabled
112
            if ($this->isDebugMode()) {
113
                // log a warning and return immediately
114
                $this->getSystemLogger()->warning($e->getMessage());
115
                return;
116
            }
117
118
            // if we're NOT in debug mode, re-throw the exception
119
            throw $e;
120
        }
121
122
        // initialize and return the entity
123
        return $this->initializeEntity(
124
            array(
125
                MemberNames::PRODUCT_ID        => $parentId,
126
                MemberNames::LINKED_PRODUCT_ID => $childId,
127
                MemberNames::LINK_TYPE_ID      => $linkTypeId
128
            )
129
        );
130
    }
131
132
    /**
133
     * Initialize the product link with the passed attributes and returns an instance.
134
     *
135
     * @param array $attr The product link attributes
136
     *
137
     * @return array The initialized product link
138
     */
139 View Code Duplication
    protected function initializeProductLink(array $attr)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
140
    {
141
142
        // load the product/linked product/link type ID
143
        $productId = $attr[MemberNames::PRODUCT_ID];
144
        $linkTypeId = $attr[MemberNames::LINK_TYPE_ID];
145
        $linkedProductId = $attr[MemberNames::LINKED_PRODUCT_ID];
146
147
        // try to load the link with the passed product/linked product/link type ID
148
        if ($entity = $this->loadProductLink($productId, $linkedProductId, $linkTypeId)) {
149
            return $this->mergeEntity($entity, $attr);
150
        }
151
152
        // simply return the attributes
153
        return $attr;
154
    }
155
156
    /**
157
     * Load's the link with the passed product/linked product/link type ID.
158
     *
159
     * @param integer $productId       The product ID of the link to load
160
     * @param integer $linkedProductId The linked product ID of the link to load
161
     * @param integer $linkTypeId      The link type ID of the product to load
162
     *
163
     * @return array The link
164
     */
165
    protected function loadProductLink($productId, $linkedProductId, $linkTypeId)
166
    {
167
        return $this->getProductLinkProcessor()->loadProductLink($productId, $linkedProductId, $linkTypeId);
168
    }
169
170
    /**
171
     * Temporary persist the last link ID.
172
     *
173
     * @param integer $lastLinkId The last link ID
174
     *
175
     * @return void
176
     */
177
    protected function setLastLinkId($lastLinkId)
178
    {
179
        $this->getSubject()->setLastLinkId($lastLinkId);
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 setLastLinkId() does only exist in the following implementations of said interface: TechDivision\Import\Prod...nk\Subjects\LinkSubject.

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...
180
    }
181
182
    /**
183
     * Persist's the passed product link data and return's the ID.
184
     *
185
     * @param array $productLink The product link data to persist
186
     *
187
     * @return string The ID of the persisted entity
188
     */
189
    protected function persistProductLink($productLink)
190
    {
191
        return $this->getProductLinkProcessor()->persistProductLink($productLink);
192
    }
193
194
    /**
195
     * Return the entity ID for the passed SKU.
196
     *
197
     * @param string $sku The SKU to return the entity ID for
198
     *
199
     * @return integer The mapped entity ID
200
     * @throws \TechDivision\Import\Product\Link\Exceptions\MapSkuToEntityIdException Is thrown if the SKU is not mapped yet
201
     */
202
    protected function mapSkuToEntityId($sku)
203
    {
204
        return $this->getSubject()->mapSkuToEntityId($sku);
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 mapSkuToEntityId() does only exist in the following implementations of said interface: TechDivision\Import\Prod...nk\Subjects\LinkSubject.

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...
205
    }
206
207
    /**
208
     * Return the entity ID for the passed SKU.
209
     *
210
     * @param string $sku The SKU to return the entity ID for
211
     *
212
     * @return integer The mapped entity ID
213
     * @throws \TechDivision\Import\Product\Link\Exceptions\MapSkuToEntityIdException Is thrown if the SKU is not mapped yet
214
     */
215
    protected function mapSku($sku)
216
    {
217
        return $this->getSubject()->mapSkuToEntityId($sku);
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 mapSkuToEntityId() does only exist in the following implementations of said interface: TechDivision\Import\Prod...nk\Subjects\LinkSubject.

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...
218
    }
219
220
    /**
221
     * Return the link type ID for the passed link type code.
222
     *
223
     * @param string $linkTypeCode The link type code to return the link type ID for
224
     *
225
     * @return integer The mapped link type ID
226
     * @throws \TechDivision\Import\Product\Link\Exceptions\MapLinkTypeCodeToIdException Is thrown if the link type code is not mapped yet
227
     */
228
    protected function mapLinkTypeCodeToLinkTypeId($linkTypeCode)
229
    {
230
        return $this->getSubject()->mapLinkTypeCodeToLinkTypeId($linkTypeCode);
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 mapLinkTypeCodeToLinkTypeId() does only exist in the following implementations of said interface: TechDivision\Import\Prod...nk\Subjects\LinkSubject, TechDivision\Import\Prod...\AbstractProductSubject, TechDivision\Import\Product\Subjects\BunchSubject.

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...
231
    }
232
233
    /**
234
     * Queries whether or not debug mode is enabled or not, default is TRUE.
235
     *
236
     * @return boolean TRUE if debug mode is enabled, else FALSE
237
     */
238
    protected function isDebugMode()
239
    {
240
        return $this->getSubject()->isDebugMode();
241
    }
242
}
243