• Добавлена автоматическая детекция типов связей • Реализован Proxy-доступ к связям через свойства • Поддержка каскадного удаления данных • Добавлены транзакции с откатом состояния • Обновлена документация и примеры использования Миграция: - Вместо методов xown()/shared() используйте прямое присваивание - Методы getXown() заменены на доступ через свойства
243 lines
8.1 KiB
Markdown
243 lines
8.1 KiB
Markdown
# GreenBean Документация
|
||
|
||
## 🌱 Введение
|
||
**GreenBean** — это современная ORM для Node.js, сочетающая простоту RedBeanPHP с мощью JavaScript.
|
||
Работает поверх JSON-хранилища, идеально подходит для прототипов, MVP и приложений с гибкой схемой данных.
|
||
|
||
### Ключевые концепции:
|
||
- **Динамическая схема** — не требует предварительного описания моделей
|
||
- **Живые объекты** — изменения сохраняются автоматически
|
||
- **Соглашения вместо конфигурации** — автоматическое разрешение связей
|
||
- **Транзакционный подход** — атомарные операции изменения данных
|
||
|
||
## 🛠 Установка
|
||
```bash
|
||
npm install greenbean uuid
|
||
```
|
||
|
||
## 🎯 Быстрый старт
|
||
### Создание простого блога
|
||
```javascript
|
||
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()`
|
||
|
||
```javascript
|
||
// Пример жизненного цикла
|
||
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. Социальная сеть
|
||
```javascript
|
||
// Пользователь и друзья (многие-ко-многим)
|
||
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. Электронная коммерция
|
||
```javascript
|
||
// Заказ и товары (многие-ко-многим)
|
||
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'
|
||
});
|
||
```
|
||
|
||
## 🔍 Расширенный поиск
|
||
### Поддерживаемые операторы
|
||
```javascript
|
||
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
|
||
});
|
||
```
|
||
|
||
### Сортировка и пагинация
|
||
```javascript
|
||
const posts = await gb.find('Post',
|
||
{ status: 'published' },
|
||
{
|
||
limit: 10,
|
||
offset: 20,
|
||
order: { created: 'DESC' }
|
||
}
|
||
);
|
||
```
|
||
|
||
## 🛡 Транзакции и безопасность
|
||
```javascript
|
||
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);
|
||
}
|
||
```
|
||
|
||
## 🧊 Заморозка данных
|
||
```javascript
|
||
// Защита от изменений
|
||
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. Сервисные классы
|
||
```javascript
|
||
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. Валидация данных
|
||
```javascript
|
||
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. Обработка ошибок
|
||
```javascript
|
||
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 | Удаляет объект и связи |
|
||
|
||
### Настройки
|
||
```javascript
|
||
new GreenBean('data.json', {
|
||
autoSave: false, // Автоматическое сохранение
|
||
freeze: false, // Начальное состояние блокировки
|
||
cacheTTL: 300000 // Время жизни кеша (5 минут)
|
||
});
|
||
```
|
||
|
||
## 📜 Лицензия
|
||
MIT License © 2025 Rockzo development
|
||
```
|