PluginProbe
Metricool – Social media and site statistics / trunk
Metricool – Social media and site statistics vtrunk
2.1.0 2.0.2 2.0.1 2.0.0 1.27 trunk
metricool / app / Features / UserSettings / UserSettingsService.php

UserSettingsService.php in Metricool – Social media and site statistics trunk, at app/Features/UserSettings/UserSettingsService.php

141 lines 4.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare(strict_types=1);
4
5 namespace Metricool\Features\UserSettings;
6
7 use Metricool\Features\UserSettings\Exceptions\StorageSubmitException;
8 use Metricool\Features\UserSettings\Exceptions\ValidationFailedExceptions;
9 use Metricool\Features\UserSettings\Exceptions\ValidatorFailedException;
10 use Metricool\Features\UserSettings\Fields\Field;
11 use Metricool\Support\Helpers\Collection;
12
13 /**
14 * This service is responsible for storing and retrieving user settings.
15 */
16 class UserSettingsService
17 {
18 /**
19 * The fields from config/user_settings.php converted to {@see Field}
20 * instances grouped in a Collection
21 * @var Collection|Field[]
22 */
23 public Collection $fields;
24
25 /**
26 * Property is used to keep track of unique field storages when storing
27 * settings, so we can submit each storage only once.
28 */
29 private array $uniqueFieldStorages = [];
30
31 public function __construct(Collection $fields)
32 {
33 $this->fields = $fields;
34 }
35
36 public function getSettings(): Collection
37 {
38 return $this->fields;
39 }
40
41 /**
42 * Return an array with keys and values of all the settings, optionally
43 * filtered by section.
44 * @throws \Exception
45 */
46 public function getSettingsResponse(?string $section = null): array
47 {
48 $response = new UserSettingsResponse($this->fields);
49
50 if (!empty($section)) {
51 $response->setSection($section);
52 }
53
54 return $response->parse()->get();
55 }
56
57 /**
58 * Validate and update settings and return their updated values. If any
59 * field storage is submittable, it will be submitted after all fields are
60 * validated.
61 *
62 * @throws ValidationFailedExceptions with all the validation errors when validation fails
63 * @throws StorageSubmitException when it fails to store data to it's storage
64 */
65 public function storeSettings(array $requestData, \WP_REST_Request $request): Collection
66 {
67 $validationErrors = [];
68 $userSettings = $this->fields->whereIn('name', array_keys($requestData));
69
70 foreach ($userSettings as &$field) {
71 $value = $requestData[$field->getName()];
72
73 try {
74 $field->setValue($value, $request);
75 } catch (ValidatorFailedException $e) {
76 $validationErrors[$field->getName()] = $e;
77 continue;
78 }
79
80 // Store the validated setting for later submission
81 $this->storeValidatedSetting($field, $value);
82 }
83
84 // After all fields are validated, save the settings through their
85 // storages
86 $this->saveValidatedSettings();
87
88 // Still throw validation errors if any, this should not block storage
89 // submission for valid fields
90 if (count($validationErrors) > 0) {
91 // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- $validationErrors contains ValidatorFailedException objects with pre-escaped messages.
92 throw new ValidationFailedExceptions($validationErrors);
93 }
94
95 return $userSettings;
96 }
97
98 /**
99 * Store the validated value into the field's storage and keep track of
100 * unique storages to submit later with {@see saveSettings()}
101 * @param mixed $value
102 */
103 private function storeValidatedSetting(Field $field, $value): void
104 {
105 $field->storage->set($field->getSettingName(), $value);
106
107 // Remember the unique storages to submit later with saveSettings()
108 if (empty($this->uniqueFieldStorages[$field->getStorageName()])) {
109 $this->uniqueFieldStorages[$field->getStorageName()] = $field->getStorage();
110 }
111 }
112
113 /**
114 * Save all updated unique storages that were mapped in
115 * {@see storeValidatedSetting}. The method collects any errors and throws a
116 * single exception if any storage submission fails.
117 *
118 * @throws StorageSubmitException when any storage submission fails - is
119 * caught in {@see UserSettingsEndpoint} and the message we return here is
120 * only shown to the user has WP_DEBUG set to true.
121 */
122 private function saveValidatedSettings(): void
123 {
124 $requestErrors = [];
125 foreach ($this->uniqueFieldStorages as $storage) {
126 try {
127 $storage->save();
128 } catch (\Exception $e) {
129 $requestErrors[$storage->name] = $e->getMessage();
130 continue; // So we can try to submit other storages
131 }
132 }
133
134 if (count($requestErrors) > 0) {
135 $exception = new StorageSubmitException(__('Something went wrong while submitting the settings!', 'metricool'));
136 $exception->setErrors($requestErrors);
137 throw $exception;
138 }
139 }
140 }
141