Completed
Push — master ( 2eb0ca...af375c )
by Marko
14s
created

RetrieveAutocompleteItemsAction::__invoke()   D

Complexity

Conditions 16
Paths 67

Size

Total Lines 132

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 132
rs 4.4532
c 0
b 0
f 0
cc 16
nc 67
nop 1

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\Action;
15
16
use Sonata\AdminBundle\Admin\AdminInterface;
17
use Sonata\AdminBundle\Admin\Pool;
18
use Sonata\AdminBundle\Filter\FilterInterface;
19
use Symfony\Component\Form\Form;
20
use Symfony\Component\HttpFoundation\JsonResponse;
21
use Symfony\Component\HttpFoundation\Request;
22
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
23
24
final class RetrieveAutocompleteItemsAction
25
{
26
    /**
27
     * @var Pool
28
     */
29
    private $pool;
30
31
    public function __construct(Pool $pool)
32
    {
33
        $this->pool = $pool;
34
    }
35
36
    /**
37
     * Retrieve list of items for autocomplete form field.
38
     *
39
     * @throws \RuntimeException
40
     * @throws AccessDeniedException
41
     *
42
     * @return JsonResponse
43
     */
44
    public function __invoke(Request $request)
45
    {
46
        $admin = $this->pool->getInstance($request->get('admin_code'));
47
        $admin->setRequest($request);
48
        $context = $request->get('_context', '');
49
50
        if ('filter' === $context) {
51
            $admin->checkAccess('list');
52
        } elseif (!$admin->hasAccess('create') && !$admin->hasAccess('edit')) {
53
            throw new AccessDeniedException();
54
        }
55
56
        // subject will be empty to avoid unnecessary database requests and keep autocomplete function fast
57
        $admin->setSubject($admin->getNewInstance());
58
59
        if ('filter' === $context) {
60
            // filter
61
            $fieldDescription = $this->retrieveFilterFieldDescription($admin, $request->get('field'));
62
            $filterAutocomplete = $admin->getDatagrid()->getFilter($fieldDescription->getName());
63
64
            $property = $filterAutocomplete->getFieldOption('property');
65
            $callback = $filterAutocomplete->getFieldOption('callback');
66
            $minimumInputLength = $filterAutocomplete->getFieldOption('minimum_input_length', 3);
67
            $itemsPerPage = $filterAutocomplete->getFieldOption('items_per_page', 10);
68
            $reqParamPageNumber = $filterAutocomplete->getFieldOption('req_param_name_page_number', '_page');
69
            $toStringCallback = $filterAutocomplete->getFieldOption('to_string_callback');
70
            $targetAdminAccessAction = $filterAutocomplete->getFieldOption('target_admin_access_action', 'list');
71
        } else {
72
            // create/edit form
73
            $fieldDescription = $this->retrieveFormFieldDescription($admin, $request->get('field'));
74
            $formAutocomplete = $admin->getForm()->get($fieldDescription->getName());
75
76
            $formAutocompleteConfig = $formAutocomplete->getConfig();
77
            if ($formAutocompleteConfig->getAttribute('disabled')) {
78
                throw new AccessDeniedException(
79
                    'Autocomplete list can`t be retrieved because the form element is disabled or read_only.'
80
                );
81
            }
82
83
            $property = $formAutocompleteConfig->getAttribute('property');
84
            $callback = $formAutocompleteConfig->getAttribute('callback');
85
            $minimumInputLength = $formAutocompleteConfig->getAttribute('minimum_input_length');
86
            $itemsPerPage = $formAutocompleteConfig->getAttribute('items_per_page');
87
            $reqParamPageNumber = $formAutocompleteConfig->getAttribute('req_param_name_page_number');
88
            $toStringCallback = $formAutocompleteConfig->getAttribute('to_string_callback');
89
            $targetAdminAccessAction = $formAutocompleteConfig->getAttribute('target_admin_access_action');
90
        }
91
92
        $searchText = $request->get('q');
93
94
        $targetAdmin = $fieldDescription->getAssociationAdmin();
95
96
        // check user permission
97
        $targetAdmin->checkAccess($targetAdminAccessAction);
98
99
        if (mb_strlen($searchText, 'UTF-8') < $minimumInputLength) {
100
            return new JsonResponse(['status' => 'KO', 'message' => 'Too short search string.'], 403);
101
        }
102
103
        $targetAdmin->setFilterPersister(null);
104
        $datagrid = $targetAdmin->getDatagrid();
105
106
        if (null !== $callback) {
107
            if (!is_callable($callback)) {
108
                throw new \RuntimeException('Callback does not contain callable function.');
109
            }
110
111
            call_user_func($callback, $targetAdmin, $property, $searchText);
112
        } else {
113
            if (is_array($property)) {
114
                // multiple properties
115
                foreach ($property as $prop) {
116
                    if (!$datagrid->hasFilter($prop)) {
117
                        throw new \RuntimeException(sprintf(
118
                            'To retrieve autocomplete items,'
119
                            .' you should add filter "%s" to "%s" in configureDatagridFilters() method.',
120
                            $prop, get_class($targetAdmin)
121
                        ));
122
                    }
123
124
                    $filter = $datagrid->getFilter($prop);
125
                    $filter->setCondition(FilterInterface::CONDITION_OR);
126
127
                    $datagrid->setValue($filter->getFormName(), null, $searchText);
128
                }
129
            } else {
130
                if (!$datagrid->hasFilter($property)) {
131
                    throw new \RuntimeException(sprintf(
132
                        'To retrieve autocomplete items,'
133
                        .' you should add filter "%s" to "%s" in configureDatagridFilters() method.',
134
                        $property,
135
                        get_class($targetAdmin)
136
                    ));
137
                }
138
139
                $datagrid->setValue($datagrid->getFilter($property)->getFormName(), null, $searchText);
140
            }
141
        }
142
143
        $datagrid->setValue('_per_page', null, $itemsPerPage);
144
        $datagrid->setValue('_page', null, $request->query->get($reqParamPageNumber, 1));
145
        $datagrid->buildPager();
146
147
        $pager = $datagrid->getPager();
148
149
        $items = [];
150
        $results = $pager->getResults();
151
152
        foreach ($results as $entity) {
153
            if (null !== $toStringCallback) {
154
                if (!is_callable($toStringCallback)) {
155
                    throw new \RuntimeException('Option "to_string_callback" does not contain callable function.');
156
                }
157
158
                $label = call_user_func($toStringCallback, $entity, $property);
159
            } else {
160
                $resultMetadata = $targetAdmin->getObjectMetadata($entity);
161
                $label = $resultMetadata->getTitle();
162
            }
163
164
            $items[] = [
165
                'id' => $admin->id($entity),
166
                'label' => $label,
167
            ];
168
        }
169
170
        return new JsonResponse([
171
            'status' => 'OK',
172
            'more' => !$pager->isLastPage(),
173
            'items' => $items,
174
        ]);
175
    }
176
177
    /**
178
     * Retrieve the filter field description given by field name.
179
     *
180
     * @param string $field
181
     *
182
     * @throws \RuntimeException
183
     *
184
     * @return \Sonata\AdminBundle\Admin\FieldDescriptionInterface
185
     */
186
    private function retrieveFilterFieldDescription(AdminInterface $admin, $field)
187
    {
188
        $admin->getFilterFieldDescriptions();
189
190
        $fieldDescription = $admin->getFilterFieldDescription($field);
191
192
        if (!$fieldDescription) {
193
            throw new \RuntimeException(sprintf('The field "%s" does not exist.', $field));
194
        }
195
196
        if (null === $fieldDescription->getTargetEntity()) {
197
            throw new \RuntimeException(sprintf('No associated entity with field "%s".', $field));
198
        }
199
200
        return $fieldDescription;
201
    }
202
203
    /**
204
     * Retrieve the form field description given by field name.
205
     *
206
     * @param string $field
207
     *
208
     * @throws \RuntimeException
209
     *
210
     * @return \Sonata\AdminBundle\Admin\FieldDescriptionInterface
211
     */
212
    private function retrieveFormFieldDescription(AdminInterface $admin, $field)
213
    {
214
        $admin->getFormFieldDescriptions();
215
216
        $fieldDescription = $admin->getFormFieldDescription($field);
217
218
        if (!$fieldDescription) {
219
            throw new \RuntimeException(sprintf('The field "%s" does not exist.', $field));
220
        }
221
222
        if (null === $fieldDescription->getTargetEntity()) {
223
            throw new \RuntimeException(sprintf('No associated entity with field "%s".', $field));
224
        }
225
226
        return $fieldDescription;
227
    }
228
}
229