Rework form data processors to allow data processing in both directions
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / system / form / builder / field / label / LabelFormField.class.php
1 <?php
2 namespace wcf\system\form\builder\field\label;
3 use wcf\data\IStorableObject;
4 use wcf\data\label\group\ViewableLabelGroup;
5 use wcf\data\label\Label;
6 use wcf\system\form\builder\data\processor\CustomFormDataProcessor;
7 use wcf\system\form\builder\field\AbstractFormField;
8 use wcf\system\form\builder\field\validation\FormFieldValidationError;
9 use wcf\system\form\builder\IFormDocument;
10 use wcf\system\form\builder\IObjectTypeFormNode;
11 use wcf\system\form\builder\TObjectTypeFormNode;
12 use wcf\system\label\LabelHandler;
13
14 /**
15 * Implementation of a form field to select labels.
16 *
17 * @author Matthias Schmidt
18 * @copyright 2001-2019 WoltLab GmbH
19 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
20 * @package WoltLabSuite\Core\System\Form\Builder\Field
21 * @since 5.2
22 */
23 class LabelFormField extends AbstractFormField implements IObjectTypeFormNode {
24 use TObjectTypeFormNode;
25
26 /**
27 * @inheritDoc
28 */
29 protected $javaScriptDataHandlerModule = 'WoltLabSuite/Core/Form/Builder/Field/Value';
30
31 /**
32 * label group whose labels can be selected via this form field
33 * @var ViewableLabelGroup
34 */
35 protected $labelGroup;
36
37 /**
38 * @inheritDoc
39 */
40 protected $templateName = '__labelFormField';
41
42 /**
43 * loaded labels grouped by label object type and object id to avoid loading the same labels
44 * over and over again for the same object and different label groups
45 * @var Label[][]
46 */
47 protected static $loadedLabels = [];
48
49 /**
50 * Returns the label group whose labels can be selected via this form field.
51 *
52 * @return ViewableLabelGroup label group whose labels can be selected
53 * @throws \BadMethodCallException if no label has been set
54 */
55 public function getLabelGroup() {
56 if ($this->labelGroup === null) {
57 throw new \BadMethodCallException("No label group has been set.");
58 }
59
60 return $this->labelGroup;
61 }
62
63 /**
64 * @inheritDoc
65 */
66 public function getObjectTypeDefinition() {
67 return 'com.woltlab.wcf.label.object';
68 }
69
70 /**
71 * @inheritDoc
72 */
73 public function hasSaveValue() {
74 return false;
75 }
76
77 /**
78 * Sets the label group whose labels can be selected via this form field and returns this
79 * form field.
80 *
81 * If no form field label has been set, the title of the label group will be set as label.
82 *
83 * @param ViewableLabelGroup $labelGroup label group whose labels can be selected
84 * @return static this form field
85 */
86 public function labelGroup(ViewableLabelGroup $labelGroup) {
87 $this->labelGroup = $labelGroup;
88
89 if ($this->label === null) {
90 $this->label($this->getLabelGroup()->getTitle());
91 }
92
93 return $this;
94 }
95
96 /**
97 * @inheritDoc
98 */
99 public function loadValue(array $data, IStorableObject $object) {
100 $objectTypeID = $this->getObjectType()->objectTypeID;
101 $objectID = $object->{$object::getDatabaseTableIndexName()};
102
103 if (!isset(static::$loadedLabels[$objectTypeID])) {
104 static::$loadedLabels[$objectTypeID] = [];
105 }
106 if (!isset(static::$loadedLabels[$objectTypeID][$objectID])) {
107 static::$loadedLabels[$objectTypeID][$objectID] = LabelHandler::getInstance()->getAssignedLabels(
108 $objectTypeID,
109 [$objectID]
110 )[$objectID];
111 }
112
113 $labelIDs = $this->getLabelGroup()->getLabelIDs();
114 /** @var Label $label */
115 foreach (static::$loadedLabels[$objectTypeID][$objectID] as $label) {
116 if (in_array($label->labelID, $labelIDs)) {
117 $this->value($label->labelID);
118 }
119 }
120
121 return $this;
122 }
123
124 /**
125 * @inheritDoc
126 */
127 public function populate() {
128 parent::populate();
129
130 $this->getDocument()->getDataHandler()->addProcessor(new CustomFormDataProcessor('label', function(IFormDocument $document, array $parameters) {
131 $value = $this->getValue();
132
133 // `-1` and `0` are special values that are irrlevent for saving
134 if ($value > 0) {
135 if (!isset($parameters[$this->getObjectProperty()])) {
136 $parameters[$this->getObjectProperty()] = [];
137 }
138
139 $parameters[$this->getObjectProperty()][$this->getLabelGroup()->groupID] = $value;
140 }
141
142 return $parameters;
143 }));
144
145 return $this;
146 }
147
148 /**
149 * @inheritDoc
150 */
151 public function readValue() {
152 if ($this->getDocument()->hasRequestData($this->getPrefixedId())) {
153 $this->value = intval($this->getDocument()->getRequestData($this->getPrefixedId()));
154 }
155
156 return $this;
157 }
158
159 /**
160 * @inheritDoc
161 */
162 public function validate() {
163 if ($this->isRequired()) {
164 if ($this->value <= 0) {
165 $this->addValidationError(new FormFieldValidationError('empty'));
166 }
167 }
168 else if ($this->value > 0 && !in_array($this->value, $this->getLabelGroup()->getLabelIDs())) {
169 $this->addValidationError(new FormFieldValidationError(
170 'invalidValue',
171 'wcf.global.form.error.noValidSelection'
172 ));
173 }
174 }
175
176 /**
177 * Returns label group fields based for the given label groups using the given object property.
178 *
179 * The id of each form fields is `{$objectProperty}{$labelGroupID}`.
180 *
181 * @param string $objectType `com.woltlab.wcf.label.object` object type
182 * @param ViewableLabelGroup[] $labelGroups label groups for which label form fields are created
183 * @param string $objectProperty object property of form fields
184 * @return static[]
185 */
186 public static function createFields($objectType, array $labelGroups, $objectProperty = 'labelIDs') {
187 $formFields = [];
188 foreach ($labelGroups as $labelGroup) {
189 $formFields[] = static::create($objectProperty . $labelGroup->groupID)
190 ->objectProperty($objectProperty)
191 ->objectType($objectType)
192 ->required($labelGroup->forceSelection)
193 ->labelGroup($labelGroup);
194 }
195
196 return $formFields;
197 }
198 }