На протяжении всего этого урока мы создадим приложение с простой регистрационной формой, представляя основные аспекты дизайна Phalcon.

Этот учебник охватывает реализацию простого приложения MVC, показывая, как быстро и легко это можно сделать с Phalcon. После разработки вы можете использовать это приложение и расширить его в соответствии с вашими потребностями. Код в этом учебнике также может быть использован в качестве игровой площадки для изучения других специфических концепций и идей Phalcon.

Если вы хотели бы сразу начать писать код, не останавливаясь на создании структуры приложения, обратитесь к разделу "Инструменты разработчика Phalcon" для автоматической генерации этой структуры. Однако, если вы взялись за инструменты разработчика и застряли, не имяя опыта работы с ними, рекомендуется вернуться назад, к этим строкам.

Лучше всего следовать данному руководству шаг за шагом. Финальный пример можно посмотреть здесь. Тем не менее, если вы запутаетесь, вы всегда можете обратиться за помощью в Discord чат или задать вопрос на форуме.

Структура файлов

Ключевой особенностью фреймворка является слабая связанность. Он не обязывает использовать определенную структуру каталогов. Вы можете использовать любую удобную структуру проекта. Тем не менее, некоторая "узнаваемость" полезна когда вы работаете в команде По этой причине в этом руководстве будет использоваться некая "стандартная" структура, с которой вы должны себя чувствовать комфортно, если вы работали с другими MVC-приложениями в прошлом.

.
└── tutorial
    ├── app
    │   ├── controllers
    │   │   ├── IndexController.php
    │   │   └── SignupController.php
    │   ├── models
    │   │   └── Users.php
    │   └── views
    └── public
        ├── css
        ├── img
        ├── index.php
        └── js

Примечание: поскольку весь код, который предоставляет Phalcon, инкапсулирован в расширение (которое вы загрузили на свой веб-сервер), вы не увидите каталог поставщика, содержащий код Phalcon. Все, что вам нужно, находится в памяти. Если вы еще не установили приложение, перейдите на страницу установки и завершите установку, прежде чем продолжить работу с этим руководством.

Если все это совершенно новое, рекомендуется также установить Phalcon Devtools TDevTools использует встроенный веб-сервер PHP, что позволяет запускать приложение практически сразу. Если вы выберете этот вариант, вам понадобится файл  .htrouter.php в корне вашего проекта со следующим содержимым:

<?php

$uri = urldecode(
    parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)
);

if ($uri !== '/' && file_exists(__DIR__ . '/public' . $uri)) {
    return false;
}

$_GET['_url'] = $_SERVER['REQUEST_URI'];

require_once __DIR__ . '/public/index.php';

В случае нашего учебника этот файл должен быть расположен в каталоге учебника.

Вы также можете использовать nginX, apache, cherokee или другие веб-серверы. Инструкции можно найти на странице настройки веб-сервера.

В противном случае, если вы хотите использовать Nginx, у нас есть руководство по настройке Nginx для Phalcon проектов.

Вы также можете использовать Apache. Советуем обратится к соответствующему разделу по его настройке.

Наконец, если вы предпочитаете Cherokee, обратитесь к нашему руководству по его настройке.

Начальная загрузка

Первый файл, который необходимо создать - bootstrap-файл. Он крайне важен, так как является основой вашего приложения и дает вам контроль над всеми его аспектами. В данном файле вы можете реализовать как инициализацию компонентов, так и поведение приложения.

Этот файл обрабатывает 3 вещи:

  • Регистрация компонентов автозагрузчиков
  • Настройка служб и регистрация их с помощью контекста впрыска зависимостей
  • Разрешение HTTP-запросов приложения

Автозагрузка

Phalcon предоставляет PSR-4-совместимый автозагрузчик. Основными вещами, которые должны быть добавлены в автозагрузчик являются ваши контроллеры и модели. Вы можете зарегистрировать директории, которые будут использоваться для поиска классов использующих пространство имён приложения. Если вы хотите ознакомится с другими стратегиями, которые вы можете использовать с автозагрузчиком, обратитесь к соответствующему руководству.

Для начала давайте зарегистрируем каталоги контроллеров controllers и моделей models нашего приложения. Не забудьте включить загрузчик из Phalcon\Loader.

public/index.php

<?php

use Phalcon\Loader;

// Определяем некоторые константы с абсолютными путями
// для использования с локальными ресурасами
define('BASE_PATH', dirname(__DIR__));
define('APP_PATH', BASE_PATH . '/app');
// ...

$loader = new Loader();

$loader->registerDirs(
    [
        APP_PATH . '/controllers/',
        APP_PATH . '/models/',
    ]
);

$loader->register();

Управление зависимостями

Так как Phalcon является слабосвязанным фреймворком, сервисы регистриуются в контейнере внедрения зависимостей. Таким образом сервисы могут быть автоматически внедрены в компоненты и другие сервисы использующие IoC. Вы часто будете сталкиваться с термином DI, который и выступает в качестве контейнера внедрения зависимостей. Внедрения зависимостей и инверсия управления (IoC) может звучать как сложная функция, но в Phalcon их использование очень просто и практично. Контейнер IoC  Phalcon состоит из следующих понятий:

  • Контейнер службы:"bag", в котором мы глобально храним службы, необходимые для работы приложения.
  • Сервис или компонент: объект обработки данных, который будет внедрен в компоненты

Каждый раз, когда платформа требует компонент или службу, она запрашивает контейнер, используя согласованное имя для службы. Таким образом, у нас есть простой способ получить объекты, необходимые для нашего приложения, такие как регистратор, подключение к базе данных и т.д.Не забудьте включить Phalcon\Di в настройку контейнера служб.

Примечание: Если вы все еще заинтересованы в деталях, пожалуйста, смотрите эту статью Мартина Фаулера. Кроме того, у нас есть отличный учебник, охватывающий множество вариантов использования.

Настройки по умолчанию

Phalcon\Di\FactoryDefault является вариантом Phalcon\Di. Чтобы сделать вещи проще, он будет автоматически регистрировать большинство компонентов, которые поставляются с Phalcon. Рекомендуется регистрировать службы вручную, но это было включено, чтобы снизить барьер входа при привыкании к управлению зависимостями. Позже вы всегда можете указать, как только вам станет более комфортно с концепцией.

Сервисы можно зарегистрировать несколькими способами, но для нашего урока мы будем использовать анонимную функцию:

public/index.php

<?php

use Phalcon\Di\FactoryDefault;

// ...

// Создать DI
$di = new FactoryDefault();

На следующем шаге мы регистрируем сервис “view”, указав директорию, где фреймворк будет искать представления. Так как данные файлы не относятся к классам, они не могут быть подгружены автозагрузчиком.

public/index.php

<?php

use Phalcon\Mvc\View;

// ...

// Настраиваем компонент представлений
$di->set(
    'view',
    function () {
        $view = new View();
        $view->setViewsDir(APP_PATH . '/views/');
        return $view;
    }
);

 

Теперь нам нужно зарегистрировать базовый URI, который будет предлагать функциональность для создания всех URI с помощью Phalcon. Компонент гарантирует, что независимо от того, выполняется ли приложение через верхний каталог или подкаталог, все URI будут корректными. Для этого урока наш базовый путь "/". Это станет важным позже в этом уроке, когда мы будем использовать класс Phalcon\Tag для создания гиперссылок.

public/index.php

<?php

use Phalcon\Url;

// ...

$container->set(
    'url',
    function () {
        $url = new Url();
        $url->setBaseUri('/');

        return $url;
    }
);

 

Обработка входящих запросов

На последнем этапе мы используем Phalcon\Mvc\Application. Данный компонент служит для инициализации окружения входящих запросов, их перенаправления и обслуживания относящихся к ним действий. После отработки всех доступных действий, компонент возвращает полученные ответы.

public/index.php

<?php

use Phalcon\Mvc\Application;

// ...

$application = new Application($container);

$response = $application->handle(
    $_SERVER["REQUEST_URI"]
);

$response->send();

Соберём все вместе

Файл tutorial/public/index.php имеет следующее содержимое:

public/index.php

<?php

use Phalcon\Di\FactoryDefault;
use Phalcon\Loader;
use Phalcon\Mvc\View;
use Phalcon\Mvc\Application;
use Phalcon\Url;

// Определите некоторые абсолютные константы пути, 
// чтобы помочь в поиске ресурсов
define('BASE_PATH', dirname(__DIR__));
define('APP_PATH', BASE_PATH . '/app');

// Регистрация автозагрузчика
$loader = new Loader();

$loader->registerDirs(
    [
        APP_PATH . '/controllers/',
        APP_PATH . '/models/',
    ]
);

$loader->register();

$container = new FactoryDefault();

$container->set(
    'view',
    function () {
        $view = new View();
        $view->setViewsDir(APP_PATH . '/views/');
        return $view;
    }
);

$container->set(
    'url',
    function () {
        $url = new Url();
        $url->setBaseUri('/');
        return $url;
    }
);

$application = new Application($container);

try {
    // Обрабатывать запросы
    $response = $application->handle(
        $_SERVER["REQUEST_URI"]
    );

    $response->send();
} catch (\Exception $e) {
    echo 'Exception: ', $e->getMessage();
}

Как видите, загрузочный файл  очень короткий и нам не нужно включать какие-либо дополнительные файлы. Поздравляем, вы только что создали гибкое MVC-приложение, используя менее чем 30 строк кода.

Создание контроллера

По умолчанию Phalcon будет искать контроллер с именем IndexController. Он является исходной точкой, когда ни один другой контроллер или действие не были запрошены (например http://localhost/). IndexController и его IndexAction должны выглядеть следующим образом:

app/controllers/IndexController.php

<?php

use Phalcon\Mvc\Controller;

class IndexController extends Controller
{
    public function indexAction()
    {
        echo '<h1>Привет!</h1>';
    }
}

Классы контроллеров должны заканчиваться суффиксом Controller, чтобы автозагрузчик смог загрузить их, а методы контроллеров должны заканчиваться суффиксом Action. Теперь можно открыть браузер и увидеть похожий результат:

Поздравляем! Вы готовы к полёту с Phalcon!

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

Отображение вывода напрямую из контроллера иногда бывает необходимым решением, но нежелательно, и сторонники шаблона MVC это подтвердят. Данные должны передаваться представлению, ответственному за отображение данных. Phalcon ищет файл представления с именем, совпадающим с именем действия внутри папки, носящей имя последнего запущенного контроллера. В нашем случае это будет выглядеть так (app/views/index/index.phtml):

Поэтому в нашем случае, если URL-адрес:

http://localhost/

 вызовет IndexController и indexAction, и он будет искать представление:

/views/index/index.phtml

 Если он будет найден, то проанализирует его и отправит вывод на экран. Наш взгляд тогда будет иметь следующее содержание:

app/views/index/index.phtml

<?php echo "<h1>Привет!</h1>";

В нашем контроллере (app/controllers/IndexController.php) сейчас существует пустое действие:

app/controllers/IndexController.php

<?php

use Phalcon\Mvc\Controller;

class IndexController extends Controller
{
    public function indexAction()
    {

    }
}

Вывод браузера останется прежним. Когда метод контроллера завершит свою работу, будет автоматически создан статический компонент Phalcon\Mvc\View. Узнать больше о использовании представлений можно здесь.

Проектирование формы регистрации

Теперь мы изменим index.phtml файл вида, чтобы добавить ссылку на новый контроллер с именем "Регистрация". Цель состоит в том, чтобы позволить пользователям зарегистрироваться в нашем приложении.

app/views/index/index.phtml

<?php

echo "<h1>Привет!</h1>";

echo PHP_EOL;

echo PHP_EOL;

echo $this->tag->linkTo(
    'signup',
    'Регистрируйся!'
);

Сгенерированный HTML-код будет выводить ссылку (тэг <a>), указывающую на наш новый контроллер:

app/views/index/index.phtml (сгенерированный)

<h1>Привет!</h1>

<a href="/signup">Регистрируйся!</a>

Для генерации тэга мы воспользовались встроенным классом Phalcon\Tag. Это служебный класс, позволяющий конструировать HTML-разметку в Phalcon-подобном стиле. Так как этот класс также зарегистрирован в DI мы можем использовать $this->tag для доступа к нему.

Примечание: Phalcon\Tag уже зарегистрирован в контейнере DI, так как мы использовали контейнер Phalcon\Di\FactoryDefault. Если вы зарегистрировали все службы самостоятельно, вам нужно будет зарегистрировать этот компонент в контейнере, чтобы сделать его доступным в вашем приложении.

Более подробно о генерации HTML можно узнать здесь.

Контроллер Signup (app/controllers/SignupController.php):

app/controllers/SignupController.php

<?php

use Phalcon\Mvc\Controller;

class SignupController extends Controller
{
    public function indexAction()
    {

    }
}

Пустое действие index говорит нам о том, что будет использоваться одноименный файл представления с нашей формой для регистрации (app/views/signup/index.phtml):

app/views/signup/index.phtml

<h2>Зарегистрируйтесь, используя эту форму</h2>

<?php echo $this->tag->form("signup/register"); ?>

    <p>
        <label for="name">Имя</label>
        <?php echo $this->tag->textField("name"); ?>
    </p>

    <p>
        <label for="email">E-Mail</label>
        <?php echo $this->tag->textField("email"); ?>
    </p>

    <p>
        <?php echo $this->tag->submitButton("Регистрация"); ?>
    </p>

</form>

В браузере это будет выглядеть так:

Класс Phalcon\Tag также содержит полезные методы для работы с формами.

Метод Phalcon\Tag::form() принимает единственный аргумент, например, относительный URI контроллера/действия приложения.

При нажатии на кнопку "Регистрация" мы увидим исключение, вызванное фреймворком. Оно говорит нам о том, что у нашего контроллера "signup" отсутствует действие "register". Наш public/index.php файл выбросит исколючение:

Exception: Action "register" was not found on handler "signup"

Реализация этого метода предотвратит выброс исключения:

app/controllers/SignupController.php

<?php

use Phalcon\Mvc\Controller;

class SignupController extends Controller
{
    public function indexAction()
    {

    }

    public function registerAction()
    {

    }
}

Снова жмем на кнопку “Регистрация” и видим пустую страницу. Поля name и email, введенные пользователем, должны сохраниться в базе данных. В соответствии с принципами MVC, все взаимодействие с БД должно вестись через модели, таким образом, следуя традициям ООП-стиля.

Создание модели

Phalcon приносит первый ORM для PHP, полностью написанный на языке C. Вместо увеличения сложности разработки, она упрощает ее.

Перед созданием нашей первой модели нам нужно создать таблицу базы данных за пределами Phalcon, чтобы сопоставить её. Простая таблица для хранения зарегистрированных пользователей может быть создана следующим образом:

create_users_table.sql

CREATE TABLE `users` (
    `id`    int(10)     unsigned NOT NULL AUTO_INCREMENT,
    `name`  varchar(70)          NOT NULL,
    `email` varchar(70)          NOT NULL,

    PRIMARY KEY (`id`)
);

Модели должны быть расположены в модели директории app/models  (app/models/Users.php). Модель сопоставляется таблице "users":

app/models/Users.php

<?php

use Phalcon\Mvc\Model;

class Users extends Model
{
    public $id;
    public $name;
    public $email;
}

Примечание: обратите внимание, что открытые свойства модели соответствуют именам полей в нашей таблице.

 

Настройка соединения с базой данных

Чтобы использовать подключение к базе данных и затем получить доступ к данным через наши модели, нам нужно указать его в процессе начальной загрузки. Соединение с базой данных — это всего лишь ещё один сервис нашего приложения, который может быть использован для различных компонентов:

public/index.php

<?php

use Phalcon\Db\Adapter\Pdo\Mysql as DbAdapter;

// Настраиваем сервис для работы с БД
$di->set(
    'db',
    function () {
        return new DbAdapter(
            [
                'host'     => '127.0.0.1',
                'username' => 'root',
                'password' => 'secret',
                'dbname'   => 'tutorial1',
            ]
        );
    }
);

При правильных настройках подключения наши модели будут готовы к работе и взаимодействию с остальными частями приложения.

Сохранение данных при работе с моделями

app/controllers/SignupController.php

<?php

use Phalcon\Mvc\Controller;

class SignupController extends Controller
{
    public function indexAction()
    {

    }

    public function registerAction()
    {
        $user = new Users();

        $user->assign(
            $this->request->getPost(),
            null,
            [
                "name",
                "email",
            ]
        );

        // Хранить и проверять на наличие ошибок
        $success = $user->save();

        if ($success) {
            echo "Спасибо за регистрацию!";
        } else {
            echo "К сожалению, возникли следующие проблемы: ";

            $messages = $user->getMessages();

            foreach ($messages as $message) {
                echo $message->getMessage(), "<br/>";
            }
        }

        $this->view->disable();
    }
}

В начале метода registerAction мы создаем экземпляр модели Users, отвечающий за записи пользователей. The class's public properties map to the fields of the users table in our database. Установка соответствующих значений в новой записи и вызов метода save() сохранит данные в базе данных. Метод save() возвращает булево значение, указывающее, успешно ли были сохранены данные в таблице или нет (true и false, соответственно).

ORM автоматически экранирует ввод для предотвращения SQL-инъекций, так что мы можем передавать тело HTTP-запроса напрямую методу save().

Дополнительная проверка выполняется автоматически для полей, которые определены как NOT null (обязательно). Если мы не введем ни одного из обязательных полей в регистрационной форме наш экран будет выглядеть следующим образом:

Список зарегистрированных пользователей

Теперь нам нужно будет получить и отобразить всех зарегистрированных пользователей в нашей базе данных

Первое, что мы собираемся сделать в нашей indexAction   IndexController, это показать результат поиска всех пользователей, что делается просто путем вызова статического метода find() из нашей модели (Users::find()).

indexAction изменится следующим образом:

app/controllers/IndexController.php

<?php

use Phalcon\Mvc\Controller;

class IndexController extends Controller
{
    /**
     * Добро пожаловать и список пользователей
     */
    public function indexAction()
    {
        $this->view->users = Users::find();
    }
}

Примечание: мы присваиваем Результаты find магическому свойству объекта view. Это устанавливает эту переменную с присвоенными данными и делает ее доступной в нашем представлении

В нашем файле редставления views/index/index.phtml мы можем использовать переменную $users следующим образом:

Вид будет выглядеть так:

views/index/index.phtml

<?php

echo "<h1>Привет!</h1>";

echo $this->tag->linkTo(["signup", "Sign Up Here!", 'class' => 'btn btn-primary']);

if ($users->count() > 0) {
    ?>
    <table class="table table-bordered table-hover">
        <thead class="thead-light">
        <tr>
            <th>#</th>
            <th>Name</th>
            <th>Email</th>
        </tr>
        </thead>
        <tfoot>
        <tr>
            <td colspan="3">Количество пользователей: <?php echo $users->count(); ?></td>
        </tr>
        </tfoot>
        <tbody>
        <?php foreach ($users as $user) { ?>
            <tr>
                <td><?php echo $user->id; ?></td>
                <td><?php echo $user->name; ?></td>
                <td><?php echo $user->email; ?></td>
            </tr>
        <?php } ?>
        </tbody>
    </table>
    <?php
}

Как вы можете видеть, наши переменные $users можно перебирать и подсчитывать. Вы можете получить дополнительную информацию о том, как работают модели, в нашем документе о моделях.

Стилевое оформление

Теперь мы можем добавить небольшие дизайнерские штрихи в наше приложение. Мы можем добавить Bootstrap CSS в наш код, чтобы он использовался во всех наших представлениях. Мы добавим index.phtml в папке  views со следующим содержимым:

app/views/index.phtml

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Phalcon Tutorial</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
</head>
<body>
<div class="container">
    <?php echo $this->getContent(); ?>
</div>
</body>
</html>

В приведенном выше шаблоне наиболее важной строкой является вызов метода getContent(). Этот метод возвращает все содержимое, которое было сгенерировано из нашего представления. Наше приложение теперь покажет:

Заключение

Как вы можете видеть, легко начать создание приложения с помощью Phalcon. Тот факт, что Phalcon работает с расширением, значительно уменьшает объем проектов, а также дает ему значительный прирост производительности.

Если вы готовы узнать больше изучайте остальной учебник далее.