@kirill-93

Как сделать мягкое удаление и использовать уникальные ключи?

Есть таблица books, в которой уникальное поле alias.
Делаю "мягкое" удаление, теперь добавилось поле deleted_at, и, чтобы удаленные книги не мешали создавать новые с таким же alias, я установил unique key на два поля: alias, deleted_at.
Теперь создавать новые записи с таким же alias можно, даже если есть такая "удаленная" запись.
Но теперь новая проблема: deleted_at по умолчанию null, поэтому можно сколько угодно создавать записей с одинаковыми alias.
Как сохранить уникальность записей?
  • Вопрос задан
  • 423 просмотра
Решения вопроса 3
Gomonov
@Gomonov
Я бы не использовал deleted_at как флаг, что сущность удалена - это больше справочная информация.
Завёл бы поле is_delete
Ответ написан
alexey-m-ukolov
@alexey-m-ukolov Куратор тега MySQL
На уровне БД (конкретно в MySQL) такое реализовать нельзя, нужно эту логику закладывать в приложение.
UPD: выше написана ерунда, читайте комментарии к ответу.

Примерно так это будет выглядеть (код из живого проекта, поэтому сущности другие, но смысл тот же):
class UniqueAmongActiveClients implements \Illuminate\Contracts\Validation\Rule
{
    public function passes($attribute, $value): bool
    {
        return Client::shouldBeUniqueAndItIs($attribute, $value, auth()->id());
    }
}

class Client extends Eloquent
{
    public static function shouldBeUniqueAndItIs(string $attribute, $value, ?int $excludedId = null): bool
    {
        $query = static::withoutTrashed()->where($attribute, $value);

        if ($excludedId) {
            $query->where('id', '<>', $excludedId);
        }

        return $query->doesntExist();
    }
}


P.S. Стандартное поле deleted_at заменять костылями, как советуют в соседних ответах, конечно, не нужно.
Ответ написан
@kirill-93 Автор вопроса
Готовый вариант такой:
1. Добавил поле is_alive tinyint(1) NULL [1]
2. Установил UNIQUE на alias + is_alive
3. Добавил обработчики на удаление и восстановление:
Book::deleting(function ($book) {
            $book->update(['is_alive' => null]);
        });

        Book::restoring(function ($book) {
            $book->update(['is_alive' => 1]);
        });
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
streetflush
@streetflush
Default value deleted_at
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы