Базовые примеры

Практические примеры использования MNRFY Framework для начинающих

Создание первой страницы

Простая статичная страница

Создайте файл /src/hello.html:

<!DOCTYPE html>
<html lang="{{ locale }}">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Привет, мир! - {{ config('app.name') }}</title>
    <link rel="stylesheet" href="{{ asset('css/app.css') }}">
</head>
<body>
    <header>
        <h1>{{ t('welcome') }}</h1>
        <p>Сегодня: {{ date('d.m.Y') }}</p>
    </header>
    
    <main>
        <h2>Это моя первая страница на MNRFY!</h2>
        <p>Текущее время: {{ date('H:i:s') }}</p>
        
        {% if config('app.debug') %}
            <div class="debug">
                <p>Режим отладки включен</p>
                <p>Время выполнения: {{ (microtime(true) - MNRFY_START_TIME) * 1000 | round(2) }}ms</p>
            </div>
        {% endif %}
    </main>
</body>
</html>

Откройте в браузере: http://yourdomain.com/hello

Работа с переменными

Страница с динамическими данными

Создайте файл /src/variables-demo.html:

<!DOCTYPE html>
<html>
<head>
    <title>Демонстрация переменных</title>
    <style>
        .demo-section { margin: 20px 0; padding: 20px; border: 1px solid #ddd; }
        .highlight { background: #fffacd; padding: 5px; }
    </style>
</head>
<body>
    <h1>Демонстрация работы с переменными</h1>
    
    <!-- Определение переменных -->
    {% set site_name = 'Мой крутой сайт' %}
    {% set version = '1.0.0' %}
    {% set author = {
        'name': 'Иван Иванов',
        'email': 'ivan@example.com',
        'skills': ['PHP', 'JavaScript', 'MNRFY']
    } %}
    
    <div class="demo-section">
        <h2>Простые переменные</h2>
        <p>Название сайта: <span class="highlight">{{ site_name }}</span></p>
        <p>Версия: <span class="highlight">{{ version }}</span></p>
    </div>
    
    <div class="demo-section">
        <h2>Объекты и массивы</h2>
        <p>Автор: <strong>{{ author.name }}</strong></p>
        <p>Email: <a href="mailto:{{ author.email }}">{{ author.email }}</a></p>
        <p>Навыки:</p>
        <ul>
            {% foreach author.skills as skill %}
                <li>{{ skill }}</li>
            {% endforeach %}
        </ul>
    </div>
    
    <div class="demo-section">
        <h2>Вычисления</h2>
        {% set a = 10 %}
        {% set b = 5 %}
        <p>{{ a }} + {{ b }} = {{ a + b }}</p>
        <p>{{ a }} * {{ b }} = {{ a * b }}</p>
        <p>Случайное число: {{ rand(1, 100) }}</p>
    </div>
    
    <div class="demo-section">
        <h2>Строки и фильтры</h2>
        {% set text = 'привет, мир из mnrfy framework!' %}
        <p>Оригинал: {{ text }}</p>
        <p>В верхнем регистре: {{ text | upper }}</p>
        <p>С заглавной буквы: {{ text | title }}</p>
        <p>Длина строки: {{ text | length }} символов</p>
    </div>
</body>
</html>

Простая форма

Форма обратной связи

Создайте файл /src/contact.html:

<!DOCTYPE html>
<html>
<head>
    <title>Обратная связь - {{ config('app.name') }}</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 40px; }
        .form-group { margin-bottom: 20px; }
        label { display: block; margin-bottom: 5px; font-weight: bold; }
        input, textarea { width: 100%; max-width: 400px; padding: 8px; border: 1px solid #ddd; }
        button { background: #007bff; color: white; padding: 10px 20px; border: none; cursor: pointer; }
        .alert { padding: 15px; margin: 20px 0; border-radius: 4px; }
        .alert-success { background: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
        .alert-error { background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
    </style>
</head>
<body>
    <h1>Форма обратной связи</h1>
    
    <!-- Показываем сообщения -->
    {% if flash('success') %}
        <div class="alert alert-success">
            {{ flash('success') }}
        </div>
    {% endif %}
    
    {% if flash('error') %}
        <div class="alert alert-error">
            {{ flash('error') }}
        </div>
    {% endif %}
    
    <form method="POST" action="/contact">
        {{ csrf() }}
        <input type="hidden" name="__handler" value="contact-form">
        
        <div class="form-group">
            <label for="name">Ваше имя:</label>
            <input type="text" id="name" name="name" value="{{ old('name') }}" required>
        </div>
        
        <div class="form-group">
            <label for="email">Email:</label>
            <input type="email" id="email" name="email" value="{{ old('email') }}" required>
        </div>
        
        <div class="form-group">
            <label for="subject">Тема:</label>
            <input type="text" id="subject" name="subject" value="{{ old('subject') }}" required>
        </div>
        
        <div class="form-group">
            <label for="message">Сообщение:</label>
            <textarea id="message" name="message" rows="5" required>{{ old('message') }}</textarea>
        </div>
        
        <button type="submit">Отправить сообщение</button>
    </form>
    
    <p><a href="/">← Вернуться на главную</a></p>
</body>
</html>

Обработчик формы

Создайте файл /src/handlers/contact-form.php:

<?php
return function($request, $response, $context) {
    // Получаем данные из формы
    $name = trim($request->input('name'));
    $email = trim($request->input('email'));
    $subject = trim($request->input('subject'));
    $message = trim($request->input('message'));
    
    // Простая валидация
    $errors = [];
    
    if (empty($name)) {
        $errors[] = 'Имя обязательно для заполнения';
    }
    
    if (empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
        $errors[] = 'Введите корректный email';
    }
    
    if (empty($subject)) {
        $errors[] = 'Тема сообщения обязательна';
    }
    
    if (empty($message)) {
        $errors[] = 'Сообщение не может быть пустым';
    }
    
    if (!empty($errors)) {
        $_SESSION['flash_error'] = implode('<br>', $errors);
        return $response->redirect('/contact');
    }
    
    // Сохраняем сообщение в файл (в реальном проекте лучше в БД)
    $logFile = MNRFY_TEMP . '/contact-messages.log';
    $logEntry = date('Y-m-d H:i:s') . " | $name | $email | $subject | $message" . PHP_EOL;
    file_put_contents($logFile, $logEntry, FILE_APPEND | LOCK_EX);
    
    // Устанавливаем сообщение об успехе
    $_SESSION['flash_success'] = "Спасибо, $name! Ваше сообщение отправлено.";
    
    return $response->redirect('/contact');
};
?>

Работа с базой данных

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

Создайте файл /src/users.html:

<!DOCTYPE html>
<html>
<head>
    <title>Пользователи - {{ config('app.name') }}</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 40px; }
        table { width: 100%; border-collapse: collapse; margin-top: 20px; }
        th, td { padding: 12px; text-align: left; border-bottom: 1px solid #ddd; }
        th { background-color: #f8f9fa; font-weight: bold; }
        tr:hover { background-color: #f5f5f5; }
        .user-stats { background: #e9ecef; padding: 20px; border-radius: 8px; margin-bottom: 20px; }
        .stat-item { display: inline-block; margin-right: 30px; }
        .badge { padding: 3px 8px; border-radius: 12px; font-size: 12px; }
        .badge-active { background: #28a745; color: white; }
        .badge-inactive { background: #dc3545; color: white; }
        .pagination { margin-top: 20px; text-align: center; }
        .pagination a { padding: 8px 12px; margin: 0 4px; text-decoration: none; border: 1px solid #ddd; }
        .pagination .active { background: #007bff; color: white; }
    </style>
</head>
<body>
    <h1>Список пользователей</h1>
    
    <!-- Получаем данные из базы -->
    {% set page = request.input('page', 1) %}
    {% set perPage = 10 %}
    {% set users = db('1752665380840')->paginate('users', page, perPage, {}, 'id', 'DESC') %}
    
    <!-- Статистика -->
    {% set totalUsers = db('1752665380840')->countItems('users') %}
    {% set activeUsers = db('1752665380840')->countItems('users', {'ban': 0}) %}
    {% set bannedUsers = totalUsers - activeUsers %}
    
    <div class="user-stats">
        <div class="stat-item">
            <strong>Всего пользователей:</strong> {{ totalUsers }}
        </div>
        <div class="stat-item">
            <strong>Активных:</strong> {{ activeUsers }}
        </div>
        <div class="stat-item">
            <strong>Заблокированных:</strong> {{ bannedUsers }}
        </div>
    </div>
    
    {% if users.data and users.data | length > 0 %}
        <table>
            <thead>
                <tr>
                    <th>ID</th>
                    <th>Логин</th>
                    <th>Имя</th>
                    <th>Баланс</th>
                    <th>Регистрация</th>
                    <th>Статус</th>
                </tr>
            </thead>
            <tbody>
                {% foreach users.data as user %}
                    <tr>
                        <td>{{ user.id }}</td>
                        <td><strong>{{ user.login }}</strong></td>
                        <td>
                            {% if user.fname or user.lname %}
                                {{ (user.fname ~ ' ' ~ user.lname) | trim }}
                            {% else %}
                                <em>Не указано</em>
                            {% endif %}
                        </td>
                        <td>{{ user.balance | money('₽') }}</td>
                        <td>{{ user.reg | date('d.m.Y H:i') }}</td>
                        <td>
                            {% if user.ban == 0 %}
                                <span class="badge badge-active">Активен</span>
                            {% else %}
                                <span class="badge badge-inactive">Заблокирован</span>
                            {% endif %}
                        </td>
                    </tr>
                {% endforeach %}
            </tbody>
        </table>
        
        <!-- Пагинация -->
        {% if users.last_page > 1 %}
            <div class="pagination">
                {% if users.prev_page %}
                    <a href="?page={{ users.prev_page }}">« Предыдущая</a>
                {% endif %}
                
                {% for pageNum in 1..users.last_page %}
                    {% if pageNum == users.current_page %}
                        <span class="active">{{ pageNum }}</span>
                    {% else %}
                        <a href="?page={{ pageNum }}">{{ pageNum }}</a>
                    {% endif %}
                {% endfor %}
                
                {% if users.next_page %}
                    <a href="?page={{ users.next_page }}">Следующая »</a>
                {% endif %}
            </div>
            
            <p>
                Показано {{ users.from }}-{{ users.to }} из {{ users.total }} записей
            </p>
        {% endif %}
    {% else %}
        <p>Пользователи не найдены.</p>
    {% endif %}
    
    <p><a href="/">← На главную</a></p>
</body>
</html>

Создание блога

Главная страница блога

Создайте файл /src/blog.html:

<!DOCTYPE html>
<html>
<head>
    <title>Блог - {{ config('app.name') }}</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 0; padding: 20px; background: #f8f9fa; }
        .container { max-width: 800px; margin: 0 auto; }
        .post { background: white; padding: 30px; margin-bottom: 30px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
        .post-title { color: #333; margin-bottom: 10px; }
        .post-meta { color: #666; font-size: 14px; margin-bottom: 20px; }
        .post-excerpt { line-height: 1.6; color: #555; }
        .read-more { color: #007bff; text-decoration: none; font-weight: bold; }
        .sidebar { background: white; padding: 20px; border-radius: 8px; margin-top: 30px; }
        .no-posts { text-align: center; padding: 60px; background: white; border-radius: 8px; }
    </style>
</head>
<body>
    <div class="container">
        <header>
            <h1>Мой блог</h1>
            <p>Добро пожаловать в мой блог на MNRFY Framework!</p>
        </header>
        
        <main>
            <!-- Имитация постов блога -->
            {% set blogPosts = [
                {
                    'id': 1,
                    'title': 'Начинаем работу с MNRFY Framework',
                    'excerpt': 'В этой статье мы рассмотрим основы работы с MNRFY Framework и создадим первое приложение.',
                    'author': 'Администратор',
                    'created_at': strtotime('-2 days'),
                    'views': 150
                },
                {
                    'id': 2,
                    'title': 'Создание форм в MNRFY',
                    'excerpt': 'Узнайте, как легко создавать и обрабатывать формы с помощью встроенных возможностей фреймворка.',
                    'author': 'Разработчик',
                    'created_at': strtotime('-5 days'),
                    'views': 89
                },
                {
                    'id': 3,
                    'title': 'Работа с базой данных',
                    'excerpt': 'Подробное руководство по работе с базой данных в MNRFY Framework с примерами кода.',
                    'author': 'Администратор',
                    'created_at': strtotime('-1 week'),
                    'views': 203
                }
            ] %}
            
            {% if blogPosts | length > 0 %}
                {% foreach blogPosts as post %}
                    <article class="post">
                        <h2 class="post-title">
                            <a href="/blog/{{ post.id }}" style="text-decoration: none; color: inherit;">
                                {{ post.title }}
                            </a>
                        </h2>
                        
                        <div class="post-meta">
                            <span>Автор: {{ post.author }}</span> • 
                            <span>{{ post.created_at | date('d.m.Y') }}</span> • 
                            <span>{{ post.views }} просмотров</span>
                        </div>
                        
                        <div class="post-excerpt">
                            <p>{{ post.excerpt }}</p>
                            <a href="/blog/{{ post.id }}" class="read-more">Читать далее →</a>
                        </div>
                    </article>
                {% endforeach %}
            {% else %}
                <div class="no-posts">
                    <h2>Пока нет постов</h2>
                    <p>Скоро здесь появится интересный контент!</p>
                </div>
            {% endif %}
        </main>
        
        <aside class="sidebar">
            <h3>О блоге</h3>
            <p>Это демонстрационный блог, созданный на MNRFY Framework.</p>
            
            <h3>Статистика</h3>
            <ul>
                <li>Всего постов: {{ blogPosts | length }}</li>
                <li>Общие просмотры: {{ blogPosts | sum('views') }}</li>
                <li>Авторов: {{ blogPosts | column('author') | unique | length }}</li>
            </ul>
            
            <h3>Последние посты</h3>
            <ul>
                {% foreach blogPosts | slice(0, 3) as post %}
                    <li>
                        <a href="/blog/{{ post.id }}">{{ post.title | truncate(30) }}</a>
                        <br><small>{{ post.created_at | date('d.m.Y') }}</small>
                    </li>
                {% endforeach %}
            </ul>
        </aside>
        
        <p><a href="/">← На главную</a></p>
    </div>
</body>
</html>

Калькулятор

Простой калькулятор с формой

Создайте файл /src/calculator.html:

<!DOCTYPE html>
<html>
<head>
    <title>Калькулятор - {{ config('app.name') }}</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 40px; background: #f0f0f0; }
        .calculator { max-width: 400px; margin: 0 auto; background: white; padding: 30px; border-radius: 10px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); }
        .form-group { margin-bottom: 20px; }
        label { display: block; margin-bottom: 8px; font-weight: bold; }
        input, select { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 16px; }
        button { width: 100%; background: #28a745; color: white; padding: 12px; border: none; border-radius: 4px; font-size: 16px; cursor: pointer; }
        button:hover { background: #218838; }
        .result { margin-top: 20px; padding: 20px; background: #e7f3ff; border-radius: 4px; text-align: center; font-size: 18px; font-weight: bold; }
        .error { background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
        .history { margin-top: 30px; }
        .history h3 { margin-bottom: 15px; }
        .history-item { padding: 10px; background: #f8f9fa; margin-bottom: 5px; border-radius: 4px; font-family: monospace; }
    </style>
</head>
<body>
    <div class="calculator">
        <h1>Калькулятор</h1>
        
        <form method="POST" action="/calculator">
            {{ csrf() }}
            <input type="hidden" name="__handler" value="calculator">
            
            <div class="form-group">
                <label for="number1">Первое число:</label>
                <input type="number" id="number1" name="number1" step="any" 
                       value="{{ old('number1') }}" required>
            </div>
            
            <div class="form-group">
                <label for="operation">Операция:</label>
                <select id="operation" name="operation" required>
                    <option value="">Выберите операцию</option>
                    <option value="add" {{ old('operation') == 'add' ? 'selected' : '' }}>Сложение (+)</option>
                    <option value="subtract" {{ old('operation') == 'subtract' ? 'selected' : '' }}>Вычитание (-)</option>
                    <option value="multiply" {{ old('operation') == 'multiply' ? 'selected' : '' }}>Умножение (*)</option>
                    <option value="divide" {{ old('operation') == 'divide' ? 'selected' : '' }}>Деление (/)</option>
                    <option value="power" {{ old('operation') == 'power' ? 'selected' : '' }}>Возведение в степень</option>
                    <option value="sqrt" {{ old('operation') == 'sqrt' ? 'selected' : '' }}>Квадратный корень</option>
                </select>
            </div>
            
            <div class="form-group">
                <label for="number2">Второе число:</label>
                <input type="number" id="number2" name="number2" step="any" 
                       value="{{ old('number2') }}">
                <small>(Не требуется для квадратного корня)</small>
            </div>
            
            <button type="submit">Вычислить</button>
        </form>
        
        <!-- Результат -->
        {% if flash('result') %}
            <div class="result">
                Результат: {{ flash('result') }}
            </div>
        {% endif %}
        
        {% if flash('error') %}
            <div class="result error">
                Ошибка: {{ flash('error') }}
            </div>
        {% endif %}
        
        <!-- История вычислений -->
        {% if session('calculator_history') %}
            <div class="history">
                <h3>История вычислений:</h3>
                {% foreach session('calculator_history') | slice(-5) | reverse as calculation %}
                    <div class="history-item">
                        {{ calculation }}
                    </div>
                {% endforeach %}
                <p><small>Показаны последние 5 вычислений</small></p>
            </div>
        {% endif %}
        
        <p style="text-align: center; margin-top: 30px;">
            <a href="/">← На главную</a>
        </p>
    </div>
    
    <script>
        // Простая клиентская валидация
        document.querySelector('select[name="operation"]').addEventListener('change', function() {
            const number2Input = document.querySelector('input[name="number2"]');
            const number2Label = document.querySelector('label[for="number2"]');
            
            if (this.value === 'sqrt') {
                number2Input.required = false;
                number2Input.disabled = true;
                number2Label.style.opacity = '0.5';
            } else {
                number2Input.required = true;
                number2Input.disabled = false;
                number2Label.style.opacity = '1';
            }
        });
    </script>
</body>
</html>

Обработчик калькулятора

Создайте файл /src/handlers/calculator.php:

<?php
return function($request, $response, $context) {
    $number1 = floatval($request->input('number1'));
    $number2 = floatval($request->input('number2'));
    $operation = $request->input('operation');
    
    try {
        $result = null;
        $expression = '';
        
        switch ($operation) {
            case 'add':
                $result = $number1 + $number2;
                $expression = "$number1 + $number2 = $result";
                break;
                
            case 'subtract':
                $result = $number1 - $number2;
                $expression = "$number1 - $number2 = $result";
                break;
                
            case 'multiply':
                $result = $number1 * $number2;
                $expression = "$number1 × $number2 = $result";
                break;
                
            case 'divide':
                if ($number2 == 0) {
                    throw new Exception('Деление на ноль невозможно');
                }
                $result = $number1 / $number2;
                $expression = "$number1 ÷ $number2 = $result";
                break;
                
            case 'power':
                $result = pow($number1, $number2);
                $expression = "$number1^$number2 = $result";
                break;
                
            case 'sqrt':
                if ($number1 < 0) {
                    throw new Exception('Квадратный корень из отрицательного числа');
                }
                $result = sqrt($number1);
                $expression = "√$number1 = $result";
                break;
                
            default:
                throw new Exception('Неизвестная операция');
        }
        
        // Форматируем результат
        $formattedResult = is_float($result) ? round($result, 8) : $result;
        
        // Сохраняем в историю
        if (!isset($_SESSION['calculator_history'])) {
            $_SESSION['calculator_history'] = [];
        }
        
        $_SESSION['calculator_history'][] = $expression;
        
        // Ограничиваем историю 50 записями
        if (count($_SESSION['calculator_history']) > 50) {
            array_shift($_SESSION['calculator_history']);
        }
        
        $_SESSION['flash_result'] = $formattedResult;
        
    } catch (Exception $e) {
        $_SESSION['flash_error'] = $e->getMessage();
    }
    
    return $response->redirect('/calculator');
};
?>

Простая авторизация

Форма входа

Создайте файл /src/login.html:

<!DOCTYPE html>
<html>
<head>
    <title>Вход в систему - {{ config('app.name') }}</title>
    <style>
        body { font-family: Arial, sans-serif; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); margin: 0; padding: 0; min-height: 100vh; display: flex; align-items: center; justify-content: center; }
        .login-container { background: white; padding: 40px; border-radius: 10px; box-shadow: 0 10px 25px rgba(0,0,0,0.2); width: 100%; max-width: 400px; }
        .login-header { text-align: center; margin-bottom: 30px; }
        .form-group { margin-bottom: 20px; }
        label { display: block; margin-bottom: 8px; font-weight: bold; color: #333; }
        input { width: 100%; padding: 12px; border: 1px solid #ddd; border-radius: 4px; font-size: 16px; box-sizing: border-box; }
        input:focus { outline: none; border-color: #667eea; }
        button { width: 100%; background: #667eea; color: white; padding: 12px; border: none; border-radius: 4px; font-size: 16px; cursor: pointer; }
        button:hover { background: #5a6fd8; }
        .alert { padding: 15px; margin-bottom: 20px; border-radius: 4px; }
        .alert-error { background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
        .alert-success { background: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
        .links { text-align: center; margin-top: 20px; }
        .links a { color: #667eea; text-decoration: none; }
        .demo-users { background: #f8f9fa; padding: 15px; margin-top: 20px; border-radius: 4px; font-size: 14px; }
    </style>
</head>
<body>
    <div class="login-container">
        <div class="login-header">
            <h1>Вход в систему</h1>
            <p>Добро пожаловать в {{ config('app.name') }}!</p>
        </div>
        
        {% if flash('error') %}
            <div class="alert alert-error">{{ flash('error') }}</div>
        {% endif %}
        
        {% if flash('success') %}
            <div class="alert alert-success">{{ flash('success') }}</div>
        {% endif %}
        
        {% if auth() %}
            <div class="alert alert-success">
                <p>Вы уже авторизованы как <strong>{{ auth().login }}</strong></p>
                <p><a href="/profile">Перейти в профиль</a> | <a href="/logout">Выйти</a></p>
            </div>
        {% else %}
            <form method="POST" action="/login">
                {{ csrf() }}
                <input type="hidden" name="__handler" value="login">
                
                <div class="form-group">
                    <label for="login">Логин:</label>
                    <input type="text" id="login" name="login" value="{{ old('login') }}" required autofocus>
                </div>
                
                <div class="form-group">
                    <label for="password">Пароль:</label>
                    <input type="password" id="password" name="password" required>
                </div>
                
                <div class="form-group">
                    <label>
                        <input type="checkbox" name="remember" value="1"> 
                        Запомнить меня
                    </label>
                </div>
                
                <button type="submit">Войти</button>
            </form>
        {% endif %}
        
        <div class="links">
            <a href="/">← На главную</a> | 
            <a href="/register">Регистрация</a>
        </div>
        
        {% if config('app.debug') %}
            <div class="demo-users">
                <strong>Демо-пользователи:</strong><br>
                admin / admin (администратор)<br>
                user / user (обычный пользователь)
            </div>
        {% endif %}
    </div>
</body>
</html>
Готово! Эти примеры покрывают основные сценарии использования MNRFY Framework. Используйте их как отправную точку для создания собственных приложений.

Полезные советы

Следующие шаги: Изучите примеры админ-панели или продвинутые примеры.