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
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
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