Completed
Pull Request — master (#31)
by Lhalaa
04:10
created

PartialSubmissionController   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 190
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 12

Test Coverage

Coverage 94.59%

Importance

Changes 0
Metric Value
wmc 24
lcom 1
cbo 12
dl 0
loc 190
ccs 70
cts 74
cp 0.9459
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
B savePartialSubmission() 0 55 9
A createOrUpdateSubmission() 0 18 3
A savePartialField() 0 17 4
A savePartialFile() 0 20 4
A uploadFile() 0 23 4
1
<?php
2
3
namespace Firesphere\PartialUserforms\Controllers;
4
5
use Exception;
6
use Firesphere\PartialUserforms\Models\PartialFieldSubmission;
7
use Firesphere\PartialUserforms\Models\PartialFileFieldSubmission;
8
use Firesphere\PartialUserforms\Models\PartialFormSubmission;
9
use SilverStripe\Assets\File;
10
use SilverStripe\Assets\Upload;
11
use SilverStripe\CMS\Controllers\ContentController;
12
use SilverStripe\Control\HTTPRequest;
13
use SilverStripe\Control\HTTPResponse;
14
use SilverStripe\ORM\DataObject;
15
use SilverStripe\ORM\ValidationException;
16
use SilverStripe\UserForms\Model\EditableFormField;
17
18
/**
19
 * Class PartialSubmissionController
20
 *
21
 * @package Firesphere\PartialUserforms\Controllers
22
 */
23
class PartialSubmissionController extends ContentController
24
{
25
    /**
26
     * Session key name
27
     */
28
    public const SESSION_KEY = 'PartialSubmissionID';
29
30
    /**
31
     * @var array
32
     */
33
    private static $url_handlers = [
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
34
        'save' => 'savePartialSubmission',
35
    ];
36
37
    /**
38
     * @var array
39
     */
40
    private static $allowed_actions = [
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
41
        'savePartialSubmission',
42
    ];
43
44
    /**
45
     * @param HTTPRequest $request
46
     * @return HTTPResponse
47
     * @throws ValidationException
48
     * @throws \SilverStripe\Control\HTTPResponse_Exception
49
     */
50 9
    public function savePartialSubmission(HTTPRequest $request)
51
    {
52 9
        if (!$request->isPOST()) {
53
            return $this->httpError(404);
54
        }
55
56 9
        $postVars = $request->postVars();
57 9
        $editableField = null;
58
59
        // We don't want SecurityID and/or the process Action stored as a thing
60 9
        unset($postVars['SecurityID'], $postVars['action_process']);
61
62
        // Check for partial params so the submission doesn't rely on session for partial page
63 9
        if (!empty($postVars['partialID'])) {
64
            $submissionID = $postVars['partialID'];
65
        } else {
66 9
            $submissionID = $request->getSession()->get(self::SESSION_KEY);
67
        }
68
69
        /** @var PartialFormSubmission $partialSubmission */
70 9
        $partialSubmission = PartialFormSubmission::get()->byID($submissionID);
71
72 9
        if (!$submissionID || !$partialSubmission) {
73 9
            $partialSubmission = PartialFormSubmission::create();
74
            // TODO: Set the Parent ID and Parent Class before write, this issue will create new submissions
75
            // every time the session expires when the user proceeds to the next step.
76
            // Also, saving a new submission without a parent creates an
77
            // "AccordionItems" as parent class (first DataObject found)
78 9
            $submissionID = $partialSubmission->write();
79
        }
80 9
        $request->getSession()->set(self::SESSION_KEY, $submissionID);
81 9
        foreach ($postVars as $field => $value) {
82
            /** @var EditableFormField $editableField */
83 9
            $editableField = $this->createOrUpdateSubmission([
84 9
                'Name'            => $field,
85 9
                'Value'           => $value,
86 9
                'SubmittedFormID' => $submissionID
87
            ]);
88
        }
89
90 9
        if ($editableField instanceof EditableFormField && !$partialSubmission->UserDefinedFormID) {
91
            // Updates parent class to the correct DataObject
92 9
            $partialSubmission->update([
93 9
                'UserDefinedFormID'    => $editableField->Parent()->ID,
94 9
                'ParentID'             => $editableField->Parent()->ID,
95 9
                'ParentClass'          => $editableField->Parent()->ClassName,
96 9
                'UserDefinedFormClass' => $editableField->Parent()->ClassName
97
            ]);
98 9
            $partialSubmission->write();
99
        }
100
101 9
        $return = $partialSubmission->exists();
102
103 9
        return new HTTPResponse($return, ($return ? 201 : 400));
0 ignored issues
show
Documentation introduced by
$return is of type boolean, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
104
    }
105
106
    /**
107
     * @param $formData
108
     * @return DataObject|EditableFormField
109
     * @throws ValidationException
110
     */
111 9
    protected function createOrUpdateSubmission($formData)
112
    {
113
        $filter = [
114 9
            'Name'            => $formData['Name'],
115 9
            'SubmittedFormID' => $formData['SubmittedFormID'],
116
        ];
117
118
        /** @var EditableFormField $editableField */
119 9
        $editableField = EditableFormField::get()->filter(['Name' => $formData['Name']])->first();
120 9
        if ($editableField instanceof EditableFormField\EditableFileField) {
121 1
            $this->savePartialFile($formData, $filter, $editableField);
122 8
        } elseif ($editableField instanceof EditableFormField) {
123 8
            $this->savePartialField($formData, $filter, $editableField);
124
        }
125
126
        // Return the ParentID to link the PartialSubmission to it's proper thingy
127 9
        return $editableField;
128
    }
129
130
    /**
131
     * @param $formData
132
     * @param array $filter
133
     * @param EditableFormField $editableField
134
     * @throws ValidationException
135
     */
136 8
    protected function savePartialField($formData, array $filter, EditableFormField $editableField)
137
    {
138 8
        $partialSubmission = PartialFieldSubmission::get()->filter($filter)->first();
139 8
        if (is_array($formData['Value'])) {
140 1
            $formData['Value'] = implode(', ', $formData['Value']);
141
        }
142 8
        if ($editableField) {
143 8
            $formData['Title'] = $editableField->Title;
144 8
            $formData['ParentClass'] = $editableField->Parent()->ClassName;
145
        }
146 8
        if (!$partialSubmission) {
147 8
            $partialSubmission = PartialFieldSubmission::create($formData);
148
        } else {
149 2
            $partialSubmission->update($formData);
150
        }
151 8
        $partialSubmission->write();
152 8
    }
153
154
    /**
155
     * @param $formData
156
     * @param array $filter
157
     * @param EditableFormField\EditableFileField $editableField
158
     * @throws ValidationException
159
     * @throws Exception
160
     */
161 1
    protected function savePartialFile($formData, array $filter, EditableFormField\EditableFileField $editableField)
162
    {
163 1
        $partialFileSubmission = PartialFileFieldSubmission::get()->filter($filter)->first();
164 1
        if (!$partialFileSubmission && $editableField) {
165
            $partialData = [
166 1
                'Name'            => $formData['Name'],
167 1
                'SubmittedFormID' => $formData['SubmittedFormID'],
168 1
                'Title'           => $editableField->Title,
169 1
                'ParentClass'     => $editableField->Parent()->ClassName
170
            ];
171 1
            $partialFileSubmission = PartialFileFieldSubmission::create($partialData);
172 1
            $partialFileSubmission->write();
173
        }
174
175 1
        if (is_array($formData['Value'])) {
176 1
            $file = $this->uploadFile($formData, $editableField, $partialFileSubmission);
0 ignored issues
show
Documentation introduced by
$partialFileSubmission is of type object<SilverStripe\ORM\DataObject>|null, but the function expects a object<Firesphere\Partia...ialFileFieldSubmission>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
177 1
            $partialFileSubmission->UploadedFileID = $file->ID ?? 0;
178 1
            $partialFileSubmission->write();
179
        }
180 1
    }
181
182
    /**
183
     * @param array $formData
184
     * @param EditableFormField\EditableFileField $field
185
     * @param PartialFileFieldSubmission $partialFileSubmission
186
     * @return bool|File
187
     * @throws Exception
188
     */
189 1
    protected function uploadFile($formData, $field, $partialFileSubmission)
190
    {
191 1
        if (!empty($formData['Value']['name'])) {
192 1
            $foldername = $field->getFormField()->getFolderName();
193
194 1
            if (!$partialFileSubmission->UploadedFileID) {
0 ignored issues
show
Documentation introduced by
The property UploadedFileID does not exist on object<Firesphere\Partia...ialFileFieldSubmission>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
195 1
                $file = File::create([
196 1
                    'ShowInSearch' => 0
197
                ]);
198
            } else {
199
                // Allow overwrite existing uploads
200
                $file = $partialFileSubmission->UploadedFile();
0 ignored issues
show
Documentation Bug introduced by
The method UploadedFile does not exist on object<Firesphere\Partia...ialFileFieldSubmission>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
201
            }
202
203
            // Upload the file from post data
204 1
            $upload = Upload::create();
205 1
            if ($upload->loadIntoFile($formData['Value'], $file, $foldername)) {
206 1
                return $file;
207
            }
208
        }
209
210
        return false;
211
    }
212
}
213