Навчальний посібник - Vökuró

Vökuró
Vökuró is a sample application, showcasing a typical web application written in Phalcon. This application focuses on:
- User Login (security)
- User Signup (security)
- User Permissions
- User management
NOTE: You can use Vökuró as a starting point for your application and enhance it further to meet your needs. By no means this is a perfect application, and it does not fit all needs.
NOTE: This tutorial assumes that you are familiar with the concepts of the Model View Controller design pattern. (see References at the end of this tutorial)
NOTE: Note the code below has been formatted to increase readability
Встановлення
Завантаження
In order to install the application, you can either clone or download it from GitHub. Ви можете відвідати сторінку GitHub, завантажити додаток і потім розпакувати його в каталог на вашому комп’ютері. Крім того, ви можете використовувати git clone
:
git clone https://github.com/phalcon/vokuro
Розширення
Для запуску Vökuró необхідно виконати певні умови. You will need to have PHP >= 7.2 installed on your machine and the following extensions:
- ctype
- curl
- dom
- json
- iconv
- mbstring
- memcached
- opcache
- openssl
- pdo
- pdo_mysql
- psr
- session
- simplexml
- xml
- xmlwriter
Phalcon повинен бути встановлений. Перейдіть на сторінку встановлення, якщо вам потрібна допомога з встановленням Phalcon.
Нарешті, вам також потрібно буде переконатися, що ви оновили пакети композера (див. розділ нижче).
Старт
Якщо всі вищезазначені вимоги задоволені, ви можете запустити додаток за допомогою локального PHP веб-сервера, виконавши таку команду в терміналі:
php -S localhost:8080 -t public/ .htrouter.php
Ця команда запустить сайт для localhost
з портом 8080
. Ви можете змінити ці налаштування відповідно до ваших потреб. Крім того, ви можете налаштувати свій сайт в Apache або nginX за допомогою віртуального хоста. Будь ласка, зверніться до відповідної документації, щоб налаштувати віртуальний хост для цих веб-серверів.
Docker
У папці resources
ви знайдете Dockerfile
, який дозволяє швидко налаштувати середовище і запустити програму. Щоб використовувати Dockerfile
нам потрібно визначити назву нашого докеризованого додатка. Для цілей цього посібника ми використаємо phalcon-tutorial-vokuro
.
З кореню додатка ми повинні компілювати проект (вам потрібно це робити лише раз):
$ docker build -t phalcon-tutorial-vokuro -f docker/Dockerfile .
а потім запустіть його
$ docker run -it --rm phalcon-tutorial-vokuro bash
Це дозволить нам отримати доступ до докеризованого середовища. Щоб перевірити версію PHP:
root@c7b43060b115:/code $ php -v
PHP 8.1.8 (cli) (built: Jul 12 2022 08:28:43) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.1.8, Copyright (c) Zend Technologies
with Xdebug v3.1.5, Copyright (c) 2002-2022, by Derick Rethans
та Phalcon:
root@c7b43060b115:/code $ php -r 'echo Phalcon\Version::get();'
4.0.0
Тепер ви маєте докеризоване середовище з усіма необхідними компонентами, щоб запустити Vökuró.
Nanobox
У теці resources
ви також знайдете файл boxfile.yml
, що дозволяє використовувати nanobox для швидкого налаштування середовища. Вам потрібно просто скопіювати файл в кореневий каталог і запустити nanobox run php-server
. Після налаштування додатка ви зможете перейти за IP-адресою, що відображається на екрані та працювати з цим додатком.
For more information on how to set up nanobox, check our [Environments Nanobox][environments-nanobox] page as well as the Nanobox Guides page
NOTE: In this tutorial, we assume that your application has been downloaded or cloned in a directory called vokuro
.
Structure
Погляньте на структуру додатка:
vokuro/
.ci
configs
db
migrations
seeds
public
resources
src
Controllers
Forms
Models
Phalcon
Plugins
Providers
tests
themes
vokuro
var
cache
acl
metaData
session
volt
logs
vendor
Каталог |
Description |
.ci |
Файли, необхідні для налаштування служб для CI |
configs |
Файли конфігурації |
db |
Містить файли міграції для бази даних |
public |
Точка входу в додаток, місце зберігання файлів css, js та зображень |
resources |
Файли Docker/nanobox для налаштування додатка |
src |
Місце розташування всіх основних файлів (контролери, форми тощо) |
src/Controllers |
Контролери |
src/Forms |
Форми |
src/Models |
Моделі бази даних |
src/Plugins |
Плагіни |
src/Providers |
Постачальники: налаштування сервісів у контейнері DI |
tests |
Тести |
themes |
Теми/подання для легкого налаштування |
themes/vokuro |
Тема додатку за замовчуванням |
var |
Різні допоміжні файли |
var/cache |
Файли кешу |
var/logs |
Журнали |
vendor |
Бібліотеки сторонній постачальників/композера |
Configuration
.env
Vökuró uses the popular Dotenv library by Vance Lucas. Бібліотека використовує .env
файл розташований в кореневій теці, який містить параметри конфігурації, такі як сервер бази даних, ім’я користувача, пароль тощо. Там є файл .env.example
, який постачається з Vökuró, який можна скопіювати та перейменувати на .env
а потім відредагувати його у відповідності до умов вашого середовища. Ви повинні зробити це спочатку, щоб ваш додаток міг працювати належним чином.
Доступні варіанти:
Option |
Description |
APP_CRYPT_SALT |
Random and long string that is used by the Phalcon\Encryption\Crypt component to produce passwords and any additional security features |
APP_BASE_URI |
Зазвичай / , якщо ваш веб-сервер спрямовує запити безпосередньо у каталог Vökuró. Якщо ви встановили Vökuró в підкаталозі, ви можете відкоригувати базовий URI |
APP_PUBLIC_URL |
Публічний URL додатку. Використовується для електронних листів. |
DB_ADAPTER |
Адаптер бази даних. Доступні адаптери: mysql , pgsql , sqlite . Будь ласка, переконайтеся, що в вашій системі встановлені відповідні розширення бази даних. |
DB_HOST |
Хост бази даних |
DB_PORT |
Порт бази даних |
DB_USERNAME |
Ім’я користувача бази даних |
DB_PASSWORD |
Пароль бази даних |
DB_NAME |
Ім’я бази даних |
MAIL_FROM_NAME |
Ім’я FROM при надсиланні електронної пошти |
MAIL_FROM_EMAIL |
FROM email при надсиланні електронної пошти |
MAIL_SMTP_SERVER |
Сервер SMTP |
MAIL_SMTP_PORT |
SMTP порт |
MAIL_SMTP_SECURITY |
Безпека SMTP (наприклад, tls ) |
MAIL_SMTP_USERNAME |
Ім’я користувача SMTP |
MAIL_SMTP_PASSWORD |
Пароль до SMTP |
CODECEPTION_URL |
Сервер Codection для випробувань. Якщо ви запускаєте тести локально, це має бути 127.0.0.1 |
CODECEPTION_PORT |
Порт Codeception |
Після того, як файл конфігурації буде збережено, перехід у браузері за цією IP-адресою відобразить щось схоже на це:

База данних
Також потрібно ініціалізувати базу даних. Vökuró uses the popular library Phinx by Rob Morgan (now the Cake Foundation). Бібліотека використовує власний файл конфігурації (phinx.php
), але для Vökuró вам не потрібно змінювати будь-які параметри, оскільки phinx.php
читає файл .env
, щоб отримати налаштування конфігурації. Це дозволяє вам встановити параметри конфігурації в одному місці.
Тепер нам потрібно буде розпочати міграцію. Щоб перевірити статус нашої бази даних:
/app $ ./vendor/bin/phinx status
Ви побачите цей екран:

Щоб ініціалізувати базу даних, нам потрібно запустити міграції:
/app $ ./vendor/bin/phinx migrate
Екран відображатиме дію:

А команда status
тепер покаже всі зелені:

Config
acl.php
Заглянувши у папку config/
, ви помітите чотири файли. Вам не потрібно змінювати ці файли, щоб запустити додаток, але якщо ви хочете їх змінити, то це саме те місце, де вони розташовані. The acl.php
file returns an array of routes that controls which routes are visible to only logged-in users.
Поточне налаштування вимагає, щоб користувач увійшов у систему, якщо хоче отрисати доступ до таких маршрутів:
users/index
users/search
users/edit
users/create
users/delete
users/changePassword
profiles/index
profiles/search
profiles/edit
profiles/create
profiles/delete
permissions/index
If you use Vökuró as a starting point for your own application, you will need to modify this file to add or remove routes to ensure that your protected routes are behind the login mechanism.
NOTE: Keeping the private routes in an array is efficient and easy to maintain for a small to medium application. Як тільки ваш додаток почне зростати, ви можете розглянути іншу техніку зберігання своїх приватних иаршрутів, наприклад: база даних з механізмом кешування.
config.php
Цей файл містить всі параметри конфігурації, які потрібно Vökuró. Usually you will not need to change this file, since the elements of the array are set by the .env
file and Dotenv. Однак, ви можете захотіти змінити місцезнаходження своїх журналів чи інші шляхи, вирішите змінити структуру каталогів.
Одним з елементів, які ви можете захотіти змінити у роботі з Vökuró на своїй локальній машині є useMail
та встановити його на false
. Це вкаже Vökuró, не намагатися підключатися до поштового сервера, щоб надіслати повідомлення при реєстрації користувача на сайті.
providers.php
Цей файл містить всіх постачальників, які потрібні Vökuró. Це список класів нашого додатку, що реєструє певні класи у контейнері DI. Якщо вам потрібно зареєструвати нові компоненти у контейнері DI, ви можете додати їх до масиву цього файлу.
routes.php
У цьому файлі містяться маршрути, які розуміє Vökuró. Роутер уже зареєстрував маршрути за замовчуванням, тому будь-які маршрутизатори, визначені в routes.php
є специфічними і нетиповими. You can add any non-standard routes you need, when customizing Vökuró, in this file. На всякий випадок нагадаємо маршрути за замовчуванням:
/:controller/:action/:parameters
Постачальники
Як було зазначено вище, Vökuró використовує класи під назвою Providers для реєстрації послуг у контейнері DI. Це один зі способів реєстрації послуг в контейнері DI, ніщо не заважає вам помістити всі ці реєстрації в один файл.
Для Vökuró ми вирішили використовувати окремі файли для кожного сервісу, та файл providers.php
(див. вище) в якості реєстраційного масиву конфігурації для цих сервісів. Це дозволяє нам мати набагато менші фрагменти коду, організовані в окремих файлах для різних сервісів, а також масив який дозволяє нам реєструвати чи відключати службу без видалення файлів. Все, що нам потрібно - це змінити масив providers.php
.
Класи постачальників розташовані в src/Providers
. Each of the provider classes implements the Phalcon\Di\ServiceProviderInterface interface. Для отримання додаткової інформації дивіться нижче у розділі завантажувача.
Composer
Vökuró uses composer to download and install supplemental PHP libraries. Бібліотеки, що використовуються:
Глянувши у composer.json
, бачимо, що необхідні такі пакунки:
"require": {
"php": ">=7.2",
"ext-openssl": "*",
"ext-phalcon": "~4.0.0-beta.2",
"robmorgan/phinx": "^0.11.1",
"swiftmailer/swiftmailer": "^5.4",
"vlucas/phpdotenv": "^3.4"
}
Якщо це нова установка, ви можете запустити
or if you want to upgrade the existing installations of the above packages:
For more information about composer, you can visit their documentation page.
Завантажувач
Точка входу
Вхідною точкою нашого додатку є public/index.php
. У цьому файлі міститься необхідний код, який збирає і завантажує додаток. Він також служить єдиною точкою входу до нашого додатка, спрощує нам відловлювання помилок, захист файлів тощо.
Давайте поглянемо на код:
<?php
use Vokuro\Application as VokuroApplication;
error_reporting(E_ALL);
$rootPath = dirname(__DIR__);
try {
require_once $rootPath . '/vendor/autoload.php';
Dotenv\Dotenv::create($rootPath)->load();
echo (new VokuroApplication($rootPath))->run();
} catch (Exception $e) {
echo $e->getMessage(), '<br>';
echo nl2br(htmlentities($e->getTraceAsString()));
}
Перш за все, ми пересвідчуємось, що маємо повноцінне звітування про помилки. Звісно, ви можете змінити це, якщо бажаєте, або переписати код, щоб звітування про помилки контролювалось через записи у вашому .env
файлі.
Блок try
/catch
згортає усі операції. Це гарантує, що на екрані з’являться всі помилки.
NOTE You will need to rework the code to enhance security. Якщо зараз станеться помилка бази даних, код catch
виведе на екран технічну інформацію щодо доступу до бази даних з інформацією про помилку. This code is intended as a tutorial not a full scale production application
Ми впевнені, що маємо доступ до всіх підтримуваних бібліотек, завантажуючи автозавантажувач композера. У composer.json
ми також визначили запис autoload
, що забезпечує автозавантаження будь-яких класів з простору імен Vokuro
з теки src
.
"autoload": {
"psr-4": {
"Vokuro\\": "app/"
},
"files": [
"app/Helpers.php"
]
}
Потім ми завантажуємо змінні середовища, визначені у нашому файлі .env
, викликаючи
Dotenv\Dotenv::create($rootPath)->load();
І нарешті ми запускаємо нашу програму.
Application
Вся логіка програми загорнута в клас Vokuro\Application
. Давайте подивимося, як це робиться:
<?php
declare(strict_types=1);
namespace Vokuro;
use Exception;
use Phalcon\Application\AbstractApplication;
use Phalcon\Di\DiInterface;
use Phalcon\Di\FactoryDefault;
use Phalcon\Di\ServiceProviderInterface;
use Phalcon\Mvc\Application as MvcApplication;
class Application
{
const APPLICATION_PROVIDER = 'bootstrap';
/**
* @var AbstractApplication
*/
protected $app;
/**
* @var DiInterface
*/
protected $di;
/**
* @var string
*/
protected $rootPath;
/**
* @param string $rootPath
*
* @throws Exception
*/
public function __construct(string $rootPath)
{
$this->di = new FactoryDefault();
$this->app = $this->createApplication();
$this->rootPath = $rootPath;
$this->di->setShared(self::APPLICATION_PROVIDER, $this);
$this->initializeProviders();
}
/**
* @return string
* @throws Exception
*/
public function run(): string
{
return (string) $this
->app
->handle($_SERVER['REQUEST_URI'])
->getContent()
;
}
/**
* @return string
*/
public function getRootPath(): string
{
return $this->rootPath;
}
/**
* @return AbstractApplication
*/
protected function createApplication(): AbstractApplication
{
return new MvcApplication($this->di);
}
/**
* @throws Exception
*/
protected function initializeProviders(): void
{
$filename = $this->rootPath
. '/configs/providers.php';
if (!file_exists($filename) || !is_readable($filename)) {
throw new Exception(
'File providers.php does not exist or is not readable.'
);
}
$providers = include_once $filename;
foreach ($providers as $providerClass) {
/** @var ServiceProviderInterface $provider */
$provider = new $providerClass;
$provider->register($this->di);
}
}
}
Конструктор класу спочатку створює новий контейнер DI та зберігає його в локальній власності. Ми використовуємо Phalcon\Di\FactoryDefault, який містить багато сервісів уже зареєстрованих для нас.
Потім ми створюємо новий Phalcon\Mvc\Application та зберігаємо його також у власність. Ми також зберігаємо кореневий шлях, тому що він потрібний кругом у додатку.
Потім ми реєструємо цей клас ( Vokuro\Application
) у контейнері Di, використовуючи ім’я bootstrap
. Це дає нам доступ до цього класу з будь-якої частини нашого застосунку через контейнер DI.
Останнє, що ми робимо - це реєструємо всіх постачальників. Хоча об’єкт Phalcon\Di\FactoryDefault має багато сервісів, які вже зареєстровані для нас, нам все ще треба реєструвати постачальників, які відповідають потребам нашої програми. As mentioned above, each provider class implements the Phalcon\Di\ServiceProviderInterface interface, so we can load each class and call the register()
method with the Di container to register each service. Для цього ми спочатку завантажимо масив конфігурації config/providers.php
, а потім зв’яжемо записи і зареєструємо кожного провайдера.
Доступні постачальники:
Постачальник |
Description |
AclProvider |
Права доступу |
AuthProvider |
Authentication |
ConfigProvider |
Значення конфігурації |
CryptProvider |
Шифрування |
DbProvider |
Доступ до бази даних |
DispatcherProvider |
Диспетчер, який використовує контролер для переходу за URL-адресою |
FlashProvider |
Флеш-повідомлення для забезпечення зворотного зв’язку з користувачем |
LoggerProvider |
Реєстратор помилок та іншої інформації |
MailProvider |
Підтримка пошти |
ModelsMetadataProvider |
Метадані для моделей |
RouterProvider |
Маршрути |
SecurityProvider |
Безпека |
SessionBagProvider |
Дані сесії |
SessionProvider |
Дані сесії |
UrlProvider |
Обробка URL |
ViewProvider |
Подання та двигун, що його формує |
run()
тепер запустить REQUEST_URI
, обробить його і поверне вміст назад. Внутрішньо програма вираховує маршрут на основі запиту, координує відповідний контролер і подання перед поверненням результату цієї операції назад користувачеві у якості відповіді.
База данних
Як зазначено вище, Vökuró можна встановити з MariaDB/MySQL/Aurora, PostgreSql або SQLite в якості сховища баз даних. Для цілей цього посібника ми використовуємо MariaDB. Таблиці, які використовує програма:
Таблиця |
Description |
email_confirmations |
Підтвердження електронною поштою для реєстрації |
failed_logins |
Невдалі спроби входу |
password_changes |
Коли було змінено пароль і ким |
permissions |
Матриця дозволів |
phinxlog |
Міграційна таблиця Phinx |
profiles |
Профіль для кожного користувача |
remember_tokens |
Remember Me functionality tokens |
reset_passwords |
Таблиця токенів скидання паролів |
success_logins |
Успішні спроби входу |
users |
Користувачі |
Моделі
Following the Model-View-Controller pattern, Vökuró has one model per database table (excluding the phinxlog
). The models allow us to interact with the database tables in an easy object-oriented manner. Моделі розташовані в каталозі /src/Models
, і кожна модель визначає відповідні поля вихідної таблиці та будь-які зв’язки між моделлю та іншими об’єктами. Деякі моделі також втілюють правила перевірки для забезпечення належного збереження даних у базі даних.
<?php
declare(strict_types=1);
namespace Vokuro\Models;
use Phalcon\Mvc\Model;
class SuccessLogins extends Model
{
/**
* @var integer
*/
public $id;
/**
* @var integer
*/
public $usersId;
/**
* @var string
*/
public $ipAddress;
/**
* @var string
*/
public $userAgent;
public function initialize()
{
$this->belongsTo(
'usersId',
Users::class,
'id',
[
'alias' => 'user',
]
);
}
}
У моделі вище ми визначили всі поля таблиці як публічні властивості для спрощення доступу:
echo $successLogin->ipAddress;
NOTE: If you notice, the property names map exactly the case (upper/lower) of the field names in the relevant table.
У методі initialize()
ми також визначили зв’язок між цією моделлю і Users
. Ми призначили поля (local/remote), а також alias
для цього зв’язку. Таким чином, ми можемо отримати доступ до користувача, що має відношення до запису цієї моделі наступним чином:
echo $successLogin->user->name;
NOTE: Feel free to open each model file and identify the relationships between the models. Check our documentation for the difference between various types of relationships
Контролери
Again following the Model-View-Controller pattern, Vökuró has one controller to handle a specific parent route. Це означає, що AboutController
обробляє маршрут /about
. Всі контролери знаходяться в каталозі /src/Cotnrollers
.
Контролер за замовчуванням це IndexController
. Всі класи контролерів мають суфікс Controller
. Кожен контролер має методи з суфіксамитAction
, а дія за замовчуванням - indexAction
. Therefore, if you visit the site with just the URL, the IndexController
will be called and the indexAction
will be executed.
Після цього, якщо ви не реєстрували певні специфічні маршрути, маршрути за замовчуванням (автоматично зареєстровані) намагатимуться прив’язувати:
to
/src/Controllers/ProfilesController.php -> searchAction
Доступні контролери, дії та маршрути для Vökuró:
Controller |
Action |
Маршрут |
Description |
About |
index |
/about |
Показує сторінкуПро проект |
Index |
index |
/ |
Типова дія - головна сторінка |
Права доступу |
index |
/permissions |
Перегляд/зміна дозволів для рівня профілю |
Privacy |
index |
/privacy |
Перегляд сторінки конфіденційності |
Profiles |
index |
/profiles |
Переглянути сторінку за замовчуванням профілів |
Profiles |
create |
/profiles/create |
Створити профіль |
Profiles |
delete |
/profiles/delete |
Видалити профіль |
Profiles |
edit |
/profiles/edit |
Редагувати профіль |
Profiles |
search |
/profiles/search |
Пошук профілів |
Session |
index |
/session |
Дія сесії за замовчуванням |
Session |
forgotPassword |
/session/forgotPassword |
Забули пароль |
Session |
login |
/session/login |
Вхід |
Session |
logout |
/session/logout |
Вихід |
Session |
signup |
/session/signup |
Зареєструватися |
Terms |
index |
/terms |
Переглянути сторінку з правилами |
UserControl |
confirmEmail |
/confirm |
Підтвердіть електронну пошту |
UserControl |
resetPassword |
/reset-password |
Скидання пароля |
Користувачі |
index |
/users |
Екран за замовчуванням для користувачів |
Користувачі |
changePassword |
/users/changePassword |
Змінити пароль користувача |
Користувачі |
create |
/users/create |
Створити користувача |
Користувачі |
delete |
/users/delete |
Видалити користувача |
Користувачі |
edit |
/users/edit |
Редагувати користувача |
Views
The last element of the Model-View-Controller pattern is the views. Vökuró використовує Volt як генератор подання для його відображень.
NOTE: Generally, one would expect to see a views
folder under the /src
folder. Однак, Vökuró використовує трохи інший підхід, зберігаючи всі файли подань у /themes/vokuro
.
Каталог подань містить теки, що відповідають кожному контролеру. Всередині кожної з цих тек є файли .volt
, що створені для відображення результатів кожної окремої дії. Наприклад, маршрут:
веде до:
ProfilesController -> createAction
і у поданнях розташований:
/themes/vokuro/profiles/create.volt
Доступні подання:
Controller |
Action |
Вигляд |
Description |
About |
index |
/about/index.volt |
Показує сторінкуПро проект |
Index |
index |
/index/index.volt |
Типова дія - головна сторінка |
Права доступу |
index |
/permissions/index.volt |
Перегляд/зміна дозволів для рівня профілю |
Privacy |
index |
/privacy/index.volt |
Перегляд сторінки конфіденційності |
Profiles |
index |
/profiles/index.volt |
Переглянути сторінку за замовчуванням профілів |
Profiles |
create |
/profiles/create.volt |
Створити профіль |
Profiles |
delete |
/profiles/delete.volt |
Видалити профіль |
Profiles |
edit |
/profiles/edit.volt |
Редагувати профіль |
Profiles |
search |
/profiles/search.volt |
Пошук профілів |
Session |
index |
/session/index.volt |
Дія сесії за замовчуванням |
Session |
forgotPassword |
/session/forgotPassword.volt |
Забули пароль |
Session |
login |
/session/login.volt |
Вхід |
Session |
logout |
/session/logout.volt |
Вихід |
Session |
signup |
/session/signup.volt |
Зареєструватися |
Terms |
index |
/terms/index.volt |
Переглянути сторінку з правилами |
Користувачі |
index |
/users/index.volt |
Екран за замовчуванням для користувачів |
Користувачі |
changePassword |
/users/changePassword.volt |
Змінити пароль користувача |
Користувачі |
create |
/users/create.volt |
Створити користувача |
Користувачі |
delete |
/users/delete.volt |
Видалити користувача |
Користувачі |
edit |
/users/edit.volt |
Редагувати користувача |
Файл /index.volt
містить основну схему сторінки, включаючи посилання на стилі, javascript і т. д. The /layouts
directory contains different layouts that are used in the application, for instance a public
one if the user is not logged in, and a private
one for logged-in users. Окремі подання вкладаються у макети та будують кінцеву сторінку.
Компоненти
У Vökuró є кілька компонентів, функціонал яких ви використовуємо по всьому додатку. Всі ці компоненти знаходяться в каталозі /src/Plugins
.
Acl
Vokuro\Plugins\Acl\Acl
is a component that implements an Access Control List for our application. ACL контролює, до яких ресурсів має доступ окремий користувач. Більше про ACL можна прочитати на нашій окремій сторінці.
In this component, We define the resources that are considered private. Вони зберігаються у внутрішньому масиві з контролером як ключом та дією як значенням, та визначають, які контролери/дії потребують аутентифікації. It also holds human-readable descriptions for actions used throughout the application.
Компонент використовує наступні методи:
Method |
Результат |
Description |
getActionDescription($action) |
string |
Повертає опис дії відповідно до її спрощеного імені |
getAcl() |
об'єкт ACL |
Повертає список ACL |
getPermissions(Profiles $profile) |
масив |
Повертає дозволи, надані профілю |
getResources() |
масив |
Повертає всі доступні ресурси та їх дії |
isAllowed($profile, $controller, $action) |
bool |
Перевіряє, чи дозволено поточному профілю отримати доступ до ресурсу |
isPrivate($controllerName) |
bool |
Перевіряє, чи є контролер приватним або ні |
rebuild() |
об'єкт ACL |
Перебудовує список доступу у файл |
Auth
Vokuro\Plugins\Auth\Auth
- це компонент, який керує автентифікацією і здійснює управління ідентифікацією в Vökuró.
Компонент використовує наступні методи:
Method |
Description |
check($credentials) |
Перевіряє облікові дані користувача |
saveSuccessLogin($user) |
Створює налаштування середовища “пам’ятати мене” - пов’язані файли cookie і генерування токенів |
registerUserThrottling($userId) |
Імплементує тротлінг імені користувача. Знижує ефективність атак грубого підбору пароля |
createRememberEnvironment(Users $user) |
Створює налаштування середовища “пам’ятати мене” - пов’язані файли cookie і генерування токенів |
hasRememberMe(): bool |
Перевірте, чи має сесія куки з міткою “пам’ятати мене” |
loginWithRememberMe(): Response |
Logs in using the information in the cookies |
checkUserFlags(Users $user) |
Перевіряє, чи користувач заблокований/неактивний/тимчасово заморожений |
getIdentity(): array / null |
Повертає поточну ідентифікацію |
getName(): string |
Повертає ім’я користувача |
remove() |
Видалення інформації про особистість користувача з сесії |
authUserById($id) |
Автентифікація користувача за його ідентифікатором |
getUser(): Users |
Отримує об’єкт, пов’язаний з користувачем у активній особистості |
findFirstByToken($token): int / null |
Повертає токен поточного користувача |
deleteToken(int $userId) |
Видаляє токен поточного користувача з сесії |
Пошта
Vokuro\Plugins\Mail\Mail
is a wrapper to Swift Mailer. Вона повертає два методи send()
і getTemplate()
, які дозволяють вам отримати шаблон з подань і заповнити його даними. Отриманий HTML може використовуватись в методі send()
разом з отримувачем та іншими параметрами для відправлення повідомлення електронною поштою.
NOTE: Note that this component is used only if useMail
is enabled in your .env
file. Також потрібно буде забезпечити коректність SMTP сервера та облікових даних.
Реєстрація
Controller
Щоб отримати доступ до всіх областей Vökuró, вам потрібно мати обліковий запис. Vökuró дозволяє зареєструватися на сайті, натиснувши кнопку Створити обліковий запис
.
Це переспрямує вас до URL-адреси /session/signup
, яка у свою чергу викличе SessionController
та signupAction
. Подивимося, що відбувається в signupAction
:
<?php
declare(strict_types=1);
namespace Vokuro\Controllers;
use Phalcon\Flash\Direct;
use Phalcon\Http\Request;
use Phalcon\Mvc\Dispatcher;
use Phalcon\Security;
use Phalcon\Mvc\View;
use Vokuro\Forms\SignUpForm;
use Vokuro\Models\Users;
/**
* @property Dispatcher $dispatcher
* @property Direct $flash
* @property Request $request
* @property Security $security
* @property View $view
*/
class SessionController extends ControllerBase
{
public function signupAction()
{
$form = new SignUpForm();
// ....
$this->view->setVar('form', $form);
}
}
Робочий процес додатку:
- Відвідуємо
/session/signup
- Створення форми, відправлення форми до подання, візуалізація форми
- Надання даних (без відправки)
- Форма ще раз відображається, але нічого більше не відбувається
- Надання даних (відбувається відправка)
- Помилки
- Якщо валідатори форми виявили помилки, то при повторному відображенні форми виводиться інформація про них
- Помилок немає
- Дані знешкоджено (приведено у відповідність до шаблону)
- Нову модель створено
- Дані збережено в базі даних
- Помилка
- Показ повідомлення на екрані і оновлення форми
- Успіх
- Запис збережено
- Показ підтвердження на екрані
- Надсилання пошти (якщо доступно)
Форма
In order to have validation for user supplied data, we are utilizing the Phalcon\Forms\Form and Phalcon\Filter\Validation* classes. Ці класи дозволяють нам створювати HTML елементи та додавати до них валідатори. Потім форма передається до подання, де HTML елементи виводяться на екран.
Коли користувач відправляє інформацію, ми відправляємо надіслані дані назад до форми, де відповідні валідатори перевіряють коректність введення та повертають будь-які потенційні повідомлення про помилки.
NOTE: All the forms for Vökuró are located in /src/Forms
Спочатку ми створили об’єкт SignUpForm
. У цьому об’єкті ми визначаємо всі HTML-елементи, які нам потрібні із їх відповідними валідаторами:
<?php
declare(strict_types=1);
namespace Vokuro\Forms;
use Phalcon\Forms\Element\Check;
use Phalcon\Forms\Element\Hidden;
use Phalcon\Forms\Element\Password;
use Phalcon\Forms\Element\Submit;
use Phalcon\Forms\Element\Text;
use Phalcon\Forms\Form;
use Phalcon\Validation\Validator\Confirmation;
use Phalcon\Validation\Validator\Email;
use Phalcon\Validation\Validator\Identical;
use Phalcon\Validation\Validator\PresenceOf;
use Phalcon\Validation\Validator\StringLength;
class SignUpForm extends Form
{
/**
* @param string|null $entity
* @param array $options
*/
public function initialize(
string $entity = null,
array $options = []
) {
$name = new Text('name');
$name->setLabel('Name');
$name->addValidators(
[
new PresenceOf(
[
'message' => 'The name is required',
]
),
]
);
$this->add($name);
$email = new Text('email');
$email->setLabel('E-Mail');
$email->addValidators(
[
new PresenceOf(
[
'message' => 'The e-mail is required',
]
),
new Email(
[
'message' => 'The e-mail is not valid',
]
),
]
);
$this->add($email);
$password = new Password('password');
$password->setLabel('Password');
$password->addValidators(
[
new PresenceOf(
[
'message' => 'The password is required',
]
),
new StringLength(
[
'min' => 8,
'messageMinimum' => 'Password is too short. ' .
'Мінімум 8 символів',
]
),
new Confirmation(
[
'message' => "Пароль не збігається " .
"confirmation",
'with' => 'confirmPassword',
]
),
]
);
$this->add($password);
$confirmPassword = new Password('confirmPassword');
$confirmPassword->setLabel('Confirm Password');
$confirmPassword->addValidators(
[
new PresenceOf(
[
'message' => 'The confirmation password ' .
'is required',
]
),
]
);
$this->add($confirmPassword);
$terms = new Check(
'terms',
[
'value' => 'yes',
]
);
$terms->setLabel('Accept terms and conditions');
$terms->addValidator(
new Identical(
[
'value' => 'yes',
'message' => 'Terms and conditions must be ' .
'accepted',
]
)
);
$this->add($terms);
$csrf = new Hidden('csrf');
$csrf->addValidator(
new Identical(
[
'value' => $this->security->getRequestToken(),
'message' => 'CSRF validation failed',
]
)
);
$csrf->clear();
$this->add($csrf);
$this->add(
new Submit(
'Sign Up',
[
'class' => 'btn btn-success',
]
)
);
}
/**
* @param string $name
*
* @return string
*/
public function messages(string $name)
{
if ($this->hasMessagesFor($name)) {
foreach ($this->getMessagesFor($name) as $message) {
return $message;
}
}
return '';
}
}
У методі initialize
ми встановлюємо всі необхідні HTML-елементи. Це такі елементи:
Елемент |
Type |
Description |
name |
Text |
Ім’я користувача |
email |
Text |
Електронна адреса для облікового запису |
password |
Password |
Пароль до облікового запису |
confirmPassword |
Password |
Підтвердження пароля |
terms |
Check |
Поставити галочку щодо прийняття умов користування |
csrf |
Hidden |
Елемент захисту CSRF |
Реєстрація |
Submit |
Кнопка надсилання |
Додавання елементів здійснюється поступово:
<?php
declare(strict_types=1);
$email = new Text('email');
$email->setLabel('E-Mail');
$email->addValidators(
[
new PresenceOf(
[
'message' => 'The e-mail is required',
]
),
new Email(
[
'message' => 'The e-mail is not valid',
]
),
]
);
$this->add($email);
Спочатку ми створюємо об’єкт Text
та задаємо в якості його назви email
. Ми також встановили мітку елемента на E-Mail
. Після цього ми прикріплюємо різні валідатори до елемента. Вони будуть викликані після того, як користувач відправить дані, які будуть передані у формі.
Як ми бачили вище, ми прикріпляємо валідатор PresenceOf
на елемент email
із повідомленням Потрібно вказати e-mail
. Валідатор перевіряє, чи користувач надав дані, коли натиснув на кнопку відправки повідомлення, та відображає повідомлення в разі помилки. Валідатор перевіряє переданий масив (як правило, $_POST
), а для цього конкретного елементу він перевірить $_POST['email']
.
Ми також прикріпляємо валідатор Email
, відповідальний за перевірку дійсної адреси електронної пошти. Як ви бачите, валідатори розміщуються у масиві, тож ви легко можете прикріпити до будь якого елемента стільки валідаторів, скільки вам потрібно.
Останнє, що ми зробимо, це додамо елемент у форму.
Ви помітите, що елемент terms
не містить жодного валідатора, підключеного до нього, тому наша форма не буде перевіряти вміст цього елемента.
Особлива увага до елементів password
та confirmPassword
. Ви побачите, що обидва елементи мають тип Password
. Ідея полягає в тому, що ви повинні ввести пароль двічі, і такі паролі повинні збігатися для того, щоб уникнути помилок.
Поле password
має два валідатори для вмісту: PresenceOf
, що є обов’язковим, і StringLength
: для того, щоб пароль був більше, ніж 8 символів. Ми також прикріплюємо третій валідатор, що називається Confirmation
. Цей спеціальний валідатор зв’язує елемент password
з елементом confirmPassword
. Коли він доданий для перевірки, то перевірить вміст обох елементів, і якщо вони не ідентичні - буде відображено повідомлення про помилку, наприклад про те, що перевірка буде невдала.
Вигляд
Тепер, коли у нас є все, що ми передаємо через форму, ми відправлчємо форму поданню для відображення:
$this->view->setVar('form', $form);
Our view now needs to render the elements:
{# ... #}
{%
set isEmailValidClass = form.messages('email') ?
'form-control is-invalid' :
'form-control'
%}
{# ... #}
<h1 class="mt-3">Sign Up</h1>
<form method="post">
{# ... #}
<div class="form-group row">
{{
form.label(
'email',
[
'class': 'col-sm-2 col-form-label'
]
)
}}
<div class="col-sm-10">
{{
form.render(
'email',
[
'class': isEmailValidClass,
'placeholder': 'Email'
]
)
}}
<div class="invalid-feedback">
{{ form.messages('email') }}
</div>
</div>
</div>
{# ... #}
<div class="form-group row">
<div class="col-sm-10">
{{
form.render(
'csrf',
[
'value': security.getToken()
]
)
}}
{{ form.messages('csrf') }}
{{ form.render('Sign Up') }}
</div>
</div>
</form>
<hr>
{{ link_to('session/login', "← Back to Login") }}
Змінна, яку ми встановили у поданні нашого об’єкта SignUpForm
називається form
. Тому ми використовуємо її напряму і викликаємо її методи. Синтаксис в Volt дещо відрізняється. In PHP, we would use $form->render()
whereas in Volt we will use form.render()
.
Подання містить спочатку результати перевірки, чи мають місце якісь помилки у нашій формі, і якщо так, то додається клас CSS is-invalid
до відповідного елемента. Цей клас додає гарну червону рамку до елемента, виділяючи помилку та відображаючи повідомлення.
Після цього ми маємо звичайні HTML-теги із відповідними стилями. Для того, щоб відобразити HTML-код кожного елементу, нам потрібно викликати render()
у form
із зазначенням імені відповідного елемента. Також зверніть увагу, що ми водночас викликаємо form.label()
з таким самим іменем елемента, щоб створити відповідні теги <label>
.
У кінці подання ми візуалізуємо приховане поле CSRF
, а також кнопку Зареєструватися
.
Відправка
As mentioned above, once the user fills the form and clicks the Sign Up
button, the form will self post i.e. it will post the data on the same controller and action (in our case /session/signup
). Відповідній дії тепер необхідно обробити ці надіслані дані:
<?php
declare(strict_types=1);
namespace Vokuro\Controllers;
use Phalcon\Flash\Direct;
use Phalcon\Http\Request;
use Phalcon\Mvc\Dispatcher;
use Phalcon\Security;
use Phalcon\Mvc\View;
use Vokuro\Forms\SignUpForm;
use Vokuro\Models\Users;
/**
* @property Dispatcher $dispatcher
* @property Direct $flash
* @property Request $request
* @property Security $security
* @property View $view
*/
class SessionController extends ControllerBase
{
public function signupAction()
{
$form = new SignUpForm();
if (true === $this->request->isPost()) {
if (false !== $form->isValid($this->request->getPost())) {
$name = $this
->request
->getPost('name', 'striptags')
;
$email = $this
->request
->getPost('email')
;
$password = $this
->request
->getPost('password')
;
$password = $this
->security
->hash($password)
;
$user = new Users(
[
'name' => $name,
'email' => $email,
'password' => $password,
'profilesId' => 2,
]
);
if ($user->save()) {
return $this->dispatcher->forward([
'controller' => 'index',
'action' => 'index',
]);
}
foreach ($user->getMessages() as $message) {
$this->flash->error((string) $message);
}
}
}
$this->view->setVar('form', $form);
}
}
If the user has submitted data, the following line will evaluate, and we will be executing code inside the if
statement:
if (true === $this->request->isPost()) {
Тут ми перевіряємо запит, що надійшов від користувача, чи це POST
. Тепер, коли це так, ми маємо використати валідатори перевірки форми і переконатись, чи немає ніяких помилок. Об’єкт Phalcon\Http\Request дозволяє нам отримати ці дані легко через використання:
$this->request->getPost()
Тепер нам потрібно передати ці опубліковані дані у формі і викликати isValid
. Це активує всі валідатори для кожного елемента і якщо якийсь із них зазнає невдачі, форма виведе повідомлення з внутрішньої колекції і поверне false
if (false !== $form->isValid($this->request->getPost())) {
Якщо все в порядку, ми використовуємо знову об’єкт Phalcon\Http\Request для отримання поданих даних, а також і для їх приведення у відповідність до встановлених нами вимог. Наведений приклад вирізає теги з надісланої стрічки name
:
$name = $this
->request
->getPost('name', 'striptags')
;
Зверніть увагу, що ми ніколи не зберігаємо точний текст паролів. Instead, we use the Phalcon\Security component and call hash
on it, to transform the supplied password to a one way hash and store that instead. Таким чином, якщо хтось скомпрометує нашу базу даних, принаймні він не матиме доступу до конкретних текстів паролів.
$password = $this
->security
->hash($password)
;
Тепер нам потрібно зберегти надані дані в базі. Ми це зробимо, створивши нову модель Users
, передавши до неї приведені у відповідність дані, а потім викликавши save
:
$user = new Users(
[
'name' => $name,
'email' => $email,
'password' => $password,
'profilesId' => 2,
]
);
if ($user->save()) {
return $this
->dispatcher
->forward(
[
'controller' => 'index',
'action' => 'index',
]
);
}
Якщо $user->save()
повертає true
, користувач буде переспрямований на головну сторінку (index/index
), і повідомлення про успіх з’явиться на екрані.
Модель
Зв’язки
Тепер ми маємо перевірити модель Users
, оскільки там закладена певна логіка, зокрема, події afterSave
і beforeValidationOnCreate
.
Якщо бажаєте, можна додати базові налаштування через метод initialize
. Це місце, де ми встановлюємо всі зв/’язки для цієї моделі. Для класу Users
ми визначили кілька зв/’язків. Чому ви можете запитати зв/’язки? Phalcon пропонує простий спосіб отримання пов’язаних даних до певної моделі.
Якщо ми хочемо перевірити всі успішні логіни для конкретного користувача, ми можемо це зробити за допомогою наступного фрагмента коду:
<?php
declare(strict_types=1);
use Vokuro\Models\SuccessLogins;
use Vokuro\Models\Users;
$user = Users::findFirst(
[
'conditions' => 'id = :id:',
'bind' => [
'id' => 7,
]
]
);
$logins = SuccessLogin::find(
[
'conditions' => 'userId = :userId:',
'bind' => [
'userId' => 7,
]
]
);
Наведений вище код витягує дані користувача з ідентифікатором 7
, а потім отримує усі успішні логіни з відповідної таблиці для цього користувача.
Використовуючи зв/’язки, ми можемо дозволити Phalcon виконато для нас всі складні прив’язки і підтягування даних. Отже, наведений вище код:
<?php
declare(strict_types=1);
use Vokuro\Models\SuccessLogins;
use Vokuro\Models\Users;
$user = Users::findFirst(
[
'conditions' => 'id = :id:',
'bind' => [
'id' => 7,
]
]
);
$logins = $user->successLogins;
$logins = $user->getRelated('successLogins');
Останні дві стрічки роблять одне і те ж. Це питання уподобань, який синтаксис ви хочете використовувати. Phalcon відкриє відповідну таблицю та профільтрує її дані за ідентифікатором користувача.
Для нашої таблиці Users
ми визначили наступні зв/’язки:
Name |
Базове поле |
Пов/’язане поле |
Модель |
passwordChanges |
id |
usersId |
PasswordChanges |
profile |
profileId |
id |
Profiles |
resetPasswords |
id |
usersId |
ResetPasswords |
successLogins |
id |
usersId |
SuccessLogins |
<?php
declare(strict_types=1);
namespace Vokuro\Models;
use Phalcon\Mvc\Model;
use Phalcon\Validation;
use Phalcon\Validation\Validator\Uniqueness;
class Users extends Model
{
// ...
public function initialize()
{
$this->belongsTo(
'profilesId',
Profiles::class,
'id',
[
'alias' => 'profile',
'reusable' => true,
]
);
$this->hasMany(
'id',
SuccessLogins::class,
'usersId',
[
'alias' => 'successLogins',
'foreignKey' => [
'message' => 'Користувача не може бути видалено через ' .
'наявність у нього активності в системі',
],
]
);
$this->hasMany(
'id',
PasswordChanges::class,
'usersId',
[
'alias' => 'passwordChanges',
'foreignKey' => [
'message' => 'Користувач не може бути видалений через ' .
'наявність у нього активності у системі',
],
]
);
$this->hasMany(
'id',
ResetPasswords::class,
'usersId', [
'alias' => 'resetPasswords',
'foreignKey' => [
'message' => 'Користувач не може бути видалений через ' .
'наявність активності у системі',
],
]);
}
// ...
}
Як ви бачите у визначених зв/’язках, ми маємо belongsTo
, а також три hasMany
. Усі зв/’язки мають псевдоніми, щоб ми могли легше отримати до них доступ. Відношення belongsTo
також має активний прапорець reusable
. Це означає, що якщо у одному запиті викликається зв/’язок більш ніж один раз, Phalcon виконуватиме запит до бази даних лише перший раз і кешуватиме результат. Будь-які наступні запити використовуватимуть кешовані результати.
Also, notable is that we define specific messages for foreign keys. Якщо конкретні зв/’язки будуть порушуватись, то визначене повідомлення буде показане.
Events
Phalcon\Mvc\Model розроблений, щоб виконувати специфічні events. Ці методи подій можуть бути розташовані або в сервісі слухача подій, або в конкретній моделі.
Для моделі Users
ми додаємо код для подій afterSave
та beforeValidationOnCreate
.
<?php
declare(strict_types=1);
namespace Vokuro\Models;
use Phalcon\Mvc\Model;
use Phalcon\Validation;
use Phalcon\Validation\Validator\Uniqueness;
class Users extends Model
{
public function beforeValidationOnCreate()
{
if (true === empty($this->password)) {
$tempPassword = preg_replace(
'/[^a-zA-Z0-9]/',
'',
base64_encode(openssl_random_pseudo_bytes(12))
);
$this->mustChangePassword = 'Y';
$this->password = $this->getDI()
->getSecurity()
->hash($tempPassword)
;
} else {
$this->mustChangePassword = 'N';
}
if ($this->getDI()->get('config')->useMail) {
$this->active = 'N';
} else {
$this->active = 'Y';
}
$this->suspended = 'N';
$this->banned = 'N';
}
}
Подія beforeValidationOnCreate
буде виконуватись щоразу, як ми матимемо новий запис (Create
), перед тим, як буде здійснено будь-яку валідацію. We check if we have a defined password and if not, we will generate a random string, then hash that string using Phalcon\Security and storing it in the password
property. Ми також встановлюємо прапорець для зміни пароля.
Якщо поле пароля не порожнє, то ми зазначаємо у полі mustChangePassword
текст ні
. Нарешті, ми встановили типові значення параметрів active
, suspended
або banned
, залежно від статусу користувача. Це гарантує, що наш запис буде готовий до додавання у базу даних.
<?php
declare(strict_types=1);
namespace Vokuro\Models;
use Phalcon\Mvc\Model;
use Phalcon\Validation;
use Phalcon\Validation\Validator\Uniqueness;
class Users extends Model
{
public function afterSave()
{
if ($this->getDI()->get('config')->useMail) {
if ($this->active == 'N') {
$emailConfirmation = new EmailConfirmations();
$emailConfirmation->usersId = $this->id;
if ($emailConfirmation->save()) {
$this->getDI()
->getFlash()
->notice(
'A confirmation mail has ' .
'надіслано на ' . $this->email
)
;
}
}
}
}
}
Подія afterSave
виконується одразу після додавання у базу нового запису про користувача. У цій події ми перевіряємо чи електронні скриньки активні (дивіться файл.env
налаштування useMail
), і якщо активні, то створюємо новий запис у таблиці EmailConfirmations
, після чого зберігаємо запис. Як тільки все буде зроблено, на екрані з’явиться сповіщення.
NOTE: Note that the EmailConfirmations
model also has an afterCreate
event, which is responsible for actually sending the email to the user.
Валідація
Модель також має метод validate
, який дозволяє нам додати валідатордо будь-якої кількості полей нашої моделі. Для таблиці Users
на потрібно, щоб email
був унікальним. As such, we attach the Uniqueness
validator to it. The validator will fire right before any save operation is performed on the model and the message will be returned if the validation fails.
<?php
declare(strict_types=1);
namespace Vokuro\Models;
use Phalcon\Mvc\Model;
use Phalcon\Validation;
use Phalcon\Validation\Validator\Uniqueness;
class Users extends Model
{
public function validation()
{
$validator = new Validation();
$validator->add(
'email',
new Uniqueness(
[
"message" => "The email is already registered",
]
)
);
return $this->validate($validator);
}
}
Підсумок
Vökuró це приклад додатку, який ми використовуємо для демонстрації деяких функцій, які пропонує Phalcon. Це, безумовно, не рішення, яке підійде для всіх потреб. However, you can use it as a starting point to develop your application.
References