Passed
Push — master ( 7c0d96...cd60cf )
by Robbie
03:07 queued 10s
created

ElementalAreaField::saveInto()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 36
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 17
nc 5
nop 1
dl 0
loc 36
rs 9.3888
c 0
b 0
f 0
1
<?php
2
3
namespace DNADesign\Elemental\Forms;
4
5
use BlocksPage;
0 ignored issues
show
Bug introduced by Guy Marriott
The type BlocksPage was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
6
use DNADesign\Elemental\Controllers\ElementalAreaController;
7
use DNADesign\Elemental\Models\BaseElement;
8
use DNADesign\Elemental\Models\ElementalArea;
9
use DNADesign\Elemental\Services\ElementTabProvider;
10
use SilverStripe\Control\Controller;
11
use SilverStripe\Core\Config\Config;
12
use SilverStripe\Core\Injector\Injector;
13
use SilverStripe\Forms\CompositeField;
14
use SilverStripe\Forms\FieldGroup;
15
use SilverStripe\Forms\FieldList;
16
use SilverStripe\Forms\GridField\GridField;
17
use SilverStripe\Forms\TabSet;
18
use SilverStripe\ORM\DataObjectInterface;
19
use Symbiote\GridFieldExtensions\GridFieldAddNewMultiClass;
20
21
class ElementalAreaField extends GridField
22
{
23
    /**
24
     * @var ElementalArea $area
25
     */
26
    protected $area;
27
28
    /**
29
     * @var array $type
30
     */
31
    protected $types = [];
32
33
    /**
34
     * @var null
35
     */
36
    protected $inputType = null;
37
38
    protected $modelClassName = BaseElement::class;
39
40
    /**
41
     * @param string $name
42
     * @param ElementalArea $area
43
     * @param string[] $blockTypes
44
     */
45
    public function __construct($name, ElementalArea $area, array $blockTypes)
46
    {
47
        $this->setTypes($blockTypes);
48
49
        $config = new ElementalAreaConfig();
50
51
        if (!empty($blockTypes)) {
52
            /** @var GridFieldAddNewMultiClass $adder */
53
            $adder = Injector::inst()->create(GridFieldAddNewMultiClass::class);
54
            $adder->setClasses($blockTypes);
55
            $config->addComponent($adder);
56
        }
57
58
        // By default, no need for a title on the editor. If there is more than one area then use `setTitle` to describe
59
        parent::__construct($name, '', $area->Elements(), $config);
0 ignored issues
show
Bug introduced by Guy Marriott
It seems like $area->Elements() can also be of type DNADesign\Elemental\Models\BaseElement[]; however, parameter $dataList of SilverStripe\Forms\GridF...ridField::__construct() does only seem to accept SilverStripe\ORM\SS_List|null, 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

59
        parent::__construct($name, '', /** @scrutinizer ignore-type */ $area->Elements(), $config);
Loading history...
60
        $this->area = $area;
61
62
        $this->addExtraClass('element-editor__container');
63
    }
64
65
    /**
66
     * @param array $types
67
     *
68
     * @return $this
69
     */
70
    public function setTypes($types)
71
    {
72
        $this->types = $types;
73
74
        return $this;
75
    }
76
77
    /**
78
     * @return array
79
     */
80
    public function getTypes()
81
    {
82
        $types = $this->types;
83
84
        $this->extend('updateGetTypes', $types);
85
86
        return $types;
87
    }
88
89
    /**
90
     * @return ElementalArea
91
     */
92
    public function getArea()
93
    {
94
        return $this->area;
95
    }
96
97
    /**
98
     * Overloaded to skip GridField implementation - this is copied from FormField.
99
     *
100
     * @param array $properties
101
     * @return \SilverStripe\ORM\FieldType\DBHTMLText|string
102
     */
103
    public function FieldHolder($properties = array())
104
    {
105
        $context = $this;
106
107
        if (count($properties)) {
108
            $context = $this->customise($properties);
109
        }
110
111
        return $context->renderWith($this->getFieldHolderTemplates());
112
    }
113
114
    public function getSchemaDataDefaults()
115
    {
116
        $schemaData = parent::getSchemaDataDefaults();
117
        $pageId = ($this->getArea() && ($page = $this->getArea()->getOwnerPage())) ? $page->ID : null;
118
        $schemaData['page-id'] = $pageId;
119
120
        $blockTypes = [];
121
122
        // Use the internal (temporary) provider to get cached tab names.
123
        /** @var ElementTabProvider $tabProvider */
124
        $tabProvider = Injector::inst()->get(ElementTabProvider::class);
125
126
        foreach ($this->getTypes() as $className => $blockTitle) {
127
            $blockTypes[] = [
128
                'name' => str_replace('\\', '-', $className),
129
                'title' => $blockTitle,
130
                'icon' => Config::inst()->get($className, 'icon'),
131
                'tabs' => $tabProvider->getTabsForElement($className),
132
            ];
133
        }
134
135
        $schemaData['element-types'] = $blockTypes;
136
        $schemaData['base-add-href'] = Controller::join_links($this->Link(), 'add-multi-class');
137
        return $schemaData;
138
    }
139
140
    /**
141
     * A getter method that seems redundant in that it is a function that returns a function,
142
     * however the returned closure is used in an array map function to return a complete FieldList
143
     * representing a read only view of the element passed in (to the closure).
144
     *
145
     * @return callable
146
     */
147
    protected function getReadOnlyBlockReducer()
148
    {
149
        return function (BaseElement $element) {
150
            $parentName = 'Element' . $element->ID;
151
            $elementFields = $element->getCMSFields();
152
153
            // Obtain highest impact fields for a summary (e.g. Title & Content)
154
            foreach ($elementFields as $field) {
155
                if (is_object($field) && $field instanceof TabSet) {
156
                    // Assign the fields of the first Tab in the TabSet - most regularly 'Root.Main'
157
                    $elementFields = $field->FieldList()->first()->FieldList();
158
                    break;
159
                }
160
            }
161
162
            // Set values (before names don't match anymore)
163
            $elementFields->setValues($element->getQueriedDatabaseFields());
164
165
            // Ensure field names are unique between elements on parent form
166
            $elementFields->recursiveWalk(function ($field) use ($parentName) {
167
                $field->setName($parentName . '_' . $field->getName());
168
            });
169
170
            // Combine into an appropriately named group
171
            $elementGroup = FieldGroup::create($elementFields);
172
            $elementGroup->setForm($this->getForm());
173
            $elementGroup->setName($parentName);
174
            $elementGroup->addExtraClass('elemental-area__element--historic');
175
176
            // Also set the important data for the rendering Component
177
            $elementGroup->setSchemaData([
178
                'data' => [
179
                    'ElementID' => $element->ID,
180
                    'ElementType' => $element->getType(),
181
                    'ElementIcon' => $element->config()->icon,
182
                    'ElementTitle' => $element->Title,
183
                    // @todo: Change this to block history permalink when that functionality becomes available.
184
                    'ElementEditLink' => $element->CMSEditLink()
185
                ]
186
            ]);
187
188
            return $elementGroup;
189
        };
190
    }
191
192
    /**
193
     * Provides a readonly representation of the GridField (superclass) Uses a reducer
194
     * {@see ElementalAreaField::getReadOnlyBlockReducer()} to fetch a read only representation of the listed class
195
     * {@see GridField::getModelClass()}
196
     *
197
     * @return CompositeField
198
     */
199
    public function performReadonlyTransformation()
200
    {
201
        /** @var CompositeField $readOnlyField */
202
        $readOnlyField = $this->castedCopy(CompositeField::class);
203
        $blockReducer = $this->getReadOnlyBlockReducer();
204
        $readOnlyField->setChildren(
205
            FieldList::create(array_map($blockReducer, $this->getArea()->Elements()->toArray()))
206
        );
207
208
        $readOnlyField = $readOnlyField->performReadonlyTransformation();
209
        return $readOnlyField
210
            ->setReadOnly(true)
211
            ->setName($this->getName())
212
            ->addExtraClass('elemental-area--read-only');
213
    }
214
215
    public function setSubmittedValue($value, $data = null)
216
    {
217
        // Content comes through as a JSON encoded list through a hidden field.
218
        return $this->setValue(json_decode($value, true));
219
    }
220
221
    public function saveInto(DataObjectInterface $dataObject)
222
    {
223
        /** @var BlocksPage $dataObject */
224
        parent::saveInto($dataObject);
225
226
        $elementData = $this->Value();
227
        $idPrefixLength = strlen(sprintf(ElementalAreaController::FORM_NAME_TEMPLATE, ''));
228
229
        foreach ($elementData as $form => $data) {
230
            // Extract the ID
231
            $elementId = (int) substr($form, $idPrefixLength);
232
233
            /** @var BaseElement $element */
234
            $element = $this->getArea()->Elements()->byID($elementId);
235
236
            if (!$element) {
237
                // Ignore invalid elements
238
                continue;
239
            }
240
241
            $fields = [];
242
243
            $fieldNamePrefix = sprintf(EditFormFactory::FIELD_NAMESPACE_TEMPLATE, $elementId, '');
244
            $prefixLength = strlen($fieldNamePrefix);
245
246
            foreach ($data as $field => $datum) {
247
                // Check that the field starts with a valid name
248
                if (strpos($field, $fieldNamePrefix) !== 0) {
249
                    continue;
250
                }
251
252
                $fields[substr($field, $prefixLength)] = $datum;
253
            }
254
255
            $element->update($fields);
256
            $element->write();
257
        }
258
    }
259
}
260