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\View\Model; 11 12use ArrayAccess; 13use ArrayIterator; 14use Traversable; 15use Zend\Stdlib\ArrayUtils; 16use Zend\View\Exception; 17use Zend\View\Model; 18use Zend\View\Variables as ViewVariables; 19 20class ViewModel implements ModelInterface, ClearableModelInterface, RetrievableChildrenInterface 21{ 22 /** 23 * What variable a parent model should capture this model to 24 * 25 * @var string 26 */ 27 protected $captureTo = 'content'; 28 29 /** 30 * Child models 31 * @var array 32 */ 33 protected $children = array(); 34 35 /** 36 * Renderer options 37 * @var array 38 */ 39 protected $options = array(); 40 41 /** 42 * Template to use when rendering this model 43 * 44 * @var string 45 */ 46 protected $template = ''; 47 48 /** 49 * Is this a standalone, or terminal, model? 50 * 51 * @var bool 52 */ 53 protected $terminate = false; 54 55 /** 56 * View variables 57 * @var array|ArrayAccess&Traversable 58 */ 59 protected $variables = array(); 60 61 /** 62 * Is this append to child with the same capture? 63 * 64 * @var bool 65 */ 66 protected $append = false; 67 68 /** 69 * Constructor 70 * 71 * @param null|array|Traversable $variables 72 * @param array|Traversable $options 73 */ 74 public function __construct($variables = null, $options = null) 75 { 76 if (null === $variables) { 77 $variables = new ViewVariables(); 78 } 79 80 // Initializing the variables container 81 $this->setVariables($variables, true); 82 83 if (null !== $options) { 84 $this->setOptions($options); 85 } 86 } 87 88 /** 89 * Property overloading: set variable value 90 * 91 * @param string $name 92 * @param mixed $value 93 * @return void 94 */ 95 public function __set($name, $value) 96 { 97 $this->setVariable($name, $value); 98 } 99 100 /** 101 * Property overloading: get variable value 102 * 103 * @param string $name 104 * @return mixed 105 */ 106 public function __get($name) 107 { 108 if (!$this->__isset($name)) { 109 return; 110 } 111 112 $variables = $this->getVariables(); 113 return $variables[$name]; 114 } 115 116 /** 117 * Property overloading: do we have the requested variable value? 118 * 119 * @param string $name 120 * @return bool 121 */ 122 public function __isset($name) 123 { 124 $variables = $this->getVariables(); 125 return isset($variables[$name]); 126 } 127 128 /** 129 * Property overloading: unset the requested variable 130 * 131 * @param string $name 132 * @return void 133 */ 134 public function __unset($name) 135 { 136 if (!$this->__isset($name)) { 137 return; 138 } 139 140 unset($this->variables[$name]); 141 } 142 143 /** 144 * Set a single option 145 * 146 * @param string $name 147 * @param mixed $value 148 * @return ViewModel 149 */ 150 public function setOption($name, $value) 151 { 152 $this->options[(string) $name] = $value; 153 return $this; 154 } 155 156 /** 157 * Get a single option 158 * 159 * @param string $name The option to get. 160 * @param mixed|null $default (optional) A default value if the option is not yet set. 161 * @return mixed 162 */ 163 public function getOption($name, $default = null) 164 { 165 $name = (string) $name; 166 return array_key_exists($name, $this->options) ? $this->options[$name] : $default; 167 } 168 169 /** 170 * Set renderer options/hints en masse 171 * 172 * @param array|Traversable $options 173 * @throws \Zend\View\Exception\InvalidArgumentException 174 * @return ViewModel 175 */ 176 public function setOptions($options) 177 { 178 // Assumption is that lowest common denominator for renderer configuration 179 // is an array 180 if ($options instanceof Traversable) { 181 $options = ArrayUtils::iteratorToArray($options); 182 } 183 184 if (!is_array($options)) { 185 throw new Exception\InvalidArgumentException(sprintf( 186 '%s: expects an array, or Traversable argument; received "%s"', 187 __METHOD__, 188 (is_object($options) ? get_class($options) : gettype($options)) 189 )); 190 } 191 192 $this->options = $options; 193 return $this; 194 } 195 196 /** 197 * Get renderer options/hints 198 * 199 * @return array 200 */ 201 public function getOptions() 202 { 203 return $this->options; 204 } 205 206 /** 207 * Clear any existing renderer options/hints 208 * 209 * @return ViewModel 210 */ 211 public function clearOptions() 212 { 213 $this->options = array(); 214 return $this; 215 } 216 217 /** 218 * Get a single view variable 219 * 220 * @param string $name 221 * @param mixed|null $default (optional) default value if the variable is not present. 222 * @return mixed 223 */ 224 public function getVariable($name, $default = null) 225 { 226 $name = (string) $name; 227 if (array_key_exists($name, $this->variables)) { 228 return $this->variables[$name]; 229 } 230 231 return $default; 232 } 233 234 /** 235 * Set view variable 236 * 237 * @param string $name 238 * @param mixed $value 239 * @return ViewModel 240 */ 241 public function setVariable($name, $value) 242 { 243 $this->variables[(string) $name] = $value; 244 return $this; 245 } 246 247 /** 248 * Set view variables en masse 249 * 250 * Can be an array or a Traversable + ArrayAccess object. 251 * 252 * @param array|ArrayAccess|Traversable $variables 253 * @param bool $overwrite Whether or not to overwrite the internal container with $variables 254 * @throws Exception\InvalidArgumentException 255 * @return ViewModel 256 */ 257 public function setVariables($variables, $overwrite = false) 258 { 259 if (!is_array($variables) && !$variables instanceof Traversable) { 260 throw new Exception\InvalidArgumentException(sprintf( 261 '%s: expects an array, or Traversable argument; received "%s"', 262 __METHOD__, 263 (is_object($variables) ? get_class($variables) : gettype($variables)) 264 )); 265 } 266 267 if ($overwrite) { 268 if (is_object($variables) && !$variables instanceof ArrayAccess) { 269 $variables = ArrayUtils::iteratorToArray($variables); 270 } 271 272 $this->variables = $variables; 273 return $this; 274 } 275 276 foreach ($variables as $key => $value) { 277 $this->setVariable($key, $value); 278 } 279 280 return $this; 281 } 282 283 /** 284 * Get view variables 285 * 286 * @return array|ArrayAccess|Traversable 287 */ 288 public function getVariables() 289 { 290 return $this->variables; 291 } 292 293 /** 294 * Clear all variables 295 * 296 * Resets the internal variable container to an empty container. 297 * 298 * @return ViewModel 299 */ 300 public function clearVariables() 301 { 302 $this->variables = new ViewVariables(); 303 return $this; 304 } 305 306 /** 307 * Set the template to be used by this model 308 * 309 * @param string $template 310 * @return ViewModel 311 */ 312 public function setTemplate($template) 313 { 314 $this->template = (string) $template; 315 return $this; 316 } 317 318 /** 319 * Get the template to be used by this model 320 * 321 * @return string 322 */ 323 public function getTemplate() 324 { 325 return $this->template; 326 } 327 328 /** 329 * Add a child model 330 * 331 * @param ModelInterface $child 332 * @param null|string $captureTo Optional; if specified, the "capture to" value to set on the child 333 * @param null|bool $append Optional; if specified, append to child with the same capture 334 * @return ViewModel 335 */ 336 public function addChild(ModelInterface $child, $captureTo = null, $append = null) 337 { 338 $this->children[] = $child; 339 if (null !== $captureTo) { 340 $child->setCaptureTo($captureTo); 341 } 342 if (null !== $append) { 343 $child->setAppend($append); 344 } 345 346 return $this; 347 } 348 349 /** 350 * Return all children. 351 * 352 * Return specifies an array, but may be any iterable object. 353 * 354 * @return array 355 */ 356 public function getChildren() 357 { 358 return $this->children; 359 } 360 361 /** 362 * Does the model have any children? 363 * 364 * @return bool 365 */ 366 public function hasChildren() 367 { 368 return (0 < count($this->children)); 369 } 370 371 /** 372 * Clears out all child models 373 * 374 * @return ViewModel 375 */ 376 public function clearChildren() 377 { 378 $this->children = array(); 379 return $this; 380 } 381 382 /** 383 * Returns an array of Viewmodels with captureTo value $capture 384 * 385 * @param string $capture 386 * @param bool $recursive search recursive through children, default true 387 * @return array 388 */ 389 public function getChildrenByCaptureTo($capture, $recursive = true) 390 { 391 $children = array(); 392 393 foreach ($this->children as $child) { 394 if ($recursive === true) { 395 $children += $child->getChildrenByCaptureTo($capture); 396 } 397 398 if ($child->captureTo() === $capture) { 399 $children[] = $child; 400 } 401 } 402 403 return $children; 404 } 405 406 /** 407 * Set the name of the variable to capture this model to, if it is a child model 408 * 409 * @param string $capture 410 * @return ViewModel 411 */ 412 public function setCaptureTo($capture) 413 { 414 $this->captureTo = (string) $capture; 415 return $this; 416 } 417 418 /** 419 * Get the name of the variable to which to capture this model 420 * 421 * @return string 422 */ 423 public function captureTo() 424 { 425 return $this->captureTo; 426 } 427 428 /** 429 * Set flag indicating whether or not this is considered a terminal or standalone model 430 * 431 * @param bool $terminate 432 * @return ViewModel 433 */ 434 public function setTerminal($terminate) 435 { 436 $this->terminate = (bool) $terminate; 437 return $this; 438 } 439 440 /** 441 * Is this considered a terminal or standalone model? 442 * 443 * @return bool 444 */ 445 public function terminate() 446 { 447 return $this->terminate; 448 } 449 450 /** 451 * Set flag indicating whether or not append to child with the same capture 452 * 453 * @param bool $append 454 * @return ViewModel 455 */ 456 public function setAppend($append) 457 { 458 $this->append = (bool) $append; 459 return $this; 460 } 461 462 /** 463 * Is this append to child with the same capture? 464 * 465 * @return bool 466 */ 467 public function isAppend() 468 { 469 return $this->append; 470 } 471 472 /** 473 * Return count of children 474 * 475 * @return int 476 */ 477 public function count() 478 { 479 return count($this->children); 480 } 481 482 /** 483 * Get iterator of children 484 * 485 * @return ArrayIterator 486 */ 487 public function getIterator() 488 { 489 return new ArrayIterator($this->children); 490 } 491} 492