[ Поиск ] - [ Пользователи ] - [ Календарь ]
Полная Версия: Курс "MVC от теории к практике"
Alehandr
Router

Всем привет! Недавно до меня дошло то, что свои курсы я пишу как простые статьи и решил изменить стратегию их ведения. Первым этапом будет поставление проблем и основных требований. Потом я буду выслушивать ВАС, дорогие мои читатели, на предмет того, как это реализовать. И уже после всех обсуждений я буду предоставлять код, написанный мной, а возможно и вами, который мы возможно и будем использовать.

Проблема:
Нам необходимо написать класс Router.

Основные требования:
Данный класс должен определить контроллер и его метод, который должен вывести страницу, которую ожидает пользователь.

На данный момент:
Сейчас у нас уже написан каркас нужного класса:
<?php if (!defined('APP')) die('Access is forbidden!!!');

class Router_Core extends Observed {

/**
* Singleton
*
*
@return Router
*/

public static function &instance() {
static $instance = null;
if ($instance === null) {
$instance = new Router();
}
return $instance;
}

public function Run($route) {
echo 'Router run: ' . $route;
}

}


if (!defined('ROUTER_CLASS')) {
class Router extends Router_Core {}
}


Ваши соображения? smile.gif



Спустя 44 минуты, 25 секунд (27.11.2009 - 19:12) VolCh написал(а):
Для начала надо определиться, как пользователь будет задавать страницу, которую ожидает увидеть smile.gif

Спустя 24 минуты, 40 секунд (27.11.2009 - 19:36) Joker написал(а):
вообще новая стратегия мне понравилась, но есть одна просьба как я понел у тебя много свободного времени, и ты их очень быстро делаешь, дак вот просьба в следующем делать паузы между лекциями как минимум неделю, а лучше смотреть чтоб 60-70% процентов учеников понимали, вот как поймут достовать из папки следующую.

З.Ы. я вот только за сабой заметил, и решил спросить Alehandr не против что общаюсь на "ты" ?

Спустя 2 часа, 53 минуты, 23 секунды (27.11.2009 - 22:30) amega написал(а):
Цитата
и ты их очень быстро делаешь

+1 эт правильно подметили

Спустя 3 часа, 10 минут, 56 секунд (28.11.2009 - 01:41) VolCh написал(а):
внесу предложение тогда:
URL может быть двух видов, вернее одного, но один для ЧПУ, другой нет

http://example.com/controler/action/var1/v.../varN/valN#name, где controler - имя контролера (класса/объекта) action - имя действия (метода), var1..N - имена параметров, передаваемых в действие, val1..N - их значения, name - HTML "метка"

или

http://example.com/c=controler&a=action&va...&varN=&valN#nam, где почти всё тоже самое, только с и а имена переменных с именем контроллера и действия (можно и длинное что-нить использовать)

В таком виде если пользователь ожидает 10-ю (id) страницу (в смысле модели) начиная с 75 строчки, длиной в 25 строк и отсортированной по дате, то будет что-то вроде http://example.com/page/show/from/75/limit/25/orderby/date (или http://example.com/?c=page&a=show&from=75&...5&orderby=date)

Есть возражения по сушеству? smile.gif

Спустя 7 часов, 14 минут, 41 секунда (28.11.2009 - 08:55) Alehandr написал(а):
Joker
На "ты", я только за. А вот по прошлым лекциям я просто не мог понять, разобрались ли или еще нет, потому, что комментариев путем не было... Теперь думаю будет иначе...

VolCh
+1, но вариант может быть только один: index.php?route=controller/action/arg1/arg2/... даже если мы и используем ЧПУ. ЧПУ просто скрывает index.php?route=

Спустя 2 часа, 51 минута, 18 секунд (28.11.2009 - 11:47) Argnist написал(а):
еще бы понять для чего это все надо...

Спустя 1 час, 27 минут, 47 секунд (28.11.2009 - 13:14) Alehandr написал(а):
Argnist
Вогнал в ступор. У нас контроллеров может быть большое множество (Контроллер новостей, блога, форума и т.д. и т.п.), так вот роутер нам нужен для того, чтобы определить какой контроллер и его метод использовать и соответственно запустить все это дело.

К примеру:
http://core/news/
Запускает контроллер News_Controller через его метод index_page
http://core/news/read/2/
Запускает контроллер News_Controller через его метод read_page с одним аргументом "2".
http://core/blog/page/3/
Запускает контроллер Blog_Controller через его метод page_page с аргументом 3.

и т.п.

Спустя 28 минут, 27 секунд (28.11.2009 - 13:43) VolCh написал(а):
Alehandr
ну можно и так, вернее так даже попроще будет, но вот в моем варианте аргументы именованные у действий, а в твоем нет..., по мне так лучше именованные, как для ЧПУ (то есть для пользователя), так и для разработчика. Хотя можно ввести третий неименованный - id (он чаще всех используется вроде, особенно если CRUD реализовывать напрямую), а дальше или по порядку неименованные или именованные в любом порядке.

Спустя 40 минут, 22 секунды (28.11.2009 - 14:23) Alehandr написал(а):
VolCh
Идея моего роутера в том, что запускать контроллеры для него почти то-же что и для пользователей окон или линуха из консоли приложения с аргументами.

К примеру:
class Test_Controller extends Controller {
public function test_page($id = 0, $text = 'О_о') {
echo (int)$id . ' : ' . htmlspecialchars($text) . '<br />';
}
}


То ссылками на его запуск будут http://site/test/test/123/asdasd/ ($id = 123, $text = asdasd) или http://site/test/test/123/ ($id = 123, $text = 'O_o') или просто http://site/test/test/ (по умолчанию)...

Спустя 7 минут, 39 секунд (28.11.2009 - 14:31) Joker написал(а):
Цитата (Alehandr @ 28.11.2009 - 10:55)
А вот по прошлым лекциям я просто не мог понять, разобрались ли или еще нет, потому, что комментариев путем не было...


Собственно то их не было потому что они быстро выходили, вот смотри вы с глоком начали почти одновременно но у него 1 лекция у тебя уже 8 я если чесно даже прочитать не успел больше половины из них.

Спустя 15 минут, 55 секунд (28.11.2009 - 14:47) Alehandr написал(а):
Joker
Ну, догоняй. Я теперь к следующей лекции буду переходить только тогда, когда будут достигнуты поставленные цели.

Спустя 22 минуты, 55 секунд (28.11.2009 - 15:10) Argnist написал(а):
пример бы как будет уже сайт выглядеть, а пока что просто набор каких то непонятных классов и как их использовать непонятно)

Спустя 4 часа, 50 минут, 22 секунды (28.11.2009 - 20:00) VolCh написал(а):
Alehandr я более похожую на консоль конструкцию предлагаю smile.gif http://site/test/test/id/123/text/asdasd/ ну или http://site/test/test/123/text/asdasd/ , то есть явно задавать имена аргументов (ну кроме id)

Согласись, что конструкция http://example.com/page/show/id/10/from/75...25/orderby/date более читаема, чем http://example.com/page/show/10/75/25/date,

Спустя 5 минут, 28 секунд (28.11.2009 - 20:05) Joker написал(а):
Цитата (Alehandr @ 28.11.2009 - 16:47)
Ну, догоняй.

Завтра постараюсь.
Цитата (Alehandr @ 28.11.2009 - 16:47)
Я теперь к следующей лекции буду переходить только тогда, когда будут достигнуты поставленные цели.

А вот это мудрое решение.


Спустя 12 часов, 3 минуты, 54 секунды (29.11.2009 - 08:09) Alehandr написал(а):
VolCh
Ну можно и так сделать. Первые 2 аргумента - контроллер и метод, остальные как ключ и значение парсим. Но тогда еще 1 проблемка: хранить аргументы в 1 классе или сразу передавать контроллеру.

Спустя 10 часов, 46 минут, 3 секунды (29.11.2009 - 18:55) VolCh написал(а):
1 - первом ли одном? smile.gif

Как вариант можно сделать объект, например, request, в котором хранить аргументы да и вообще, всё относящееся к запросу, например, куки, данные сессиии и т. д. ) и его передавать контроллеру.

Спустя 47 минут, 57 секунд (29.11.2009 - 19:43) Alehandr написал(а):
VolCh
В одном smile.gif
На счет Request'а верно подметил.

Ну чтож, будем реализовывать?

Спустя 7 дней, 12 часов, 46 минут, 29 секунд (7.12.2009 - 08:30) Alehandr написал(а):
И что, все забили на курс?

Спустя 3 дня, 6 часов, 11 минут, 31 секунда (10.12.2009 - 14:41) VolCh написал(а):
Сорри, уезжал в другой город, завтра включусь smile.gif

Спустя 3 дня, 16 часов, 22 минуты, 9 секунд (14.12.2009 - 07:04) VolCh написал(а):
Примерно так получилось:

Запрос (core/request.php)
<?php if (!defined('APP')) die('Access is forbidden!!!');

class Request_Core {
/**
*
@var string Маршрут
*/

public $route;
/**
*
@var string Контроллёр
*/

public $controller;
/**
*
@var string Действие
*/

public $action;
/**
*
@var array Параметры запроса
*/

public $params = array();
/**
* Singleton
*
*
@return Request
*/

public static function &instance() {
static $instance = null;
if ($instance === null) {
$instance = new Request();
}
return $instance;
}

}


if (!defined('REQUEST_CLASS')) {
class Request extends Request_Core { }
}



Роутер (core/router.php)

<?php if (!defined('APP')) die('Access is forbidden!!!');

class Router_Core extends Object {

/**
* Singleton
*
*
@return Router
*/

public static function &instance() {
static $instance = null;
if ($instance === null) {
$instance = new Router();
}
return $instance;
}

public function Run($route) {
$request = Request::instance();
$request->route = $route;
$parts = explode('/', $route);
if (count($parts) == 1) {
$request->controller = empty($parts[0]) ? 'default' : $parts[0];
$request->action = 'index';
} else {
$request->controller = array_shift($parts);
$request->action = array_shift($parts);
while (!is_null($name = array_shift($parts))) {
$value = array_shift($parts);
$request->params[$name] = is_null($value) ? '' : $value;
}
}

$controller = $this->getController($request->controller);
call_user_func(array($controller, $request->action . '_page'), $request);
}
}


if (!defined('ROUTER_CLASS')) {
class Router extends Router_Core {}
}



Ну и для тестирования создал контроллер (controllers/default.php)

<?php
class
default_Controller extends Object {
public function index_page(Request $request) {
echo "This is index";
var_dump($request);
}
}


Вроде работает smile.gif

Спустя 1 день, 11 часов, 46 минут, 38 секунд (15.12.2009 - 18:50) Alehandr написал(а):
VolCh
Молодец. Но, почему логика Запроса находится в Роутере? (определение параметров и т.п.)... Подумай...

Спустя 32 минуты, 46 секунд (15.12.2009 - 19:23) VolCh написал(а):
Наверное я неправильно понимаю "разделение труда" стартера, роутера, запроса и контроллера. По-моему, именно роутер должен определить, что в запросе (хотя формирование объекта Запрос с передачей ему uri , наверное, и следует перенести в стартер), но определить что в запросе является именем контроллера, его действием, а что параметрами - имхо, задача как раз роутера. Можно, конечно, в Запрос перенести функцию разбивки uri на части, но тогда роутер сведется к двум (если вынести создание Запроса в стартер) строчкам
        $controller = $this->getController($request->controller);
call_user_func(array($controller, $request->action . '_page'), $request);

И еще мне кажется, что оставив парсинг uri на долю роутера мы можем в будущем реализовать любую, сколь угодно сложную, схему соответствия URL и вызовов контроллер->действие(параметры)

Спустя 19 часов, 35 минут, 23 секунды (16.12.2009 - 14:58) Alehandr написал(а):
VolCh
Мне кажется самым целесообразным будет так:
Роутер берет часть пути под определение контроллера и страницы, а остальное отдает Запросу на определение аргументов... Ведь аргументы как раз и относятся к запросу.

Спустя 7 часов, 2 минуты, 3 секунды (16.12.2009 - 22:00) VolCh написал(а):
Alehandr
Как мне кажется задача роутера как раз разбор запроса (не только на базе урл, еще могут быть и POST-данные например) и формирование "описательного" объекта Request.

Ведь в теории у нас могут быть запросы из которых контроллер и его действие выбирается не столь однозначно, как /controller/action/params, но и, например, при соответствии URL'a какому-то регулярному выражению, наличию каких-то параметров в POST запросе, наличии каких-то данных в сессии/куках (например сведениях об аутенфикации), а явно контроллер/действие нигде не упоминаться.

Например, правило роутинга может быть такое:
Если url запроса соответствует маске /yyyy/mm/dd/ (псевдокод smile.gif ), запрос сделан POST методом с полями title и body, но без поля id, то надо вызвать метод Add контроллера Events, передав ему параметр date, сформированный на базе yyyy/mm/dd
Если всё то же самое, но задано поле id, то надо вызвать метод Edit, передав ему и id, вдобавок к date
Если же маске соответствует, но метод GET, то вызвать метод List контроллера Events c date

В таком случае, разделив опредление контроллера/действия и параметров, придётся дважды анализировать собственно HTTP запрос на основе одних и тех же правил - первый раз в роутере для определения контроллера/действия, второй раз в Запросе, чтобы определить какие параметры передать контроллеру.

Спустя 7 часов, 45 минут, 43 секунды (17.12.2009 - 05:46) Alehandr написал(а):
VolCh
Маски - потом. POST т.п. уже на совести контроллеров.

Спустя 20 дней, 1 час, 48 минут, 59 секунд (7.01.2010 - 07:35) Alehandr написал(а):
Залил то, что сейчас есть с маленькими исправлениями: http://slil.ru/28446957.

Теперь о проблеме:

Router:
<?php if (!defined('APP')) die('Access is forbidden!!!');

class Router_Core extends Object {

/**
*
@var mixed Конфигурация
*/

public $config;

/**
*
@var string Контроллер
*/

public $controller = 'system';

/**
*
@var string Страница (метод контроллера)
*/

public $function = 'index';

/**
* Singleton
*
*
@return Router
*/

public static function &instance() {
static $instance = null;
if ($instance === null) {
$instance = new Router();
}
return $instance;
}

public function Run($route) {
$route = explode('/', $route);
if (!empty($route[0])) {
$this->controller = $route[0];
unset($route[0]);

if (count($route) % 2 == 1) {
$this->function = $route[1];
unset($route[1]);
}
}

$route = implode('/', $route);

Registry::get('Request')->Init($route);
if (!$this->FireEvent('Out')) {
$this->Out();
}
}


public function Out() {
$controller = $this->getController($this->controller);
if ($controller !== null) {
$page = $this->function . '_page';
if (method_exists($controller, $page)) {
$controller->$page();
} else {
$this->getController('System')->_404_page();
}
}
else {
$this->getController('System')->_404_page();
}
}

}


if (!defined('ROUTER_CLASS')) {
class Router extends Router_Core {}
}


Request:
<?php if (!defined('APP')) die('Access is forbidden!!!');

class Request_Core extends Object {

/**
*
@var mixed Аргументы
*/

public $args = array();

public function Init($route) {
if ($this->FireEvent('Init', array('route' => &$route))) return;

$route = explode('/', $route);
for ($i = 0 ; $i < count($route) ; $i+=2) {
$this->args[$route[$i]] = $route[$i+1];
}
}

}


if (!defined('REQUEST_CLASS')) {
class Request extends Request_Core { }
}


Functions.php
<?php if (!defined('APP')) die('Access is forbidden!!!');

function __autoload($classname) {
$class = explode('_', strtolower($classname));

$folder = 'classes';
if (count($class) > 1) switch($class[1]) {
case 'controller':
$folder = 'controllers';
break;
case 'model':
$folder = 'models';
break;
}

if ($class[count($class) - 1] != 'core' && is_file(APP . $folder . DS . $class[0] . EXT)) { //Проверяем на наличие файла в папке приложения
define(strtoupper($class[0]) . '_' . strtoupper($folder));
require(APP . $folder . DS . $class[0] . EXT);
return true;
}

if (is_file(CORE . $folder . DS . $class[0] . EXT)) { //Проверяем на наличие в папке ядра
require(CORE . $folder . DS . $class[0] . EXT);
return true;
}

return false;
}

function GET($name, $hsc = true) {
return $hsc == true ? htmlspecialchars(Registry::get('Request')->args[$name]) : Registry::get('Request')->args[$name];
}

function POST($name, $hsc = true) {
return $hsc == true ? htmlspecialchars($_POST[$name]) : $_POST[$name];
}

2 новых функции потом будут перенесены в помощники(хелперы)...

Обсудим?

п.с.: Вернемся к классу Configurator и сделаем конфигурацию для роутера, а именно: контроллер по умолчанию. Кто?

Спустя 2 года, 24 дня, 10 часов, 2 минуты, 59 секунд (1.02.2012 - 16:38) Dagot написал(а):
  private function _router() {

$request = $_SERVER["REQUEST_URI"];
preg_match_all("~(.*)(/|\?|\&)~iU", $request, $url);

$url = $url[1];


$url[0] = '/';

$this->_urls = $url;


$this->_url = implode('/', $url);

$this->_url = trim ($this->_url, '/ ');

$this->_url = (empty($this->_url)) ? '/' : '/' . $this->_url . '/';

$controler = 'main';
$functionControler = 'index';
$argument = array();

if (count($url) > 1 && isset($url[1])) {

$controler = $url[1];

$functionControler = isset($url[2])
? $url[2]
: 'index';

$argument = array();

if( isset($url[2]) && isset($url[3]) ) {

unset ($url[0]);
unset ($url[1]);
unset ($url[2]);

$argument = $url;

}



}

$this->_controler = $controler;
$this->_functionControler = $functionControler;
$this->_argument = $argument;

}

Спустя 38 секунд (1.02.2012 - 16:39) Dagot написал(а):
Закоментить надо если кому непонятно?
Быстрый ответ:

 Графические смайлики |  Показывать подпись
Здесь расположена полная версия этой страницы.
Invision Power Board © 2001-2024 Invision Power Services, Inc.