Completed
Push — 24.x ( 47a554 )
by Tim
01:53
created

ProductInventoryObserver::loadRawEntity()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
/**
4
 * TechDivision\Import\Product\Observers\ProductInventoryObserver
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 TechDivision\Import\Observers\StateDetectorInterface;
24
use TechDivision\Import\Observers\AttributeLoaderInterface;
25
use TechDivision\Import\Observers\DynamicAttributeObserverInterface;
26
use TechDivision\Import\Product\Utils\ColumnKeys;
27
use TechDivision\Import\Product\Utils\MemberNames;
28
use TechDivision\Import\Product\Services\ProductBunchProcessorInterface;
29
use TechDivision\Import\Subjects\SubjectInterface;
30
use TechDivision\Import\Observers\ObserverFactoryInterface;
31
use TechDivision\Import\Observers\StateDetectorAwareObserverInterface;
32
use TechDivision\Import\Utils\EntityStatus;
33
use TechDivision\Import\Observers\EntityMergers\EntityMergerInterface;
34
use TechDivision\Import\Product\Utils\EntityTypeCodes;
35
36
/**
37
 * Observer that creates/updates the product's inventory.
38
 *
39
 * @author    Tim Wagner <[email protected]>
40
 * @copyright 2016 TechDivision GmbH <[email protected]>
41
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
42
 * @link      https://github.com/techdivision/import-product
43
 * @link      http://www.techdivision.com
44
 */
45
class ProductInventoryObserver extends AbstractProductImportObserver implements DynamicAttributeObserverInterface, StateDetectorAwareObserverInterface, ObserverFactoryInterface
46
{
47
48
    /**
49
     * The product bunch processor instance.
50
     *
51
     * @var \TechDivision\Import\Product\Services\ProductBunchProcessorInterface
52
     */
53
    protected $productBunchProcessor;
54
55
    /**
56
     * The attribute loader instance.
57
     *
58
     * @var \TechDivision\Import\Observers\AttributeLoaderInterface
59
     */
60
    protected $attributeLoader;
61
62
    /**
63
     * The entity merger instance.
64
     *
65
     * @var \TechDivision\Import\Observers\EntityMergers\EntityMergerInterface
66
     */
67
    protected $entityMerger;
68
69
    /**
70
     * The array with the column mappings that has to be computed.
71
     *
72
     * @var array
73
     */
74
    protected $columns = array();
75
76
    /**
77
     * Initialize the observer with the passed product bunch processor instance.
78
     *
79
     * @param \TechDivision\Import\Product\Services\ProductBunchProcessorInterface    $productBunchProcessor The product bunch processor instance
80
     * @param \TechDivision\Import\Observers\AttributeLoaderInterface                 $attributeLoader       The attribute loader instance
81
     * @param \TechDivision\Import\Observers\EntityMergers\EntityMergerInterface|null $entityMerger          The entity merger instance
82
     * @param \TechDivision\Import\Observers\StateDetectorInterface|null              $stateDetector         The state detector instance to use
83
     */
84 2 View Code Duplication
    public function __construct(
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...
85
        ProductBunchProcessorInterface $productBunchProcessor,
86
        AttributeLoaderInterface $attributeLoader,
87
        EntityMergerInterface $entityMerger = null,
88
        StateDetectorInterface $stateDetector = null
89
    ) {
90
91
        // initialize the bunch processor and the attribute loader instance
92 2
        $this->productBunchProcessor = $productBunchProcessor;
93 2
        $this->attributeLoader = $attributeLoader;
94 2
        $this->entityMerger = $entityMerger;
95
96
        // pass the state detector to the parent method
97 2
        parent::__construct($stateDetector);
98 2
    }
99
100
    /**
101
     * Will be invoked by the observer visitor when a factory has been defined to create the observer instance.
102
     *
103
     * @param \TechDivision\Import\Subjects\SubjectInterface $subject The subject instance
104
     *
105
     * @return \TechDivision\Import\Observers\ObserverInterface The observer instance
106
     */
107
    public function createObserver(SubjectInterface $subject)
108
    {
109
110
        // load the header stock mappings from the subject
111
        $headerStockMappings = $subject->getHeaderStockMappings();
0 ignored issues
show
Bug introduced by
The method getHeaderStockMappings() does not exist on TechDivision\Import\Subjects\SubjectInterface. Did you maybe mean getHeader()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
112
113
        // prepare the array with column name => type mapping
114
        foreach ($headerStockMappings as $columnName => $mappings) {
115
            // explode the mapping details
116
            list (, $type) = $mappings;
117
            // add the column name => type mapping
118
            $this->columns[$columnName] = $type;
119
        }
120
121
        // return the instance itself
122
        return $this;
123
    }
124
125
    /**
126
     * Returns an array of the columns with their types to detect state.
127
     *
128
     * @return array The array with the column names as key and their type as value
129
     */
130
    public function getColumns()
131
    {
132
        return array_intersect_key($this->columns, $this->getHeaders());
133
    }
134
135
    /**
136
     * Return's the product bunch processor instance.
137
     *
138
     * @return \TechDivision\Import\Product\Services\ProductBunchProcessorInterface The product bunch processor instance
139
     */
140 1
    protected function getProductBunchProcessor()
141
    {
142 1
        return $this->productBunchProcessor;
143
    }
144
145
    /**
146
     * Process the observer's business logic.
147
     *
148
     * @return array The processed row
149
     */
150 2
    protected function process()
151
    {
152
153
        // query whether or not, we've found a new SKU => means we've found a new product
154 2
        if ($this->hasBeenProcessed($this->getValue(ColumnKeys::SKU))) {
155 1
            return;
156
        }
157
158
        // prepare, initialize and persist the stock status/item
159 1
        if ($this->hasChanges($entity = $this->initializeStockItem($this->prepareStockItemAttributes()))) {
160 1
            $this->persistStockItem($entity);
161
        }
162 1
    }
163
164
    /**
165
     * Prepare the basic attributes of the stock status/item entity that has to be persisted.
166
     *
167
     * @return array The prepared stock status/item attributes
168
     */
169 1
    protected function prepareAttributes()
170
    {
171
172
        // load the ID of the product that has been created recently
173 1
        $lastEntityId = $this->getSubject()->getLastEntityId();
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 getLastEntityId() does only exist in the following implementations of said interface: TechDivision\Import\Observers\EntitySubjectImpl, 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...
174
175
        // initialize the stock status data
176 1
        $websiteId =  $this->getValue(ColumnKeys::WEBSITE_ID, 0);
177
178
        // return the prepared stock status
179 1
        return $this->initializeEntity(
180 1
            $this->loadRawEntity(
181
                array(
182 1
                    MemberNames::PRODUCT_ID   => $lastEntityId,
183 1
                    MemberNames::WEBSITE_ID   => $websiteId,
184 1
                    MemberNames::STOCK_ID     => 1
185
                )
186
            )
187
        );
188
    }
189
190
    /**
191
     * Merge's and return's the entity with the passed attributes and set's the
192
     * passed status.
193
     *
194
     * @param array       $entity        The entity to merge the attributes into
195
     * @param array       $attr          The attributes to be merged
196
     * @param string|null $changeSetName The change set name to use
197
     *
198
     * @return array The merged entity
199
     * @todo https://github.com/techdivision/import/issues/179
200
     */
201 View Code Duplication
    protected function mergeEntity(array $entity, array $attr, $changeSetName = null)
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...
202
    {
203
        return array_merge(
204
            $entity,
205
            $this->entityMerger ? $this->entityMerger->merge($this, $entity, $attr) : $attr,
206
            array(EntityStatus::MEMBER_NAME => $this->detectState($entity, $attr, $changeSetName))
207
        );
208
    }
209
210
    /**
211
     * Prepare the stock item attributes of the entity that has to be persisted.
212
     *
213
     * @return array The prepared stock status item
214
     */
215 1
    protected function prepareStockItemAttributes()
216
    {
217 1
        return array_merge($this->prepareAttributes(), $this->attributeLoader->load($this, $this->getHeaderStockMappings()));
218
    }
219
220
    /**
221
     * Load's and return's a raw customer entity without primary key but the mandatory members only and nulled values.
222
     *
223
     * @param array $data An array with data that will be used to initialize the raw entity with
224
     *
225
     * @return array The initialized entity
226
     */
227 1
    protected function loadRawEntity(array $data = array())
228
    {
229 1
        return $this->getProductBunchProcessor()->loadRawEntity(EntityTypeCodes::CATALOGINVENTORY_STOCK_ITEM, $data);
230
    }
231
232
    /**
233
     * Initialize the stock item with the passed attributes and returns an instance.
234
     *
235
     * @param array $attr The stock item attributes
236
     *
237
     * @return array The initialized stock item
238
     */
239 1
    protected function initializeStockItem(array $attr)
240
    {
241 1
        return $attr;
242
    }
243
244
    /**
245
     * Return's the appings for the table column => CSV column header.
246
     *
247
     * @return array The header stock mappings
248
     */
249 1
    protected function getHeaderStockMappings()
250
    {
251 1
        return $this->getSubject()->getHeaderStockMappings();
0 ignored issues
show
Bug introduced by
The method getHeaderStockMappings() does not exist on TechDivision\Import\Subjects\SubjectInterface. Did you maybe mean getHeader()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
252
    }
253
254
    /**
255
     * Persist's the passed stock item data and return's the ID.
256
     *
257
     * @param array $stockItem The stock item data to persist
258
     *
259
     * @return void
260
     */
261 1
    protected function persistStockItem($stockItem)
262
    {
263 1
        $this->getProductBunchProcessor()->persistStockItem($stockItem);
264 1
    }
265
}
266