Completed
Push — 18.x ( c47ecf...a942ab )
by Tim
09:41
created

LinkObserver::prepareAttributes()   A

Complexity

Conditions 4
Paths 7

Size

Total Lines 34

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
dl 0
loc 34
ccs 0
cts 23
cp 0
rs 9.376
c 0
b 0
f 0
cc 4
nc 7
nop 0
crap 20
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 link type code from the row
95
            $linkTypeId = $this->mapLinkTypeCodeToLinkTypeId($this->getValue(ColumnKeys::LINK_TYPE_CODE));
96
            // initialize the column name
97
            $columnName = ColumnKeys::LINK_PARENT_SKU;
98
            // extract the parent + child ID from the row
99
            $parentId = $this->mapSku($this->getValue($columnName));
100
            $childId = $this->mapSkuToEntityId($this->getValue($columnName = ColumnKeys::LINK_CHILD_SKU));
101
        } catch (\Exception $e) {
102
            // query whether or not, debug mode is enabled
103
            if ($this->isDebugMode()) {
104
                // stop processing the row
105
                $this->skipRow();
0 ignored issues
show
Deprecated Code introduced by
The method TechDivision\Import\Obse...ractObserver::skipRow() 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...
106
                // log a warning and return immediately
107
                $this->getSystemLogger()->warning($e->getMessage());
108
                return;
109
            }
110
111
            // if we're NOT in debug mode, re-throw the exception
112
            throw $columnName ? $this->wrapException(array($columnName), $e) : $e;
0 ignored issues
show
Documentation introduced by
array($columnName) is of type array<integer,?,{"0":"?"}>, 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...
113
        }
114
115
        // initialize and return the entity
116
        return $this->initializeEntity(
117
            array(
118
                MemberNames::PRODUCT_ID        => $parentId,
119
                MemberNames::LINKED_PRODUCT_ID => $childId,
120
                MemberNames::LINK_TYPE_ID      => $linkTypeId
121
            )
122
        );
123
    }
124
125
    /**
126
     * Initialize the product link with the passed attributes and returns an instance.
127
     *
128
     * @param array $attr The product link attributes
129
     *
130
     * @return array The initialized product link
131
     */
132 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...
133
    {
134
135
        // load the product/linked product/link type ID
136
        $productId = $attr[MemberNames::PRODUCT_ID];
137
        $linkTypeId = $attr[MemberNames::LINK_TYPE_ID];
138
        $linkedProductId = $attr[MemberNames::LINKED_PRODUCT_ID];
139
140
        // try to load the link with the passed product/linked product/link type ID
141
        if ($entity = $this->loadProductLink($productId, $linkedProductId, $linkTypeId)) {
142
            return $this->mergeEntity($entity, $attr);
143
        }
144
145
        // simply return the attributes
146
        return $attr;
147
    }
148
149
    /**
150
     * Load's the link with the passed product/linked product/link type ID.
151
     *
152
     * @param integer $productId       The product ID of the link to load
153
     * @param integer $linkedProductId The linked product ID of the link to load
154
     * @param integer $linkTypeId      The link type ID of the product to load
155
     *
156
     * @return array The link
157
     */
158
    protected function loadProductLink($productId, $linkedProductId, $linkTypeId)
159
    {
160
        return $this->getProductLinkProcessor()->loadProductLink($productId, $linkedProductId, $linkTypeId);
161
    }
162
163
    /**
164
     * Temporary persist the last link ID.
165
     *
166
     * @param integer $lastLinkId The last link ID
167
     *
168
     * @return void
169
     */
170
    protected function setLastLinkId($lastLinkId)
171
    {
172
        $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...
173
    }
174
175
    /**
176
     * Persist's the passed product link data and return's the ID.
177
     *
178
     * @param array $productLink The product link data to persist
179
     *
180
     * @return string The ID of the persisted entity
181
     */
182
    protected function persistProductLink($productLink)
183
    {
184
        return $this->getProductLinkProcessor()->persistProductLink($productLink);
185
    }
186
187
    /**
188
     * Return the entity ID for the passed SKU.
189
     *
190
     * @param string $sku The SKU to return the entity ID for
191
     *
192
     * @return integer The mapped entity ID
193
     * @throws \TechDivision\Import\Product\Exceptions\MapSkuToEntityIdException Is thrown if the SKU is not mapped yet
194
     */
195
    protected function mapSkuToEntityId($sku)
196
    {
197
        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, 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...
198
    }
199
200
    /**
201
     * Return the entity ID for the passed SKU.
202
     *
203
     * @param string $sku The SKU to return the entity ID for
204
     *
205
     * @return integer The mapped entity ID
206
     * @throws \TechDivision\Import\Product\Exceptions\MapSkuToEntityIdException Is thrown if the SKU is not mapped yet
207
     */
208
    protected function mapSku($sku)
209
    {
210
        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, 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...
211
    }
212
213
    /**
214
     * Return the link type ID for the passed link type code.
215
     *
216
     * @param string $linkTypeCode The link type code to return the link type ID for
217
     *
218
     * @return integer The mapped link type ID
219
     * @throws \TechDivision\Import\Product\Exceptions\MapLinkTypeCodeToIdException Is thrown if the link type code is not mapped yet
220
     */
221
    protected function mapLinkTypeCodeToLinkTypeId($linkTypeCode)
222
    {
223
        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...
224
    }
225
226
    /**
227
     * Queries whether or not debug mode is enabled or not, default is TRUE.
228
     *
229
     * @return boolean TRUE if debug mode is enabled, else FALSE
230
     */
231
    protected function isDebugMode()
232
    {
233
        return $this->getSubject()->isDebugMode();
234
    }
235
}
236