Completed
Push — master ( be024c...a4094f )
by Vincent
16s queued 11s
created

ListMapper::add()   F

Complexity

Conditions 15
Paths 342

Size

Total Lines 72

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 72
rs 3.3083
c 0
b 0
f 0
cc 15
nc 342
nop 3

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
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Sonata Project package.
7
 *
8
 * (c) Thomas Rabaix <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Sonata\AdminBundle\Datagrid;
15
16
use Sonata\AdminBundle\Admin\AdminInterface;
17
use Sonata\AdminBundle\Admin\FieldDescriptionCollection;
18
use Sonata\AdminBundle\Admin\FieldDescriptionInterface;
19
use Sonata\AdminBundle\Builder\ListBuilderInterface;
20
use Sonata\AdminBundle\Mapper\BaseMapper;
21
22
/**
23
 * This class is used to simulate the Form API.
24
 *
25
 * @final since sonata-project/admin-bundle 3.52
26
 *
27
 * @author Thomas Rabaix <[email protected]>
28
 */
29
class ListMapper extends BaseMapper
30
{
31
    /**
32
     * @var FieldDescriptionCollection
33
     */
34
    protected $list;
35
36
    public function __construct(
37
        ListBuilderInterface $listBuilder,
38
        FieldDescriptionCollection $list,
39
        AdminInterface $admin
40
    ) {
41
        parent::__construct($listBuilder, $admin);
42
        $this->list = $list;
43
    }
44
45
    /**
46
     * @param string      $name
47
     * @param string|null $type
48
     *
49
     * @return $this
50
     */
51
    public function addIdentifier($name, $type = null, array $fieldDescriptionOptions = [])
52
    {
53
        $fieldDescriptionOptions['identifier'] = true;
54
55
        if (!isset($fieldDescriptionOptions['route']['name'])) {
56
            $routeName = ($this->admin->hasAccess('edit') && $this->admin->hasRoute('edit')) ? 'edit' : 'show';
57
            $fieldDescriptionOptions['route']['name'] = $routeName;
58
        }
59
60
        if (!isset($fieldDescriptionOptions['route']['parameters'])) {
61
            $fieldDescriptionOptions['route']['parameters'] = [];
62
        }
63
64
        return $this->add($name, $type, $fieldDescriptionOptions);
65
    }
66
67
    /**
68
     * @param FieldDescriptionInterface|string $name
69
     * @param string|null                      $type
70
     *
71
     * @throws \LogicException
72
     *
73
     * @return $this
74
     */
75
    public function add($name, $type = null, array $fieldDescriptionOptions = [])
76
    {
77
        // Default sort on "associated_property"
78
        if (isset($fieldDescriptionOptions['associated_property'])) {
79
            if (!isset($fieldDescriptionOptions['sortable'])) {
80
                $fieldDescriptionOptions['sortable'] = true;
81
            }
82
            if (!isset($fieldDescriptionOptions['sort_parent_association_mappings'])) {
83
                $fieldDescriptionOptions['sort_parent_association_mappings'] = [[
84
                    'fieldName' => $name,
85
                ]];
86
            }
87
            if (!isset($fieldDescriptionOptions['sort_field_mapping'])) {
88
                $fieldDescriptionOptions['sort_field_mapping'] = [
89
                    'fieldName' => $fieldDescriptionOptions['associated_property'],
90
                ];
91
            }
92
        }
93
94
        // Ensure batch and action pseudo-fields are tagged as virtual
95
        if (\in_array($type, ['actions', 'batch', 'select'], true)) {
96
            $fieldDescriptionOptions['virtual_field'] = true;
97
        }
98
99
        if (\array_key_exists('identifier', $fieldDescriptionOptions) && !\is_bool($fieldDescriptionOptions['identifier'])) {
100
            throw new \InvalidArgumentException(sprintf('Value for "identifier" option must be boolean, %s given.', \gettype($fieldDescriptionOptions['identifier'])));
101
        }
102
103
        if ($name instanceof FieldDescriptionInterface) {
104
            $fieldDescription = $name;
105
            $fieldDescription->mergeOptions($fieldDescriptionOptions);
106
        } elseif (\is_string($name)) {
107
            if ($this->admin->hasListFieldDescription($name)) {
108
                throw new \LogicException(sprintf(
109
                    'Duplicate field name "%s" in list mapper. Names should be unique.',
110
                    $name
111
                ));
112
            }
113
114
            $fieldDescription = $this->admin->getModelManager()->getNewFieldDescriptionInstance(
115
                $this->admin->getClass(),
116
                $name,
117
                $fieldDescriptionOptions
118
            );
119
        } else {
120
            throw new \TypeError(
121
                'Unknown field name in list mapper. '
0 ignored issues
show
Unused Code introduced by
The call to TypeError::__construct() has too many arguments starting with 'Unknown field name in l...e interface or string.'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
122
                .'Field name should be either of FieldDescriptionInterface interface or string.'
123
            );
124
        }
125
126
        if (null === $fieldDescription->getLabel()) {
127
            $fieldDescription->setOption(
128
                'label',
129
                $this->admin->getLabelTranslatorStrategy()->getLabel($fieldDescription->getName(), 'list', 'label')
130
            );
131
        }
132
133
        if (isset($fieldDescriptionOptions['header_style'])) {
134
            @trigger_error(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
135
                'The "header_style" option is deprecated, please, use "header_class" option instead.',
136
                E_USER_DEPRECATED
137
            );
138
        }
139
140
        if (!isset($fieldDescriptionOptions['role']) || $this->admin->isGranted($fieldDescriptionOptions['role'])) {
141
            // add the field with the FormBuilder
142
            $this->builder->addField($this->list, $type, $fieldDescription, $this->admin);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Sonata\AdminBundle\Builder\BuilderInterface as the method addField() does only exist in the following implementations of said interface: Sonata\AdminBundle\Tests\App\Builder\ListBuilder, Sonata\AdminBundle\Tests\App\Builder\ShowBuilder.

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
        return $this;
146
    }
147
148
    public function get($name)
149
    {
150
        return $this->list->get($name);
151
    }
152
153
    public function has($key)
154
    {
155
        return $this->list->has($key);
156
    }
157
158
    public function remove($key)
159
    {
160
        $this->admin->removeListFieldDescription($key);
161
        $this->list->remove($key);
162
163
        return $this;
164
    }
165
166
    final public function keys()
167
    {
168
        return array_keys($this->list->getElements());
169
    }
170
171
    public function reorder(array $keys)
172
    {
173
        $this->list->reorder($keys);
174
175
        return $this;
176
    }
177
}
178