Completed
Push — master ( ff1c82...da0740 )
by
unknown
12s
created

ElementalAreaController::apiSaveForm()   B

Complexity

Conditions 9
Paths 16

Size

Total Lines 57
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 31
nc 16
nop 1
dl 0
loc 57
rs 8.0555
c 0
b 0
f 0

How to fix   Long Method   

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
namespace DNADesign\Elemental\Controllers;
4
5
use DNADesign\Elemental\Forms\EditFormFactory;
6
use DNADesign\Elemental\Models\BaseElement;
7
use Exception;
8
use Psr\Log\LoggerInterface;
9
use SilverStripe\Admin\LeftAndMain;
10
use SilverStripe\Control\HTTPRequest;
11
use SilverStripe\Control\HTTPResponse;
12
use SilverStripe\Control\HTTPResponse_Exception;
13
use SilverStripe\Core\Convert;
14
use SilverStripe\Core\Injector\Injector;
15
use SilverStripe\Forms\Form;
16
use SilverStripe\Security\SecurityToken;
17
18
/**
19
 * Controller for "ElementalArea" - handles loading and saving of in-line edit forms in an elemental area in admin
20
 */
21
class ElementalAreaController extends LeftAndMain
22
{
23
    private static $url_segment = 'elemental-area';
0 ignored issues
show
introduced by Guy Marriott
The private property $url_segment is not used, and could be removed.
Loading history...
24
25
    private static $ignore_menuitem = true;
0 ignored issues
show
introduced by Guy Marriott
The private property $ignore_menuitem is not used, and could be removed.
Loading history...
26
27
    private static $url_handlers = [
0 ignored issues
show
introduced by Robbie Averill
The private property $url_handlers is not used, and could be removed.
Loading history...
28
        // API access points with structured data
29
        'POST api/saveForm/$ID' => 'apiSaveForm',
30
    ];
31
32
    private static $allowed_actions = [
0 ignored issues
show
introduced by Will Rossiter
The private property $allowed_actions is not used, and could be removed.
Loading history...
33
        'elementForm',
34
        'schema',
35
        'apiSaveForm',
36
    ];
37
38
    public function getClientConfig()
39
    {
40
        $clientConfig = parent::getClientConfig();
41
        $clientConfig['form']['elementForm'] = [
42
            'schemaUrl' => $this->Link('schema/elementForm'),
43
            'saveUrl' => $this->Link('api/saveForm'),
44
            'saveMethod' => 'post',
45
            'payloadFormat' => 'json',
46
        ];
47
        return $clientConfig;
48
    }
49
50
    /**
51
     * @param HTTPRequest|null $request
52
     * @return Form
53
     * @throws HTTPResponse_Exception
54
     */
55
    public function elementForm(HTTPRequest $request = null)
56
    {
57
        // Get ID either from posted back value, or url parameter
58
        if (!$request) {
59
            $this->jsonError(400);
60
            return null;
61
        }
62
        $id = $request->param('ID');
63
        if (!$id) {
64
            $this->jsonError(400);
65
            return null;
66
        }
67
        return $this->getElementForm($id) ?: $this->jsonError(404);
0 ignored issues
show
Bug introduced by Guy Marriott
$id of type string is incompatible with the type integer expected by parameter $elementID of DNADesign\Elemental\Cont...oller::getElementForm(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

67
        return $this->getElementForm(/** @scrutinizer ignore-type */ $id) ?: $this->jsonError(404);
Loading history...
68
    }
69
70
    /**
71
     * @param int $elementID
72
     * @return Form|null Returns null if no element exists for the given ID
73
     */
74
    public function getElementForm($elementID)
75
    {
76
        $scaffolder = Injector::inst()->get(EditFormFactory::class);
77
        $element = BaseElement::get()->byID($elementID);
78
79
        if (!$element) {
0 ignored issues
show
introduced by Guy Marriott
$element is of type SilverStripe\ORM\DataObject, thus it always evaluated to true.
Loading history...
80
            return null;
81
        }
82
83
        /** @var Form $form */
84
        $form = $scaffolder->getForm(
85
            $this,
86
            'ElementForm_'.$elementID,
87
            ['Record' => $element]
88
        );
89
90
        return $form;
91
    }
92
93
    /**
94
     * Save an inline edit form for a block
95
     *
96
     * @param HTTPRequest $request
97
     * @return HTTPResponse|null JSON encoded string or null if an exception is thrown
98
     * @throws HTTPResponse_Exception
99
     */
100
    public function apiSaveForm(HTTPRequest $request)
101
    {
102
        // Validate required input data
103
        if (!isset($this->urlParams['ID'])) {
104
            $this->jsonError(400);
105
            return null;
106
        }
107
108
        $data = Convert::json2array($request->getBody());
109
        if (empty($data)) {
110
            $this->jsonError(400);
111
            return null;
112
        }
113
114
        // Inject request body as request vars
115
        foreach ($data as $key => $value) {
116
            $request->offsetSet($key, $value);
117
        }
118
119
        // Check security token
120
        if (!SecurityToken::inst()->checkRequest($request)) {
121
            $this->jsonError(400);
122
            return null;
123
        }
124
125
        /** @var BaseElement $element */
126
        $element = BaseElement::get()->byID($this->urlParams['ID']);
127
        // Ensure the element can be edited by the current user
128
        if (!$element || !$element->canEdit()) {
0 ignored issues
show
introduced by Robbie Averill
$element is of type DNADesign\Elemental\Models\BaseElement, thus it always evaluated to true. If $element can have other possible types, add them to src/Controllers/ElementalAreaController.php:125
Loading history...
129
            $this->jsonError(403);
130
            return null;
131
        }
132
133
        // Remove the pseudo namespaces that were added by the form factory
134
        $data = $this->removeNamespacesFromFields($data, $element->ID);
0 ignored issues
show
Bug introduced by Robbie Averill
It seems like $data can also be of type boolean; however, parameter $data of DNADesign\Elemental\Cont...eNamespacesFromFields() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

134
        $data = $this->removeNamespacesFromFields(/** @scrutinizer ignore-type */ $data, $element->ID);
Loading history...
135
136
        try {
137
            $updated = false;
138
            $element->update($data);
139
            // Check if anything will actually be changed before writing
140
            if ($element->isChanged()) {
141
                $element->write();
142
                // Track changes so we can return to the client
143
                $updated = true;
144
            }
145
        } catch (Exception $ex) {
146
            Injector::inst()->get(LoggerInterface::class)->debug($ex->getMessage());
147
148
            $this->jsonError(500);
149
            return null;
150
        }
151
152
        $body = Convert::raw2json([
153
            'status' => 'success',
154
            'updated' => $updated,
155
        ]);
156
        return HTTPResponse::create($body)->addHeader('Content-Type', 'application/json');
157
    }
158
159
    /**
160
     * Remove the pseudo namespaces that were added to form fields by the form factory
161
     *
162
     * @param array $data
163
     * @param int $elementID
164
     * @return array
165
     */
166
    protected function removeNamespacesFromFields(array $data, $elementID)
167
    {
168
        $output = [];
169
        $template = sprintf(EditFormFactory::FIELD_NAMESPACE_TEMPLATE, $elementID, '');
170
        foreach ($data as $key => $value) {
171
            // Only look at fields that match the namespace template
172
            if (substr($key, 0, strlen($template)) !== $template) {
173
                continue;
174
            }
175
176
            $fieldName = substr($key, strlen($template));
177
            $output[$fieldName] = $value;
178
        }
179
        return $output;
180
    }
181
}
182