Основы работы с БД

Изучите базовые принципы работы с базами данных в MNRFY Framework

Введение

MNRFY Framework предоставляет мощный и интуитивный интерфейс для работы с различными типами баз данных. Система поддерживает MySQL, PostgreSQL и SQLite, обеспечивая единообразный API для всех типов подключений.

🔗 Множественные подключения

Подключайтесь к нескольким базам данных одновременно

🛡️ Безопасность

Автоматическая защита от SQL-инъекций

⚡ Производительность

Встроенное кеширование запросов

🔄 Транзакции

Поддержка транзакций для безопасных операций

Настройка подключения

Конфигурационный файл database.json

Создайте файл /src/config/database.json для настройки подключений:

{
    "connections": {
        "1752665380840": {
            "type": "mysql",
            "host": "localhost",
            "port": 3306,
            "database": "mnrfy_app",
            "username": "mnrfy_user",
            "password": "secure_password",
            "charset": "utf8mb4",
            "collation": "utf8mb4_unicode_ci",
            "strict": true,
            "engine": "InnoDB"
        },
        "sqlite_connection": {
            "type": "sqlite",
            "path": "/src/sqlite/app.db"
        },
        "postgres_connection": {
            "type": "postgresql",
            "host": "localhost",
            "port": 5432,
            "database": "mnrfy_app",
            "username": "postgres",
            "password": "password",
            "charset": "utf8",
            "schema": "public"
        }
    },
    "default": "1752665380840"
}

Типы баз данных

Тип Описание Конфигурация
MySQL Популярная реляционная БД host, port, database, username, password
PostgreSQL Мощная объектно-реляционная БД host, port, database, username, password, schema
SQLite Локальная файловая БД path (путь к файлу .db)

Автоматическое создание SQLite

При использовании IDE MNRFY, SQLite файлы создаются автоматически в папке /src/sqlite/:

/src/sqlite/
├── users.db           # База пользователей
├── products.db        # База товаров
└── logs.db            # База логов

Основные методы работы

Получение подключения

<!-- В шаблонах -->
{% set database = db('1752665380840') %}

<!-- Использование по умолчанию -->
{% set database = db() %}

Получение записей (SELECT)

<!-- Все записи -->
{% set users = db('1752665380840')->getItems('users') %}

<!-- С условиями -->
{% set activeUsers = db('1752665380840')->getItems('users', {
    'active': 1,
    'ban': 0
}) %}

<!-- С выбором полей -->
{% set usernames = db('1752665380840')->getItems('users', {}, [
    'id', 'login', 'email'
]) %}

<!-- С сортировкой -->
{% set sortedUsers = db('1752665380840')->getItems(
    'users',           # таблица
    {'ban': 0},       # условия
    ['*'],            # поля
    'created_at',     # поле сортировки
    'DESC'            # направление
) %}

<!-- С лимитом -->
{% set recentUsers = db('1752665380840')->getItems(
    'users',
    {'active': 1},
    ['*'],
    'created_at',
    'DESC',
    null,             # сырой SQL
    10                # лимит
) %}

Получение одной записи

<!-- По ID -->
{% set user = db('1752665380840')->getItem('users', {'id': userId}) %}

<!-- По логину -->
{% set user = db('1752665380840')->getItem('users', {'login': username}) %}

<!-- По email -->
{% set user = db('1752665380840')->getItem('users', {
    'email': email,
    'active': 1
}) %}

Подсчет записей

<!-- Общее количество -->
{% set totalUsers = db('1752665380840')->countItems('users') %}

<!-- С условиями -->
{% set activeUsers = db('1752665380840')->countItems('users', {'active': 1}) %}
{% set bannedUsers = db('1752665380840')->countItems('users', {'ban': 1}) %}
{% set onlineUsers = db('1752665380840')->countItems('users', {
    'last_seen': '>' ~ (time() - 300)
}) %}

Условия WHERE

Простые условия

<!-- Равенство -->
{% set admins = db('1752665380840')->getItems('users', {'role': 'admin'}) %}

<!-- Не равенство -->
{% set nonGuests = db('1752665380840')->getItems('users', {'role': '!=guest'}) %}

<!-- Числовые сравнения -->
{% set richUsers = db('1752665380840')->getItems('users', {'balance': '>1000'}) %}
{% set youngUsers = db('1752665380840')->getItems('users', {'age': '<25'}) %}
{% set eligibleUsers = db('1752665380840')->getItems('users', {'age': '>=18'}) %}

Поиск по шаблону

<!-- LIKE запросы -->
{% set johnUsers = db('1752665380840')->getItems('users', {'name': '%John%'}) %}
{% set gmailUsers = db('1752665380840')->getItems('users', {'email': '%@gmail.com'}) %}

<!-- Начинается с -->
{% set aUsers = db('1752665380840')->getItems('users', {'login': 'admin%'}) %}

Множественный выбор (IN)

<!-- Массив значений -->
{% set moderators = db('1752665380840')->getItems('users', {
    'role': ['admin', 'moderator', 'supervisor']
}) %}

{% set specificUsers = db('1752665380840')->getItems('users', {
    'id': [1, 5, 10, 15, 20]
}) %}

Логические операторы

AND условия (по умолчанию)

<!-- Все условия должны выполняться -->
{% set targetUsers = db('1752665380840')->getItems('users', {
    'active': 1,
    'ban': 0,
    'verified': 1,
    'balance': '>100'
}) %}

OR условия

<!-- Любое из условий -->
{% set vipUsers = db('1752665380840')->getItems('users', {
    'role': 'admin',
    'balance': '>10000',
    'is_premium': 1,
    'implode': 'OR'
}) %}

Работа с датами

Временные интервалы

<!-- Сегодня -->
{% set todayUsers = db('1752665380840')->getItems('users', {
    'created_at': '>=' ~ strtotime('today')
}) %}

<!-- За последнюю неделю -->
{% set weekUsers = db('1752665380840')->getItems('users', {
    'created_at': '>' ~ (time() - 604800)
}) %}

<!-- За последний час онлайн -->
{% set recentlyOnline = db('1752665380840')->getItems('users', {
    'last_seen': '>' ~ (time() - 3600)
}) %}

Диапазоны дат

<!-- Между датами -->
{% set monthUsers = db('1752665380840')->getItems('users', {
    'created_at': '>=' ~ strtotime('first day of this month'),
    'created_at ': '<=' ~ strtotime('last day of this month')
}) %}

Пагинация

Автоматическая пагинация

<!-- Получение страницы из запроса -->
{% set page = request.input('page', 1) %}
{% set perPage = 20 %}

<!-- Пагинированный результат -->
{% set users = db('1752665380840')->paginate(
    'users',           # таблица
    page,              # номер страницы
    perPage,           # записей на страницу
    {'ban': 0},       # условия
    'created_at',      # сортировка
    'DESC'             # направление
) %}

<!-- Отображение данных -->
{% foreach users.data as user %}
    <div class="user-card">
        <h3>{{ user.login }}</h3>
        <p>{{ user.email }}</p>
    </div>
{% endforeach %}

<!-- Информация о пагинации -->
<div class="pagination-info">
    <p>
        Показано {{ users.from }} - {{ users.to }} 
        из {{ users.total }} записей
    </p>
    
    <p>Страница {{ users.current_page }} из {{ users.last_page }}</p>
</div>

Навигация по страницам

<!-- Простая навигация -->
<nav class="pagination">
    {% if users.prev_page %}
        <a href="?page={{ users.prev_page }}" class="prev">
            ← Предыдущая
        </a>
    {% endif %}
    
    <span class="current">
        Страница {{ users.current_page }}
    </span>
    
    {% if users.next_page %}
        <a href="?page={{ users.next_page }}" class="next">
            Следующая →
        </a>
    {% endif %}
</nav>

<!-- Детальная навигация -->
<nav class="pagination-detailed">
    {% if users.current_page > 1 %}
        <a href="?page=1">Первая</a>
    {% endif %}
    
    {% for page in (users.current_page - 2)..(users.current_page + 2) %}
        {% if page >= 1 and page <= users.last_page %}
            {% if page == users.current_page %}
                <span class="active">{{ page }}</span>
            {% else %}
                <a href="?page={{ page }}">{{ page }}</a>
            {% endif %}
        {% endif %}
    {% endfor %}
    
    {% if users.current_page < users.last_page %}
        <a href="?page={{ users.last_page }}">Последняя</a>
    {% endif %}
</nav>

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

Список пользователей с фильтрами

<!-- Получение параметров фильтров -->
{% set filterRole = request.input('role') %}
{% set filterStatus = request.input('status') %}
{% set search = request.input('search') %}

<!-- Построение условий -->
{% set conditions = {} %}

{% if filterRole %}
    {% set conditions = conditions | merge({'role': filterRole}) %}
{% endif %}

{% if filterStatus == 'active' %}
    {% set conditions = conditions | merge({'ban': 0}) %}
{% elseif filterStatus == 'banned' %}
    {% set conditions = conditions | merge({'ban': 1}) %}
{% endif %}

{% if search %}
    {% set conditions = conditions | merge({'login': '%' ~ search ~ '%'}) %}
{% endif %}

<!-- Получение данных -->
{% set page = request.input('page', 1) %}
{% set users = db('1752665380840')->paginate(
    'users', page, 25, conditions, 'created_at', 'DESC'
) %}

<!-- Форма фильтров -->
<form method="GET" class="filters">
    <div class="filter-group">
        <label>Роль:</label>
        <select name="role">
            <option value="">Все роли</option>
            <option value="user" {{ filterRole == 'user' ? 'selected' : '' }}>Пользователи</option>
            <option value="admin" {{ filterRole == 'admin' ? 'selected' : '' }}>Администраторы</option>
        </select>
    </div>
    
    <div class="filter-group">
        <label>Статус:</label>
        <select name="status">
            <option value="">Все статусы</option>
            <option value="active" {{ filterStatus == 'active' ? 'selected' : '' }}>Активные</option>
            <option value="banned" {{ filterStatus == 'banned' ? 'selected' : '' }}>Заблокированные</option>
        </select>
    </div>
    
    <div class="filter-group">
        <label>Поиск:</label>
        <input type="text" name="search" value="{{ search }}" placeholder="Логин пользователя">
    </div>
    
    <button type="submit">Фильтровать</button>
    <a href="?">Сбросить</a>
</form>

<!-- Результаты -->
<div class="results">
    <h3>Найдено пользователей: {{ users.total }}</h3>
    
    {% foreach users.data as user %}
        <div class="user-row">
            <div class="user-info">
                <strong>{{ user.login }}</strong>
                <span class="role role-{{ user.role }}">{{ user.role | upper }}</span>
                <span class="status {{ user.ban ? 'banned' : 'active' }}">
                    {{ user.ban ? 'Заблокирован' : 'Активен' }}
                </span>
            </div>
            
            <div class="user-stats">
                <span>Баланс: {{ user.balance | money('₽') }}</span>
                <span>Регистрация: {{ user.created_at | date('d.m.Y') }}</span>
            </div>
        </div>
    {% endforeach %}
</div>

Статистическая сводка

<!-- Основная статистика -->
<div class="dashboard-stats">
    {% set totalUsers = db('1752665380840')->countItems('users') %}
    {% set activeUsers = db('1752665380840')->countItems('users', {'ban': 0}) %}
    {% set onlineUsers = db('1752665380840')->countItems('users', {
        'last_seen': '>' ~ (time() - 300)
    }) %}
    {% set todayUsers = db('1752665380840')->countItems('users', {
        'created_at': '>=' ~ strtotime('today')
    }) %}
    
    <div class="stat-card">
        <div class="stat-number">{{ totalUsers | number }}</div>
        <div class="stat-label">Всего пользователей</div>
    </div>
    
    <div class="stat-card">
        <div class="stat-number">{{ activeUsers | number }}</div>
        <div class="stat-label">Активных пользователей</div>
        <div class="stat-percentage">
            {{ (activeUsers / totalUsers * 100) | round(1) }}%
        </div>
    </div>
    
    <div class="stat-card">
        <div class="stat-number">{{ onlineUsers | number }}</div>
        <div class="stat-label">Сейчас онлайн</div>
    </div>
    
    <div class="stat-card">
        <div class="stat-number">{{ todayUsers | number }}</div>
        <div class="stat-label">Новых сегодня</div>
    </div>
</div>
Безопасность: Все запросы автоматически защищены от SQL-инъекций. MNRFY использует подготовленные запросы (prepared statements) для всех операций с базой данных.
Следующий шаг: Изучите запросы и операторы для более сложных операций с БД.