Completed
Push — 22.x ( d7197a...bcdba3 )
by Tim
01:45
created

ProductBundleObserver::process()   C

Complexity

Conditions 13
Paths 78

Size

Total Lines 101

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 182

Importance

Changes 0
Metric Value
dl 0
loc 101
ccs 0
cts 63
cp 0
rs 5.2933
c 0
b 0
f 0
cc 13
nc 78
nop 0
crap 182

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * TechDivision\Import\Product\Bundle\Observers\ProductBundleObserver
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-bundle
18
 * @link      http://www.techdivision.com
19
 */
20
21
namespace TechDivision\Import\Product\Bundle\Observers;
22
23
use TechDivision\Import\Utils\ProductTypes;
24
use TechDivision\Import\Product\Bundle\Utils\ColumnKeys;
25
use TechDivision\Import\Product\Observers\AbstractProductImportObserver;
26
27
/**
28
 * A SLSB that handles the process to import product bunches.
29
 *
30
 * @author    Tim Wagner <[email protected]>
31
 * @copyright 2016 TechDivision GmbH <[email protected]>
32
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
33
 * @link      https://github.com/techdivision/import-product-bundle
34
 * @link      http://www.techdivision.com
35
 */
36
class ProductBundleObserver extends AbstractProductImportObserver
37
{
38
39
    /**
40
     * The artefact type.
41
     *
42
     * @var string
43
     */
44
    const ARTEFACT_TYPE = 'bundles';
45
46
    /**
47
     *
48
     * @var array
49
     */
50
    protected $columns = array(
51
        'name'               => array(ColumnKeys::BUNDLE_VALUE_NAME, ColumnKeys::BUNDLE_VALUES),
52
        'type'               => array(ColumnKeys::BUNDLE_VALUE_TYPE, ColumnKeys::BUNDLE_VALUES),
53
        'required'           => array(ColumnKeys::BUNDLE_VALUE_REQUIRED, ColumnKeys::BUNDLE_VALUES),
54
        'sku'                => array(ColumnKeys::BUNDLE_VALUE_SKU, ColumnKeys::BUNDLE_VALUES),
55
        'price'              => array(ColumnKeys::BUNDLE_VALUE_PRICE, ColumnKeys::BUNDLE_VALUES),
56
        'default'            => array(ColumnKeys::BUNDLE_VALUE_DEFAULT, ColumnKeys::BUNDLE_VALUES),
57
        'default_qty'        => array(ColumnKeys::BUNDLE_VALUE_DEFAULT_QTY, ColumnKeys::BUNDLE_VALUES),
58
        'price_type'         => array(ColumnKeys::BUNDLE_VALUE_PRICE_TYPE, ColumnKeys::BUNDLE_VALUES),
59
        'can_change_qty'     => array(ColumnKeys::BUNDLE_VALUE_CAN_CHANGE_QTY, ColumnKeys::BUNDLE_VALUES),
60
        'position'           => array(ColumnKeys::BUNDLE_VALUE_POSITION, ColumnKeys::BUNDLE_VALUES),
61
        'selection_position' => array(ColumnKeys::BUNDLE_VALUE_SELECTION_POSITION, ColumnKeys::BUNDLE_VALUES)
62
    );
63
64
    /**
65
     * Process the observer's business logic.
66
     *
67
     * @return array The processed row
68
     */
69
    protected function process()
70
    {
71
72
        // query whether or not we've found a bundle product
73
        if ($this->getValue(ColumnKeys::PRODUCT_TYPE) !== ProductTypes::BUNDLE) {
74
            return;
75
        }
76
77
        // query whether or not, we've a bundle
78
        if ($bundleValues = $this->getValue(ColumnKeys::BUNDLE_VALUES)) {
79
            // initialize the array for the product bundles
80
            $artefacts = array();
81
82
            // load the parent SKU from the row
83
            $parentSku = $this->getValue(ColumnKeys::SKU);
84
85
            // explode the positions of the bundle values
86
            $bundleValuesPosition = $this->getValue(ColumnKeys::BUNDLE_VALUES_POSITION, array(), function ($value) {
87
                return $this->explode($value, $this->getMultipleFieldDelimiter());
88
            });
89
90
            // initialize the bundle with the found values
91
            foreach ($this->explode($bundleValues, $this->getMultipleValueDelimiter()) as $bundleValue) {
92
                // initialize the array with the columns
93
                $columns = array(
94
                    ColumnKeys::BUNDLE_PARENT_SKU    => $parentSku,
95
                    ColumnKeys::STORE_VIEW_CODE      => $this->getValue(ColumnKeys::STORE_VIEW_CODE),
96
                    ColumnKeys::BUNDLE_SKU_TYPE      => $this->getValue(ColumnKeys::BUNDLE_SKU_TYPE),
97
                    ColumnKeys::BUNDLE_PRICE_TYPE    => $this->getValue(ColumnKeys::BUNDLE_PRICE_TYPE),
98
                    ColumnKeys::BUNDLE_PRICE_VIEW    => $this->getValue(ColumnKeys::BUNDLE_PRICE_VIEW),
99
                    ColumnKeys::BUNDLE_WEIGHT_TYPE   => $this->getValue(ColumnKeys::BUNDLE_WEIGHT_TYPE),
100
                    ColumnKeys::BUNDLE_SHIPMENT_TYPE => $this->getValue(ColumnKeys::BUNDLE_SHIPMENT_TYPE),
101
                );
102
103
                // initialize the array with the original column names
104
                $originalColumNames = array(
105
                    ColumnKeys::BUNDLE_PARENT_SKU    => ColumnKeys::SKU,
106
                    ColumnKeys::STORE_VIEW_CODE      => ColumnKeys::STORE_VIEW_CODE,
107
                    ColumnKeys::BUNDLE_SKU_TYPE      => ColumnKeys::BUNDLE_SKU_TYPE,
108
                    ColumnKeys::BUNDLE_PRICE_TYPE    => ColumnKeys::BUNDLE_PRICE_TYPE,
109
                    ColumnKeys::BUNDLE_PRICE_VIEW    => ColumnKeys::BUNDLE_PRICE_VIEW,
110
                    ColumnKeys::BUNDLE_WEIGHT_TYPE   => ColumnKeys::BUNDLE_WEIGHT_TYPE,
111
                    ColumnKeys::BUNDLE_SHIPMENT_TYPE => ColumnKeys::BUNDLE_SHIPMENT_TYPE
112
                );
113
114
                // initialize the columns
115
                foreach ($this->columns as $column) {
116
                    // initialize column/original column name
117
                    $columnName = $originalColumName = null;
118
                    // explode the column and originl column name
119
                    if (sizeof($column) > 1) {
120
                        list ($columnName, $originalColumName) = $column;
121
                    }
122
                    // initialize the column
123
                    $columns[$columnName] = null;
124
                    // initialize the original column name
125
                    $originalColumNames[$columnName] = $originalColumName;
126
                }
127
128
                // explode the values
129
                $explodedValues = $this->explode($bundleValue, $this->getMultipleFieldDelimiter());
130
131
                // iterate over the given values and append them to the columns
132
                foreach ($explodedValues as $values) {
133
                    // initialize key/value
134
                    $key = $value = null;
135
                    // extract the column key => value pair
136
                    if (strpos($values, '=')) {
137
                        list ($key, $value) = $this->explode($values, '=');
138
                    }
139
                    // query whether or not we've to append the column
140
                    if (isset($this->columns[$key])) {
141
                        // explode the column name
142
                        list ($columnName, ) = $this->columns[$key];
143
                        // add the value
144
                        $columns[$columnName] = $value;
145
                    }
146
                }
147
148
                // iterate over the positions
149
                foreach ($bundleValuesPosition as $position) {
150
                    // initialize name/value
151
                    $name = $value = null;
152
                    // extract the column name => value pair
153
                    if (strpos($position, '=')) {
154
                        list ($name, $value) = $this->explode($position, '=');
155
                    }
156
                    // query whether or not we've to append the column
157
                    if (isset($columns[ColumnKeys::BUNDLE_VALUE_NAME]) && $columns[ColumnKeys::BUNDLE_VALUE_NAME] === $name) {
158
                        $columns[ColumnKeys::BUNDLE_VALUE_POSITION] = $value;
159
                    }
160
                }
161
162
                //  initialize the product bundle itself and append it to the artefacts
163
                $artefacts[] = $this->newArtefact($columns, $originalColumNames);
164
            }
165
166
            // append the bundles to the subject
167
            $this->addArtefacts($artefacts);
168
        }
169
    }
170
171
    /**
172
     * Create's and return's a new empty artefact entity.
173
     *
174
     * @param array $columns             The array with the column data
175
     * @param array $originalColumnNames The array with a mapping from the old to the new column names
176
     *
177
     * @return array The new artefact entity
178
     */
179
    protected function newArtefact(array $columns, array $originalColumnNames)
180
    {
181
        return $this->getSubject()->newArtefact($columns, $originalColumnNames);
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 newArtefact() does only exist in the following implementations of said interface: TechDivision\Import\Plugins\ExportableSubjectImpl, 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...
182
    }
183
184
    /**
185
     * Add the passed product type artefacts to the product with the
186
     * last entity ID.
187
     *
188
     * @param array $artefacts The product type artefacts
189
     *
190
     * @return void
191
     * @uses \TechDivision\Import\Product\Bundle\Subjects\BunchSubject::getLastEntityId()
192
     */
193
    protected function addArtefacts(array $artefacts)
194
    {
195
        $this->getSubject()->addArtefacts(ProductBundleObserver::ARTEFACT_TYPE, $artefacts);
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 addArtefacts() does only exist in the following implementations of said interface: TechDivision\Import\Plugins\ExportableSubjectImpl, 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...
196
    }
197
}
198