Completed
Pull Request — master (#28)
by
unknown
21:00 queued 10:59
created

CleanUpLinkObserver::process()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 26
rs 9.504
c 0
b 0
f 0
cc 4
nc 3
nop 0
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
            // load the row/entity ID of the parent product
93
            $parentId = $this->getLastPrimaryKey();
94
95
            // load the link type mappings
96
            $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...
97
98
            // prepare the links for the found link types and clean up
99
            foreach ($linkTypes as $linkTypeCode => $columns) {
100
                // shift the column with the header information from the stack
101
                list ($columnNameChildSkus, $callbackChildSkus) = array_shift($columns);
102
103
                // query whether or not, we've up sell, cross sell or relation products
104
                $links = $this->getValue($columnNameChildSkus, [], $callbackChildSkus);
105
106
                // Start clean up
107
                $this->cleanUpLinks($parentId, $linkTypeCode, $links);
108
            }
109
        }
110
    }
111
112
    /**
113
     * Delete not exists import links from database.
114
     *
115
     * @param int    $parentProductId The ID of the parent product
116
     * @param string $linkTypeCode    The link type code to prepare the artefacts for
117
     * @param array  $childData       The array of variants
118
     *
119
     * @return void
120
     */
121
    protected function cleanUpLinks($parentProductId, $linkTypeCode, array $childData)
122
    {
123
        // we maybe don't want delete everything
124
        if (empty($childData)) {
125
            return;
126
        }
127
128
        // load the SKU of the parent product
129
        $parentSku = $this->getValue(ColumnKeys::SKU);
130
131
        // extract the link type code from the row
132
        $linkTypeId = $this->mapLinkTypeCodeToLinkTypeId($linkTypeCode);
133
134
        // remove the old variantes from the database
135
        $this->getProductLinkProcessor()
136
            ->deleteProductLink(
137
                array(
138
                    MemberNames::PRODUCT_ID => $parentProductId,
139
                    MemberNames::SKU => $childData,
140
                    MemberNames::LINK_TYPE_ID => $linkTypeId,
141
                )
142
            );
143
144
        // log a debug message that the image has been removed
145
        $this->getSubject()
146
            ->getSystemLogger()
147
            ->debug(
148
                $this->getSubject()->appendExceptionSuffix(
149
                    sprintf(
150
                        'Successfully clean up links for product with SKU "%s" except "%s"',
151
                        $parentSku,
152
                        implode(', ', $childData)
153
                    )
154
                )
155
            );
156
    }
157
158
    /**
159
     * Return's the PK to create the product => variant relation.
160
     *
161
     * @return integer The PK to create the relation with
162
     */
163
    protected function getLastPrimaryKey()
164
    {
165
        return $this->getLastEntityId();
166
    }
167
168
    /**
169
     * Return the link type ID for the passed link type code.
170
     *
171
     * @param string $linkTypeCode The link type code to return the link type ID for
172
     *
173
     * @return integer The mapped link type ID
174
     * @throws \TechDivision\Import\Product\Exceptions\MapLinkTypeCodeToIdException Is thrown if the link type code is
175
     *     not mapped yet
176
     */
177
    protected function mapLinkTypeCodeToLinkTypeId($linkTypeCode)
178
    {
179
        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...
180
    }
181
}
182