JSONTextExtension   A
last analyzed

Complexity

Total Complexity 20

Size/Duplication

Total Lines 115
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
wmc 20
lcom 1
cbo 5
dl 0
loc 115
rs 10
c 0
b 0
f 0

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 3
B onBeforeWrite() 0 25 6
A updateJSON() 0 20 4
B updateCMSFields() 0 28 7
1
<?php
2
3
/**
4
 * This {@link DataExtension} allows you to declare arbitrary input fields in
5
 * your getCMSFields() methods without them ever going near an equivalent DBField.
6
 *
7
 * The SilverStripe default gives you one DBField for every input field declared
8
 * in getCMSFields(). This extension however allows you to use a single blob
9
 * of JSON, stored in a single {@link JSONTextField} and manage each key=>value pair
10
 * from individual form input fields, without needing to declare equivalent or further
11
 * database fields. All you need to do is add a `$json_field_map` config static to
12
 * your model (See below).
13
 *
14
 * Notes:
15
 * - Untested on non CHAR DB field-types (v0.8)
16
 * - Only works for single dimensional JSON data
17
 *
18
 * <code>
19
 * private static $db = [
20
 *      'TestJSON' => JSONText::class,
21
 * ];
22
 *
23
 * private static $json_field_map = [
24
 *      'TestJSON' => ['TestField1', 'TestField2']
25
 * ];
26
 *
27
 * public function getCMSFields()
28
 * {
29
 *      $fields = parent::getCMSFields();
30
 *      $fields->addFieldsToTab('Root.Main', [
31
 *          TextField::create('TestField1', 'Test 1'),
32
 *          TextField::create('TestField2', 'Test 2'),
33
 *          TextField::create('TestJSON', 'Some JSON') // Uses a TextField for demo, normally this would be hidden from CMS users
34
 *      ]);
35
 *
36
 *      return $fields;
37
 * }
38
 * </code>
39
 *
40
 * @package jsontext
41
 * @subpackage control
42
 * @author Russell Michell <[email protected]>
43
 */
44
45
namespace PhpTek\JSONText\Extension;
46
47
use PhpTek\JSONText\Exception\JSONTextException;
48
use PhpTek\JSONText\ORM\FieldType\JSONText;
49
use SilverStripe\CMS\Controllers\CMSPageEditController;
50
use SilverStripe\Control\Controller;
51
use SilverStripe\Control\Director;
52
use SilverStripe\ORM\DataExtension;
53
54
class JSONTextExtension extends DataExtension
55
{
56
57
    public function __construct()
58
    {
59
        // Helpful class applicability message
60
        if (!Director::is_cli() && !class_exists(CMSPageEditController::class)) {
61
            $msg = 'Please install the silverstripe/cms package in order to use this extension.';
62
            throw new JSONTextException($msg);
63
        }
64
65
        return parent::__construct();
0 ignored issues
show
Bug introduced by
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
66
    }
67
68
    /**
69
     * Pre-process incoming CMS POST data, and modify any available {@link JSONText}
70
     * data for presentation in "headless" input fields.
71
     *
72
     * @return null
73
     */
74
    public function onBeforeWrite()
75
    {
76
        parent::onBeforeWrite();
77
78
        $owner = $this->getOwner();
79
        $controller = Controller::curr();
80
        $postVars = $controller->getRequest()->postVars();
81
        $fieldMap = $owner->config()->get('json_field_map');
82
        $doUpdate = (
83
            count($postVars) &&
84
            !empty($fieldMap) &&
85
            in_array(get_class($controller), [CMSPageEditController::class])
86
        );
87
88
        if (!$doUpdate) {
89
            return null;
90
        }
91
92
        // Could also use DataObject::getSchema()->fieldSpecs()
93
        foreach ($owner->config()->get('db') as $field => $type) {
94
            if ($type === JSONText::class) {
95
                $this->updateJSON($postVars, $owner);
96
            }
97
        }
98
    }
99
100
    /**
101
     * Called from {@link $this->onBeforeWrote()}. Inserts or updates each available
102
     * JSONText DB field with the appropriate input-field data, as per the model's
103
     * "json_field_map" config static.
104
     *
105
     * @param  array $postVars
106
     * @param  DataObject $owner
107
     * @return void
108
     * @throws JSONTextException
109
     */
110
    public function updateJSON(array $postVars, $owner)
111
    {
112
        $jsonFieldMap = $owner->config()->get('json_field_map');
113
114
        foreach ($jsonFieldMap as $jsonField => $mappedFields) {
115
            $jsonFieldData = [];
116
117
            foreach ($mappedFields as $fieldName) {
118
                if (!isset($postVars[$fieldName])) {
119
                    $msg = sprintf('%s doesn\'t exist in POST data.', $fieldName);
120
                    throw new JSONTextException($msg);
121
                }
122
123
                $jsonFieldData[$fieldName] = $postVars[$fieldName];
124
            }
125
126
            $fieldValue = singleton(JSONText::class)->toJson($jsonFieldData);
127
            $owner->setField($jsonField, $fieldValue);
128
        }
129
    }
130
131
    /**
132
     * The CMS input fields declared in the json_field_map static, are not DB-backed,
133
     * by virtue of this extension, they are backed by specific values represented
134
     * in the relevant JSON data. Therefore we need to pre-populate each such field's
135
     * value.
136
     *
137
     * @param  FieldList $fields
138
     * @return void
139
     */
140
    public function updateCMSFields(\SilverStripe\Forms\FieldList $fields)
141
    {
142
        $owner = $this->getOwner();
143
        $jsonFieldMap = $owner->config()->get('json_field_map');
144
145
        if (empty($jsonFieldMap)) {
146
            return;
147
        }
148
149
        foreach ($jsonFieldMap as $jsonField => $mappedFields) {
150
            if (!$owner->getField($jsonField)) {
151
                continue;
152
            }
153
154
            $jsonFieldObj = $owner->dbObject($jsonField);
155
156
            foreach ($mappedFields as $fieldName) {
157
                if (!$fieldValue = $jsonFieldObj->query('->>', $fieldName)) {
158
                    continue;
159
                }
160
161
                if ($fieldValue = array_values($jsonFieldObj->toArray($fieldValue))) {
162
                    $fieldValue = $fieldValue[0];
163
                    $owner->setField($fieldName, $fieldValue);
164
                }
165
            }
166
        }
167
    }
168
}
169