За последние 24 часа нас посетили 22415 программистов и 1169 роботов. Сейчас ищет 661 программист ...

ЧПУ

Тема в разделе "PHP для новичков", создана пользователем Taktreba, 23 янв 2018.

Метки:
  1. Taktreba

    Taktreba Активный пользователь

    С нами с:
    11 янв 2017
    Сообщения:
    543
    Симпатии:
    132
    Сразу к делу )
    Вот так я реализовал на своем сайте переключение контента по разделам
    PHP:
    1. $id = '';
    2. if (!empty($_GET)) {
    3.     $id = empty($_GET['id']) ? '' : trim(strip_tags($_GET['id']));
    4. }
    5. $blocks = ['header', 'content', 'footer'];
    6. ?>
    7. <?php foreach ($blocks as $block): ?>
    8.     <?php if ($block == 'header'): ?>
    9.         <?php include 'header.php'; ?>
    10.     <?php elseif ($block == 'content'): ?>
    11.         <?php
    12.         switch ($id) {
    13.             case 'arenda':
    14.                 include 'menu/arenda.php';
    15.                 break;
    16.             case 'store':
    17.                 include 'menu/store.php';
    18.                 break;
    19.             case 'errorpage':
    20.                 include 'menu/errorpage.php';
    21.                 break;
    22.             case 'emailconfirm':
    23.                 include 'menu/emailconfirm.php';
    24.                 break;
    25.             case 'lc':
    26.  
    27.                 if ($_SESSION['session_id']) {
    28.                     include 'menu/lc.php';
    29.                 } else {
    30.                     include 'menu/default.php';
    31.                 }
    32.  
    33.                 break;
    34.             default:
    35.                 include 'menu/default.php';
    36.                 break;
    37.         }
    38.         ?>
    39.     <?php elseif ($block == 'footer'): ?>
    40.         <?php include 'footer.php'; ?>
    41.     <?php endif; ?>
    42. <?php endforeach; ?>
    Подключаем header, footer, а в контенте свитчем по id страницу контента
    что в итоге мне дает вот такой url http://mysite/index.php?id=activation or http://mysite/index.php?id=somemenu

    Вопрос, какой вы посоветуете самый просто способ превратить мой url в http://mysite/activation or http://mysite/somemenu. Что я имею ввиду под "самый простой" - без туевой хучи классов и роутинга? вот я хочу добавить админку по адресу http://mysite/~ как мне этого добиться?
     
  2. Алекс8

    Алекс8 Активный пользователь

    С нами с:
    18 май 2017
    Сообщения:
    1.730
    Симпатии:
    359
    все зависит от структуры сайта))
    я себе для простого проекта сделал роутинг простой))
    добавляю экшен в контроллер - он сразу становится доступным по ссылке..
    все экшены которые недоступны в контроллере - выдают 404 ошибку)
     
  3. askanim

    askanim Старожил

    С нами с:
    7 апр 2016
    Сообщения:
    2.201
    Симпатии:
    166
    Адрес:
    GABRIEL
    @Taktreba я в базе у себя для каждой категории или позиции товара храню чпу строку для него.
     
  4. miketomlin

    miketomlin Старожил

    С нами с:
    9 авг 2016
    Сообщения:
    3.794
    Симпатии:
    650
    @Taktreba, самый простой способ – это полностью избавиться от промежуточного использования строки параметров и работать в скрипте напрямую с путем в исходном адресе. Гляньте мою статью Как сделать единую точку входа с ЧПУ?
    --- Добавлено ---
    Без роутинга не получится. Ваш свитч – это уже примитивный роутер.
     
  5. abrdabr

    abrdabr Новичок

    С нами с:
    28 янв 2017
    Сообщения:
    774
    Симпатии:
    65
    @Taktreba действительно зачем лишние свечи? при добавлении файла нужно лезть и прописывать его... проще же проверять наличие файла в директории и инклудить а если отсутствует до индексную страницу
    и зачем целая куча выходов\входов в пхп если они лишние?
     
  6. Алекс8

    Алекс8 Активный пользователь

    С нами с:
    18 май 2017
    Сообщения:
    1.730
    Симпатии:
    359
    Я вот такой вот делал роутер для простого сайта..
    PHP:
    1. <?php
    2.  
    3. /*
    4.  * Нет лицензии))
    5.  */
    6.  
    7. namespace natCMF\core;
    8.  
    9. /**
    10.  * Простой роутинг
    11.  *
    12.  *  Необходимый вид файла .htaccess
    13.  *      RewriteEngine on
    14.  *      RewriteCond %{REQUEST_FILENAME} !-f
    15.  *      RewriteCond %{REQUEST_FILENAME} !-d
    16.  *      RewriteRule ^(.*)$ index.php?route=$1 [L,QSA]
    17.  *
    18.  *  Теперь если в адресной строке у нас вот такая конструкция
    19.  *  http://test.ru/site/page/34
    20.  *  То:
    21.  *      site - это контроллер
    22.  *      page - это метод экшена в контроллере
    23.  *      34 - это параметр передаваемый в метод экшена
    24.  *
    25.  * Если строка выглядит так
    26.  *  http://test.ru/page/34
    27.  *  То роутер проверяет наличие контроллера PageController и если его нет
    28.  * считает что это экшен контролера по умолчанию
    29.  * таким образом мы пряем контроллер по умолчанию из строки
    30.  *
    31.  * ВНИМАНИЕ: в методе parseURI и __construct стоят редиректы
    32.  * 1. Для того что бы убирать дефолтные значения из роута
    33.  * 2. Для того что бы убирать слеш в конце роута
    34.  * TODO: Заменить на правила в htaccess
    35.  *
    36.  * @author я
    37.  */
    38. class Router {
    39.  
    40.     /**
    41.      * Текущий путь, даже если в адресной строке путь не указан
    42.      * то все равно этот массив заполняется значениями по умолчанию
    43.      * @var array
    44.      */
    45.     public $gets = [];
    46.  
    47.     /**
    48.      * Контроллер, экшен и параметры по умолчанию
    49.      * такие вещи лучше хранить в реестре где то
    50.      * и вставять сюда при создании объекта
    51.      * но в данном случае для примера поместил сюда
    52.      * @var array
    53.      */
    54.     protected $defaultGets = ['site', 'index', []];
    55.  
    56.     /**
    57.      * Имя класса который вызывается в результате текушего роута
    58.      * @var string
    59.      */
    60.     protected $classController = '';
    61.  
    62.     /**
    63.      * Имя метода экшена который вызывается в результате текущего роута
    64.      * @var string
    65.      */
    66.     protected $methodAction = '';
    67.  
    68.     /**
    69.      * Набор параметров который должен принять текущий экшен
    70.      * @var array
    71.      */
    72.     protected $methodParams = [];
    73.  
    74.     /**
    75.      * Конструктор
    76.      * парсим URI
    77.      */
    78.     public function __construct() {
    79.         /* Парсим URI */
    80.         $this->parseURI();
    81.  
    82.         /* Удаляем дефолтный контроллер/экшен */
    83.         if (!isset($this->gets['2']) && isset($this->gets['0']) && isset($this->gets['1'])) {
    84.             if ($this->gets['0'] == $this->defaultGets['0'] && $this->gets['1'] == $this->defaultGets['1']) {
    85.                 App::redirectexit('/');
    86.             }
    87.         }
    88.  
    89.         /* Удаляем дефолтный экшен если не указан контроллер и нет параметров */
    90.         if (!isset($this->gets['1']) && isset($this->gets['0'])) {
    91.             if ($this->gets['0'] == $this->defaultGets['1']) {
    92.                 App::redirectexit('/');
    93.             }
    94.         }
    95.  
    96.         /* Удаляем дефолтный экшен если указан контроллер и нет параметров */
    97.         if (!isset($this->gets['2']) && isset($this->gets['1'])) {
    98.             if ($this->gets['1'] == $this->defaultGets['1']) {
    99.                 unset($this->gets['1']);
    100.                 App::redirectexit(implode('/', $this->gets));
    101.             }
    102.         }
    103.  
    104.         /* Фиксируем контроллер, экшен и параметры */
    105.         $this->fixController();
    106.         $this->fixAction();
    107.         $this->fixParams();
    108.     }
    109.  
    110.     /**
    111.      * Парсим URI
    112.      */
    113.     private function parseURI() {
    114.  
    115.         /* Получили строку из $_GET['route'] параметра */
    116.         $uri = (string) filter_input(INPUT_GET, 'route');
    117.  
    118.         /* Убрали слеш в конце роута */
    119.         if (mb_substr($uri, -1) == '/') {
    120.             App::redirectexit(trim($uri, '/'));
    121.         }
    122.  
    123.         /* Разбили строку и обрезали по бокам слеши */
    124.         $this->gets = explode('/', trim($uri, '/')) ?? [];
    125.     }
    126.  
    127.     /**
    128.      * Фиксируем контроллер который надо использовать в роуте
    129.      * и проверяем наличие такого контроллера, если такого контроллера нет
    130.      * то подставляем контроллер по умолчанию
    131.      * Таким образом мы скрываем из строки контроллер по умолчанию
    132.      * @return boolean
    133.      */
    134.     private function fixController() {
    135.  
    136.         if (empty($this->gets['0'])) {
    137.             /* Если пусто после указания компонента то берем контроллер по умолчанию */
    138.             $this->gets['0'] = $this->defaultGets['0'];
    139.         } else {
    140.  
    141.             /* Если что то указано в первом параметре то проверяем наличие такого контроллера */
    142.             if (!class_exists('\natCMF\controllers\\' . $this->parseRouteString($this->gets['0']) . 'Controller')) {
    143.  
    144.                 /* Если нет такого метода то вставляем в начало массива контроллер по умолчанию */
    145.                 array_unshift($this->gets, $this->defaultGets['0']);
    146.             }
    147.         }
    148.         $this->classController = '\natCMF\controllers\\' . $this->parseRouteString($this->gets['0']) . 'Controller';
    149.  
    150.         return true;
    151.     }
    152.  
    153.     /**
    154.      * Фиксируем экшен который надо использовать в роуте,
    155.      * @return boolean
    156.      */
    157.     private function fixAction() {
    158.         if (empty($this->gets['1'])) {
    159.             /* Если нет экшена то берем экшен по умолчанию */
    160.             $this->gets['1'] = $this->defaultGets['1'];
    161.         } else {
    162.             /* Если экшен есть то проверяем его существования иначе отдает 404 экшен */
    163.             if (!method_exists($this->classController, 'action' . $this->parseRouteString($this->gets['1']))) {
    164.                 $this->gets['1'] = '404';
    165.             }
    166.         }
    167.         $this->methodAction = 'action' . $this->parseRouteString($this->gets['1']);
    168.         return true;
    169.     }
    170.  
    171.     /**
    172.      * Фиксируем параметры которые надо использовать в роуте
    173.      * @return boolean
    174.      */
    175.     private function fixParams() {
    176.         if (empty($this->gets['2'])) {
    177.             $this->gets['2'] = [];
    178.         } else {
    179.             /* Пересобираем gets выделяя параметры в отдельный массив */
    180.             $params = $this->gets;
    181.             $this->gets = [];
    182.             $this->gets['0'] = $params['0'];
    183.             $this->gets['1'] = $params['1'];
    184.             unset($params['0']);
    185.             unset($params['1']);
    186.             $this->gets['2'] = array_values($params);
    187.         }
    188.  
    189.         /* Для метода проверяем количество принимаемых параметров */
    190.         $classMethod = new \ReflectionMethod($this->classController, $this->methodAction);
    191.  
    192.         /* Отдаем ровно столько сколько нужно отдать.. */
    193.         $this->methodParams = array_slice($this->gets['2'], 0, count($classMethod->getParameters()));
    194.  
    195.         return true;
    196.     }
    197.  
    198.     /**
    199.      * Обрабатываем строку типа page-info что бы получить PageInfo
    200.      * @param string $inStr
    201.      * @return string
    202.      */
    203.     private function parseRouteString($inStr) {
    204.         $result = '';
    205.         $names = explode('-', $inStr);
    206.         foreach ($names as $name) {
    207.             $result .= ucfirst($name);
    208.         }
    209.         return $result;
    210.     }
    211.  
    212.     /* Поехали (с) */
    213.  
    214.     public function run() {
    215.  
    216.         /* Создаем объект контроллера */
    217.         $controller = new $this->classController;
    218.  
    219.         /* Вызываем метод объекта */
    220.         call_user_func_array(array($controller, $this->methodAction), $this->methodParams);
    221.     }
    222.  
    223. }
     
  7. runcore

    runcore Старожил

    С нами с:
    12 окт 2012
    Сообщения:
    3.625
    Симпатии:
    158
    @Алекс8
    хм.
    советую переименовать метод parseURI в parseURIAndMayBeRedirect
    вообще логика работы странная местами. но дело хозяйское
     
  8. Алекс8

    Алекс8 Активный пользователь

    С нами с:
    18 май 2017
    Сообщения:
    1.730
    Симпатии:
    359
    редиректы вчера добавил когда выкладывал)) захотелось поиграться)) раньше их не было))
    а кроме редиректов почему странная логика?
     
  9. Walk

    Walk Активный пользователь

    С нами с:
    7 сен 2008
    Сообщения:
    452
    Симпатии:
    86
  10. runcore

    runcore Старожил

    С нами с:
    12 окт 2012
    Сообщения:
    3.625
    Симпатии:
    158
    неочевидная логика, как по мне. получается что нельзя использовать на сайте контроллер с именем, которое будет пересекаться с именем хоть одного экшена, любого другого контроллера.
    например, добавил на сайт новости /market/news
    а потом решил добавить глобальную ленту новостей типа /news/4, и все. роутер начнет вызывать не то что задумано.
    я бы сделал его более гибким, но менее интеллектуальным. я сам хочу управлять роутингом, а не надеяться каждый раз, что он сможет переварить любой новый необычный роутинг.

    если запросили неизвестный контроллер, зачем подменять его на дефолтный? запросили бред - это 404. и нечего его вообще обрабатывать дальше.

    ну и по мелочи, по коду бы сделал подругому моменты..

    как я и сказал, дело вкуса.
     
  11. Алекс8

    Алекс8 Активный пользователь

    С нами с:
    18 май 2017
    Сообщения:
    1.730
    Симпатии:
    359
    это роутинг для совсем простых сайтов)) я его делал для движка лендинга) 5-6 страниц и все))
     
  12. Danil005

    Danil005 Активный пользователь

    С нами с:
    2 янв 2015
    Сообщения:
    528
    Симпатии:
    30
    В .htaccess у меня так сделано было:

    RewriteRule ^auth$ /nav?page=auth
    Где ^auth$ - это как хочешь, чтобы выглядела ссылка.
    /nav?page=auth - адрес до файла
    --- Добавлено ---
    Но придется каждую ссылку прописывать:
    RewriteRule ^activation$ http://mysite/index.php?id=activation
    RewriteRule ^someone$ http://mysite/index.php?id=someone
    RewriteRule ^~$ http://mysite/admin
     
  13. Taktreba

    Taktreba Активный пользователь

    С нами с:
    11 янв 2017
    Сообщения:
    543
    Симпатии:
    132
    имеет какое то ввлияние на быстродействие?
     
  14. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.555
    Симпатии:
    1.754
  15. Danil005

    Danil005 Активный пользователь

    С нами с:
    2 янв 2015
    Сообщения:
    528
    Симпатии:
    30
    Не замечал.)
    Да нет, не лень, но это тоже является решением.
     
  16. Poznakomlus

    Poznakomlus Активный пользователь

    С нами с:
    12 сен 2014
    Сообщения:
    96
    Симпатии:
    19
    Адрес:
    Киев
    Мини велосипед
    PHP:
    1. <?php # Author - Fedor Vlasenko
    2. define('METHOD', $_SERVER['REQUEST_METHOD']);
    3. define('URI', parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH));
    4.  
    5. function router($url, ...$args)
    6. {
    7.     (empty($args[1]) || false !== strpos($args[0], METHOD))
    8.     && (URI === $url || preg_match('#^' . $url . '$#iu', URI, $match))
    9.     && die(call_user_func_array(end($args), $match ?? []));
    10. }
    11.  
    12. router('/', function () {
    13.     echo 'Main Micro';
    14. });
    15.  
    16. router('/article/(.*[^/])', 'GET', function (...$args) {
    17.     echo 'Article: ', $args[1];
    18. });
    19.  
    20. header($_SERVER['SERVER_PROTOCOL'] . ' 404 Not Found');
    21. echo '404';
     
    runcore нравится это.
  17. Алекс8

    Алекс8 Активный пользователь

    С нами с:
    18 май 2017
    Сообщения:
    1.730
    Симпатии:
    359
    PHP:
    1. router('/article/(.*[^/])', 'GET', function (...$args) {
    2.     echo 'Article: ', $args[1];
    3. });
    я смотрел несколько разных роутеров.. и честно говоря не понял прикол вот таких вот роутов)
    в yii2 роуты указываются в массиве и фреймворк сам роутит куда что надо...
    а вот нафига в мини роутерах такие записи.. они предлагают инклудить файлы в зависимости от роута?
    я поэтому для себя и написал свой минироутер)) потому что я хотел просто добавить контроллер и экшен в нем и больше ничего не добавлять))
    а тут получается для каждого экшена надо добавлять свой роут.. или я гоню?)
     
  18. keren

    keren Новичок

    С нами с:
    15 ноя 2017
    Сообщения:
    513
    Симпатии:
    42
    Роутер должен распределять запросы, а если там вывод то это вряд ли можно назвать роутером.
     
  19. Алекс8

    Алекс8 Активный пользователь

    С нами с:
    18 май 2017
    Сообщения:
    1.730
    Симпатии:
    359
    ну у меня вроде тоже распределяет) смотрит что в строке - и вызывает нужные экшены..
     
  20. Poznakomlus

    Poznakomlus Активный пользователь

    С нами с:
    12 сен 2014
    Сообщения:
    96
    Симпатии:
    19
    Адрес:
    Киев
    что вам мешает прописать параметры в массиве, json, yaml, ini, базе...
    И затем в цикле передавать параметры
    Это пример в функциональном стиле, для общего понимания, не для больших приложений.
    Роутер со "шлюхами" это прежде всего Reflection, где вы будете заглядывать в методы контроллера, смотреть вызываемые параметры и подставлять в них значения.
    как будто вы это не делаете используя классы
    Ребята я привел пример, и он вроде один из маленьких и универсальных, позволяющий отработать различные виды запросов.
    Советую для изучения рассмотреть Fat-Free Framework