Страницы

Поиск по вопросам

понедельник, 6 января 2020 г.

Динамическое изменение requirements для route-а. Symfony 3.3

#php #url #symfony2 #symfony #symfony3


Возможно ли динамически изменять параметр requirements для @Route ?

Что я имею ввиду:


В базе имеется список услуг, у который есть свой url (service1, service2 и т.д)
Имеется шаблон, который будет использоваться в данном случае.


Имею подобный Action:

/**
 * @Route(
 *     "/{service_url}/",
 *     requirements = {
 *         "service_url": "service1|service2|service3|service4"
 *     }
 * )
 */
public function blablaAction($service_url)
{
    ...code...
}


Могу ли я данный параметр изменять динамически? 

Т.е. добавил новую услугу в базе, получаю url новой услуги и добавляю его в параметр
requirements.
Можно, конечно, каждый раз "руками" добавлять новую услугу, но это будет довольно
неудобно, т.к. хочется, чтобы подобное действие в дальнейшем происходило без участия
программиста.
    


Ответы

Ответ 1



Естественно это возможно. Вернее так, я думаю это естественно возможно, но сам этого не делал. Давайте по порядку. В одном из ответов ссылались на Symfony CMF и их динамический роутер. Если открыть эту страницу документации вы увидите что там пишут в общем то о том, о чем я вам напишу ниже. Вот что пишут там https://symfony.com/doc/3.3/routing/routing_from_database.html Это не магия, это попросту кастомный лоадер. Это вообще говоря не проблема и никогда проблемой не было. Сейчас я Вам все объясню. Смотрите. Symfony - крайне гибкая система. По сути своей Symfony - это набор независимых компонентов. Вам нужно внимательнее изучить компонент Routing. Если внимательно прочитать документацию, то можно заметить что вы, например, можете создать свой, "кастомный", загрузчик роутов (обзову их по-русски именно так). И вот понимая что вы можете создать свой загрузчик, вы понимаете что можете загружать роуты из любого места и они у вас могут быть любой конфигурации. Будь то файл, будь то база данных, будь то что то пришедшее из космоса и естественно ваши роуты могут реагировать на состояние вашей системы, хоть температуру окружающей среды. Так же вам нужно понять что аннотации - это одно из описаний роутов которые подгружаются загрузчиком роутов... правильно! который загружает роуты описанные в аннотациях. Давайте теперь ближе к делу. Вам нужно сделать роуты, которые зависят от моделей. Нет ничего проще! Открываете документацию вот тут . Смотрите как создается кастомный Loader, создаете сервис с тэгом routing.loader и добавляете в описание своих роутов в (routing.yml). Все! В этот сервис вы можете добавить все что угодно и описывать роуты как угодно. Слева, справа, сверху и снизу! Не забудьте просто убрать существующие аннотации в контроллере и управляйте своими роутами в вашем собственном Loader-е Имеем в сухом остатке. Создаете свой лоадер согласно документации. (В его конструктор вы можете добавить EntityManager) Описываете роуты взависимости от ваших пожеланий (забирая данные из того же EntityManager) Добавляете его в routing.yml Радуетесь. Еще раз ссылка на документацию, она живет здесь https://symfony.com/doc/3.3/routing/custom_route_loader.html p.s. Последнее. согласно документации The routes defined using custom route loaders will be automatically cached by the framework. So whenever you change something in the loader class itself, don't forget to clear the cache. Это не должно сбивать вас с толку. Естественно что вся логика внутри сервиса остается динамической, но кэш надо будет чистить после изменений (я так понимаю что в dev режиме тоже)

Ответ 2



Такое возможно. Но только нужно что б@Route может состоять из имени урла (имя не изменяется) и его динамической части /services/{service_url}/. Как показано в примере по ссылке выше, правило будет выглядеть так: // src/AppBundle/Controller/ArticleController.php // ... class ArticleController extends Controller { /** * @Route( * "/articles/{_locale}/{year}/{slug}.{_format}", * defaults={"_format": "html"}, * requirements={ * "_locale": "en|fr", * "_format": "html|rss", * "year": "\d+" * } * ) */ public function showAction($_locale, $year, $slug) { } } В Вашем же случае должно быть как то так: /** * @Route( * "/services/{_service_url}/", * defaults={"_service_url": "sevice1"}, * requirements={ * "_service_url": "service1|service2|service3|service4" * } * ) */ public function blablaAction($_service_url){...} То есть необходимо наличие имени урла перед динамической частью.

Ответ 3



Динамически менять requirements не получится, так как этот параметр компилируется в кэш-файл и должен быть независим от внешних источников типа базы данных, так как скомпилируется единожды, затем будет использоваться скомпилированный файл. В проекте Symfony CMF решили немного исправить эту проблему и создали свой вариант роутера, который не кэшируется и всегда проверяет все условия в ран-тайме. Таким образом, они создали динамический роутер, который по данным из базы данных отдает нужную страницу. Подробнее про этот роутер можно прочитать здесь. Кроме того, если нужно просто отсеить все url-ы, которых нет в базе, то можно в самом контроллере запросить в БД нужный url и в случае, если он не найден, выдать ответ 404: /** * @Route( * "/{serviceUrl}/", * requirements = { * "serviceUrl": ".+" * } * ) */ public function blablaAction($serviceUrl) { $servicePage = $this->getRepository(ServicePage::class)->findBy(['url' => $serviceUrl]); if (!$servicePage) { return $this->createNotFoundException('Service url /' . $serviceUrl . ' was not found'); } ...code... }

Ответ 4



Вам это не нужно. Такой подход в корне неверен. Вам нужно воспользоваться банальным подходом (который был предложен выше, это /services/{serviceId}/) и проверять услугу внутри контроллера, там вы сможете сделать какие угодно проверки с услугами. В требованиях к идентификатору услуги можно оставить только regexp. Если вы попытаетесь реализовать выбранную вами схему, то вы пойдёте против системы и фреймворка, и обречёте себя, и следующих программистов, на постоянную боль от исправления багов в роутах и поддержку этого гиблого решения.

Комментариев нет:

Отправить комментарий