Passed
Pull Request — 4 (#930)
by Steve
04:26 queued 43s
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
                $originalTitle = $element->Title ??
42
                    sprintf('(Untitled %s)', ucfirst($element->config()->get('singular_name')));
43
                $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

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