1<?php 2/** 3 * Zend Framework (http://framework.zend.com/) 4 * 5 * @link http://github.com/zendframework/zf2 for the canonical source repository 6 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 7 * @license http://framework.zend.com/license/new-bsd New BSD License 8 */ 9 10namespace Zend\Navigation\Page; 11 12use Zend\Mvc\ModuleRouteListener; 13use Zend\Mvc\Router\RouteMatch; 14use Zend\Mvc\Router\RouteStackInterface; 15use Zend\Navigation\Exception; 16 17/** 18 * Represents a page that is defined using controller, action, route 19 * name and route params to assemble the href 20 */ 21class Mvc extends AbstractPage 22{ 23 /** 24 * Action name to use when assembling URL 25 * 26 * @var string 27 */ 28 protected $action; 29 30 /** 31 * Controller name to use when assembling URL 32 * 33 * @var string 34 */ 35 protected $controller; 36 37 /** 38 * URL query part to use when assembling URL 39 * 40 * @var array|string 41 */ 42 protected $query; 43 44 /** 45 * Params to use when assembling URL 46 * 47 * @see getHref() 48 * @var array 49 */ 50 protected $params = array(); 51 52 /** 53 * RouteInterface name to use when assembling URL 54 * 55 * @see getHref() 56 * @var string 57 */ 58 protected $route; 59 60 /** 61 * Cached href 62 * 63 * The use of this variable minimizes execution time when getHref() is 64 * called more than once during the lifetime of a request. If a property 65 * is updated, the cache is invalidated. 66 * 67 * @var string 68 */ 69 protected $hrefCache; 70 71 /** 72 * RouteInterface matches; used for routing parameters and testing validity 73 * 74 * @var RouteMatch 75 */ 76 protected $routeMatch; 77 78 /** 79 * If true and set routeMatch than getHref will use routeMatch params 80 * to assemble uri 81 * @var bool 82 */ 83 protected $useRouteMatch = false; 84 85 /** 86 * Router for assembling URLs 87 * 88 * @see getHref() 89 * @var RouteStackInterface 90 */ 91 protected $router = null; 92 93 /** 94 * Default router to be used if router is not given. 95 * 96 * @see getHref() 97 * 98 * @var RouteStackInterface 99 */ 100 protected static $defaultRouter = null; 101 102 /** 103 * Default route name 104 * 105 * @var string 106 */ 107 protected static $defaultRoute = null; 108 109 // Accessors: 110 111 /** 112 * Returns whether page should be considered active or not 113 * 114 * This method will compare the page properties against the route matches 115 * composed in the object. 116 * 117 * @param bool $recursive [optional] whether page should be considered 118 * active if any child pages are active. Default is 119 * false. 120 * @return bool whether page should be considered active or not 121 */ 122 public function isActive($recursive = false) 123 { 124 if (!$this->active) { 125 $reqParams = array(); 126 if ($this->routeMatch instanceof RouteMatch) { 127 $reqParams = $this->routeMatch->getParams(); 128 129 if (isset($reqParams[ModuleRouteListener::ORIGINAL_CONTROLLER])) { 130 $reqParams['controller'] = $reqParams[ModuleRouteListener::ORIGINAL_CONTROLLER]; 131 } 132 133 $pageParams = $this->params; 134 if (null !== $this->controller) { 135 $pageParams['controller'] = $this->controller; 136 } 137 if (null !== $this->action) { 138 $pageParams['action'] = $this->action; 139 } 140 141 if (null !== $this->getRoute()) { 142 if ( 143 $this->routeMatch->getMatchedRouteName() === $this->getRoute() 144 && (count(array_intersect_assoc($reqParams, $pageParams)) == count($pageParams)) 145 ) { 146 $this->active = true; 147 return $this->active; 148 } else { 149 return parent::isActive($recursive); 150 } 151 } 152 } 153 154 $pageParams = $this->params; 155 156 if (null !== $this->controller) { 157 $pageParams['controller'] = $this->controller; 158 } else { 159 /** 160 * @todo In ZF1, this was configurable and pulled from the front controller 161 */ 162 $pageParams['controller'] = 'index'; 163 } 164 165 if (null !== $this->action) { 166 $pageParams['action'] = $this->action; 167 } else { 168 /** 169 * @todo In ZF1, this was configurable and pulled from the front controller 170 */ 171 $pageParams['action'] = 'index'; 172 } 173 174 if (count(array_intersect_assoc($reqParams, $pageParams)) == count($pageParams)) { 175 $this->active = true; 176 return true; 177 } 178 } 179 180 return parent::isActive($recursive); 181 } 182 183 /** 184 * Returns href for this page 185 * 186 * This method uses {@link RouteStackInterface} to assemble 187 * the href based on the page's properties. 188 * 189 * @see RouteStackInterface 190 * @return string page href 191 * @throws Exception\DomainException if no router is set 192 */ 193 public function getHref() 194 { 195 if ($this->hrefCache) { 196 return $this->hrefCache; 197 } 198 199 $router = $this->router; 200 if (null === $router) { 201 $router = static::$defaultRouter; 202 } 203 204 if (!$router instanceof RouteStackInterface) { 205 throw new Exception\DomainException( 206 __METHOD__ 207 . ' cannot execute as no Zend\Mvc\Router\RouteStackInterface instance is composed' 208 ); 209 } 210 211 if ($this->useRouteMatch() && $this->getRouteMatch()) { 212 $rmParams = $this->getRouteMatch()->getParams(); 213 214 if (isset($rmParams[ModuleRouteListener::ORIGINAL_CONTROLLER])) { 215 $rmParams['controller'] = $rmParams[ModuleRouteListener::ORIGINAL_CONTROLLER]; 216 unset($rmParams[ModuleRouteListener::ORIGINAL_CONTROLLER]); 217 } 218 219 if (isset($rmParams[ModuleRouteListener::MODULE_NAMESPACE])) { 220 unset($rmParams[ModuleRouteListener::MODULE_NAMESPACE]); 221 } 222 223 $params = array_merge($rmParams, $this->getParams()); 224 } else { 225 $params = $this->getParams(); 226 } 227 228 229 if (($param = $this->getController()) !== null) { 230 $params['controller'] = $param; 231 } 232 233 if (($param = $this->getAction()) !== null) { 234 $params['action'] = $param; 235 } 236 237 switch (true) { 238 case ($this->getRoute() !== null || static::getDefaultRoute() !== null): 239 $name = ($this->getRoute() !== null) ? $this->getRoute() : static::getDefaultRoute(); 240 break; 241 case ($this->getRouteMatch() !== null): 242 $name = $this->getRouteMatch()->getMatchedRouteName(); 243 break; 244 default: 245 throw new Exception\DomainException('No route name could be found'); 246 } 247 248 $options = array('name' => $name); 249 250 // Add the fragment identifier if it is set 251 $fragment = $this->getFragment(); 252 if (null !== $fragment) { 253 $options['fragment'] = $fragment; 254 } 255 256 if (null !== ($query = $this->getQuery())) { 257 $options['query'] = $query; 258 } 259 260 $url = $router->assemble($params, $options); 261 262 return $this->hrefCache = $url; 263 } 264 265 /** 266 * Sets action name to use when assembling URL 267 * 268 * @see getHref() 269 * 270 * @param string $action action name 271 * @return Mvc fluent interface, returns self 272 * @throws Exception\InvalidArgumentException if invalid $action is given 273 */ 274 public function setAction($action) 275 { 276 if (null !== $action && !is_string($action)) { 277 throw new Exception\InvalidArgumentException( 278 'Invalid argument: $action must be a string or null' 279 ); 280 } 281 282 $this->action = $action; 283 $this->hrefCache = null; 284 return $this; 285 } 286 287 /** 288 * Returns action name to use when assembling URL 289 * 290 * @see getHref() 291 * 292 * @return string|null action name 293 */ 294 public function getAction() 295 { 296 return $this->action; 297 } 298 299 /** 300 * Sets controller name to use when assembling URL 301 * 302 * @see getHref() 303 * 304 * @param string|null $controller controller name 305 * @return Mvc fluent interface, returns self 306 * @throws Exception\InvalidArgumentException if invalid controller name is given 307 */ 308 public function setController($controller) 309 { 310 if (null !== $controller && !is_string($controller)) { 311 throw new Exception\InvalidArgumentException( 312 'Invalid argument: $controller must be a string or null' 313 ); 314 } 315 316 $this->controller = $controller; 317 $this->hrefCache = null; 318 return $this; 319 } 320 321 /** 322 * Returns controller name to use when assembling URL 323 * 324 * @see getHref() 325 * 326 * @return string|null controller name or null 327 */ 328 public function getController() 329 { 330 return $this->controller; 331 } 332 333 /** 334 * Sets URL query part to use when assembling URL 335 * 336 * @see getHref() 337 * @param array|string|null $query URL query part 338 * @return self fluent interface, returns self 339 */ 340 public function setQuery($query) 341 { 342 $this->query = $query; 343 $this->hrefCache = null; 344 return $this; 345 } 346 347 /** 348 * Returns URL query part to use when assembling URL 349 * 350 * @see getHref() 351 * 352 * @return array|string|null URL query part (as an array or string) or null 353 */ 354 public function getQuery() 355 { 356 return $this->query; 357 } 358 359 /** 360 * Sets params to use when assembling URL 361 * 362 * @see getHref() 363 * @param array|null $params [optional] page params. Default is null 364 * which sets no params. 365 * @return Mvc fluent interface, returns self 366 */ 367 public function setParams(array $params = null) 368 { 369 $this->params = empty($params) ? array() : $params; 370 $this->hrefCache = null; 371 return $this; 372 } 373 374 /** 375 * Returns params to use when assembling URL 376 * 377 * @see getHref() 378 * 379 * @return array page params 380 */ 381 public function getParams() 382 { 383 return $this->params; 384 } 385 386 /** 387 * Sets route name to use when assembling URL 388 * 389 * @see getHref() 390 * 391 * @param string $route route name to use when assembling URL 392 * @return Mvc fluent interface, returns self 393 * @throws Exception\InvalidArgumentException if invalid $route is given 394 */ 395 public function setRoute($route) 396 { 397 if (null !== $route && (!is_string($route) || strlen($route) < 1)) { 398 throw new Exception\InvalidArgumentException( 399 'Invalid argument: $route must be a non-empty string or null' 400 ); 401 } 402 403 $this->route = $route; 404 $this->hrefCache = null; 405 return $this; 406 } 407 408 /** 409 * Returns route name to use when assembling URL 410 * 411 * @see getHref() 412 * 413 * @return string route name 414 */ 415 public function getRoute() 416 { 417 return $this->route; 418 } 419 420 /** 421 * Get the route match. 422 * 423 * @return \Zend\Mvc\Router\RouteMatch 424 */ 425 public function getRouteMatch() 426 { 427 return $this->routeMatch; 428 } 429 430 /** 431 * Set route match object from which parameters will be retrieved 432 * 433 * @param RouteMatch $matches 434 * @return Mvc fluent interface, returns self 435 */ 436 public function setRouteMatch(RouteMatch $matches) 437 { 438 $this->routeMatch = $matches; 439 return $this; 440 } 441 442 /** 443 * Get the useRouteMatch flag 444 * 445 * @return bool 446 */ 447 public function useRouteMatch() 448 { 449 return $this->useRouteMatch; 450 } 451 452 /** 453 * Set whether the page should use route match params for assembling link uri 454 * 455 * @see getHref() 456 * @param bool $useRouteMatch [optional] 457 * @return Mvc 458 */ 459 public function setUseRouteMatch($useRouteMatch = true) 460 { 461 $this->useRouteMatch = (bool) $useRouteMatch; 462 $this->hrefCache = null; 463 return $this; 464 } 465 466 /** 467 * Get the router. 468 * 469 * @return null|RouteStackInterface 470 */ 471 public function getRouter() 472 { 473 return $this->router; 474 } 475 476 /** 477 * Sets router for assembling URLs 478 * 479 * @see getHref() 480 * 481 * @param RouteStackInterface $router Router 482 * @return Mvc fluent interface, returns self 483 */ 484 public function setRouter(RouteStackInterface $router) 485 { 486 $this->router = $router; 487 return $this; 488 } 489 490 /** 491 * Sets the default router for assembling URLs. 492 * 493 * @see getHref() 494 * @param RouteStackInterface $router Router 495 * @return void 496 */ 497 public static function setDefaultRouter($router) 498 { 499 static::$defaultRouter = $router; 500 } 501 502 /** 503 * Gets the default router for assembling URLs. 504 * 505 * @return RouteStackInterface 506 */ 507 public static function getDefaultRouter() 508 { 509 return static::$defaultRouter; 510 } 511 512 /** 513 * Set default route name 514 * 515 * @param string $route 516 * @return void 517 */ 518 public static function setDefaultRoute($route) 519 { 520 static::$defaultRoute = $route; 521 } 522 523 /** 524 * Get default route name 525 * 526 * @return string 527 */ 528 public static function getDefaultRoute() 529 { 530 return static::$defaultRoute; 531 } 532 533 // Public methods: 534 535 /** 536 * Returns an array representation of the page 537 * 538 * @return array associative array containing all page properties 539 */ 540 public function toArray() 541 { 542 return array_merge( 543 parent::toArray(), 544 array( 545 'action' => $this->getAction(), 546 'controller' => $this->getController(), 547 'params' => $this->getParams(), 548 'route' => $this->getRoute(), 549 'router' => $this->getRouter(), 550 'route_match' => $this->getRouteMatch(), 551 ) 552 ); 553 } 554} 555