Completed
Push — master ( f39c4d...b2e354 )
by Sam
03:35 queued 03:17
created

FormSchema::getSchema()   B

Complexity

Conditions 3
Paths 4

Size

Total Lines 25
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 15
nc 4
nop 1
dl 0
loc 25
rs 8.8571
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Forms\Schema;
4
5
use InvalidArgumentException;
6
use SilverStripe\Control\HTTPRequest;
7
use SilverStripe\Forms\CompositeField;
8
use SilverStripe\Forms\Form;
9
use SilverStripe\Forms\FormField;
10
use SilverStripe\ORM\ValidationResult;
11
12
/**
13
 * Represents a {@link Form} as structured data which allows a frontend library to render it.
14
 * Includes information about the form as well as its fields.
15
 * Can create a "schema" (structure only) as well as "state" (data only).
16
 */
17
class FormSchema
18
{
19
    /**
20
     * Request the schema part
21
     */
22
    const PART_SCHEMA = 'schema';
23
24
    /**
25
     * Request the state part
26
     */
27
    const PART_STATE = 'state';
28
29
    /**
30
     * Request the errors from a {@see ValidationResult}
31
     */
32
    const PART_ERRORS = 'errors';
33
34
    /**
35
     * Request errors if invalid, or state if valid
36
     */
37
    const PART_AUTO = 'auto';
38
39
    /**
40
     * Returns a representation of the provided {@link Form} as structured data,
41
     * based on the request data.
42
     *
43
     * @param array|string $schemaParts Array or list of requested parts.
44
     * @param string $schemaID ID for this schema. Required.
45
     * @param Form $form Required for 'state' or 'schema' response
46
     * @param ValidationResult $result Required for 'error' response
47
     * @return array
48
     */
49
    public function getMultipartSchema($schemaParts, $schemaID, Form $form = null, ValidationResult $result = null)
50
    {
51
        if (!is_array($schemaParts)) {
52
            $schemaParts = preg_split('#\s*,\s*#', $schemaParts) ?: [];
53
        }
54
        $wantSchema = in_array('schema', $schemaParts);
55
        $wantState = in_array('state', $schemaParts);
56
        $wantErrors = in_array('errors', $schemaParts);
57
        $auto = in_array('auto', $schemaParts);
58
59
        // Require ID
60
        if (empty($schemaID)) {
61
            throw new InvalidArgumentException("schemaID is required");
62
        }
63
        $return = ['id' => $schemaID];
64
65
        // Default to schema if not set
66
        if ($form && ($wantSchema || empty($schemaParts))) {
67
            $return['schema'] = $this->getSchema($form);
68
        }
69
70
        // Return 'state' if requested, or if there are errors and 'auto'
71
        if ($form && ($wantState || ($auto && !$result))) {
72
            $return['state'] = $this->getState($form);
73
        }
74
75
        // Return errors if 'errors' or 'auto'
0 ignored issues
show
Unused Code Comprehensibility introduced by
42% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
76
        if ($result && ($wantErrors || $auto)) {
77
            $return['errors'] = $this->getErrors($result);
78
        }
79
80
        return $return;
81
    }
82
83
    /**
84
     * Gets the schema for this form as a nested array.
85
     *
86
     * @param Form $form
87
     * @return array
88
     */
89
    public function getSchema(Form $form)
90
    {
91
        $schema = [
92
            'name' => $form->getName(),
93
            'id' => $form->FormName(),
94
            'action' => $form->FormAction(),
95
            'method' => $form->FormMethod(),
96
            'attributes' => $form->getAttributes(),
97
            'data' => [],
98
            'fields' => [],
99
            'actions' => []
100
        ];
101
102
        /** @var FormField $action */
103
        foreach ($form->Actions() as $action) {
104
            $schema['actions'][] = $action->getSchemaData();
105
        }
106
107
        /** @var FormField $field */
108
        foreach ($form->Fields() as $field) {
109
            $schema['fields'][] = $field->getSchemaData();
110
        }
111
112
        return $schema;
113
    }
114
115
    /**
116
     * Gets the current state of this form as a nested array.
117
     *
118
     * @param Form $form
119
     * @return array
120
     */
121
    public function getState(Form $form)
122
    {
123
        $state = [
124
            'id' => $form->FormName(),
125
            'fields' => [],
126
            'messages' => [],
127
        ];
128
129
        // flattened nested fields are returned, rather than only top level fields.
130
        $state['fields'] = array_merge(
131
            $this->getFieldStates($form->Fields()),
132
            $this->getFieldStates($form->Actions())
133
        );
134
135
        if ($message = $form->getSchemaMessage()) {
136
            $state['messages'][] = $message;
137
        }
138
139
        return $state;
140
    }
141
142
    /**
143
     * @param ValidationResult $result
144
     * @return array List of errors
145
     */
146
    public function getErrors(ValidationResult $result)
147
    {
148
        $messages = [];
149
        foreach ($result->getMessages() as $message) {
150
            $messages[] = $this->getSchemaForMessage($message);
151
        }
152
        return $messages;
153
    }
154
155
    /**
156
     * Return form schema for encoded validation message
157
     *
158
     * @param array $message Internal ValidationResult format for this message
159
     * @return array Form schema format for this message
160
     */
161
    protected function getSchemaForMessage($message)
162
    {
163
        // Form schema messages treat simple strings as plain text, so nest for html messages
164
        $value = $message['message'];
165
        if ($message['messageCast'] === ValidationResult::CAST_HTML) {
166
            $value = ['html' => $message];
167
        }
168
        return [
169
            'value' => $value,
170
            'type' => $message['messageType'],
171
            'field' => empty($message['fieldName']) ? null : $message['fieldName'],
172
        ];
173
    }
174
175
    protected function getFieldStates($fields)
176
    {
177
        $states = [];
178
        /** @var FormField $field */
179
        foreach ($fields as $field) {
180
            $states[] = $field->getSchemaState();
181
182
            if ($field instanceof CompositeField) {
183
                $subFields = $field->FieldList();
184
                $states = array_merge($states, $this->getFieldStates($subFields));
185
            }
186
        }
187
        return $states;
188
    }
189
}
190