Создание гибкой функции мягкого удаления для таблиц базы данных MySql с помощью AdonisJS

Если вы хотите мягко удалить данные в Adonis v5, к сожалению, это не встроено в основную архитектуру. Если вы не знакомы с концепцией мягкого удаления, это концепция, когда вы удаляете данные из базы данных, вы фактически не удаляете их, а скорее устанавливаете флаг, указывающий, что они находятся в удаленном состоянии. Эти строки не должны возвращаться в запросах к базе данных, но все равно будут существовать, если они понадобятся в будущем.

Мягкое удаление дает несколько преимуществ в зависимости от требований вашего бизнеса.

  • По закону от вас могут потребовать хранить данные либо в базе данных, либо в резервной копии, но вам потребуется время перехода для реализации резервного копирования.
  • Связанные данные зависят от того, существуют ли удаленные данные, даже если они больше не будут использоваться пользователем или другими службами.
  • Он может создать «корзину для мусора» для пользователей, что ускорит и упростит восстановление данных.
  • Создает отслеживаемую историю для внутреннего аудита или аудита клиентов.

Есть много преимуществ, но знайте, что вы принимаете решение хранить большой объем данных, и должны понимать последствия этого в долгосрочной перспективе.

Начиная

Флаг, который мы будем использовать в этом руководстве, - это столбец, добавляемый в таблицы, которые мы хотим назвать deleted_at. Этот столбец поможет нам узнать, какие строки базы данных активны, а какие удалены для будущих запросов и обновлений. Для начала у нас уже должен быть проект Adonis, созданный с выбранной нами базой данных. Мы будем использовать MySql в качестве основы. Мы также предполагаем, что эти два шага в этом руководстве уже выполнены. После того, как проект и схема базы данных настроены, нам нужно будет создать нашу первую миграцию.

node ace make:migration posts

Это создаст перенос сообщений, который мы будем использовать для создания и обратимого удаления сообщений в нашей базе данных. Для мягкого удаления мы будем использовать deleted_at с типом столбца datetime. Таким образом, мы можем отслеживать как мягкое удаление публикации, так и время ее мягкого удаления. Мягкое удаление также может быть выполнено альтернативно, используя столбец is_deleted с типом boolean и отслеживая изменения, как правило, с помощью столбца updated_at.

// <app-name>/database/migrations/012345678987654321_posts.ts
import BaseSchema from '@ioc:Adonis/Lucid/Schema'
export default class Posts extends BaseSchema {
  protected tableName = 'posts'
public async up() {
    this.schema.createTable(this.tableName, (table) => {
      table.increments('id').primary()
      table.string("name", 254).notNullable();
      table.text("description");
      table.dateTime("deleted_at").defaultTo(null);
      table.timestamps(true)
    })
  }
public async down() {
    this.schema.dropTable(this.tableName)
  }
}

После выполнения миграции мы можем перенести нашу базу данных и настроить таблицу сообщений.

node ace migration:run

Подключение ORM

Нам нужно создать нашу модель, чтобы определить поля наших столбцов для ORM Адониса. Это будет иметь решающее значение для реализации мягкого удаления на лету и программно в различных функциях и контроллерах. Без этой модели для мягкого удаления потребовалось бы не только больше строк дублированного кода, но и больше ручного труда в любом месте и везде, где нам нужно управлять парадигмой мягкого удаления.

Следующая команда запустит нашу модель Post:

node ace make:model Post
// <app-name>/app/Models/Post.ts
import { DateTime } from 'luxon'
import { column, BaseModel } from '@ioc:Adonis/Lucid/Orm'
export default class Post extends BaseModel {    
  @column({ isPrimary: true })
  public id: string
  @column()
  public name: string
  @column()
  public body: string
  @column.dateTime({ serializeAs: null})
  public deletedAt: DateTime
  @column.dateTime({ autoCreate: true })
  public createdAt: DateTime
  @column.dateTime({ autoCreate: true, autoUpdate: true })
  public updatedAt: DateTime
}

Реализация мягкого удаления

Поскольку мы хотим, чтобы мягкое удаление происходило для любого возможного количества моделей, мы собираемся извлечь реализацию как службу Adonis. Поскольку в Adonis нет активной команды ace для создания службы, мы вручную создадим папку наших служб внутри папки приложения и создадим SoftDelete.ts служебный файл.

// <my-app>/app/Services/SoftDelete.ts
import { LucidRow } from '@ioc:Adonis/Lucid/Model'
import { DateTime } from 'luxon';
// Optional null check query
export const softDeleteQuery = (query: ModelQueryBuilderContract<typeof BaseModel>) => {
  query.whereNull('deleted_at')
}
export const softDelete = async (row: LucidRow, column: string = 'deletedAt') => {
  if(row[column]) {
    if(row[column].isLuxonDateTime) {
      // Deleted represented by a datetime 
      row[column] = DateTime.local();
    } else {
      // Deleted represented by a boolean 
      row[column] = true;
    }
    await row.save();
  }
}

Функция softDelete является наиболее важной частью и движком для масштабного распределения функций мягкого удаления на любое количество моделей. softDeleteQuery не является обязательным, и мы добавим его в запросы модели Post. Обе функции необходимо обновить в зависимости от того, как вы реализуете свой столбец с мягким удалением. При необходимости обновите обе функции, чтобы проверить соответствие столбца boolean или datetime, а также обновить имя столбца, по которому проверяются функции. Напоминаем, что в примерах этого руководства мы используем столбец deleted_at.

Добавление сервисов в модели

Мы собираемся добавить только что созданный сервис в модель Post. Adonis поставляется со встроенными хуками, которые позволяют нам перехватывать или отменять жизненный цикл модели. В нашем случае мы переопределим функцию удаления и обновим выборку и поиск, чтобы не включать строки, которые были удалены с помощью обратного удаления.

Требуемый импорт:

import { beforeFind,  beforeFetch } from '@ioc:Adonis/Lucid/Orm'
import { softDelete, softDeleteQuery } from '../Services/SoftDelete'

Ниже представлена ​​обобщенная модель Post, показывающая только что созданные импорты и реализации функций.

// Summarized Post.ts
import { beforeFind, beforeFetch } from '@ioc:Adonis/Lucid/Orm'
import { softDelete, softDeleteQuery } from '../Services/SoftDelete';
export default class Post extends BaseModel {
  // ... Additional model details here
  @beforeFind()
  public static softDeletesFind = softDeleteQuery;  
  @beforeFetch()
  public static softDeletesFetch = softDeleteQuery;
  
  public async softDelete(column?: string) {
    await softDelete(this, column);
  }
}

В приведенном выше коде у вас есть два варианта. Вы можете добавить к модели дополнительный метод, как это сделали мы. Это позволяет нам сохранить собственное удаление, а также добавить мягкое удаление. Опасность заключается в том, что если вы реализуете мягкое удаление без надлежащей документации и проверки кода, другой разработчик может использовать основной метод удаления, не зная, что мягкое удаление является методом перехода. Если вы хотите избежать этого, то вместо добавления нового метода вы можете переопределить метод удаления путем переназначения.

public async delete = softDelete;

Удалить все

Давайте продолжим и протестируем этот новый метод мягкого удаления. Мы пропустим создание маршрута и контроллера и продемонстрируем функции контроллера, которые будут вызывать get и delete.

В этом первом примере показано простое удаление, реализующее наш метод мягкого удаления.

public async delete ({ request, response, auth }: HttpContextContract) {
  try {
    const postId = request.input('id')
    const post = await Post.findOrFail(postId)
    await post.softDelete()
    return response.json({})
  } catch (error) {
    return response.json(error)
  }
}

В следующем примере демонстрируется реализация перехватчиков `beforeFetch` и` beforeFind`. В результате наши запросы вернут все строки, которые не были удалены обратимо.

public async getAll({ response }: HttpContextContract) {
  try {
    const posts = await Post.all()
    return response.json(posts)
  } catch (error) {
    return response.json(error)
  }
}

Вот и все! Мы не создали парадигму мягкого удаления, которую можно легко масштабировать на любую модель в нашей системе.

Последние мысли

Внедрение мягкого удаления - это мощная функция, которая позволяет вам сохранять и контролировать все данные в вашей базе данных. Он обладает как преимуществами сохранения данных и долгосрочного управления, так и предупреждением об обслуживании данных и экспоненциальном росте в долгосрочной перспективе. Если вы знаете обо всех вариантах и ​​последствиях, реализация мягкого удаления может стать уникальным и мощным инструментом по мере расширения вашего приложения или продукта.