Completed
Push — master ( 5e765a...ee4b10 )
by Tim
03:06
created

AttributeObserverTrait::deleteVarcharAttribute()

Size

Total Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 1
ccs 0
cts 0
cp 0
c 0
b 0
f 0
nc 1
1
<?php
2
3
/**
4
 * TechDivision\Import\Observers\AttributeObserverTrait
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 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
18
 * @link      http://www.techdivision.com
19
 */
20
21
namespace TechDivision\Import\Observers;
22
23
use TechDivision\Import\Utils\LoggerKeys;
24
use TechDivision\Import\Utils\MemberNames;
25
use TechDivision\Import\Utils\StoreViewCodes;
26
use TechDivision\Import\Utils\OperationNames;
27
use TechDivision\Import\Utils\BackendTypeKeys;
28
use TechDivision\Import\Utils\ConfigurationKeys;
29
30
/**
31
 * Observer that creates/updates the EAV attributes.
32
 *
33
 * @author    Tim Wagner <[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
37
 * @link      http://www.techdivision.com
38
 */
39
trait AttributeObserverTrait
40
{
41
42
    /**
43
     * The ID of the attribute to create the values for.
44
     *
45
     * @var integer
46
     */
47
    protected $attributeId;
48
49
    /**
50
     * The attribute code of the attribute to create the values for.
51
     *
52
     * @var string
53
     */
54
    protected $attributeCode;
55
56
    /**
57
     * The backend type of the attribute to create the values for.
58
     *
59
     * @var string
60
     */
61
    protected $backendType;
62
63
    /**
64
     * The attribute value in process.
65
     *
66
     * @var mixed
67
     */
68
    protected $attributeValue;
69
70
    /**
71
     * The array with the column keys that has to be cleaned up when their values are empty.
72
     *
73
     * @var array
74
     */
75
    protected $cleanUpEmptyColumnKeys;
76
77
    /**
78
     * The array with the default column values.
79
     *
80
     * @var array
81
     */
82
    protected $defaultColumnValues;
83
84
    /**
85
     * The attribute we're actually processing.
86
     *
87
     * @var array
88
     */
89
    protected $attribute;
90
91
    /**
92
     * The entity's existing attribues.
93
     *
94
     * @var array
95
     */
96
    protected $attributes;
97
98
    /**
99
     * The operation that has to be executed to update the attribute.
100
     *
101
     * @var string
102
     */
103
    protected $operation;
104
105
    /**
106
     * The attribute code that has to be processed.
107
     *
108
     * @return string The attribute code
109
     */
110 1
    public function getAttributeCode()
111
    {
112 1
        return $this->attributeCode;
113
    }
114
115
    /**
116
     * The attribute value that has to be processed.
117
     *
118
     * @return string The attribute value
119
     */
120 1
    public function getAttributeValue()
121
    {
122 1
        return $this->attributeValue;
123
    }
124
125
    /**
126
     * Remove all the empty values from the row and return the cleared row.
127
     *
128
     * @return array The cleared row
129
     */
130 7
    protected function clearRow()
131
    {
132
133
        // initialize the array with the column keys that has to be cleaned-up
134 7
        $this->cleanUpEmptyColumnKeys = array();
135
136
        // query whether or not column names that has to be cleaned up have been configured
137 7
        if ($this->getSubject()->getConfiguration()->hasParam(ConfigurationKeys::CLEAN_UP_EMPTY_COLUMNS)) {
0 ignored issues
show
Bug introduced by
It seems like getSubject() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
138
            // if yes, load the column names
139
            $cleanUpEmptyColumns = $this->getSubject()->getCleanUpColumns();
0 ignored issues
show
Bug introduced by
It seems like getSubject() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
140
141
            // translate the column names into column keys
142
            foreach ($cleanUpEmptyColumns as $cleanUpEmptyColumn) {
143
                if ($this->hasHeader($cleanUpEmptyColumn)) {
0 ignored issues
show
Bug introduced by
It seems like hasHeader() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
144
                    $this->cleanUpEmptyColumnKeys[$cleanUpEmptyColumn] = $this->getHeader($cleanUpEmptyColumn);
0 ignored issues
show
Bug introduced by
It seems like getHeader() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
145
                }
146
            }
147
        }
148
149
        // initialize the array with the default column values
150 7
        $this->defaultColumnValues = array();
151
152
        // iterate over the default column values to figure out whether or not the column exists
153 7
        $defaultColumnValues = $this->getSubject()->getDefaultColumnValues();
0 ignored issues
show
Bug introduced by
It seems like getSubject() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
154
155
        // prepare the array with the default column values, BUT we only take
156
        // care of default columns WITHOUT any value, because in only in this
157
        // case the default EAV value from the DB should be used when a empty
158
        // column value has been found to create a NEW attribute value
159 7
        foreach ($defaultColumnValues as $columnName => $defaultColumnValue) {
160
            if ($defaultColumnValue === '') {
161
                $this->defaultColumnValues[$columnName] = $this->getHeader($columnName);
0 ignored issues
show
Bug introduced by
It seems like getHeader() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
162
            }
163
        }
164
165
        // remove all the empty values from the row, expected the columns has to be cleaned-up
166 7
        foreach ($this->row as $key => $value) {
0 ignored issues
show
Bug introduced by
The property row does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
167
            // query whether or not the value is empty AND the column has NOT to be cleaned-up
168 7
            if (($value === null || $value === '') &&
169 7
                in_array($key, $this->cleanUpEmptyColumnKeys) === false &&
170 7
                in_array($key, $this->defaultColumnValues) === false
171
            ) {
172 7
                unset($this->row[$key]);
173
            }
174
        }
175
176
        // finally return the clean row
177 7
        return $this->row;
178
    }
179
180
    /**
181
     * Returns the value(s) of the primary key column(s). As the primary key column can
182
     * also consist of two columns, the return value can be an array also.
183
     *
184
     * @return mixed The primary key value(s)
185
     */
186 7
    protected function getPrimaryKeyValue()
187
    {
188 7
        return $this->getValue($this->getPrimaryKeyColumnName());
0 ignored issues
show
Bug introduced by
It seems like getValue() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
189
    }
190
191
    /**
192
     * Process the observer's business logic.
193
     *
194
     * @return void
195
     */
196 7
    protected function process()
197
    {
198
199
        // initialize the store view code
200 7
        $this->prepareStoreViewCode();
0 ignored issues
show
Bug introduced by
It seems like prepareStoreViewCode() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
201
202
        // load the store ID, use the admin store if NO store view code has been set
203 7
        $storeId = $this->getRowStoreId(StoreViewCodes::ADMIN);
0 ignored issues
show
Bug introduced by
It seems like getRowStoreId() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
204
205
        // load the entity's existing attributes
206 7
        $this->getAttributesByPrimaryKeyAndStoreId($this->getPrimaryKey(), $storeId);
207
208
        // load the store view - if no store view has been set, we assume the admin
209
        // store view, which will contain the default (fallback) attribute values
210 7
        $storeViewCode = $this->getSubject()->getStoreViewCode(StoreViewCodes::ADMIN);
0 ignored issues
show
Bug introduced by
It seems like getSubject() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
211
212
        // query whether or not the row has already been processed
213 7
        if ($this->storeViewHasBeenProcessed($pk = $this->getPrimaryKeyValue(), $storeViewCode)) {
214
            // log a message
215
            $this->getSystemLogger()->warning(
216
                $this->appendExceptionSuffix(
0 ignored issues
show
Bug introduced by
It seems like appendExceptionSuffix() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
217
                    sprintf(
218
                        'Attributes for "%s" "%s" + store view code "%s" has already been processed',
219
                        $this->getPrimaryKeyColumnName(),
220
                        $pk,
221
                        $storeViewCode
222
                    )
223
                )
224
            );
225
226
            // return immediately
227
            return;
228
        }
229
230
        // load the attributes by the found attribute set and the backend types
231 7
        $attributes = $this->getAttributes();
232 7
        $backendTypes = $this->getBackendTypes();
233
234
        // load the header keys
235 7
        $headers = array_flip($this->getHeaders());
0 ignored issues
show
Bug introduced by
It seems like getHeaders() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
236
237
        // remove all the empty values from the row
238 7
        $row = $this->clearRow();
239
240
        // iterate over the attributes and append them to the row
241 7
        foreach ($row as $key => $attributeValue) {
242
            // query whether or not attribute with the found code exists
243 6
            if (!isset($attributes[$attributeCode = $headers[$key]])) {
244
                // log a message in debug mode
245 1
                if ($this->isDebugMode()) {
0 ignored issues
show
Bug introduced by
It seems like isDebugMode() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
246 1
                    $this->getSystemLogger()->debug(
247 1
                        $this->appendExceptionSuffix(
0 ignored issues
show
Bug introduced by
It seems like appendExceptionSuffix() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
248 1
                            sprintf(
249 1
                                'Can\'t find attribute with attribute code "%s"',
250 1
                                $attributeCode
251
                            )
252
                        )
253
                    );
254
                }
255
256
                // stop processing
257 1
                continue;
258
            } else {
259
                // log a message in debug mode
260 5
                if ($this->isDebugMode()) {
0 ignored issues
show
Bug introduced by
It seems like isDebugMode() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
261
                // log a message in debug mode
262 2
                    $this->getSystemLogger()->debug(
263 2
                        $this->appendExceptionSuffix(
0 ignored issues
show
Bug introduced by
It seems like appendExceptionSuffix() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
264 2
                            sprintf(
265 2
                                'Found attribute with attribute code "%s"',
266 2
                                $attributeCode
267
                            )
268
                        )
269
                    );
270
                }
271
            }
272
273
            // if yes, load the attribute by its code
274 5
            $this->attribute = $attributes[$attributeCode];
275
276
            // load the backend type => to find the apropriate entity
277 5
            $backendType = $this->attribute[MemberNames::BACKEND_TYPE];
278 5
            if ($backendType === null) {
279
                // log a message in debug mode
280 1
                $this->getSystemLogger()->warning(
281 1
                    $this->appendExceptionSuffix(
0 ignored issues
show
Bug introduced by
It seems like appendExceptionSuffix() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
282 1
                        sprintf(
283 1
                            'Found EMTPY backend type for attribute "%s"',
284 1
                            $attributeCode
285
                        )
286
                    )
287
                );
288
                // stop processing
289 1
                continue;
290
            }
291
292
            // do nothing on static backend type
293 4
            if ($backendType === BackendTypeKeys::BACKEND_TYPE_STATIC) {
294 1
                continue;
295
            }
296
297
            // query whether or not we've found a supported backend type
298 3
            if (isset($backendTypes[$backendType])) {
299
                // initialize attribute ID/code and backend type
300 2
                $this->backendType = $backendType;
301 2
                $this->attributeCode = $attributeCode;
302 2
                $this->attributeId = $this->attribute[MemberNames::ATTRIBUTE_ID];
303
304
                // set the attribute value as well as the original attribute value
305 2
                $this->attributeValue = $attributeValue;
306
307
                // initialize the persist method for the found backend type
308 2
                list ($persistMethod, , $deleteMethod) = $backendTypes[$backendType];
309
310
                // prepare the attribute vale and query whether or not it has to be persisted
311 2
                if ($this->hasChanges($value = $this->initializeAttribute($this->prepareAttributes()))) {
0 ignored issues
show
Bug introduced by
It seems like $this->prepareAttributes() targeting TechDivision\Import\Obse...it::prepareAttributes() can also be of type null; however, TechDivision\Import\Obse...::initializeAttribute() does only seem to accept array, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
312
                    // query whether or not the entity's value has to be persisted or deleted. if the value is
313
                    // an empty string and the status is UPDATE, then the value exists and has to be deleted
314
                    // We need to user $attributeValue instead of $value[MemberNames::VALUE] in cases where
315
                    // value was casted by attribute type. E.g. special_price = 0 if value is empty string in CSV
316 2
                    switch ($this->operation) {
317
                        // create/update the attribute
318 2
                        case OperationNames::CREATE:
319 1
                        case OperationNames::UPDATE:
320 1
                            $this->$persistMethod($value);
321 1
                            break;
322
                        // delete the attribute
323 1
                        case OperationNames::DELETE:
324
                            $this->$deleteMethod(array(MemberNames::VALUE_ID => $value[MemberNames::VALUE_ID]));
325
                            break;
326
                        // skip the attribute
327 1
                        case OperationNames::SKIP:
328 1
                            $this->getSubject()->getSystemLogger()->debug(sprintf('Skipped processing attribute "%s"', $attributeCode));
0 ignored issues
show
Bug introduced by
It seems like getSubject() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
329 1
                            break;
330
                        // should never happen
331
                        default:
332 2
                            $this->getSubject()->getSystemLogger()->debug(sprintf('Found invalid entity status "%s" for attribute "%s"', $value[MemberNames::VALUE] ?? 'NULL', $attributeCode));
0 ignored issues
show
Bug introduced by
It seems like getSubject() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
333
                    }
334
                } else {
335
                    $this->getSubject()->getSystemLogger()->debug(sprintf('Skip to persist value for attribute "%s"', $attributeCode));
0 ignored issues
show
Bug introduced by
It seems like getSubject() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
336
                }
337
338
                // continue with the next value
339 2
                continue;
340
            }
341
342
            // log the debug message
343 1
            $this->getSystemLogger()->debug(
344 1
                $this->getSubject()->appendExceptionSuffix(
0 ignored issues
show
Bug introduced by
It seems like getSubject() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
345 1
                    sprintf(
346 1
                        'Found invalid backend type %s for attribute "%s"',
347 1
                        $backendType,
348 1
                        $attributeCode
349
                    )
350
                )
351
            );
352
        }
353 7
    }
354
355
    /**
356
     * Prepare the attributes of the entity that has to be persisted.
357
     *
358
     * @return array|null The prepared attributes
359
     */
360 2
    protected function prepareAttributes()
361
    {
362
363
        // load the ID of the product that has been created recently
364 2
        $lastEntityId = $this->getPrimaryKey();
365
366
        // load the store ID, use the admin store if NO store view code has been set
367 2
        $storeId = $this->getRowStoreId(StoreViewCodes::ADMIN);
0 ignored issues
show
Bug introduced by
It seems like getRowStoreId() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
368
369
        // prepare the attribute values
370 2
        return $this->initializeEntity(
371 2
            $this->loadRawEntity(
372
                array(
373 2
                   $this->getPrimaryKeyMemberName() => $lastEntityId,
374 2
                    MemberNames::ATTRIBUTE_ID       => $this->attributeId,
375 2
                    MemberNames::STORE_ID           => $storeId
376
                )
377
            )
378
        );
379
    }
380
381
    /**
382
     * Initialize the category product with the passed attributes and returns an instance.
383
     *
384
     * @param array $attr The category product attributes
385
     *
386
     * @return array The initialized category product
387
     */
388 2
    protected function initializeAttribute(array $attr)
389
    {
390 2
        return $attr;
391
    }
392
393
    /**
394
     * Load's and return's a raw customer entity without primary key but the mandatory members only and nulled values.
395
     *
396
     * @param array $data An array with data that will be used to initialize the raw entity with
397
     *
398
     * @return array The initialized entity
399
     */
400 2
    protected function loadRawEntity(array $data = array())
401
    {
402
403
        // laod the callbacks for the actual attribute code
404 2
        $callbacks = $this->getCallbacksByType($this->attributeCode);
405
406
        // invoke the pre-cast callbacks
407 2
        foreach ($callbacks as $callback) {
408 1
            $this->attributeValue = $callback->handle($this);
409
        }
410
411
        // load the default value
412 2
        $defaultValue = isset($this->attribute[MemberNames::DEFAULT_VALUE]) ? $this->attribute[MemberNames::DEFAULT_VALUE] : '';
413
414
        // load the value that has to be casted
415 2
        $value = $this->attributeValue === '' || $this->attributeValue === null ? $defaultValue : $this->attributeValue;
416
417
        // cast the value
418 2
        $castedValue = $this->castValueByBackendType($this->backendType, $value);
0 ignored issues
show
Bug introduced by
It seems like castValueByBackendType() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
419
420
        // merge the casted value into the passed data and return it
421 2
        return array_merge(array(MemberNames::VALUE => $castedValue), $data);
422
    }
423
424
    /**
425
     * Initialize's and return's a new entity with the status 'create'.
426
     *
427
     * @param array $attr The attributes to merge into the new entity
428
     *
429
     * @return array The initialized entity
430
     */
431 2
    protected function initializeEntity(array $attr = array())
432
    {
433
434
        // initialize the operation name
435 2
        $this->operation = OperationNames::CREATE;
436
437
        // query whether or not the colunm IS empty and it is NOT in the
438
        // array with the default column values, because in that case we
439
        // want to skip processing the attribute
440 2
        if (array_key_exists($this->attributeCode, $this->defaultColumnValues) === false && ($this->attributeValue === '' || $this->attributeValue == null)) {
441 1
            $this->operation = OperationNames::SKIP;
442
        }
443
444
        // initialize the entity with the passed data
445 2
        return parent::initializeEntity($attr);
446
    }
447
448
    /**
449
     * Merge's and return's the entity with the passed attributes and set's the
450
     * passed status.
451
     *
452
     * @param array       $entity        The entity to merge the attributes into
453
     * @param array       $attr          The attributes to be merged
454
     * @param string|null $changeSetName The change set name to use
455
     *
456
     * @return array The merged entity
457
     */
458
    protected function mergeEntity(array $entity, array $attr, $changeSetName = null)
459
    {
460
461
        // we want to update the attribute, if we're here
462
        $this->operation = OperationNames::UPDATE;
463
464
        // query whether or not the column is EMPTY
465
        if ($this->attributeValue === '' || $this->attributeValue === null) {
466
            // if the value is empty AND it is IN the array with default column values
467
            // BUT it is NOT in the array with columns we want to clean-up the default
468
            // column value has to be removed, because we do NOT want to override the
469
            // value existing in the database
470
            if (array_key_exists($this->attributeCode, $this->defaultColumnValues) &&
471
                array_key_exists($this->attributeCode, $this->cleanUpEmptyColumnKeys) === false
472
            ) {
473
                // remove the value from the array with the column values, because
474
                // this is the default value from the database and it should NOT
475
                // override the value from the entity in that case
476
                unset($attr[MemberNames::VALUE]);
477
            } else {
478
                // otherwise keep the value and DELETE the whole attribute from
479
                // the database
480
                $this->operation = OperationNames::DELETE;
481
            }
482
        }
483
484
        // merge and return the data
485
        return parent::mergeEntity($entity, $attr, $changeSetName);
486
    }
487
488
    /**
489
     * Return's the array with callbacks for the passed type.
490
     *
491
     * @param string $type The type of the callbacks to return
492
     *
493
     * @return array The callbacks
494
     */
495 2
    protected function getCallbacksByType($type)
496
    {
497 2
        return $this->getSubject()->getCallbacksByType($type);
0 ignored issues
show
Bug introduced by
It seems like getSubject() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
498
    }
499
500
    /**
501
     * Return's mapping for the supported backend types (for the product entity) => persist methods.
502
     *
503
     * @return array The mapping for the supported backend types
504
     */
505 7
    protected function getBackendTypes()
506
    {
507 7
        return $this->getSubject()->getBackendTypes();
0 ignored issues
show
Bug introduced by
It seems like getSubject() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
508
    }
509
510
    /**
511
     * Return's the attributes for the attribute set of the product that has to be created.
512
     *
513
     * @return array The attributes
514
     * @throws \Exception
515
     */
516 7
    protected function getAttributes()
517
    {
518 7
        return $this->getSubject()->getAttributes();
0 ignored issues
show
Bug introduced by
It seems like getSubject() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
519
    }
520
521
    /**
522
     * Intializes the existing attributes for the entity with the passed primary key.
523
     *
524
     * @param string  $pk      The primary key of the entity to load the attributes for
525
     * @param integer $storeId The ID of the store view to load the attributes for
526
     *
527
     * @return array The entity attributes
528
     */
529
    abstract protected function getAttributesByPrimaryKeyAndStoreId($pk, $storeId);
530
531
    /**
532
     * Return's the logger with the passed name, by default the system logger.
533
     *
534
     * @param string $name The name of the requested system logger
535
     *
536
     * @return \Psr\Log\LoggerInterface The logger instance
537
     * @throws \Exception Is thrown, if the requested logger is NOT available
538
     */
539
    abstract protected function getSystemLogger($name = LoggerKeys::SYSTEM);
540
541
    /**
542
     * Return's the PK to create the product => attribute relation.
543
     *
544
     * @return integer The PK to create the relation with
545
     */
546
    abstract protected function getPrimaryKey();
547
548
    /**
549
     * Return's the PK column name to create the product => attribute relation.
550
     *
551
     * @return string The PK column name
552
     */
553
    abstract protected function getPrimaryKeyMemberName();
554
555
    /**
556
     * Return's the column name that contains the primary key.
557
     *
558
     * @return string the column name that contains the primary key
559
     */
560
    abstract protected function getPrimaryKeyColumnName();
561
562
    /**
563
     * Queries whether or not the passed PK and store view code has already been processed.
564
     *
565
     * @param string $pk            The PK to check been processed
566
     * @param string $storeViewCode The store view code to check been processed
567
     *
568
     * @return boolean TRUE if the PK and store view code has been processed, else FALSE
569
     */
570
    abstract protected function storeViewHasBeenProcessed($pk, $storeViewCode);
571
572
    /**
573
     * Persist's the passed varchar attribute.
574
     *
575
     * @param array $attribute The attribute to persist
576
     *
577
     * @return void
578
     */
579
    abstract protected function persistVarcharAttribute($attribute);
580
581
    /**
582
     * Persist's the passed integer attribute.
583
     *
584
     * @param array $attribute The attribute to persist
585
     *
586
     * @return void
587
     */
588
    abstract protected function persistIntAttribute($attribute);
589
590
    /**
591
     * Persist's the passed decimal attribute.
592
     *
593
     * @param array $attribute The attribute to persist
594
     *
595
     * @return void
596
     */
597
    abstract protected function persistDecimalAttribute($attribute);
598
599
    /**
600
     * Persist's the passed datetime attribute.
601
     *
602
     * @param array $attribute The attribute to persist
603
     *
604
     * @return void
605
     */
606
    abstract protected function persistDatetimeAttribute($attribute);
607
608
    /**
609
     * Persist's the passed text attribute.
610
     *
611
     * @param array $attribute The attribute to persist
612
     *
613
     * @return void
614
     */
615
    abstract protected function persistTextAttribute($attribute);
616
617
    /**
618
     * Delete's the datetime attribute with the passed value ID.
619
     *
620
     * @param array       $row  The attributes of the entity to delete
621
     * @param string|null $name The name of the prepared statement that has to be executed
622
     *
623
     * @return void
624
     */
625
    abstract protected function deleteDatetimeAttribute(array $row, $name = null);
626
627
    /**
628
     * Delete's the decimal attribute with the passed value ID.
629
     *
630
     * @param array       $row  The attributes of the entity to delete
631
     * @param string|null $name The name of the prepared statement that has to be executed
632
     *
633
     * @return void
634
     */
635
    abstract protected function deleteDecimalAttribute(array $row, $name = null);
636
637
    /**
638
     * Delete's the integer attribute with the passed value ID.
639
     *
640
     * @param array       $row  The attributes of the entity to delete
641
     * @param string|null $name The name of the prepared statement that has to be executed
642
     *
643
     * @return void
644
     */
645
    abstract protected function deleteIntAttribute(array $row, $name = null);
646
647
    /**
648
     * Delete's the text attribute with the passed value ID.
649
     *
650
     * @param array       $row  The attributes of the entity to delete
651
     * @param string|null $name The name of the prepared statement that has to be executed
652
     *
653
     * @return void
654
     */
655
    abstract protected function deleteTextAttribute(array $row, $name = null);
656
657
    /**
658
     * Delete's the varchar attribute with the passed value ID.
659
     *
660
     * @param array       $row  The attributes of the entity to delete
661
     * @param string|null $name The name of the prepared statement that has to be executed
662
     *
663
     * @return void
664
     */
665
    abstract protected function deleteVarcharAttribute(array $row, $name = null);
666
667
    /**
668
     * Query whether or not the entity has to be processed.
669
     *
670
     * @param array $entity The entity to query for
671
     *
672
     * @return boolean TRUE if the entity has to be processed, else FALSE
673
     */
674
    abstract protected function hasChanges(array $entity);
675
676
    /**
677
     * Query whether or not a value for the column with the passed name exists.
678
     *
679
     * @param string $name The column name to query for a valid value
680
     *
681
     * @return boolean TRUE if the value is set, else FALSE
682
     */
683
    abstract protected function hasValue($name);
684
}
685