В данном посте я хочу сохранить обсуждение и решение проблемы с офф форума Kohana. Проблему обозначаю в первой цитате, решение с подсказки сообщества (использовать коллбеки) во второй, т.е. сам код. Работает для kohana 3.2, для 3.3 надо править, чего я не сделал в виду не надобности. У меня проект на 3.2. :)
seyfer July 2013
Добрый день.Возможно уже существует решение, но я не могу придумать его сам.
Есть структура в папке controller admin admin/settings
В admin лежат контроллеры админки. И базовые, скажем так, классы, для подразделов. В подразделе (например settings) лежат контроллеры страниц подраздела. Работает это таким образом.
Если запрашивается ссылка /admin/settings/ то отрабатывает базовый контроллер, если же /admin/settings/users, то это уже контроллер users в папке settings.
Реализуют это сейчас два роута:
//поддиректории в admin
Route::set(‘subadmin’, ‘(< directory >)/< controller >(/< action >)(/page< page >)(/< id >)(/< id2 >)’, array
(
‘controller’ => ‘[a-z]+’,
‘page’ => ‘[0-9]+’,
‘action’ => ‘[\pL]+’,
//’directory’ => ‘admin(/settings){1}’
‘directory’ => ‘admin/[\pL]+’,
‘page’ => ‘[0-9]+’,
))
->defaults(array(
‘directory’ => ‘admin’,
‘controller’ => ‘main’,
‘action’ => ‘index’,
));
//роут для админки
Route::set(‘admin’, ‘admin(/< controller >(/< action >(/page< page >)(/< id >)(/< id2 >)))’, array(
‘directory’ => ‘admin’,
‘page’ => ‘[0-9]+’,
))
->defaults(array(
‘directory’ => ‘admin’,
‘controller’ => ‘main’,
‘action’ => ‘index’,
));А что мне делать, если я хочу третий уровень? Например по ссылке /admin/settings/users/client/ должен отработать контроллер Client который лежит в папке /admin/settings/users/. Т.е. логика та же, что со вторым уровнем, но чтобы она повторялась на любой уровень вложенности.
Не писать же мне роуты на каждый случай? subsettings, subusers…Меня устроит решение с бесконечным уровнем вложенности и с другой логикой.
WinterSilence July 2013
правило для проверки параметра directory подкорректироватьseyfer July 2013
@WinterSilence , как ?WinterSilence July 2013
как-то так: ‘directory’ => ‘[\w-/]+’ только наверное имеет смысл сделать жадную проверкуseyfer July 2013
@WinterSilence, это для роута admin ? И тогда я могу удалить subadmin?А жадная как будет выглядеть?
seyfer July 2013
У меня не получается сделать. Есть у кого-то еще идеи или готовое решение?ButscH July 2013
Вообще по аналогии с этим: http://kerkness.ca/kowiki/doku.php?id=routing:building_routes_with_subdirectoriesseyfer July 2013
@ButscH, спасибо, но не то. В данном случае интересно было только регулярное выражение..‘directory’ => ‘.+?’
Дальше автор приводит пример двух роутов, в которых жестко задан путь
‘directory’ => ‘content/library’
Я же хочу, чтобы работало по логике описанной мной выше и без создания дополнительных роутов. Я создал роут admin, а дальше в директории admin делаю подкаталоги, и они сразу доступны, как описано в первом посте темы.
ButscH July 2013
Вообще @seyfer ты мне кажется ты мало рассуждаешь логически, т.к. ты сам представь работу твоего роута, как роутер поймет что есть что.Вот скажи, что будет в этой строке директорией, что контроллером, что экшеном dir1/dir2/dir3/controller и что в этой dir1/dir2/dir3/controller/action.
Если сам не догадаешься, вот тебе подстказка:
dir1/dir2/dir3/controller Directory: dir1/dir2 Controller: dir3 Action: controller (Экшен мы не указывали, роут сработал не верно)
dir1/dir2/dir3/controller/action Directory: dir1/dir2/dir3 Controller: controller Action: action
Т.е. роут всегда будет считать последний параметр урл – экшен, предпослдений – контролллер, все остальное директория.
В общем это не самый умный роут.
Тебе нужно логическое разделение на переход от директории к контроллеру, например: dir1/dir2/dir3/controller.action или dir1/dir2/dir3/controller-action, (< directory >/)< controller >(-< action >(/page< page >)(/< id >)(/< id2 >)) хотя бы так.
WinterSilence July 2013
@ButscH +1@seyfer освой регулярные выражения http://ru.wikipedia.org/wiki/Регулярные_выражения всю жизнь за тебя другие люди проблемы решать не будут, мы скорее по части посоветовать, но знания то всё равно нужны ^^
biakaveron July 2013
Почему не рассматриваются коллбэки?seyfer July 2013
@ButscH, я как раз таки очень много рассуждаю логически по долгу работы.Ты видимо не понял задачу. Прочитай еще раз первый пост. Если бы не возникла проблема, я бы не писал. Я хочу именно по той логике, которую я описал в первом посте.
dir1/dir2/dir3/controller Directory: dir1/dir2 Controller: dir3 Action: controller (Экшен мы не указывали, роут сработал не верно)
Вот тебе тоже подумать. Конкретно под приведенный пример роута ссылкаТак работает. Есть папка админ, есть папка settings, так работает.
/admin/settings/seancesettings
Теперь я создаю папку seance в settings и переношу туда контроллер. Так не работает/admin/settings/seance/settings/
И так не работает/admin/settings/seance/settings/index
Потому, чтоprotected _directory => string(14) “admin/settings”
protected _controller => string(6) “seance”
protected _action => string(8) “settings”
protected _uri => string(30) “admin/settings/seance/settings”
А settings это контроллер, я не хочу указывать action. А даже если указать, тоprotected _directory => string(14) “admin/settings”
protected _controller => string(6) “seance”
protected _action => string(8) “settings”
protected _uri => string(36) “admin/settings/seance/settings/index”
То же самое. И так мы видим, что проблема все таки есть.Теперь рассмотрим пример советчика учить регулярки.
@WinterSilence сказал
‘[\w-/]+’
, окСсылка /admin/settings/seance/settings/ открылась, ура. А вот ни один экшн не работает. Потому, что контроллером считается
protected _directory => string(30) “admin/settings/seance/settings”
protected _controller => string(6) “update”
protected _action => string(5) “index”
protected _uri => string(37) “admin/settings/seance/settings/update”
И последний вариант из статьи, приведенной @ButscH, которому все время что-то кажется, или он не умеет уважительно общаться с собеседником..+?
не открылась ссылка admin/settings/seance/settings, ведь не работает так, сказал же.protected _directory => string(14) “admin/settings”
protected _controller => string(6) “seance”
protected _action => string(8) “settings”
protected _uri => string(30) “admin/settings/seance/settings”
Но ты конечно умнее всех.Пока что лучший вариант от @WinterSilence –
‘[\w-/]+’
, только заставить бы работать экшены. Если бы все так просто было, я бы не создавал тему.
Как видно задача не тривиальная, регулярными выражениями ее не решить, т.к. логика так сказать кастомная. Вот решение через callback.
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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 |
<?php defined('SYSPATH') or die('No direct script access.'); /** * Description of subdirectory * * @ author seyfer */ class Route_Subdirectory extends Myroute { /** * * @var type */ protected static $pattern = "(< directory >)(/< controller >)(/< action >)(/page< page >)(/< id >)(/< id2 >)"; /** * * @var type */ protected static $defaults = array(); /** * * @var type */ protected static $baseDir; /** * * @param type $uri * @return boolean|\ArrayIterator */ public static function route($uri) { // Debug::vars("uri", $uri); static::$defaults = Route::get("sub")->defaults(); // Debug::vars("defaults", static::$defaults); static::$baseDir = APPPATH . "classes/controller/"; $result = static::processUri($uri); Debug::vars("result", $result); $matches = static::checkCompiled($result, $uri); if (!$matches) return FALSE; $params = static::formParams($matches); $finalResult = array_merge($result, $params); // $finalResult['params'] = $params; Debug::vars("finalResult", $finalResult); return $finalResult; } } ?> And base class <?php defined('SYSPATH') or die('No direct script access.'); /** * Description of myroute * * @ author seyfer */ class Myroute { /** * паттерн для компиляции * @var type */ protected static $pattern; /** * * @var type */ protected static $defaults = array(); /** * * @var type */ protected static $baseDir; /** * роут * @param type $uri * @return boolean|\ArrayIterator */ public static function route($uri) { } protected static function processUri($uri) { $name = static::$baseDir; $segments = explode(DIRECTORY_SEPARATOR, $uri); Debug::vars($segments); $segmentsRecIterator = new ArrayIterator($segments); $directory = ""; $directories = array(); $controller = ""; $action = ""; $params = array(); foreach ($segmentsRecIterator as $segment) { //доабавить сегмент $name .= $segment; // Debug::vars($name); //проверить на папку if ($dirChecked = static::checkDir($name, $segment, $directory)) $directories[] = $dirChecked; //имя контроллера для проверки action if ($controller) $controllerName = static::currentControllerName($directories, $controller); // Debug::vars("controllerName", $controllerName); //шаблон для проверки action $callable = static::getCallableName($controllerName, $segment); if ($controllerChecked = static::checkController($name, $segment)) $controller = $controllerChecked; if ($actionChecked = static::checkAction($callable, $segment)) $action = $segment; $name .= DIRECTORY_SEPARATOR; } //если последний контроллер $directories = static::checkLastSegment($controller, $segment, $directories); $directory = implode(DIRECTORY_SEPARATOR, $directories); //результат парсинга uri $result = array( "directory" => $directory, "controller" => $controller, "action" => $action, ); return $result; } /** * * @param type $result * @param type $uri * @return type */ protected static function checkCompiled($result, $uri) { //подготовить паттерн $matchPattern = array_merge($result, array('page' => '[0-9]+')); //компиляция $patternCompiled = Route::compile(static::$pattern, $result); // Debug::vars("pattern", $patternCompiled); //если скомпилирован, то все ок preg_match($patternCompiled, $uri, $matches); // Debug::vars("matches", $matches); return $matches; } /** * * @param type $matches * @return type */ protected static function formParams($matches) { foreach ($matches as $key => $val) { if (!is_numeric($key)) { $params[$key] = $val; } } return $params; } /** * шаблон вызова * @param type $controllerName * @param type $segment * @return type */ protected static function getCallableName($controllerName, $segment) { //шаблон для проверки action return "$controllerName::action_$segment"; } /** * если последний сегмент контроллер, то удалить из папок * @param type $controller * @param type $segment * @param type $directories * @return type */ protected static function checkLastSegment($controller, $segment, $directories) { if ($controller == $segment) { if (in_array($segment, $directories)) { // Debug::vars($directories, array($segment)); $directories = array_diff($directories, array($segment)); } } return $directories; } /** * * @param type $callable * @param type $segment * @return boolean */ protected static function checkAction($callable, $segment) { if (is_callable($callable)) { return $segment; } return FALSE; } /** * * @param type $name * @param type $segment * @return boolean */ protected static function checkController($name, $segment) { if (is_file($name . EXT)) { // Debug::vars("IS FILE controller", $name); return $segment; } return FALSE; } /** * * @param type $name * @param type $segment * @param string $directory * @return boolean */ protected static function checkDir($name, $segment, $directory) { if (is_dir($name)) { // Debug::vars("dir " . $name); if (!empty($directory)) $directory .= DIRECTORY_SEPARATOR; return $segment; } return FALSE; } /** * вычислить имя текущего контроллера * @param type $directories * @param type $controller * @return type */ protected static function currentControllerName($directories, $controller) { $controllerName = "Controller_"; if ($directories) { $dirNamesUp = array_map(function($item) { return ucfirst($item); }, $directories); $controllerName .= implode("_", $dirNamesUp) . "_"; } $controllerName .= ucfirst($controller); return $controllerName; } } ?> |