Completed
Pull Request — master (#28)
by
unknown
10:14
created

CleanUpLinkObserver   A

Complexity

Total Complexity 10

Size/Duplication

Total Lines 144
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
wmc 10
lcom 1
cbo 5
dl 0
loc 144
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 11 1
A getProductLinkProcessor() 0 4 1
A process() 0 28 4
A cleanUpLinks() 0 36 2
A getLastPrimaryKey() 0 4 1
A mapLinkTypeCodeToLinkTypeId() 0 4 1
1
<?php
2
3
/**
4
 * TechDivision\Import\Product\Link\Observers\CleanUpLinkObserver
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    Martin Eisenführer <[email protected]>
15
 * @copyright 2020 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-variant
18
 * @link      http://www.techdivision.com
19
 */
20
21
namespace TechDivision\Import\Product\Link\Observers;
22
23
use TechDivision\Import\Observers\StateDetectorInterface;
24
use TechDivision\Import\Product\Link\Services\ProductLinkProcessorInterface;
25
use TechDivision\Import\Product\Link\Utils\ColumnKeys;
26
use TechDivision\Import\Product\Link\Utils\ConfigurationKeys;
27
use TechDivision\Import\Product\Link\Utils\MemberNames;
28
use TechDivision\Import\Product\Observers\AbstractProductImportObserver;
29
30
/**
31
 * Observer that cleaned up a product's link information.
32
 *
33
 * @author    Martin Eisenführer <[email protected]>
34
 * @copyright 2020 TechDivision GmbH <[email protected]>
35
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
36
 * @link      https://github.com/techdivision/import-product-variant
37
 * @link      http://www.techdivision.com
38
 */
39
class CleanUpLinkObserver extends AbstractProductImportObserver
40
{
41
42
    /**
43
     * The product link processor instance.
44
     *
45
     * @var \TechDivision\Import\Product\Link\Services\ProductLinkProcessorInterface
46
     */
47
    protected $productLinkProcessor;
48
49
    /**
50
     * Initialize the observer with the passed product link processor instance.
51
     *
52
     * @param \TechDivision\Import\Product\Link\Services\ProductLinkProcessorInterface $productLinkProcessor The
53
     *     product link processor instance
54
     * @param StateDetectorInterface|null $stateDetector The state detector instance to use
55
     */
56
    public function __construct(
57
        ProductLinkProcessorInterface $productLinkProcessor,
58
        StateDetectorInterface $stateDetector = null
59
    ) {
60
61
        // pass the state detector to the parent constructor
62
        parent::__construct($stateDetector);
63
64
        // initialize the product link processor instance
65
        $this->productLinkProcessor = $productLinkProcessor;
66
    }
67
68
    /**
69
     * Return's the product variant processor instance.
70
     *
71
     * @return \TechDivision\Import\Product\Link\Services\ProductLinkProcessorInterface The product variant processor
72
     *     instance
73
     */
74
    protected function getProductLinkProcessor()
75
    {
76
        return $this->productLinkProcessor;
77
    }
78
79
    /**
80
     * Process the observer's business logic.
81
     *
82
     * @return void
83
     * @throws \Exception
84
     */
85
    protected function process()
86
    {
87
88
        // query whether or not the product links has to be cleaned up
89
        if ($this->getSubject()->getConfiguration()->hasParam(ConfigurationKeys::CLEAN_UP_LINKS)
90
            && $this->getSubject()->getConfiguration()->getParam(ConfigurationKeys::CLEAN_UP_LINKS)
91
        ) {
92
93
            // load the row/entity ID of the parent product
94
            $parentId = $this->getLastPrimaryKey();
95
96
            // load the link type mappings
97
            $linkTypes = $this->getSubject()->getLinkTypeMappings();
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 getLinkTypeMappings() 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...
98
99
            // prepare the links for the found link types and clean up
100
            foreach ($linkTypes as $linkTypeCode => $columns) {
101
102
                // shift the column with the header information from the stack
103
                list ($columnNameChildSkus, $callbackChildSkus) = array_shift($columns);
104
105
                // query whether or not, we've up sell, cross sell or relation products
106
                $links = $this->getValue($columnNameChildSkus, [], $callbackChildSkus);
107
108
                // Start clean up
109
                $this->cleanUpLinks($parentId, $linkTypeCode, $links);
110
            }
111
        }
112
    }
113
114
    /**
115
     * Delete not exists import links from database.
116
     *
117
     * @param int $parentProductId The ID of the parent product
118
     * @param array $childData The array of variants
119
     *
120
     * @return void
121
     */
122
    protected function cleanUpLinks($parentProductId, $linkTypeCode, array $childData)
123
    {
124
        // we maybe don't want delete everything
125
        if (empty($childData)) {
126
            return;
127
        }
128
129
        // load the SKU of the parent product
130
        $parentSku = $this->getValue(ColumnKeys::SKU);
131
132
        // extract the link type code from the row
133
        $linkTypeId = $this->mapLinkTypeCodeToLinkTypeId($linkTypeCode);
134
135
        // remove the old variantes from the database
136
        $this->getProductLinkProcessor()
137
            ->deleteProductLink(
138
                array(
139
                    MemberNames::PRODUCT_ID => $parentProductId,
140
                    MemberNames::SKU => $childData,
141
                    MemberNames::LINK_TYPE_ID => $linkTypeId,
142
                )
143
            );
144
145
        // log a debug message that the image has been removed
146
        $this->getSubject()
147
            ->getSystemLogger()
148
            ->debug(
149
                $this->getSubject()->appendExceptionSuffix(
150
                    sprintf(
151
                        'Successfully clean up links for product with SKU "%s" except "%s"',
152
                        $parentSku,
153
                        implode(', ', $childData)
154
                    )
155
                )
156
            );
157
    }
158
159
    /**
160
     * Return's the PK to create the product => variant relation.
161
     *
162
     * @return integer The PK to create the relation with
163
     */
164
    protected function getLastPrimaryKey()
165
    {
166
        return $this->getLastEntityId();
167
    }
168
169
    /**
170
     * Return the link type ID for the passed link type code.
171
     *
172
     * @param string $linkTypeCode The link type code to return the link type ID for
173
     *
174
     * @return integer The mapped link type ID
175
     * @throws \TechDivision\Import\Product\Exceptions\MapLinkTypeCodeToIdException Is thrown if the link type code is
176
     *     not mapped yet
177
     */
178
    protected function mapLinkTypeCodeToLinkTypeId($linkTypeCode)
179
    {
180
        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...
181
    }
182
}
183