Completed
Push — develop ( f3a4fe...df0b60 )
by Tom
03:55
created

DeleteCommand::_deletePath()   B

Complexity

Conditions 5
Paths 3

Size

Total Lines 36
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 36
rs 8.439
c 0
b 0
f 0
cc 5
eloc 21
nc 3
nop 4
1
<?php
2
3
namespace N98\Magento\Command\Config\Store;
4
5
use Magento\Config\Model\ResourceModel\Config\Data\Collection;
6
use Magento\Framework\App\Config\Storage\WriterInterface;
7
use N98\Magento\Command\Config\AbstractConfigCommand;
8
use Symfony\Component\Console\Input\InputArgument;
9
use Symfony\Component\Console\Input\InputInterface;
10
use Symfony\Component\Console\Input\InputOption;
11
use Symfony\Component\Console\Output\OutputInterface;
12
13
class DeleteCommand extends AbstractConfigCommand
14
{
15
    /**
16
     * @var Collection
17
     */
18
    private $collection;
19
20
    /**
21
     * @var array
22
     */
23
    protected $_scopes = array(
24
        'default',
25
        'websites',
26
        'stores',
27
    );
28
29
    protected function configure()
30
    {
31
        $this
32
            ->setName('config:store:delete')
33
            ->setDescription('Deletes a store config item')
34
            ->addArgument('path', InputArgument::REQUIRED, 'The config path')
35
            ->addOption(
36
                'scope',
37
                null,
38
                InputOption::VALUE_OPTIONAL,
39
                'The config value\'s scope (default, websites, stores)',
40
                'default'
41
            )
42
            ->addOption('scope-id', null, InputOption::VALUE_OPTIONAL, 'The config value\'s scope ID')
43
            ->addOption('all', null, InputOption::VALUE_NONE, 'Delete all entries by path')
44
        ;
45
46
        $help = <<<HELP
47
To delete all entries if a path you can set the option --all.
48
HELP;
49
        $this->setHelp($help);
50
    }
51
52
    /**
53
     * @param Collection $collection
54
     */
55
    public function inject(Collection $collection)
56
    {
57
        $this->collection = $collection;
58
    }
59
60
    /**
61
     * @param \Symfony\Component\Console\Input\InputInterface $input
62
     * @param \Symfony\Component\Console\Output\OutputInterface $output
63
     * @return int|void
64
     */
65
    protected function execute(InputInterface $input, OutputInterface $output)
66
    {
67
        $this->detectMagento($output, true);
68
        if (!$this->initMagento()) {
69
            return;
70
        }
71
72
        $this->_validateScopeParam($input->getOption('scope'));
73
        $scopeId = $this->_convertScopeIdParam($input->getOption('scope'), $input->getOption('scope-id'));
74
75
        $deleted = array();
76
77
        $paths = $this->resolvePaths($input->getArgument('path'), $scopeId);
78
79
        $configWriter = $this->getConfigWriter();
80
        foreach ($paths as $path) {
81
            $deleted = array_merge($deleted, $this->_deletePath($input, $configWriter, $path, $scopeId));
82
        }
83
84
        if (count($deleted) > 0) {
85
            $this->getHelper('table')
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Symfony\Component\Console\Helper\HelperInterface as the method setHeaders() does only exist in the following implementations of said interface: N98\Util\Console\Helper\TableHelper, Symfony\Component\Console\Helper\TableHelper.

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...
86
                ->setHeaders(array('deleted path', 'scope', 'id'))
87
                ->setRows($deleted)
88
                ->render($output);
89
        }
90
    }
91
92
    /**
93
     *
94
     */
95
    private function resolvePaths($path, $scopeId)
96
    {
97
        if (false === strstr($path, '*')) {
98
            return (array) $path;
99
        }
100
101
        $paths = array();
102
103
        $collection = clone $this->collection;
104
105
        $searchPath = str_replace('*', '%', $path);
106
        $collection->addFieldToFilter('path', array('like' => $searchPath));
107
108
        if ($scopeId) {
109
            $collection->addFieldToFilter('scope_id', $scopeId);
110
        }
111
112
        $collection->addOrder('path', 'ASC');
113
114
        foreach ($collection as $item) {
115
            $paths[] = $item->getPath();
116
        }
117
118
        $paths = array_unique($paths);
119
120
        return $paths;
121
    }
122
123
    /**
124
     * @param InputInterface $input
125
     * @param WriterInterface $configWriter
126
     * @param                $path
127
     * @param                $scopeId
128
     *
129
     * @return array
130
     */
131
    protected function _deletePath(
132
        InputInterface $input,
133
        WriterInterface $configWriter,
134
        $path,
135
        $scopeId
136
    ) {
137
        $deleted = array();
138
        if ($input->getOption('all')) {
139
            $storeManager = $this->getObjectManager()->get('Magento\Store\Model\StoreManager');
140
141
            // Delete default
142
            $this->delete($configWriter, $deleted, $path, 'default', 0);
143
144
            $deleted[] = array(
145
                'path'    => $path,
146
                'scope'   => 'default',
147
                'scopeId' => 0,
148
            );
149
150
            // Delete websites
151
            foreach ($storeManager->getWebsites() as $website) {
152
                $this->delete($configWriter, $deleted, $path, 'websites', $website->getId());
153
            }
154
155
            // Delete stores
156
            foreach ($storeManager->getStores() as $store) {
157
                $this->delete($configWriter, $deleted, $path, 'stores', $store->getId());
158
            }
159
        } else {
160
            foreach ($this->resolveScopeIds($path, $input->getOption('scope'), $scopeId) as $item) {
161
                $this->delete($configWriter, $deleted, $path, $item[1], $item[2]);
162
            }
163
        }
164
165
        return $deleted;
166
    }
167
168
    private function delete(WriterInterface $configWriter, &$deleted, $path, $scope, $scopeId)
169
    {
170
        $configWriter->delete($path, $scope, $scopeId);
171
172
        $deleted[] = array(
173
            'path'    => $path,
174
            'scope'   => $scope,
175
            'scopeId' => $scopeId,
176
        );
177
    }
178
179
    /**
180
     * @param string $path
181
     * @param string $scope
182
     * @param int|null $scopeId
183
     *
184
     * @return array
185
     */
186
    private function resolveScopeIds($path, $scope, $scopeId)
187
    {
188
        $result = array();
189
190
        if ($scopeId !== null) {
191
            $result[] = array($path, $scope, $scopeId);
192
193
            return $result;
194
        }
195
196
        $collection = clone $this->collection;
197
198
        $collection->addFieldToFilter('path', array('eq' => $path));
199
        $collection->addFieldToFilter('scope', array('eq' => $scope));
200
        $collection->addOrder('scope_id', 'ASC');
201
202
        $collection->clear();
203
204
        foreach ($collection as $item) {
205
            $result[] = array($item->getPath(), $item->getScope(), $item->getScopeId());
206
        }
207
208
        return $result;
209
    }
210
}
211