Автоматическая обработка форм

Создание и обработка форм без написания PHP кода с помощью встроенных возможностей MNRFY

Введение

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

CSRF защита

Важно! Все формы должны содержать CSRF токен для защиты от межсайтовой подделки запросов.
<form method="POST" action="/">
    {{ csrf() }}  <!-- Обязательно в каждой форме! -->
    <!-- Остальные поля формы -->
</form>

Создание записей (CREATE)

Базовая форма создания

<!-- Форма создания пользователя -->
<form method="POST" action="/">
    {{ csrf() }}
    
    <!-- Конфигурация обработки -->
    <input type="hidden" name="__database" value="1752665380840">
    <input type="hidden" name="__table" value="users">
    <input type="hidden" name="__action" value="create">
    
    <div class="form-group">
        <label>Логин *</label>
        <input type="text" name="login" required minlength="3" maxlength="20">
    </div>
    
    <div class="form-group">
        <label>Имя</label>
        <input type="text" name="fname" maxlength="50">
    </div>
    
    <div class="form-group">
        <label>Фамилия</label>
        <input type="text" name="lname" maxlength="50">
    </div>
    
    <div class="form-group">
        <label>Email</label>
        <input type="email" name="email">
    </div>
    
    <div class="form-group">
        <label>Баланс</label>
        <input type="number" name="balance" value="0" step="0.01">
    </div>
    
    <div class="form-group">
        <label>Роль</label>
        <select name="role">
            <option value="user">Пользователь</option>
            <option value="moderator">Модератор</option>
            <option value="admin">Администратор</option>
        </select>
    </div>
    
    <div class="form-group">
        <label>
            <input type="checkbox" name="is_active" value="1" checked>
            Активен
        </label>
    </div>
    
    <button type="submit" class="btn btn-primary">Создать пользователя</button>
</form>

Обязательные поля для создания

Поле Описание Пример
__database ID подключения к БД 1752665380840
__table Имя таблицы users
__action Действие create

Автоматические поля

При создании записи автоматически добавляются:

Обновление записей (UPDATE)

Форма редактирования

<!-- Получаем данные пользователя -->
{% set user = db('1752665380840')->getItem('users', {'id': userId}) %}

<form method="POST" action="/">
    {{ csrf() }}
    
    <!-- Конфигурация обработки -->
    <input type="hidden" name="__database" value="1752665380840">
    <input type="hidden" name="__table" value="users">
    <input type="hidden" name="__action" value="update">
    <input type="hidden" name="id" value="{{ user.id }}">  <!-- ID записи для обновления -->
    
    <div class="form-group">
        <label>Логин</label>
        <input type="text" name="login" value="{{ user.login }}" readonly>
        <small>Логин нельзя изменить</small>
    </div>
    
    <div class="form-group">
        <label>Имя</label>
        <input type="text" name="fname" value="{{ user.fname }}">
    </div>
    
    <div class="form-group">
        <label>Фамилия</label>
        <input type="text" name="lname" value="{{ user.lname }}">
    </div>
    
    <div class="form-group">
        <label>Email</label>
        <input type="email" name="email" value="{{ user.email }}">
    </div>
    
    <div class="form-group">
        <label>Баланс</label>
        <input type="number" name="balance" value="{{ user.balance }}" step="0.01">
    </div>
    
    <div class="form-group">
        <label>Роль</label>
        <select name="role">
            <option value="user" {{ user.role == 'user' ? 'selected' : '' }}>Пользователь</option>
            <option value="moderator" {{ user.role == 'moderator' ? 'selected' : '' }}>Модератор</option>
            <option value="admin" {{ user.role == 'admin' ? 'selected' : '' }}>Администратор</option>
        </select>
    </div>
    
    <div class="form-group">
        <label>
            <input type="checkbox" name="is_active" value="1" {{ user.is_active ? 'checked' : '' }}>
            Активен
        </label>
    </div>
    
    <button type="submit" class="btn btn-success">Сохранить изменения</button>
</form>

Обязательные поля для обновления

Поле Описание
__action Значение update
id ID записи для обновления

Удаление записей (DELETE)

Простое удаление

<form method="POST" action="/" 
      onsubmit="return confirm('Вы уверены, что хотите удалить этого пользователя?')">
    {{ csrf() }}
    
    <input type="hidden" name="__database" value="1752665380840">
    <input type="hidden" name="__table" value="users">
    <input type="hidden" name="__action" value="delete">
    <input type="hidden" name="id" value="{{ user.id }}">
    
    <button type="submit" class="btn btn-danger">
        <i class="fas fa-trash"></i> Удалить пользователя
    </button>
</form>

Мягкое удаление

<!-- Помечаем как удаленный вместо физического удаления -->
<form method="POST" action="/">
    {{ csrf() }}
    
    <input type="hidden" name="__database" value="1752665380840">
    <input type="hidden" name="__table" value="users">
    <input type="hidden" name="__action" value="update">
    <input type="hidden" name="id" value="{{ user.id }}">
    <input type="hidden" name="deleted_at" value="{{ time() }}">
    <input type="hidden" name="is_active" value="0">
    
    <button type="submit" class="btn btn-warning">Заблокировать пользователя</button>
</form>

Поиск записей

Форма поиска

<form method="POST" action="/">
    {{ csrf() }}
    
    <input type="hidden" name="__database" value="1752665380840">
    <input type="hidden" name="__table" value="users">
    <input type="hidden" name="__action" value="search">
    
    <div class="search-form">
        <div class="form-group">
            <label>Поисковый запрос</label>
            <input type="text" name="q" placeholder="Имя, логин или email..." 
                   value="{{ request.input('q') }}">
        </div>
        
        <!-- Поля для поиска -->
        <input type="hidden" name="fields[]" value="login">
        <input type="hidden" name="fields[]" value="fname">
        <input type="hidden" name="fields[]" value="lname">
        <input type="hidden" name="fields[]" value="email">
        
        <!-- Дополнительные фильтры -->
        <div class="form-group">
            <label>Роль</label>
            <select name="role">
                <option value="">Все роли</option>
                <option value="user" {{ request.input('role') == 'user' ? 'selected' : '' }}>Пользователь</option>
                <option value="moderator" {{ request.input('role') == 'moderator' ? 'selected' : '' }}>Модератор</option>
                <option value="admin" {{ request.input('role') == 'admin' ? 'selected' : '' }}>Администратор</option>
            </select>
        </div>
        
        <div class="form-group">
            <label>
                <input type="checkbox" name="is_active" value="1" 
                       {{ request.input('is_active') ? 'checked' : '' }}>
                Только активные
            </label>
        </div>
        
        <!-- Настройки поиска -->
        <input type="hidden" name="limit" value="50">
        <input type="hidden" name="order_by" value="created_at">
        <input type="hidden" name="order_direction" value="DESC">
        
        <button type="submit" class="btn btn-primary">
            <i class="fas fa-search"></i> Найти
        </button>
        
        <a href="?" class="btn btn-secondary">Сбросить</a>
    </div>
</form>

Отображение результатов поиска

<!-- Результаты поиска сохраняются в переменной search_results -->
{% if search_results is defined %}
    <div class="search-results">
        <h3>Результаты поиска</h3>
        <p>Найдено {{ search_results | length }} записей</p>
        
        {% if search_results | length > 0 %}
            <div class="results-list">
                {% foreach search_results as user %}
                    <div class="result-item">
                        <h4>{{ user.login }}</h4>
                        <p>{{ user.fname }} {{ user.lname }}</p>
                        <p>{{ user.email }}</p>
                        <span class="badge">{{ user.role }}</span>
                        
                        <div class="actions">
                            <a href="/user/edit/{{ user.id }}" class="btn btn-sm btn-warning">Редактировать</a>
                        </div>
                    </div>
                {% endforeach %}
            </div>
        {% else %}
            <p>По вашему запросу ничего не найдено</p>
        {% endif %}
    </div>
{% endif %}

Обработка результатов

Flash сообщения

После обработки формы доступны flash сообщения:

<!-- Сообщения об успехе -->
{% if flash('success') %}
    <div class="alert alert-success">
        <i class="fas fa-check-circle"></i> {{ flash('success') }}
    </div>
{% endif %}

<!-- Сообщения об ошибках -->
{% if flash('error') %}
    <div class="alert alert-danger">
        <i class="fas fa-exclamation-circle"></i> {{ flash('error') }}
    </div>
{% endif %}

<!-- Ошибки валидации -->
{% if flash('validation_errors') %}
    <div class="alert alert-warning">
        <h5>Ошибки валидации:</h5>
        <ul>
            {% foreach flash('validation_errors') as error %}
                <li>{{ error }}</li>
            {% endforeach %}
        </ul>
    </div>
{% endif %}

Сохранение данных формы

<!-- Старые значения сохраняются при ошибке валидации -->
<form method="POST" action="/">
    {{ csrf() }}
    
    <div class="form-group">
        <label>Имя</label>
        <input type="text" name="fname" value="{{ old('fname') }}">
    </div>
    
    <div class="form-group">
        <label>Email</label>
        <input type="email" name="email" value="{{ old('email') }}">
    </div>
    
    <button type="submit">Отправить</button>
</form>

Дополнительные возможности

Перенаправления

<!-- Перенаправление после успешной обработки -->
<input type="hidden" name="__redirect" value="/users">
<input type="hidden" name="__success_message" value="Пользователь успешно создан">

<!-- Перенаправление при ошибке -->
<input type="hidden" name="__error_redirect" value="/users/create">

Пакетные операции

<form method="POST" action="/">
    {{ csrf() }}
    
    <input type="hidden" name="__database" value="1752665380840">
    <input type="hidden" name="__table" value="users">
    <input type="hidden" name="__action" value="batch_update">
    
    <table class="table">
        <thead>
            <tr>
                <th><input type="checkbox" id="select-all"></th>
                <th>Пользователь</th>
                <th>Роль</th>
                <th>Статус</th>
            </tr>
        </thead>
        <tbody>
            {% foreach users as user %}
                <tr>
                    <td>
                        <input type="checkbox" name="selected_ids[]" value="{{ user.id }}">
                    </td>
                    <td>{{ user.login }}</td>
                    <td>{{ user.role }}</td>
                    <td>{{ user.is_active ? 'Активен' : 'Заблокирован' }}</td>
                </tr>
            {% endforeach %}
        </tbody>
    </table>
    
    <div class="batch-actions">
        <select name="batch_action">
            <option value="">Выберите действие</option>
            <option value="activate">Активировать</option>
            <option value="deactivate">Деактивировать</option>
            <option value="delete">Удалить</option>
        </select>
        
        <button type="submit" class="btn btn-primary">Применить к выбранным</button>
    </div>
</form>

Валидация на стороне клиента

HTML5 валидация

<form method="POST" action="/" novalidate>
    {{ csrf() }}
    
    <div class="form-group">
        <label>Email *</label>
        <input type="email" name="email" required 
               pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}"
               title="Введите корректный email адрес">
        <div class="invalid-feedback">
            Введите корректный email адрес
        </div>
    </div>
    
    <div class="form-group">
        <label>Пароль *</label>
        <input type="password" name="password" required 
               minlength="8" maxlength="50"
               pattern="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d@$!%*?&]{8,}$"
               title="Пароль должен содержать минимум 8 символов, включая заглавную букву, строчную букву и цифру">
        <div class="invalid-feedback">
            Пароль не соответствует требованиям
        </div>
    </div>
    
    <div class="form-group">
        <label>Телефон</label>
        <input type="tel" name="phone" 
               pattern="[0-9]{10,11}"
               placeholder="89001234567"
               title="Введите номер телефона (10-11 цифр)">
    </div>
    
    <button type="submit">Отправить</button>
</form>

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

CRUD интерфейс для товаров

<!-- Создание товара -->
<form method="POST" action="/" class="product-form">
    {{ csrf() }}
    
    <input type="hidden" name="__database" value="1752665380840">
    <input type="hidden" name="__table" value="products">
    <input type="hidden" name="__action" value="create">
    <input type="hidden" name="__redirect" value="/admin/products">
    
    <div class="row">
        <div class="col-md-8">
            <div class="form-group">
                <label>Название товара *</label>
                <input type="text" name="name" required maxlength="100">
            </div>
            
            <div class="form-group">
                <label>Описание</label>
                <textarea name="description" rows="5"></textarea>
            </div>
            
            <div class="row">
                <div class="col-md-6">
                    <div class="form-group">
                        <label>Цена *</label>
                        <input type="number" name="price" step="0.01" min="0" required>
                    </div>
                </div>
                <div class="col-md-6">
                    <div class="form-group">
                        <label>Цена со скидкой</label>
                        <input type="number" name="sale_price" step="0.01" min="0">
                    </div>
                </div>
            </div>
        </div>
        
        <div class="col-md-4">
            <div class="form-group">
                <label>Категория</label>
                <select name="category_id">
                    <option value="">Выберите категорию</option>
                    {% set categories = db('1752665380840')->getItems('categories', {'active': 1}) %}
                    {% foreach categories as category %}
                        <option value="{{ category.id }}">{{ category.name }}</option>
                    {% endforeach %}
                </select>
            </div>
            
            <div class="form-group">
                <label>Количество на складе</label>
                <input type="number" name="stock" value="0" min="0">
            </div>
            
            <div class="form-group">
                <label>SKU</label>
                <input type="text" name="sku" maxlength="50">
            </div>
            
            <div class="form-check">
                <label>
                    <input type="checkbox" name="is_active" value="1" checked>
                    Активен
                </label>
            </div>
            
            <div class="form-check">
                <label>
                    <input type="checkbox" name="is_featured" value="1">
                    Рекомендуемый
                </label>
            </div>
        </div>
    </div>
    
    <div class="form-actions">
        <button type="submit" class="btn btn-primary">
            <i class="fas fa-save"></i> Создать товар
        </button>
        <a href="/admin/products" class="btn btn-secondary">Отмена</a>
    </div>
</form>

Быстрое редактирование в таблице

<table class="table table-editable">
    <thead>
        <tr>
            <th>ID</th>
            <th>Название</th>
            <th>Цена</th>
            <th>Статус</th>
            <th>Действия</th>
        </tr>
    </thead>
    <tbody>
        {% foreach products as product %}
            <tr data-id="{{ product.id }}">
                <td>{{ product.id }}</td>
                <td>
                    <form method="POST" action="/" class="inline-edit-form">
                        {{ csrf() }}
                        <input type="hidden" name="__database" value="1752665380840">
                        <input type="hidden" name="__table" value="products">
                        <input type="hidden" name="__action" value="update">
                        <input type="hidden" name="id" value="{{ product.id }}">
                        
                        <input type="text" name="name" value="{{ product.name }}" 
                               onchange="this.form.submit()">
                    </form>
                </td>
                <td>
                    <form method="POST" action="/" class="inline-edit-form">
                        {{ csrf() }}
                        <input type="hidden" name="__database" value="1752665380840">
                        <input type="hidden" name="__table" value="products">
                        <input type="hidden" name="__action" value="update">
                        <input type="hidden" name="id" value="{{ product.id }}">
                        
                        <input type="number" name="price" value="{{ product.price }}" 
                               step="0.01" onchange="this.form.submit()">
                    </form>
                </td>
                <td>
                    <form method="POST" action="/" class="inline-edit-form">
                        {{ csrf() }}
                        <input type="hidden" name="__database" value="1752665380840">
                        <input type="hidden" name="__table" value="products">
                        <input type="hidden" name="__action" value="update">
                        <input type="hidden" name="id" value="{{ product.id }}">
                        
                        <select name="is_active" onchange="this.form.submit()">
                            <option value="1" {{ product.is_active ? 'selected' : '' }}>Активен</option>
                            <option value="0" {{ not product.is_active ? 'selected' : '' }}>Неактивен</option>
                        </select>
                    </form>
                </td>
                <td>
                    <form method="POST" action="/" style="display: inline;" 
                          onsubmit="return confirm('Удалить товар?')">
                        {{ csrf() }}
                        <input type="hidden" name="__database" value="1752665380840">
                        <input type="hidden" name="__table" value="products">
                        <input type="hidden" name="__action" value="delete">
                        <input type="hidden" name="id" value="{{ product.id }}">
                        
                        <button type="submit" class="btn btn-sm btn-danger">
                            <i class="fas fa-trash"></i>
                        </button>
                    </form>
                </td>
            </tr>
        {% endforeach %}
    </tbody>
</table>
Следующий шаг: Изучите пользовательские обработчики форм для создания более сложной логики.