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

firep91613

Новичок
То же и по отдельным классам, в идеальном мире у тебя для каждого действия один класс с одним публичным методом, инъекции зависимостей через конструктор и __invoke() запускающий код.
Ок. Как тогда лучше передать? Вот контроллер главной страницы. Он использует модель PostsModel, этой модели надо передать экземпляр класса Db.
PHP:
<?php

namespace App\Controllers;

use \App\Models\PostsModel;

class HomePageController
{
    private PostsModel $postModel;

    public function __construct()
    {
        $this->postModel = new PostsModel();
    }

    public function index(): void
    {
        $posts = $this->postModel->getAllPosts();
        require_once VIEWS . '/home.php';
    }
}
Вот автозагрузка и маршрут:
PHP:
require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/bootstrap.php';
require_once __DIR__ . '/../config/constants.php';

$router->get('', [HomePageController::class, 'index']);
В контейнер сунул Db.
PHP:
<?php

use \App\Classes\ServiceContainer;
use \App\Classes\Db;

$container = new ServiceContainer();

$container->setService(Db::class, function () {
    $db_config = include CONFIG . '/db_config.php';
    return new Db($db_config);
});
Я передавал $container в Router, из роутера в контроллер, из контроллера в модель. Но это же бред полный? Как грамтно в модель передавать без идеотизма? Нужен отдельный класс со статическим методом, где лежат сервисы все? Я чет запутался...
 

miketomlin

Новичок
Стоит ли вид делать классом?
Нет нужды. Достаточно набора ф-ций.

Но это же бред полный?
Не полный. Но неудобно. Мы же это уже обсуждали. Когда я «вещал» про реестр и т.п.

Можешь прямо во фронте инициализировать твой БД-шный объект и запихнуть его в реестр, а потом извлекать при обращении к твоим БД-шным методам. Помнишь db()->query('SELECT 1')?

Вот вообще через глоб. переменную: query. Фу-фу-фу, но принцип тот же. И эта обертка использует стандартный БД-шный объект, но разницы опять-таки нет. При использовании своего БД-шного объекта ты обычно инкапсулируешь стандартный.

При использовании DI можно использовать автомонтирование (автосвязывание, автовайринг).
 
Последнее редактирование:

firep91613

Новичок
Можешь прямо во фронте инициализировать твой БД-шный объект и запихнуть его в реестр, а потом извлекать при обращении к твоим БД-шным методам. Помнишь db()->query('SELECT 1')?
А, да, я что-то совсем забыл про это...
При использовании DI можно использовать автомонтирование (автосвязывание, автовайринг).
PHP:
$container->setService(HomePageController::class, function ($container) {
    return new HomePageController($container->getService(Db::class));
});
PHP:
require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/bootstrap.php';
require_once __DIR__ . '/../config/constants.php';
$router->get('', [$container->getService(HomePageController::class), 'index']);
Это имеется ввиду?
 

firep91613

Новичок
Я тут статейку на хабре прочитал. И пришел к выводу, что модели нужно явно передавать класс Db, контроллену нужно передавать класс модели.
PHP:
<?php

use \App\Classes;
use \App\Controllers;
use \App\Models;

$container = new Classes\ServiceContainer();

$container->setService(Classes\Db::class, function () {
    $db_config = include CONFIG . '/db_config.php';
    return new Classes\Db($db_config);
});

$container->setService(Models\PostsModel::class, function ($container) {
    return new Models\PostsModel($container->getService(Classes\Db::class));
});

$container->setService(Controllers\HomePageController::class, function ($container) {
    return new Controllers\HomePageController($container->getService(Models\PostsModel::class));
});
Так же лучше? Все четко разложено, кто от кого зависит. В роутере просто достать нужный сервис.
 

AmdY

Пью пиво
Команда форума
Да, ты верно делаешь. Единственное важный момент что в реальности всё это делается не руками, а через рефлексию. Она анализирует твой класс и сама подставляет нужные зависимости, а самим зависимостям их зависимости.
Вот нагуглил случайную статью по теме, не ручаюсь что там всё правильно, но вроде что-то есть https://habr.com/ru/articles/655399/
 

miketomlin

Новичок
Это имеется ввиду?
Выше написали, в чем суть автомонтирования и на чем оно базируется. Но учти, что рефлексия – это жуть.

Я тут статейку на хабре прочитал. И пришел к выводу, что модели нужно явно передавать класс Db, контроллену нужно передавать класс модели.
Это и есть «ручное» DI, когда ты разделяешь создание и использование, но без реестра (сервис-локатора и т.п.).

С контроллером не все так однозначно в плане DI. И твой вариант с контроллером в виде сервиса тоже какой-то мутный. Это все равно что переливать из пустого в порожнее. Явно дергай контроллер/экшен, либо анонимку, если нужна специфическая инициализация (конфигурирование) контроллера/экшена.
 

firep91613

Новичок
С контроллером не все так однозначно в плане DI. И твой вариант с контроллером в виде сервиса тоже какой-то мутный. Это все равно что переливать из пустого в порожнее. Явно дергай контроллер/экшен, либо анонимку, если нужна специфическая инициализация (конфигурирование) контроллера/экшена.
Это роутер надо переделывать? Вторым параметром метод роутера должен принимать анонимную функцию которая будет вызывать контроллер?

Я никак не могу решить проблему с последним классом в цепочке. Он там доходит до класса Db и валится с ошибкой, что класса 'array' не существует. Db принимает массив (конфиг). В методе писать, что если аргумент типа 'array' подключай $db_config явно - это же костыль?
 

miketomlin

Новичок
Это роутер надо переделывать?
Нет, сам создай объект контроллера (если роутер этого не умеет).

Я никак не могу решить проблему с последним классом в цепочке. Он там доходит до класса Db и валится с ошибкой, что класса 'array' не существует. Db принимает массив (конфиг). В методе писать, что если аргумент типа 'array' подключай $db_config явно - это же костыль?
О чем речь? Об автомонтировании? Естественно, ты должен проверять типы аргументов. Инициализация аргументов-не классов – это тоже целая эпопея (сродни рефлексии). Ты должен где-то хранить всю иерархию значений скалярных аргументов, элементов массивов.
 

firep91613

Новичок
О чем речь? Об автомонтировании? Естественно, ты должен проверять типы аргументов. Инициализация аргументов-не классов – это тоже целая эпопея (сродни рефлексии). Ты должен где-то хранить всю иерархию значений скалярных аргументов, элементов массивов.
PHP:
class App
{
    private static DB $db;

    public function __construct(protected Router $router, protected array $request, protected Config $config)
    {
        static::$db = new DB($config->db ?? []);
    }

    public static function db(): DB
    {
        return static::$db;
    }

    public function run()
    {
        try {
            echo $this->router->resolve($this->request['uri'], strtolower($this->request['method']));
        } catch (RouteNotFoundException) {
            http_response_code(404);

            echo View::make('error/404');
        }
    }
}
В индексном файле:
PHP:
$container = new Container();
$router    = new Router($container);

$router->get('/', [HomeController::class, 'index']);

(new App(
    $router,
    ['uri' => $_SERVER['REQUEST_URI'], 'method' => $_SERVER['REQUEST_METHOD']],
    new Config($_ENV)
))->run();
Нашел решение проблемы. Это костыльно?
 

firep91613

Новичок
Извиняюсь, ввел в заблуждение. Прошляпил, скопировал строку из статьи на хабре, но у меня свойство называется $services, а там $objects. Только сейчас заметил...

В общем алгоритм такой: в роутер передается контейнер, у него вызывается метод getService, там проверяется, есть ли в контейнере класс, если нет, вызывается метод рефлексии.

Сейчас достаточно положить в контейнер только Db.

Так ведь должно работать?
 

firep91613

Новичок
http://localhost:8080/fasdfasdf - мусорный адрес.
PHP:
$router->get('(?P<slug>[a-z0-9-]+)', [Controllers\PostPageController::class, 'index']) - маршрут.
URI совпадает с регуляркой. Соответственно вызывается PostPageController, который вызывает модель, которая, в свою очередь, делает запрос к БД и ничего не находит.

Как с таких случаях делают? В роутере надо вызвать метод PostPageController и вернуть результат запроса, если ничего, то вызвать контроллер NotFound?
 

miketomlin

Новичок
Как с таких случаях делают?
Ф-ция модели или хелпер (вспомогательная ф-ция) вроде findOrFail, которая вызывает исключение HTTP 404. Обрабатываешь его там, где тебе удобнее, чтобы все стройно ложилось в логику вывода. Можно вне контроллера, даже если штатный вывод у тебя происходит внутри контроллера. Получится что-то вроде полного ветвления: если все ОК, вывести запрошенную страницу, иначе вывести 404-ую. Вот из той же статьи, ссылку на которую я давал выше:
PHP:
class HttpException extends Exception {}

try {
    // вызов контроллера
} catch (Exception $e) {
    error(
        $e instanceof HttpException ? $e->getCode() : 503,
        $e->getMessage()
    );
}
P.S. Для вызова таких исключений обычно используют ф-цию abort.
 
Последнее редактирование:

AnrDaemon

Продвинутый новичок
О каком подтасовывании идет речь?!
О простом. Ты пропустил определение "path" и перепрыгнул к определениям "query" и "fragment".
Нет никакого тройного равенства, там всё предельно чётко указано. Читай то, что написано, а не то, что тебе хочется прочитать.

Я тут прочитал пару статеек о MVC
Забудь и не вспоминай. Все эти "статейки об MVC" перепечатываются последние 25 лет без применения мозга, только чтобы увеличить выдачу в поисковиках.
 

miketomlin

Новичок
Ты пропустил определение "path" и перепрыгнул к определениям "query" и "fragment".
Нет никакого тройного равенства,
Т.е. ты не согласен с моим выводом из определения пути:
Если кратко, путь состоит из pchar или слеша (возможно, во множественном числе).
Ну, ОК. Пусть у тебя путь состоит НЕ из «путейных» символов и слеша 🤣
 

firep91613

Новичок
Я сделал MVC. Без админки пока что. Если скину архив, посмотрит кто-нибудь?
 

firep91613

Новичок
Я так понимаю качать никто не хочет. Под спойлер все выкладывать?
 
Сверху