Completed
Pull Request — master (#75)
by Tim
03:14
created

AbstractMultiselectCallback::getRowStoreId()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
ccs 0
cts 4
cp 0
rs 10
cc 1
eloc 2
nc 1
nop 1
crap 2
1
<?php
2
3
/**
4
 * TechDivision\Import\Callbacks\AbstractMultiselectCallback
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
18
 * @link      http://www.techdivision.com
19
 */
20
21
namespace TechDivision\Import\Callbacks;
22
23
use TechDivision\Import\Utils\MemberNames;
24
use TechDivision\Import\Utils\RegistryKeys;
25
use TechDivision\Import\Utils\StoreViewCodes;
26
27
/**
28
 * A callback implementation that converts the passed multiselect value.
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
34
 * @link      http://www.techdivision.com
35
 */
36
abstract class AbstractMultiselectCallback extends AbstractCallback
37
{
38
39
    /**
40
     * Will be invoked by a observer it has been registered for.
41
     *
42
     * @param string $attributeCode  The code of the attribute the passed value is for
43
     * @param mixed  $attributeValue The value to handle
44
     *
45
     * @return mixed|null The modified value
46
     * @see \TechDivision\Import\Callbacks\CallbackInterface::handle()
47
     */
48
    public function handle($attributeCode, $attributeValue)
49
    {
50
51
        // explode the multiselect values
52
        $vals = explode('|', $attributeValue);
53
54
        // initialize the array for the mapped values
55
        $mappedValues = array();
56
57
        // convert the option values into option value ID's
58
        foreach ($vals as $val) {
59
            // load the ID of the actual store
60
            $storeId = $this->getStoreId(StoreViewCodes::ADMIN);
61
62
            // try to load the attribute option value and add the option ID
63
            if ($eavAttributeOptionValue = $this->loadEavAttributeOptionValueByAttributeCodeAndStoreIdAndValue($attributeCode, $storeId, $val)) {
64
                $mappedValues[] = $eavAttributeOptionValue[MemberNames::OPTION_ID];
65
                continue;
66
            }
67
68
            // query whether or not we're in debug mode
69 View Code Duplication
            if ($this->isDebugMode()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
70
                // log a warning and continue with the next value
71
                $this->getSystemLogger()->warning(
72
                    $this->appendExceptionSuffix(
73
                        sprintf(
74
                            'Can\'t find multiselect option value "%s" for attribute %s',
75
                            $val,
76
                            $attributeCode
77
                        )
78
                    )
79
                );
80
81
                // add the missing option value to the registry
82
                $this->mergeAttributesRecursive(
83
                    array(
84
                        RegistryKeys::MISSING_OPTION_VALUES => array(
85
                            $attributeCode => array(
86
                                $val => array(
87
                                    $this->raiseCounter($val),
88
                                    array($this->getUniqueIdentifier() => true)
89
                                )
90
                            )
91
                        )
92
                    )
93
                );
94
95
                // continue with the next option value
96
                continue;
97
            }
98
99
            // throw an exception if the attribute is not available
100
            throw new \Exception(
101
                $this->appendExceptionSuffix(
102
                    sprintf(
103
                        'Can\'t find multiselect option value "%s" for attribute %s',
104
                        $val,
105
                        $attributeCode
106
                    )
107
                )
108
            );
109
        }
110
111
        // return NULL, if NO value can be mapped to an option
112
        if (sizeof($mappedValues) === 0) {
113
            return;
114
        }
115
116
        // re-concatenate and return the values
117
        return implode(',', $mappedValues);
118
    }
119
120
    /**
121
     * Return's the store ID of the actual row, or of the default store
122
     * if no store view code is set in the CSV file.
123
     *
124
     * @param string|null $default The default store view code to use, if no store view code is set in the CSV file
125
     *
126
     * @return integer The ID of the actual store
127
     * @throws \Exception Is thrown, if the store with the actual code is not available
128
     */
129
    protected function getRowStoreId($default = null)
130
    {
131
        return $this->getSubject()->getRowStoreId($default);
132
    }
133
134
    /**
135
     * Load's and return's the EAV attribute option value with the passed code, store ID and value.
136
     *
137
     * @param string  $attributeCode The code of the EAV attribute option to load
138
     * @param integer $storeId       The store ID of the attribute option to load
139
     * @param string  $value         The value of the attribute option to load
140
     *
141
     * @return array The EAV attribute option value
142
     */
143
    protected function loadEavAttributeOptionValueByAttributeCodeAndStoreIdAndValue($attributeCode, $storeId, $value)
144
    {
145
        return $this->getSubject()->loadEavAttributeOptionValueByAttributeCodeAndStoreIdAndValue($attributeCode, $storeId, $value);
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 loadEavAttributeOptionVa...odeAndStoreIdAndValue() does only exist in the following implementations of said interface: 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...
146
    }
147
}
148