Сегодня поговорим о добавлении настроек на уровне пользователя. Есть несколько способов сделать это и мы сначала рассмотрим их, а потом перейдем к выбору, который я сделал в своем приложении Pulse.
Так много возможностей
По мере того как ваше приложение растёт, всё более вероятно, что ваши пользователи захотят настраивать его под себя. И предоставление им возможности делать это — очень важно для дальнейшего успеха приложения.
Самый простой способ решить эту проблему — пользовательские настройки, причем их реализация может значительно различаться. В случае с Pulse я остановился на базе данных. При этом у меня были варианты, либо просто добавить столбец настроек в таблицу Users, либо сделать таблицу с настройками и соединить её с пользователем с помощью внешнего ключа.
Хотя второй вариант дает больше возможностей, но требует выполнения второго запроса, для получения пользовательских настроек. В то время как Laravel уже подгрузила запись для аутентифицированного пользователя и добавление дополнительного столбца избавляет от дополнительного запроса к базе данных.
Добавление функционала
Первое, что нам нужно сделать, это добавить столбец настроек в таблицу пользователей:
// Users migration $table -> text("settings");
Вы можете выбрать формат большого текста, если нужно, но в большинстве случаев стандартного текста должно хватить для всех ваших данных.
Далее, нужно сказать Laravel, что этот атрибут является массивом и должен использовать JSON кодирование/декодирование при доступе к нему. Мы можем легко сделать это, добавив столбец в массив $casts в модели User, например:
// User model protected $casts = ["settings" => "array"];
Получение пользовательских настроек
Пришло время добавить несколько вспомогательных методов для работы с настройками. Мы начнем с простого метода доступа, который позволяет указать имя параметра, который нам нужно, а также резервное значение, которое мы можем использовать, если параметр не существует (по дефолту мы будем использовать null):
/** * Retrieve a setting with a given name or fall back to the default. * */ public function setting(string $name, $default = null) { if (array_key_exists($name, $this->settings)) { return $this->settings[$name]; } return $default; }
Если вы хотите получить все настройки одновременно, то просто обратитесь к свойству настроек в самой модели пользователя, например, так:
$settings = $user->settings;
Запись настроек в базу данных
Помимо доступа, нам также понадобятся средства для обновления настроек. Однако это не просто перезапись массива. Если бы мы это сделали, то удалили бы все настройки, которых нет в новом массиве, выданном на замену.
Кроме того, разумно предположить, что в приложении есть страница настроек, где вы можете изменить и сохранить несколько настроек одновременно, поэтому для смягчения обеих этих проблем мы будем использовать array_merge, для гарантии, что любые новые настройки будут автоматически добавлены, а любые существующие настройки — обновлены, чтобы использовать их новые значения (при условии, что новое значение предоставляется).
Мы также добавим логический параметр $save, который по умолчанию имеет значение true. Таким образом, вызов метода автоматически сохранит изменения, однако, если вы хотите выполнить дальнейшие операции над моделью User перед сохранением, у вас есть возможность отключить функцию сохранения. Мы будем называть этот метод «settings», поскольку потенциально работаем более чем с одной настройкой:
/** * Update one or more settings and then optionally save the model. * */ public function settings(array $revisions, bool $save = true) : self { $this->settings = array_merge($this->settings, $revisions); if ($save) { $this->save(); } return $this; }
Тестирование
В завершении давайте добавим несколько модульных тестов, которые подтвердят, что методы работают так, как мы ожидаем. Мы проверим, что можно получить доступ к настройке, что можно вернуть дефолтное значению, и что мы можем сохранить измененные настройки в базе данных:
/** @test */ public function a_user_can_get_a_setting() { $settings = ["settings" => ["foo" => "bar"]]; $user = factory(User::class, 1)->create($settings); $this->assertEquals("bar", $user->setting("foo")); $this->assertNull($user->setting("baz")); $this->assertEquals(5, $user->setting("baz", 5)); } /** @test */ public function a_user_can_change_settings() { $settings = ["settings" => ["foo" => "bar"]]; $user = factory(User::class, 1)->create($settings); $this->assertEquals( "world", $user->settings(["foo" => "world"], false)->setting("foo") ); $this->assertEquals( "hello", $user->settings(["baz" => "hello"], false)->setting("baz") ); $this->assertEquals( ["foo" => "bar"], $user->refresh()->settings ); } /** @test */ public function a_user_can_change_and_save_settings() { $settings = ["settings" => ["foo" => "bar"]]; $user = factory(User::class, 1)->create($settings); $this->assertEquals( "world", $user->settings(["foo" => "world"])->setting("foo") ); $this->assertEquals( ["foo" => "world"], $user->refresh()->settings ); }
Завершение
Хотя в Laravel нет встроенных пользовательских настроек, но, как вы увидели в этой статье, добавить такой функционал очень просто, в том числе, сделать дефолтные настройки, автоматическое сохранение и массовое обновление.
Еще раз спасибо и хорошего кода!
Автор: Matt Kingshott
Перевод: Алексей Широков
Наш Телеграм-канал — следите за новостями о Laravel.