Розділення функціональності
Дата оновлення перекладу 2023-09-01
Розділення функціональності
Недоліком вашого фреймворку зараз є те, що нам треба копіювати та вставляти код у
front.php
кожний раз, коли ми створюємо новий сайт. 60 рядків коду - це не багато,
але було б добре, якщо б ми могли огорнути цей код у відповідний клас. Це дало б нам
кращу можливість повторного використання та полегшило б тестування, і це тільки
декілька переваг.
Якщо ви уважніше подивитеся на код, front.php
має одне введення - Запит, і одне
виведення - Відповідь. Наш клас фреймворку дотримуватиметься цього простого принципу:
логіка полягає у створенні Відповіді, повʼязаної з Запитом.
Давайте створимо наш власний простір імен для нашого фреймворку Simplex
. Перемістіть
логіку обробки запитів у власний клас Simplex\\Framework
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
// example.com/src/Simplex/Framework.php
namespace Simplex;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Controller\ArgumentResolver;
use Symfony\Component\HttpKernel\Controller\ControllerResolver;
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
use Symfony\Component\Routing\Matcher\UrlMatcher;
class Framework
{
public function __construct(
private UrlMatcher $matcher,
private ControllerResolver $controllerResolver,
private ArgumentResolver $argumentResolver,
) {
}
public function handle(Request $request): Response
{
$this->matcher->getContext()->fromRequest($request);
try {
$request->attributes->add($this->matcher->match($request->getPathInfo()));
$controller = $this->controllerResolver->getController($request);
$arguments = $this->argumentResolver->getArguments($request, $controller);
return call_user_func_array($controller, $arguments);
} catch (ResourceNotFoundException $exception) {
return new Response('Not Found', 404);
} catch (\Exception $exception) {
return new Response('An error occurred', 500);
}
}
}
Та відповідно оновіть example.com/web/front.php
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// example.com/web/front.php
// ...
$request = Request::createFromGlobals();
$routes = include __DIR__.'/../src/app.php';
$context = new Routing\RequestContext();
$matcher = new Routing\Matcher\UrlMatcher($routes, $context);
$controllerResolver = new ControllerResolver();
$argumentResolver = new ArgumentResolver();
$framework = new Simplex\Framework($matcher, $controllerResolver, $argumentResolver);
$response = $framework->handle($request);
$response->send();
Щоб закінчити з реорганізацією, давайте перемістимо все, окрім визначень маршрутів,
з example.com/src/app.php
в інший простір імен - Calendar
.
Для автозавантаження класів, визначених у просторах імен Simplex
і Calendar
,
оновіть файл composer.json
:
1 2 3 4 5 6
{
"...": "...",
"autoload": {
"psr-4": { "": "src/" }
}
}
Note
Для оновлення автозавантажувача Composer, запустіть composer dump-autoload
.
Перемістіть контролер в Calendar\Controller\LeapYearController
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
// example.com/src/Calendar/Controller/LeapYearController.php
namespace Calendar\Controller;
use Calendar\Model\LeapYear;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class LeapYearController
{
public function index(Request $request, int $year): Response
{
$leapYear = new LeapYear();
if ($leapYear->isLeapYear($year)) {
return new Response('Yep, this is a leap year!');
}
return new Response('Nope, this is not a leap year.');
}
}
А також перемістіть функцію is_leap_year()
в її власний клас:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// example.com/src/Calendar/Model/LeapYear.php
namespace Calendar\Model;
class LeapYear
{
public function isLeapYear(int $year = null): bool
{
if (null === $year) {
$year = date('Y');
}
return 0 == $year % 400 || (0 == $year % 4 && 0 != $year % 100);
}
}
Не забудьте відповідно оновити файл example.com/src/app.php
:
1 2 3 4
$routes->add('leap_year', new Routing\Route('/is_leap_year/{year}', array(
'year' => null,
'_controller' => 'Calendar\Controller\LeapYearController::indexAction',
)));
Щоб підсумувати, ось нова розмітка файлу:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
example.com
├── composer.json
├── composer.lock
├── src
│ ├── app.php
│ └── Simplex
│ └── Framework.php
│ └── Calendar
│ └── Controller
│ │ └── LeapYearController.php
│ └── Model
│ └── LeapYear.php
├── vendor
│ └── autoload.php
└── web
└── front.php
Ось і все! Наш додаток тепер має чотири різних шари і кожний з них має чітко визначену ціль:
web/front.php
: Фронт-контролер; єдиний відкритий PHP-код, який створює інтерфейс з клієнтом (отримує Запит та відправляє Відповідь) та надає шаблонний код для ініціалізації фремйворку та нашого додатку;src/Simplex
: Повторно використовуваний код фреймворку, який абстрагує обробку вхідних Запитів (до речі, він робить ваші контролери та шаблони легкотестованими - ви скоро дізнаєтеся про це більше);src/Calendar
: Наш специфічний для додатку код (контролери та модель);src/app.php
: Налаштування конфігурації/фреймворку додатку.