Source Code: SmartFactory/UserSettingsManager.php

  1: <?php
  2: /**
  3:  * This file contains the implementation of the interface ISettingsManager
  4:  * in the class UserSettingsManager for management of the user settings.
  5:  *
  6:  * @package System
  7:  *
  8:  * @author Oleg Schildt
  9:  */
 10: 
 11: namespace SmartFactory;
 12: 
 13: use \SmartFactory\Interfaces\ISettingsManager;
 14: use \SmartFactory\Interfaces\IErrorHandler;
 15: use \SmartFactory\DatabaseWorkers\DBWorker;
 16: use SmartFactory\Interfaces\ISettingsValidator;
 17: 
 18: /**
 19:  * Class for management of the user settings.
 20:  *
 21:  * When you introduce a new user setting. You have to create a column for it in the corresponding
 22:  * table add it to the initialization and specify the data type.
 23:  *
 24:  * The user settings are loaded only once by the start of the session and are kept until the session
 25:  * is valid. Saving of the user settings updates both the session and the database. When a settings are
 26:  * requested, it is taken from the session, not from the database.
 27:  *
 28:  * @see  ConfigSettingsManager
 29:  * @see  RuntimeSettingsManager
 30:  *
 31:  * @uses DatabaseWorkers\DBWorker
 32:  *
 33:  * @author Oleg Schildt
 34:  */
 35: class UserSettingsManager implements ISettingsManager
 36: {
 37:     /**
 38:      * Internal variable for storing the user id.
 39:      *
 40:      * @var string
 41:      *
 42:      * @see UserSettingsManager::setUserID()
 43:      *
 44:      * @author Oleg Schildt
 45:      */
 46:     protected string $user_id = "";
 47: 
 48:     /**
 49:      * Internal variable for storing the dbworker.
 50:      *
 51:      * @var ?DatabaseWorkers\DBWorker
 52:      *
 53:      * @author Oleg Schildt
 54:      */
 55:     protected ?DatabaseWorkers\DBWorker $dbworker;
 56: 
 57:     /**
 58:      * Internal variable for storing the state whether the translation should be started or commited or
 59:      * no transaction should be used because the saving process is a part of other saving process.
 60:      *
 61:      * @var bool
 62:      *
 63:      * @author Oleg Schildt
 64:      */
 65:     protected bool $no_own_transaction = true;
 66: 
 67:     /**
 68:      * Internal array for storing the settings tables.
 69:      *
 70:      * @var array
 71:      *
 72:      * @author Oleg Schildt
 73:      */
 74:     protected array $settings_tables = [];
 75: 
 76:     /**
 77:      * Internal array for storing the auxiliary tables for storing the multichoce values.
 78:      *
 79:      * @var array
 80:      *
 81:      * @author Oleg Schildt
 82:      */
 83:     protected array $multichoice_tables = [];
 84: 
 85:     /**
 86:      * Internal variable for storing the current context.
 87:      *
 88:      * @var string
 89:      *
 90:      * @see UserSettingsManager::getContext()
 91:      * @see UserSettingsManager::setContext()
 92:      *
 93:      * @author Oleg Schildt
 94:      */
 95:     protected string $context = "default";
 96: 
 97:     /**
 98:      * Internal variable for storing the validator.
 99:      *
100:      * @var ?\SmartFactory\Interfaces\ISettingsValidator
101:      *
102:      * @see UserSettingsManager::getValidator()
103:      * @see UserSettingsManager::setValidator()
104:      *
105:      * @author Oleg Schildt
106:      */
107:     protected ?\SmartFactory\Interfaces\ISettingsValidator $validator = null;
108: 
109:     /**
110:      * Internal variable for storing the array of the settings values.
111:      *
112:      * @var array
113:      *
114:      * @author Oleg Schildt
115:      */
116:     protected array $settings = [];
117: 
118:     /**
119:      * This is internal auxiliary function for checking that the settings
120:      * manager is intialized correctly.
121:      *
122:      * @return void
123:      *
124:      * @throws \Exception
125:      * It might throw an exception in the case of any errors:
126:      *
127:      * - if some parameters are missing.
128:      * - if dbworker does not extend {@see \SmartFactory\DatabaseWorkers\DBWorker}.
129:      * - if some parameters are not of the proper type.
130:      *
131:      * @author Oleg Schildt
132:      */
133:     protected function validateParameters(): void
134:     {
135:         if (empty($this->dbworker)) {
136:             throw new \Exception("The 'dbworker' is not specified!");
137:         }
138: 
139:         if (!$this->dbworker instanceof DBWorker) {
140:             throw new \Exception(sprintf("The 'dbworker' does not extends the class '%s'!", DBWorker::class));
141:         }
142: 
143:         if (empty($this->settings_tables)) {
144:             throw new \Exception("The 'settings_tables' are not defined!");
145:         }
146: 
147:         if (!is_array($this->settings_tables)) {
148:             throw new \Exception("Settings tables 'settings_tables' must be an array!");
149:         }
150: 
151:         if (!empty($this->multichoice_tables) && !is_array($this->multichoice_tables)) {
152:             throw new \Exception("Multichoice tables 'multichoice_tables' must be an array!");
153:         }
154:     } // validateParameters
155: 
156:     /**
157:      * This is internal auxiliary function for storing the settings
158:      * to the target user table defined by the initialization.
159:      *
160:      * @param array $data
161:      * The array with the settings values to be saved.
162:      *
163:      * @return void
164:      *
165:      * @throws \Throwable
166:      * It might throw an exception in the case of any errors:
167:      *
168:      * - if the query fails or if some object names are invalid.
169:      * - if some parameters are missing.
170:      * - if dbworker does not extend {@see \SmartFactory\DatabaseWorkers\DBWorker}.
171:      * - if some parameters are not of the proper type.
172:      * - if the query fails or if some object names are invalid.
173:      *
174:      * @see UserSettingsManager::loadSettingsData()
175:      *
176:      * @author Oleg Schildt
177:      */
178:     protected function saveSettingsData(array $data): void
179:     {
180:         $this->validateParameters();
181: 
182:         $this->dbworker->connect();
183: 
184:         if (empty($this->no_own_transaction)) {
185:             $this->dbworker->start_transaction();
186:         }
187: 
188:         try {
189:             foreach ($this->settings_tables as $table => $fields) {
190:                 // First field is the ID field
191:                 $uid_field = "";
192:                 $uid_field_type = "";
193: 
194:                 $update_string = "";
195: 
196:                 $insert_fields_string = "";
197:                 $insert_values_string = "";
198: 
199:                 $c = 0;
200:                 foreach ($fields as $field => $field_type) {
201:                     $c++;
202: 
203:                     if ($c == 1) {
204:                         $uid_field = $field;
205:                         $uid_field_type = $field_type;
206:                         continue;
207:                     }
208: 
209:                     $value = "";
210: 
211:                     if (!empty($data[$table . "." . $field])) {
212:                         $value = $data[$table . "." . $field];
213:                     }
214: 
215:                     if (isset($data[$field])) {
216:                         $value = $data[$field];
217:                     }
218: 
219:                     $value = $this->dbworker->prepare_for_query($value, $field_type);
220: 
221:                     $update_string .= $field . " = " . $value . ",\n";
222: 
223:                     $insert_fields_string .= $field . ", ";
224:                     $insert_values_string .= $value . ", ";
225:                 }
226: 
227:                 $user_id = $this->dbworker->prepare_for_query($this->user_id, $uid_field_type);
228: 
229:                 $insert_fields_string .= $uid_field . ", ";
230:                 $insert_values_string .= $user_id . ", ";
231: 
232:                 $query = "select\n";
233:                 $query .= "1\n";
234:                 $query .= "from " . $table . "\n";
235:                 $query .= "where " . $uid_field . " = " . $user_id;
236: 
237:                 $this->dbworker->execute_query($query);
238: 
239:                 $must_insert = false;
240:                 if (!$this->dbworker->fetch_row()) {
241:                     $must_insert = true;
242:                 }
243: 
244:                 $this->dbworker->free_result();
245: 
246:                 if ($must_insert) {
247:                     $query = "insert into " . $table . "\n";
248:                     $query .= "(" . trim($insert_fields_string, ", ") . ")\n";
249:                     $query .= "values\n";
250:                     $query .= "(" . trim($insert_values_string, ", ") . ")\n";
251:                 } else {
252:                     $query = "update " . $table . " set\n";
253:                     $query .= trim($update_string, ",\n") . "\n";
254:                     $query .= "where " . $uid_field . " = " . $user_id;
255:                 }
256: 
257:                 $this->dbworker->execute_query($query);
258:             }
259: 
260:             // update the subtables
261: 
262:             foreach ($this->multichoice_tables as $table => $fields) {
263:                 // First field is the ID field
264:                 $uid_field = "";
265:                 $uid_field_type = "";
266: 
267:                 // Second field is the value field
268:                 $value_field = "";
269:                 $value_field_type = "";
270: 
271:                 $c = 1;
272:                 foreach ($fields as $field => $field_type) {
273:                     if ($c == 1) {
274:                         $uid_field = $field;
275:                         $uid_field_type = $field_type;
276:                     } elseif ($c == 2) {
277:                         $value_field = $field;
278:                         $value_field_type = $field_type;
279:                     } else {
280:                         break;
281:                     }
282: 
283:                     $c++;
284:                 }
285: 
286:                 if (!empty($data[$table])) {
287:                     $value = $data[$table];
288:                 } else {
289:                     $value = [];
290:                 }
291: 
292:                 // insert the values that are in the list but not in the table
293: 
294:                 $in_list = "";
295:                 $user_id = $this->dbworker->prepare_for_query($this->user_id, $uid_field_type);
296: 
297:                 foreach ($value as $entry) {
298:                     if (empty($entry)) {
299:                         continue;
300:                     }
301: 
302:                     $entry = $this->dbworker->prepare_for_query($entry, $value_field_type);
303: 
304:                     $query = "select 1 from $table where $uid_field = $user_id";
305: 
306:                     if ($value == "NULL") {
307:                         $query .= " and $value_field is NULL";
308:                     } else {
309:                         $query .= " and $value_field = $entry";
310:                     }
311: 
312:                     $this->dbworker->execute_query($query);
313: 
314:                     $must_insert = true;
315:                     if ($this->dbworker->fetch_row()) {
316:                         $must_insert = false;
317:                     }
318: 
319:                     $this->dbworker->free_result();
320: 
321:                     if ($must_insert) {
322:                         $query = "insert into $table ($uid_field, $value_field) values ($user_id, $entry)";
323: 
324:                         $this->dbworker->execute_query($query);
325:                     }
326: 
327:                     $in_list .= $entry . ",\n";
328:                 }
329: 
330:                 $in_list = trim($in_list, " ,\n\r");
331: 
332:                 $where = "where " . $uid_field . " = " . $user_id;
333: 
334:                 // delete the values that are no more in the new list but still in the table.
335: 
336:                 if (empty($in_list)) {
337:                     $query = "delete from $table\n" . $where;
338:                 } else {
339:                     $query = "delete from $table\n" . $where . " and " . $value_field . " not in ($in_list)";
340:                 }
341: 
342:                 $this->dbworker->execute_query($query);
343:             }
344:         } catch (\Throwable $ex) {
345:             if (empty($this->no_own_transaction)) {
346:                 $this->dbworker->rollback_transaction();
347:             }
348: 
349:             singleton(IErrorHandler::class)->handleException($ex, E_USER_ERROR);
350: 
351:             throw $ex;
352:         }
353: 
354:         if (empty($this->no_own_transaction)) {
355:             $this->dbworker->commit_transaction();
356:         }
357:     } // saveSettingsData
358: 
359:     /**
360:      * This is internal auxiliary function for loading the settings from the target user table
361:      * defined by the initialization.
362:      *
363:      * @param array &$data
364:      * The target array with the settings values to be loaded.
365:      *
366:      * @return void
367:      *
368:      * @throws \Exception
369:      * It might throw an exception in the case of any errors:
370:      *
371:      * - if the query fails or if some object names are invalid.
372:      * - if some parameters are missing.
373:      * - if dbworker does not extend {@see \SmartFactory\DatabaseWorkers\DBWorker}.
374:      * - if some parameters are not of the proper type.
375:      * - if the query fails or if some object names are invalid.
376:      *
377:      * @see UserSettingsManager::saveSettingsData()
378:      *
379:      * @author Oleg Schildt
380:      */
381:     protected function loadSettingsData(array &$data): void
382:     {
383:         $this->validateParameters();
384: 
385:         $this->dbworker->connect();
386: 
387:         foreach ($this->settings_tables as $table => $fields) {
388:             // First field is the ID field
389:             $uid_field = "";
390:             $uid_field_type = "";
391: 
392:             foreach ($fields as $field => $field_type) {
393:                 $uid_field = $field;
394:                 $uid_field_type = $field_type;
395:                 break;
396:             }
397: 
398:             $query = "select\n";
399: 
400:             $query .= implode(", ", array_keys($fields)) . "\n";
401: 
402:             $query .= "from " . $table . "\n";
403: 
404:             $user_id = $this->dbworker->prepare_for_query($this->user_id, $uid_field_type);
405:             $query .= "where " . $uid_field . " = " . $user_id;
406: 
407:             $this->dbworker->execute_query($query);
408: 
409:             if ($this->dbworker->fetch_row()) {
410:                 foreach ($fields as $field => $type) {
411:                     $data[$field] = $this->dbworker->field_by_name($field, $type); // short name (only field name)
412:                     $data[$table . "." . $field] = $data[$field]; // full name (table name and field name)
413: 
414:                 }
415:             }
416: 
417:             $this->dbworker->free_result();
418:         }
419: 
420:         if (empty($this->multichoice_tables)) {
421:             return;
422:         }
423: 
424:         foreach ($this->multichoice_tables as $table => $fields) {
425:             // First field is the ID field
426:             $uid_field = "";
427:             $uid_field_type = "";
428: 
429:             // Second field is the value field
430:             $value_field = "";
431:             $value_field_type = "";
432: 
433:             $c = 1;
434:             foreach ($fields as $field => $field_type) {
435:                 if ($c == 1) {
436:                     $uid_field = $field;
437:                     $uid_field_type = $field_type;
438:                 } elseif ($c == 2) {
439:                     $value_field = $field;
440:                     $value_field_type = $field_type;
441:                 } else {
442:                     break;
443:                 }
444: 
445:                 $c++;
446:             }
447: 
448:             $query = "select " . $value_field . "\n";
449: 
450:             $query .= "from " . $table . "\n";
451: 
452:             $user_id = $this->dbworker->prepare_for_query($this->user_id, $uid_field_type);
453:             $query .= "where " . $uid_field . " = " . $user_id;
454: 
455:             $this->dbworker->execute_query($query);
456: 
457:             $data[$table] = [];
458: 
459:             while ($this->dbworker->fetch_row()) {
460:                 $data[$table][] = $this->dbworker->field_by_name($value_field, $value_field_type);
461:             }
462: 
463:             $this->dbworker->free_result();
464:         }
465:     } // loadSettingsData
466: 
467:     /**
468:      * Initializes the settings manager parameters.
469:      *
470:      * @param array $parameters
471:      * Settings for saving and loading as an associative array in the form key => value:
472:      *
473:      * - $parameters["dbworker"] - the dbworker to use for loading and storing settings.
474:      * - $parameters["settings_tables"] - the definitions of the settings tables.
475:      * - $parameters["multichoice_tables"] - the definitions of the auxiliary tables for the multichoice values.
476:      *
477:      * Example:
478:      *
479:      * ```php
480:      *   $instance->init([
481:      *           "dbworker" => app_dbworker(),
482:      *
483:      *           "settings_tables" => [
484:      *                "users" => [
485:      *                    "id" => DBWorker::DB_NUMBER,
486:      *                    "language" => DBWorker::DB_STRING,
487:      *                    "time_zone" => DBWorker::DB_STRING
488:      *                ],
489:      *                "user_forum_settings" => [
490:      *                    "user_id" => DBWorker::DB_NUMBER,
491:      *                    "signature" => DBWorker::DB_STRING,
492:      *                    "status" => DBWorker::DB_STRING,
493:      *                    "hide_pictures" => DBWorker::DB_NUMBER,
494:      *                    "hide_signatures" => DBWorker::DB_NUMBER
495:      *                ]
496:      *           ],
497:      *
498:      *           "multichoice_tables" => [
499:      *                "user_colors" => [
500:      *                    "user_id" => DBWorker::DB_NUMBER,
501:      *                    "color" => DBWorker::DB_STRING
502:      *                ]
503:      *           ]
504:      *   ]);
505:      * ```
506:      *
507:      * @return void
508:      *
509:      * @throws \Exception
510:      * It might throw an exception in the case of any errors:
511:      *
512:      * - if some parameters are missing.
513:      * - if dbworker does not extend {@see \SmartFactory\DatabaseWorkers\DBWorker}.
514:      * - if some parameters are not of the proper type.
515:      *
516:      * @throws \Exception
517:      * It might throw an exception in the case of any errors.
518:      *
519:      * @author Oleg Schildt
520:      */
521:     public function init(array $parameters): void
522:     {
523:         if (!empty($parameters["dbworker"])) {
524:             $this->dbworker = $parameters["dbworker"];
525:         }
526: 
527:         if (!empty($parameters["settings_tables"])) {
528:             $this->settings_tables = $parameters["settings_tables"];
529:         }
530:         if (!empty($parameters["multichoice_tables"])) {
531:             $this->multichoice_tables = $parameters["multichoice_tables"];
532:         }
533: 
534:         $this->validateParameters();
535:     } // init
536: 
537:     /**
538:      * Sets the dbworker to be used for working with the database.
539:      *
540:      * @param DatabaseWorkers\DBWorker $dbworker
541:      * The dbworker to be used for working with the database.
542:      *
543:      * @return void
544:      *
545:      * @see RecordsetManager::getDBWorker()
546:      *
547:      * @author Oleg Schildt
548:      */
549: 
550:     /**
551:      * Sets the dbworker to use for loading and storing settings.
552:      *
553:      * @param DatabaseWorkers\DBWorker $dbworker
554:      * The dbworker to be used for loading and storing settings.
555:      *
556:      * @param bool $no_own_transcation
557:      * The state whether the translation should be started or commited or
558:      * no transaction should be used because the saving process is a part of other saving process.
559:      *
560:      * @return void
561:      *
562:      * @throws \Exception
563:      * It might throw an exception in the case of any errors.
564:      *
565:      * @author Oleg Schildt
566:      * @see RecordsetManager::getDBWorker()
567:      *
568:      */
569:     public function setDBWorker(DatabaseWorkers\DBWorker $dbworker, bool $no_own_transcation = false): void
570:     {
571:         $this->dbworker = $dbworker;
572:         $this->no_own_transaction = $no_own_transcation;
573: 
574:         if (empty($this->dbworker)) {
575:             throw new \Exception("The 'dbworker' is not specified!");
576:         }
577: 
578:         if (!$this->dbworker instanceof DBWorker) {
579:             throw new \Exception(sprintf("The 'dbworker' does not extends the class '%s'!", DBWorker::class));
580:         }
581:     } // setDBWorker
582: 
583:     /**
584:      * Sets the validator for the settings.
585:      *
586:      * @param ISettingsValidator $validator
587:      * The settings validator.
588:      *
589:      * @return void
590:      *
591:      * @see UserSettingsManager::getValidator()
592:      * @see UserSettingsManager::validateSettings()
593:      *
594:      * @author Oleg Schildt
595:      */
596:     public function setValidator(ISettingsValidator $validator): void
597:     {
598:         $this->validator = $validator;
599:     } // setValidator
600: 
601:     /**
602:      * Returns the validator for the settings.
603:      *
604:      * @return ?\SmartFactory\Interfaces\ISettingsValidator
605:      * Returns the validator for the settings or null if none is defined.
606:      *
607:      * @see UserSettingsManager::setValidator()
608:      * @see UserSettingsManager::validateSettings()
609:      *
610:      * @author Oleg Schildt
611:      */
612:     public function getValidator(): ?\SmartFactory\Interfaces\ISettingsValidator
613:     {
614:         return $this->validator;
615:     } // getValidator
616: 
617:     /**
618:      * Sets the settings context.
619:      *
620:      * Settings might be edited not in one dialog,
621:      * There can be several masks for different type of settings,
622:      * or the settings can be configured in a wizard.
623:      * In this case, only a subset of settings has to be
624:      * saved and validated.
625:      * To be able to write a flexible validator for the subsets,
626:      * the $context is used. It can be - step1, step1, server_settings,
627:      * db_connection_settings etc.
628:      *
629:      * @param string $context
630:      * The settings context.
631:      *
632:      * @return void
633:      *
634:      * @see UserSettingsManager::getContext()
635:      *
636:      * @author Oleg Schildt
637:      */
638:     public function setContext(string $context = "default"): void
639:     {
640:         $this->context = $context;
641:     } // setContext
642: 
643:     /**
644:      * Returns the current settings context.
645:      *
646:      * Settings might be edited not in one dialog,
647:      * There can be several masks for different type of settings,
648:      * or the settings can be configured in a wizard.
649:      * In this case, only a subset of settings has to be
650:      * saved and validated.
651:      * To be able to write a flexible validator for the subsets,
652:      * the $context is used. It can be - step1, step1, server_settings,
653:      * db_connection_settings etc.
654:      *
655:      * @return string
656:      * Returns the current settings context.
657:      *
658:      * @see UserSettingsManager::setContext()
659:      *
660:      * @author Oleg Schildt
661:      */
662:     public function getContext(): string
663:     {
664:         return $this->context;
665:     } // getContext
666: 
667:     /**
668:      * Sets a settings parameter.
669:      *
670:      * @param string $name
671:      * The name of the settings parameter.
672:      *
673:      * @param mixed $value
674:      * The value of the settings parameter.
675:      *
676:      * @return void
677:      *
678:      * @throws \Exception
679:      * It might throw an exception in the case of any errors:
680:      *
681:      * - if some parameters are missing.
682:      * - if dbworker does not extend {@see \SmartFactory\DatabaseWorkers\DBWorker}.
683:      * - if some parameters are not of the proper type.
684:      * - if the query fails or if some object names are invalid.
685:      *
686:      * @see UserSettingsManager::getParameter()
687:      * @see UserSettingsManager::setParameters()
688:      *
689:      * @author Oleg Schildt
690:      */
691:     public function setParameter(string $name, mixed $value): void
692:     {
693:         if (empty($this->settings)) {
694:             $this->loadSettings();
695:         }
696: 
697:         $this->settings[$name] = $value;
698:     } // setParameter
699: 
700:     /**
701:      * Sets settings parameters from an array.
702:      *
703:      * @param array $parameters
704:      * Array of parameters in the form key => value.
705:      *
706:      * @param bool $force_creation
707:      * Flag which defines whether the parameter should be created
708:      * if not exists. If false, only existing parameters are updated.
709:      * In the UserSettingsManager, this flag is ignored, because
710:      * the parameters are mapped to the database fields and cannot be created
711:      * on the fly.
712:      *
713:      * @return void
714:      *
715:      * @throws \Exception
716:      * It might throw an exception in the case of any errors:
717:      *
718:      * - if some parameters are missing.
719:      * - if dbworker does not extend {@see \SmartFactory\DatabaseWorkers\DBWorker}.
720:      * - if the query fails or if some object names are invalid.
721:      * - if the config file is not readable.
722:      *
723:      * @see UserSettingsManager::getParameter()
724:      * @see UserSettingsManager::setParameter()
725:      *
726:      * @author Oleg Schildt
727:      */
728:     public function setParameters(array $parameters, bool $force_creation = false): void
729:     {
730:         if (empty($this->settings)) {
731:             $this->loadSettings();
732:         }
733: 
734:         foreach ($parameters as $key => $val) {
735:             if (!array_key_exists($key, $this->settings)) {
736:                 continue;
737:             }
738: 
739:             $this->settings[$key] = $val;
740:         }
741:     } // setParameters
742: 
743:     /**
744:      * Returns the value of a settings parameter.
745:      *
746:      * @param string $name
747:      * The name of the settings parameter.
748:      *
749:      * @param mixed|null $default
750:      * The default value of the settings parameter if it is not set yet.
751:      * The parameter is a conformable way to pre-set a parameter
752:      * to a default value if its value is not set yet.
753:      *
754:      * @return mixed
755:      * Returns the value of the settings parameter.
756:      *
757:      * @throws \Exception
758:      * It might throw an exception in the case of any errors:
759:      *
760:      * - if some parameters are missing.
761:      * - if dbworker does not extend {@see \SmartFactory\DatabaseWorkers\DBWorker}.
762:      * - if some parameters are not of the proper type.
763:      * - if the query fails or if some object names are invalid.
764:      *
765:      * @see UserSettingsManager::setParameter()
766:      * @see UserSettingsManager::setParameters()
767:      *
768:      * @author Oleg Schildt
769:      */
770:     public function getParameter(string $name, mixed $default = null): mixed
771:     {
772:         if (empty($this->settings)) {
773:             $this->loadSettings();
774:         }
775: 
776:         if (!isset($this->settings[$name])) {
777:             return $default;
778:         }
779: 
780:         return $this->settings[$name];
781:     } // getParameter
782: 
783:     /**
784:      * Validates the current settings values.
785:      *
786:      * It should be called after settings the new values of the parameters
787:      * and before their saving.
788:      *
789:      * @return void
790:      *
791:      * @throws \Exception
792:      * It might throw exceptions in the case of any errors.
793:      *
794:      * @uses Interfaces\ISettingsValidator
795:      *
796:      * @see  UserSettingsManager::getValidator()
797:      * @see  UserSettingsManager::setValidator()
798:      *
799:      * @author Oleg Schildt
800:      */
801:     public function validateSettings(): void
802:     {
803:         if (empty($this->validator)) {
804:             return;
805:         }
806: 
807:         $this->validator->validate($this, $this->context);
808:     } // validateSettings
809: 
810:     /**
811:      * Loads the settings from the target user table.
812:      *
813:      * The user ID must be set before loading settings, see {@see \SmartFactory\UserSettingsManager::setUserID()}.
814:      *
815:      * @return void
816:      *
817:      * @throws \Exception
818:      * It might throw an exception in the case of any errors:
819:      *
820:      * - if some parameters are missing.
821:      * - if dbworker does not extend {@see \SmartFactory\DatabaseWorkers\DBWorker}.
822:      * - if some parameters are not of the proper type.
823:      * - if the query fails or if some object names are invalid.
824:      *
825:      * @see UserSettingsManager::saveSettings()
826:      *
827:      * @author Oleg Schildt
828:      */
829:     public function loadSettings(): void
830:     {
831:         $this->loadSettingsData($this->settings);
832:     } // loadSettings
833: 
834:     /**
835:      * Saves the settings from to the target user table.
836:      *
837:      * @return void
838:      *
839:      * @throws \Exception|\Throwable
840:      * It might throw an exception in the case of any errors:
841:      *
842:      * - if some parameters are missing.
843:      * - if dbworker does not extend {@see \SmartFactory\DatabaseWorkers\DBWorker}.
844:      * - if some parameters are not of the proper type.
845:      * - if the query fails or if some object names are invalid.
846:      *
847:      * @see UserSettingsManager::loadSettings()
848:      *
849:      * @author Oleg Schildt
850:      */
851:     public function saveSettings(): void
852:     {
853:         if (empty($this->settings)) {
854:             $this->loadSettings();
855:         }
856: 
857:         $this->saveSettingsData($this->settings);
858:     } // saveSettings
859: 
860:     /**
861:      * Sets the user id to be used for loading settings.
862:      *
863:      * The user ID must be set before loading settings, see {@see \SmartFactory\UserSettingsManager::loadSettings()}.
864:      *
865:      * @param string $user_id
866:      * The user ID.
867:      *
868:      * @return void
869:      *
870:      * @see UserSettingsManager::loadSettings()
871:      *
872:      * @see UserSettingsManager::getUserID()
873:      * @author Oleg Schildt
874:      */
875:     public function setUserID(string $user_id): void
876:     {
877:         $this->user_id = $user_id;
878:     } // setUserID
879: 
880:     /**
881:      * Gets the user id to be used for loading settings.
882:      *
883:      * @return int|string|null
884:      * Returns the user id or null if not set.
885:      *
886:      * @see UserSettingsManager::setUserID()
887:      *
888:      * @author Oleg Schildt
889:      */
890:     public function getUserID(): int|string|null
891:     {
892:         return $this->user_id;
893:     } // getUserID
894: } // UserSettingsManager