Completed
Push — 19.x ( 8328ae...eef76a )
by Tim
05:57
created

ProductAttributeObserver::getColumns()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 3
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 2
1
<?php
2
3
/**
4
 * TechDivision\Import\Product\Observers\ProductAttributeObserver
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\Subjects\SubjectInterface;
24
use TechDivision\Import\Observers\StateDetectorInterface;
25
use TechDivision\Import\Observers\ObserverFactoryInterface;
26
use TechDivision\Import\Observers\AbstractAttributeObserver;
27
use TechDivision\Import\Product\Utils\ColumnKeys;
28
use TechDivision\Import\Product\Utils\MemberNames;
29
use TechDivision\Import\Product\Services\ProductBunchProcessorInterface;
30
use TechDivision\Import\Observers\StateDetectorAwareObserverInterface;
31
use TechDivision\Import\Utils\BackendTypeKeys;
32
33
/**
34
 * Observer that creates/updates the product's attributes.
35
 *
36
 * @author    Tim Wagner <[email protected]>
37
 * @copyright 2016 TechDivision GmbH <[email protected]>
38
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
39
 * @link      https://github.com/techdivision/import-product
40
 * @link      http://www.techdivision.com
41
 */
42
class ProductAttributeObserver extends AbstractAttributeObserver implements StateDetectorAwareObserverInterface, ObserverFactoryInterface
43
{
44
45
    /**
46
     * The product bunch processor instance.
47
     *
48
     * @var \TechDivision\Import\Product\Services\ProductBunchProcessorInterface
49
     */
50
    protected $productBunchProcessor;
51
52
    /**
53
     * The array with the column mappings that has to be computed.
54
     *
55
     * @var array
56
     */
57
    protected $columns = array();
58
59
    /**
60
     * The backend type => data type mappings.
61
     *
62
     * @var array
63
     */
64
    protected $backendTypeKeyMappings = array(
65
        BackendTypeKeys::BACKEND_TYPE_DATETIME => 'string',
66
        BackendTypeKeys::BACKEND_TYPE_DECIMAL => 'float',
67
        BackendTypeKeys::BACKEND_TYPE_FLOAT => 'float',
68
        BackendTypeKeys::BACKEND_TYPE_INT => 'integer',
69
        BackendTypeKeys::BACKEND_TYPE_STATIC => 'string',
70
        BackendTypeKeys::BACKEND_TYPE_TEXT => 'string',
71
        BackendTypeKeys::BACKEND_TYPE_VARCHAR => 'string'
72
    );
73
74
    /**
75
     * Initialize the observer with the passed product bunch processor instance.
76
     *
77
     * @param \TechDivision\Import\Product\Services\ProductBunchProcessorInterface $productBunchProcessor The product bunch processor instance
78
     * @param \TechDivision\Import\Observers\StateDetectorInterface|null           $stateDetector         The state detector instance to use
79
     */
80
    public function __construct(ProductBunchProcessorInterface $productBunchProcessor, StateDetectorInterface $stateDetector = null)
81
    {
82
83
        // initialize the bunch processor instance
84
        $this->productBunchProcessor = $productBunchProcessor;
85
86
        // pass the state detector to the parent method
87
        parent::__construct($stateDetector);
88
    }
89
90
    /**
91
     * Will be invoked by the observer visitor when a factory has been defined to create the observer instance.
92
     *
93
     * @param \TechDivision\Import\Subjects\SubjectInterface $subject The subject instance
94
     *
95
     * @return \TechDivision\Import\Observers\ObserverInterface The observer instance
96
     */
97
    public function createObserver(SubjectInterface $subject)
98
    {
99
100
        // load the attributes
101
        $attributes = $subject->getAttributes();
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 getAttributes() does only exist in the following implementations of said interface: TechDivision\Import\Observers\EntitySubjectImpl, TechDivision\Import\Prod...\AbstractProductSubject, TechDivision\Import\Product\Subjects\BunchSubject, TechDivision\Import\Subjects\AbstractEavSubject.

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...
102
103
        // create the attribute name => type mapping
104
        foreach ($attributes as $attribute) {
105
            $this->columns[$attribute[MemberNames::ATTRIBUTE_ID]] = isset($this->backendTypeKeyMappings[$attribute[MemberNames::BACKEND_TYPE]]) ? $this->backendTypeKeyMappings[$attribute[MemberNames::BACKEND_TYPE]] : 'string';
106
        }
107
108
        // return the instance
109
        return $this;
110
    }
111
112
    /**
113
     * Returns an array of the columns with their types to detect state.
114
     *
115
     * @return array The array with the column names as key and their type as value
116
     */
117
    public function getColumns(array $attribute = array())
118
    {
119
        return array(MemberNames::VALUE => $this->columns[$attribute[MemberNames::ATTRIBUTE_ID]]);
120
    }
121
122
    /**
123
     * Intializes the existing attributes for the entity with the passed primary key.
124
     *
125
     * @param string  $pk      The primary key of the entity to load the attributes for
126
     * @param integer $storeId The ID of the store view to load the attributes for
127
     *
128
     * @return array The entity attributes
129
     */
130
    protected function getAttributesByPrimaryKeyAndStoreId($pk, $storeId)
131
    {
132
        return array();
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
    protected function getProductBunchProcessor()
141
    {
142
        return $this->productBunchProcessor;
143
    }
144
145
    /**
146
     * Return's the PK to create the product => attribute relation.
147
     *
148
     * @return integer The PK to create the relation with
149
     */
150
    protected function getPrimaryKey()
151
    {
152
        return $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...
153
    }
154
155
    /**
156
     * Return's the PK column name to create the product => attribute relation.
157
     *
158
     * @return string The PK column name
159
     */
160
    protected function getPrimaryKeyMemberName()
161
    {
162
        return MemberNames::ENTITY_ID;
163
    }
164
165
    /**
166
     * Return's the column name that contains the primary key.
167
     *
168
     * @return string the column name that contains the primary key
169
     */
170
    protected function getPrimaryKeyColumnName()
171
    {
172
        return ColumnKeys::SKU;
173
    }
174
175
    /**
176
     * Queries whether or not the passed PK and store view code has already been processed.
177
     *
178
     * @param string $pk            The PK to check been processed
179
     * @param string $storeViewCode The store view code to check been processed
180
     *
181
     * @return boolean TRUE if the PK and store view code has been processed, else FALSE
182
     */
183
    protected function storeViewHasBeenProcessed($pk, $storeViewCode)
184
    {
185
        return $this->getSubject()->storeViewHasBeenProcessed($pk, $storeViewCode);
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 storeViewHasBeenProcessed() does only exist in the following implementations of said interface: 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...
186
    }
187
188
    /**
189
     * Persist's the passed varchar attribute.
190
     *
191
     * @param array $attribute The attribute to persist
192
     *
193
     * @return void
194
     */
195
    protected function persistVarcharAttribute($attribute)
196
    {
197
        $this->getProductBunchProcessor()->persistProductVarcharAttribute($attribute);
198
    }
199
200
    /**
201
     * Persist's the passed integer attribute.
202
     *
203
     * @param array $attribute The attribute to persist
204
     *
205
     * @return void
206
     */
207
    protected function persistIntAttribute($attribute)
208
    {
209
        $this->getProductBunchProcessor()->persistProductIntAttribute($attribute);
210
    }
211
212
    /**
213
     * Persist's the passed decimal attribute.
214
     *
215
     * @param array $attribute The attribute to persist
216
     *
217
     * @return void
218
     */
219
    protected function persistDecimalAttribute($attribute)
220
    {
221
        $this->getProductBunchProcessor()->persistProductDecimalAttribute($attribute);
222
    }
223
224
    /**
225
     * Persist's the passed datetime attribute.
226
     *
227
     * @param array $attribute The attribute to persist
228
     *
229
     * @return void
230
     */
231
    protected function persistDatetimeAttribute($attribute)
232
    {
233
        $this->getProductBunchProcessor()->persistProductDatetimeAttribute($attribute);
234
    }
235
236
    /**
237
     * Persist's the passed text attribute.
238
     *
239
     * @param array $attribute The attribute to persist
240
     *
241
     * @return void
242
     */
243
    protected function persistTextAttribute($attribute)
244
    {
245
        $this->getProductBunchProcessor()->persistProductTextAttribute($attribute);
246
    }
247
248
    /**
249
     * Delete's the datetime attribute with the passed value ID.
250
     *
251
     * @param array       $row  The attributes of the entity to delete
252
     * @param string|null $name The name of the prepared statement that has to be executed
253
     *
254
     * @return void
255
     */
256
    protected function deleteDatetimeAttribute(array $row, $name = null)
257
    {
258
        $this->getProductBunchProcessor()->deleteProductDatetimeAttribute($row, $name);
259
    }
260
261
    /**
262
     * Delete's the decimal attribute with the passed value ID.
263
     *
264
     * @param array       $row  The attributes of the entity to delete
265
     * @param string|null $name The name of the prepared statement that has to be executed
266
     *
267
     * @return void
268
     */
269
    protected function deleteDecimalAttribute(array $row, $name = null)
270
    {
271
        $this->getProductBunchProcessor()->deleteProductDecimalAttribute($row, $name);
272
    }
273
274
    /**
275
     * Delete's the integer attribute with the passed value ID.
276
     *
277
     * @param array       $row  The attributes of the entity to delete
278
     * @param string|null $name The name of the prepared statement that has to be executed
279
     *
280
     * @return void
281
     */
282
    protected function deleteIntAttribute(array $row, $name = null)
283
    {
284
        $this->getProductBunchProcessor()->deleteProductIntAttribute($row, $name);
285
    }
286
287
    /**
288
     * Delete's the text attribute with the passed value ID.
289
     *
290
     * @param array       $row  The attributes of the entity to delete
291
     * @param string|null $name The name of the prepared statement that has to be executed
292
     *
293
     * @return void
294
     */
295
    protected function deleteTextAttribute(array $row, $name = null)
296
    {
297
        $this->getProductBunchProcessor()->deleteProductTextAttribute($row, $name);
298
    }
299
300
    /**
301
     * Delete's the varchar attribute with the passed value ID.
302
     *
303
     * @param array       $row  The attributes of the entity to delete
304
     * @param string|null $name The name of the prepared statement that has to be executed
305
     *
306
     * @return void
307
     */
308
    protected function deleteVarcharAttribute(array $row, $name = null)
309
    {
310
        return $this->getProductBunchProcessor()->deleteProductVarcharAttribute($row, $name);
311
    }
312
}
313