Passed
Pull Request — 4 (#930)
by Steve
03:22
created

getElementalAreaFieldNames()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 7
c 1
b 0
f 0
nc 3
nop 1
dl 0
loc 11
rs 10
1
<?php
2
3
namespace DNADesign\Elemental\Validators;
4
5
use DNADesign\Elemental\Controllers\ElementalAreaController;
6
use DNADesign\Elemental\Forms\EditFormFactory;
7
use DNADesign\Elemental\Models\BaseElement;
8
use DNADesign\Elemental\Models\ElementalArea;
9
use SilverStripe\Core\Config\Config;
10
use SilverStripe\Forms\Validator;
11
use SilverStripe\ORM\DataObject;
12
use SilverStripe\ORM\ValidationResult;
13
14
class ElementalAreasValidator extends Validator
15
{
16
    /**
17
     * @param array $data
18
     */
19
    public function php($data)
20
    {
21
        $valid = true;
22
        $areaErrors = [];
23
        $areaFieldNames = $this->getElementalAreaFieldNames($data['ClassName']);
24
        foreach ($areaFieldNames as $areaFieldName) {
25
            $elementsData = $data[$areaFieldName] ?? [];
26
            if (empty($elementsData)) {
27
                continue;
28
            }
29
            foreach (array_values($elementsData) as $elementData) {
30
                $elementID = $this->getElementID($elementData);
31
                if (!$elementID) {
32
                    continue;
33
                }
34
                $key = sprintf(EditFormFactory::FIELD_NAMESPACE_TEMPLATE, $elementID, 'ClassName');
35
                $className = $elementData[$key] ?? '';
36
                if (!$className) {
37
                    continue;
38
                }
39
                /** @var BaseElement $element */
40
                $element = DataObject::get_by_id($className, $elementID, false);
0 ignored issues
show
Bug introduced by
$elementID of type string is incompatible with the type boolean|integer expected by parameter $idOrCache of SilverStripe\ORM\DataObject::get_by_id(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

40
                $element = DataObject::get_by_id($className, /** @scrutinizer ignore-type */ $elementID, false);
Loading history...
41
                if (!$element) {
42
                    continue;
43
                }
44
                $originalTitle = $element->Title ??
45
                   sprintf('(Untitled %s)', ucfirst($element->config()->get('singular_name')));
46
                $formData = ElementalAreaController::removeNamespacesFromFields($elementData, $elementID);
0 ignored issues
show
Bug introduced by
$elementID of type string is incompatible with the type integer expected by parameter $elementID of DNADesign\Elemental\Cont...eNamespacesFromFields(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

46
                $formData = ElementalAreaController::removeNamespacesFromFields($elementData, /** @scrutinizer ignore-type */ $elementID);
Loading history...
47
                $element->updateFromFormData($formData);
48
                /** @var ValidationResult $validationResult */
49
                $validationResult = $element->validate();
50
                if ($validationResult->isValid()) {
51
                    continue;
52
                }
53
                if (!array_key_exists($areaFieldName, $areaErrors)) {
54
                    $areaErrors[$areaFieldName] = [
55
                        'The elements below have the following errors:' // TODO _t()
56
                    ];
57
                }
58
                foreach ($validationResult->getMessages() as $message) {
59
                    $this->validationError(
60
                        "PageElements_{$elementID}_{$message['fieldName']}",
61
                        $message['message'],
62
                        $message['messageType'],
63
                        $message['messageCast']
64
                    );
65
                    $areaErrors[$areaFieldName][] = sprintf(
66
                        '%s - %s',
67
                        $originalTitle,
68
                        $message['message']
69
                    );
70
                }
71
                $valid = false;
72
            }
73
        }
74
        if (!$valid) {
75
            foreach ($areaErrors as $areaFieldName => $errors) {
76
                $this->validationError(
77
                    $areaFieldName,
78
                    implode('<br>', $errors),
79
                    ValidationResult::TYPE_ERROR,
80
                    ValidationResult::CAST_HTML
81
                );
82
            }
83
            // TODO: see what happens when you have multiple cms tabs
84
            // Show a generic form message. Ideally this would be done in admin LeftAndMain.EditForm.js
85
            // TODO: this is defined in en.js, needs to be in en.yml too (preferably admin, not elemental)
86
            $msg = _t(
87
                'VALIDATION_ERRORS_ON_PAGE',
88
                'There are validation errors on this page, please fix them before saving or publishing.'
89
            );
90
            // If message above is change to javascript, instead set a blank string here to hide the
91
            // generic form message by overriding any PageElement_3_Title type of message which will
92
            // show as a generic form message since it won't match dataFieldByName($field) in
93
            // Form::loadMessageFrom($data)
94
            $this->validationError('GenericFormMessage', $msg);
95
        }
96
        return $valid;
97
    }
98
99
    /**
100
     * @param string $parentClassName
101
     * @return array
102
     */
103
    private function getElementalAreaFieldNames(string $parentClassName): array
104
    {
105
        $fieldNames = [];
106
        $hasOnes = Config::inst()->get($parentClassName, 'has_one');
107
        foreach ($hasOnes as $fieldName => $className) {
108
            if ($className !== ElementalArea::class) {
109
                continue;
110
            }
111
            $fieldNames[] = $fieldName;
112
        }
113
        return $fieldNames;
114
    }
115
116
    /**
117
     * @param array $elementData
118
     * @return string
119
     */
120
    private function getElementID(array $elementData): string
121
    {
122
        foreach (array_keys($elementData) as $key) {
123
            $rx = str_replace(['%d', '%s'], ['([0-9]+)', '(.+)'], EditFormFactory::FIELD_NAMESPACE_TEMPLATE);
124
            if (!preg_match("#^{$rx}$#", $key, $match)) {
125
                continue;
126
            }
127
            return $match[1];
128
        }
129
        return '';
130
    }
131
}
132