GreenBean/README.md
Vufer be9be042b8 feat(core): Реализация автоматических связей через соглашения
• Добавлена автоматическая детекция типов связей
• Реализован Proxy-доступ к связям через свойства
• Поддержка каскадного удаления данных
• Добавлены транзакции с откатом состояния
• Обновлена документация и примеры использования

Миграция:
- Вместо методов xown()/shared() используйте прямое присваивание
- Методы getXown() заменены на доступ через свойства
2025-02-17 23:23:00 +03:00

8.1 KiB
Raw Permalink Blame History

GreenBean Документация

🌱 Введение

GreenBean — это современная ORM для Node.js, сочетающая простоту RedBeanPHP с мощью JavaScript. Работает поверх JSON-хранилища, идеально подходит для прототипов, MVP и приложений с гибкой схемой данных.

Ключевые концепции:

  • Динамическая схема — не требует предварительного описания моделей
  • Живые объекты — изменения сохраняются автоматически
  • Соглашения вместо конфигурации — автоматическое разрешение связей
  • Транзакционный подход — атомарные операции изменения данных

🛠 Установка

npm install greenbean uuid

🎯 Быстрый старт

Создание простого блога

import GreenBean from 'greenbean';

// 1. Инициализация
const gb = new GreenBean('blog.json', {
  autoSave: true // Автосохранение при изменениях
});

// 2. Создание сущностей
const author = await gb.dispense('Author', {
  name: 'Анна',
  bio: 'JS разработчик из Москвы'
});

const post = await gb.dispense('Post', {
  title: 'Мой первый пост',
  content: '...'
});

// 3. Установка связей
post.author = author; // Один-ко-многим
post.tags = [        // Многие-ко-многим
  await gb.dispense('Tag', {name: 'программирование'}),
  await gb.dispense('Tag', {name: 'учебник'})
];

// 4. Добавление комментариев (Один-ко-многим)
post.comments = [
  await gb.dispense('Comment', {text: 'Отличный пост!'}),
  await gb.dispense('Comment', {text: 'Спасибо за статью'})
];

// 5. Получение связанных данных
const loadedPost = await gb.load('Post', post.id);
console.log('Автор:', (await loadedPost.author).name);
console.log('Теги:', await loadedPost.tags);
console.log('Комментарии:', await loadedPost.comments);

🔄 Жизненный цикл бина

  1. Созданиеdispense()
  2. Модификация — прямое изменение свойств
  3. Сохранение — автоматическое или через store()
  4. Удалениеtrash()
// Пример жизненного цикла
const user = await gb.dispense('User'); // 1. Создание
user.name = 'Иван';                     // 2. Модификация
await gb.store(user);                   // 3. Сохранение
await gb.trash(user);                   // 4. Удаление

🤝 Работа со связями

Правила именования

Тип связи Паттерн Пример
Один-ко-многим parent.children user.posts
Многие-ко-многим parent_relation таблица post_tags
Один-к-одному parent.prop + уникальность user.profile

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

1. Социальная сеть

// Пользователь и друзья (многие-ко-многим)
const user = await gb.dispense('User');
user.friends = [
  await gb.dispense('User', {name: 'Мария'}),
  await gb.dispense('User', {name: 'Петр'})
];

// Группы (один-ко-многим)
const group = await gb.dispense('Group');
group.members = [
  await gb.dispense('User', {name: 'Анна'}),
  await gb.dispense('User', {name: 'Сергей'})
];

2. Электронная коммерция

// Заказ и товары (многие-ко-многим)
const order = await gb.dispense('Order');
order.products = [
  await gb.dispense('Product', {name: 'Ноутбук'}),
  await gb.dispense('Product', {name: 'Чехол'})
];

// Платежная информация (один-к-одному)
order.payment = await gb.dispense('Payment', {
  amount: 999.99,
  status: 'completed'
});

🔍 Расширенный поиск

Поддерживаемые операторы

const results = await gb.find('User', {
  age: { gt: 18, lt: 30 },    // Больше 18 и меньше 30
  name: { like: 'Ив%' },      // Начинается с "Ив"
  email: { neq: null },       // Email не null
  rating: { between: [4, 5] } // Рейтинг от 4 до 5
});

Сортировка и пагинация

const posts = await gb.find('Post', 
  { status: 'published' },
  {
    limit: 10,
    offset: 20,
    order: { created: 'DESC' }
  }
);

🛡 Транзакции и безопасность

try {
  await gb.transaction(async () => {
    const product = await gb.dispense('Product');
    product.stock -= 1;
    
    const order = await gb.dispense('Order');
    order.items = [product];
    
    if (product.stock < 0) {
      throw new Error('Нет в наличии');
    }
  });
} catch (error) {
  console.error('Ошибка транзакции:', error.message);
}

🧊 Заморозка данных

// Защита от изменений
gb.freeze();

try {
  const user = await gb.dispense('User'); // Ошибка!
} catch (e) {
  console.error('База заморожена');
}

// Временная разморозка
gb.freeze(false);
const tempUser = await gb.dispense('User');
gb.freeze(true);

🧩 Лучшие практики

1. Сервисные классы

class UserService {
  constructor(gb) {
    this.gb = gb;
  }

  async register(userData) {
    const user = await this.gb.dispense('User', userData);
    user.verificationToken = crypto.randomBytes(32).toString('hex');
    return this.gb.store(user);
  }
}

2. Валидация данных

function validatePost(post) {
  const errors = [];
  
  if (!post.title || post.title.length < 5) {
    errors.push('Название слишком короткое');
  }
  
  if (!post.content || post.content.length < 100) {
    errors.push('Минимум 100 символов в содержании');
  }
  
  return errors;
}

3. Обработка ошибок

async function safeDeleteUser(userId) {
  try {
    const user = await gb.load('User', userId);
    await gb.trash(user);
    return true;
  } catch (error) {
    console.error(`Ошибка удаления пользователя ${userId}:`, error);
    return false;
  }
}

📚 Полное API

Основные методы

Метод Параметры Возвращает Описание
dispense(type, props) Тип, свойства Бин Создает новый объект
store(bean) Бин ID Сохраняет изменения
load(type, id) Тип, ID Бин/null Загружает по ID
find(type, criteria) Тип, критерии Массив бинов Поиск с фильтрацией
trash(bean) Бин void Удаляет объект и связи

Настройки

new GreenBean('data.json', {
  autoSave: false,  // Автоматическое сохранение
  freeze: false,    // Начальное состояние блокировки
  cacheTTL: 300000  // Время жизни кеша (5 минут)
});

📜 Лицензия

MIT License © 2025 Rockzo development