Completed
Pull Request — master (#51)
by Tim
09:44
created

getEavAttributeOptionValueByOptionValueAndStoreId()   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 0
Metric Value
dl 0
loc 4
ccs 0
cts 4
cp 0
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 2
crap 2
1
<?php
2
3
/**
4
 * TechDivision\Import\Callbacks\MultiselectCallback
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
use TechDivision\Import\Utils\FrontendInputTypes;
27
28
/**
29
 * A callback implementation that converts the passed multiselect value.
30
 *
31
 * @author    Tim Wagner <[email protected]>
32
 * @copyright 2016 TechDivision GmbH <[email protected]>
33
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
34
 * @link      https://github.com/techdivision/import
35
 * @link      http://www.techdivision.com
36
 */
37
class MultiselectCallback extends AbstractCallback
38
{
39
40
    /**
41
     * Will be invoked by a observer it has been registered for.
42
     *
43
     * @param string $attributeCode  The code of the attribute the passed value is for
44
     * @param mixed  $attributeValue The value to handle
45
     *
46
     * @return mixed|null The modified value
47
     * @see \TechDivision\Import\Callbacks\CallbackInterface::handle()
48
     */
49
    public function handle($attributeCode, $attributeValue)
50
    {
51
52
        // explode the multiselect values
53
        $vals = explode('|', $attributeValue);
54
55
        // initialize the array for the mapped values
56
        $mappedValues = array();
57
58
        // convert the option values into option value ID's
59
        foreach ($vals as $val) {
60
            // load the ID of the actual store
61
            $storeId = $this->getRowStoreId(StoreViewCodes::ADMIN);
62
63
            // try to load the attribute option value and add the option ID
64
            if ($eavAttributeOptionValue = $this->getEavAttributeOptionValueByOptionValueAndStoreId($val, $storeId)) {
65
                $mappedValues[] = $eavAttributeOptionValue[MemberNames::OPTION_ID];
66
                continue;
67
            }
68
69
            // query whether or not we're in debug mode
70 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...
71
                // log a warning and continue with the next value
72
                $this->getSystemLogger()->warning(
73
                    $this->appendExceptionSuffix(
74
                        sprintf(
75
                            'Can\'t find multiselect option value "%s" for attribute %s',
76
                            $val,
77
                            $attributeCode
78
                        )
79
                    )
80
                );
81
82
                // add the missing option value to the registry
83
                $this->mergeAttributesRecursive(
84
                    array(
85
                        RegistryKeys::MISSING_OPTION_VALUES => array(
86
                            $attributeCode => array(
87
                                $val => FrontendInputTypes::MULTISELECT
88
                            )
89
                        )
90
                    )
91
                );
92
93
                // continue with the next option value
94
                continue;
95
            }
96
97
            // throw an exception if the attribute is not available
98
            throw new \Exception(
99
                $this->appendExceptionSuffix(
100
                    sprintf(
101
                        'Can\'t find multiselect option value "%s" for attribute %s',
102
                        $val,
103
                        $attributeCode
104
                    )
105
                )
106
            );
107
        }
108
109
        // return NULL, if NO value can be mapped to an option
110
        if (sizeof($mappedValues) === 0) {
111
            return;
112
        }
113
114
        // re-concatenate and return the values
115
        return implode(',', $mappedValues);
116
    }
117
118
    /**
119
     * Return's the store ID of the actual row, or of the default store
120
     * if no store view code is set in the CSV file.
121
     *
122
     * @param string|null $default The default store view code to use, if no store view code is set in the CSV file
123
     *
124
     * @return integer The ID of the actual store
125
     * @throws \Exception Is thrown, if the store with the actual code is not available
126
     */
127
    protected function getRowStoreId($default = null)
128
    {
129
        return $this->getSubject()->getRowStoreId($default);
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 getRowStoreId() 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...
130
    }
131
132
    /**
133
     * Return's the attribute option value with the passed value and store ID.
134
     *
135
     * @param mixed   $value   The option value
136
     * @param integer $storeId The ID of the store
137
     *
138
     * @return array|boolean The attribute option value instance
139
     */
140
    protected function getEavAttributeOptionValueByOptionValueAndStoreId($value, $storeId)
141
    {
142
        return $this->getSubject()->getEavAttributeOptionValueByOptionValueAndStoreId($value, $storeId);
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 getEavAttributeOptionVal...OptionValueAndStoreId() 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...
143
    }
144
}
145