Skip to content

Кастомные методы

Kodzero SDK позволяет расширять модели собственными методами для реализации бизнес-логики.

Регистрация метода

Используйте статический метод registerMethod() для добавления кастомных методов к модели:

typescript
Model.registerMethod('methodName', function() {
  // Внутри метода доступен this — экземпляр модели
  const data = this.data()
  // ...
})

Простой пример

typescript
interface Car {
  _id: string | null
  make: string
  model: string
  year: number
}

const Car = kodzero.createModel<Car>({
  collection: 'cars'
})

// Регистрируем кастомный метод
Car.registerMethod('getDescription', function() {
  const data = this.data()
  return `${data.make} ${data.model} (${data.year})`
})

// Использование
const car = await Car.get('car_id')
console.log(car.getDescription())
// "Toyota Corolla (2020)"

Типизация кастомных методов

Для полной поддержки TypeScript создайте интерфейс для кастомных методов:

typescript
// Интерфейс данных
interface Car {
  _id: string | null
  make: string
  model: string
  year: number
  price: number
}

// Интерфейс кастомных методов
interface CarMethods {
  getDescription: () => string
  isVintage: () => boolean
  applyDiscount: (percent: number) => number
}

// Создаём модель с обоими типами
const Car = kodzero.createModel<Car, CarMethods>({
  collection: 'cars'
})

// Регистрируем методы
Car.registerMethod('getDescription', function() {
  const data = this.data()
  return `${data.make} ${data.model} (${data.year})`
})

Car.registerMethod('isVintage', function() {
  return this.data().year < 1980
})

Car.registerMethod('applyDiscount', function(percent: number) {
  const price = this.data().price
  return price - (price * percent / 100)
})

// Теперь TypeScript знает о кастомных методах
const car = await Car.get('car_id')
car.getDescription()      // ✅ TypeScript знает тип возвращаемого значения
car.isVintage()           // ✅ 
car.applyDiscount(10)     // ✅
car.unknownMethod()       // ❌ Ошибка компиляции

Практические примеры

Форматирование данных

typescript
interface User {
  _id: string | null
  firstName: string
  lastName: string
  email: string
}

interface UserMethods {
  getFullName: () => string
  getInitials: () => string
  formatEmail: () => string
}

const User = kodzero.createModel<User, UserMethods>({
  collection: 'users'
})

User.registerMethod('getFullName', function() {
  const { firstName, lastName } = this.data()
  return `${firstName} ${lastName}`
})

User.registerMethod('getInitials', function() {
  const { firstName, lastName } = this.data()
  return `${firstName[0]}${lastName[0]}`.toUpperCase()
})

User.registerMethod('formatEmail', function() {
  return this.data().email.toLowerCase()
})

Вычисляемые свойства

typescript
interface Order {
  _id: string | null
  items: Array<{ name: string; price: number; quantity: number }>
  discount: number
}

interface OrderMethods {
  getSubtotal: () => number
  getDiscount: () => number
  getTotal: () => number
  getItemCount: () => number
}

const Order = kodzero.createModel<Order, OrderMethods>({
  collection: 'orders'
})

Order.registerMethod('getSubtotal', function() {
  return this.data().items.reduce((sum, item) => {
    return sum + (item.price * item.quantity)
  }, 0)
})

Order.registerMethod('getDiscount', function() {
  const subtotal = this.getSubtotal()
  return subtotal * (this.data().discount / 100)
})

Order.registerMethod('getTotal', function() {
  return this.getSubtotal() - this.getDiscount()
})

Order.registerMethod('getItemCount', function() {
  return this.data().items.reduce((count, item) => count + item.quantity, 0)
})

// Использование
const order = await Order.get('order_id')
console.log('Подитог:', order.getSubtotal())
console.log('Скидка:', order.getDiscount())
console.log('Итого:', order.getTotal())
console.log('Товаров:', order.getItemCount())

Бизнес-логика

typescript
interface Task {
  _id: string | null
  title: string
  status: 'pending' | 'in_progress' | 'completed'
  priority: 'low' | 'medium' | 'high'
  dueDate: string
}

interface TaskMethods {
  isOverdue: () => boolean
  isHighPriority: () => boolean
  canComplete: () => boolean
  getStatusLabel: () => string
}

const Task = kodzero.createModel<Task, TaskMethods>({
  collection: 'tasks'
})

Task.registerMethod('isOverdue', function() {
  const dueDate = new Date(this.data().dueDate)
  return dueDate < new Date() && this.data().status !== 'completed'
})

Task.registerMethod('isHighPriority', function() {
  return this.data().priority === 'high'
})

Task.registerMethod('canComplete', function() {
  return this.data().status !== 'completed'
})

Task.registerMethod('getStatusLabel', function() {
  const labels = {
    pending: 'Ожидает',
    in_progress: 'В работе',
    completed: 'Завершена'
  }
  return labels[this.data().status]
})

// Использование
const tasks = await Task.findMany()
const overdueTasks = tasks.filter(async (taskData) => {
  const task = await Task.get(taskData._id)
  return task.isOverdue()
})

Доступ к данным в методах

Внутри кастомного метода:

  • this.data() — получить текущие данные
  • this.set(key, value) — изменить данные
  • this.save() — сохранить изменения
  • this.update() — обновить документ
  • this.delete() — удалить документ
  • this.validate() — валидировать данные
typescript
interface Product {
  _id: string | null
  name: string
  price: number
  stock: number
}

interface ProductMethods {
  reduceStock: (quantity: number) => Promise<void>
  isInStock: () => boolean
}

const Product = kodzero.createModel<Product, ProductMethods>({
  collection: 'products'
})

Product.registerMethod('reduceStock', async function(quantity: number) {
  const currentStock = this.data().stock
  if (currentStock < quantity) {
    throw new Error('Недостаточно товара на складе')
  }
  this.set('stock', currentStock - quantity)
  await this.update()
})

Product.registerMethod('isInStock', function() {
  return this.data().stock > 0
})

// Использование
const product = await Product.get('product_id')

if (product.isInStock()) {
  await product.reduceStock(5)
}

Следующие шаги

Опубликовано под лицензией ISC.