sfObjectRoute.class.php 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. <?php
  2. /*
  3. * This file is part of the symfony package.
  4. * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
  5. *
  6. * For the full copyright and license information, please view the LICENSE
  7. * file that was distributed with this source code.
  8. */
  9. /**
  10. * sfObjectRoute represents a route that is bound to PHP object(s).
  11. *
  12. * An object route can represent a single object or a list of objects.
  13. *
  14. * @package symfony
  15. * @subpackage routing
  16. * @author Fabien Potencier <fabien.potencier@symfony-project.com>
  17. * @version SVN: $Id: sfObjectRoute.class.php 15850 2009-02-27 16:48:19Z fabien $
  18. */
  19. class sfObjectRoute extends sfRequestRoute
  20. {
  21. protected
  22. $object = false,
  23. $objects = false;
  24. /**
  25. * Constructor.
  26. *
  27. * @param string $pattern The pattern to match
  28. * @param array $defaults An array of default parameter values
  29. * @param array $requirements An array of requirements for parameters (regexes)
  30. * @param array $options An array of options
  31. *
  32. * @see sfRoute
  33. */
  34. public function __construct($pattern, array $defaults = array(), array $requirements = array(), array $options = array())
  35. {
  36. if (!isset($options['model']))
  37. {
  38. throw new InvalidArgumentException(sprintf('You must pass a "model" option for a %s object (%s).', get_class($this), $pattern));
  39. }
  40. if (!isset($options['type']))
  41. {
  42. throw new InvalidArgumentException(sprintf('You must pass a "type" option for a %s object (%s).', get_class($this), $pattern));
  43. }
  44. if (!in_array($options['type'], array('object', 'list')))
  45. {
  46. throw new InvalidArgumentException(sprintf('The "type" option can only be "object" or "list", "%s" given (%s).', $options['type'], $pattern));
  47. }
  48. parent::__construct($pattern, $defaults, $requirements, $options);
  49. }
  50. /**
  51. * Returns true if the parameters matches this route, false otherwise.
  52. *
  53. * @param mixed $params The parameters
  54. * @param array $context The context
  55. *
  56. * @return Boolean true if the parameters matches this route, false otherwise.
  57. */
  58. public function matchesParameters($params, $context = array())
  59. {
  60. return parent::matchesParameters('object' == $this->options['type'] ? $this->convertObjectToArray($params) : $params);
  61. }
  62. /**
  63. * Generates a URL from the given parameters.
  64. *
  65. * @param mixed $params The parameter values
  66. * @param array $context The context
  67. * @param Boolean $absolute Whether to generate an absolute URL
  68. *
  69. * @return string The generated URL
  70. */
  71. public function generate($params, $context = array(), $absolute = false)
  72. {
  73. return parent::generate('object' == $this->options['type'] ? $this->convertObjectToArray($params) : $params, $context, $absolute);
  74. }
  75. /**
  76. * Gets the object related to the current route and parameters.
  77. *
  78. * This method is only accessible if the route is bound and of type "object".
  79. *
  80. * @return Object The related object
  81. */
  82. public function getObject()
  83. {
  84. if (!$this->isBound())
  85. {
  86. throw new LogicException('The route is not bound.');
  87. }
  88. if ('object' != $this->options['type'])
  89. {
  90. throw new LogicException(sprintf('The route "%s" is not of type "object".', $this->pattern));
  91. }
  92. if (false !== $this->object)
  93. {
  94. return $this->object;
  95. }
  96. // check the related object
  97. if (!($this->object = $this->getObjectForParameters($this->parameters)) && (!isset($this->options['allow_empty']) || !$this->options['allow_empty']))
  98. {
  99. throw new sfError404Exception(sprintf('Unable to find the %s object with the following parameters "%s").', $this->options['model'], str_replace("\n", '', var_export($this->filterParameters($this->parameters), true))));
  100. }
  101. return $this->object;
  102. }
  103. /**
  104. * Gets the list of objects related to the current route and parameters.
  105. *
  106. * This method is only accessible if the route is bound and of type "list".
  107. *
  108. * @return array And array of related objects
  109. */
  110. public function getObjects()
  111. {
  112. if (!$this->isBound())
  113. {
  114. throw new LogicException('The route is not bound.');
  115. }
  116. if ('list' != $this->options['type'])
  117. {
  118. throw new LogicException(sprintf('The route "%s" is not of type "list".', $this->pattern));
  119. }
  120. if (false !== $this->objects)
  121. {
  122. return $this->objects;
  123. }
  124. $this->objects = $this->getObjectsForParameters($this->parameters);
  125. if (!count($this->objects) && isset($this->options['allow_empty']) && !$this->options['allow_empty'])
  126. {
  127. throw new sfError404Exception(sprintf('No %s object found for the following parameters "%s").', $this->options['model'], str_replace("\n", '', var_export($this->filterParameters($this->parameters), true))));
  128. }
  129. return $this->objects;
  130. }
  131. protected function getObjectForParameters($parameters)
  132. {
  133. $className = $this->options['model'];
  134. if (!isset($this->options['method']))
  135. {
  136. throw new InvalidArgumentException(sprintf('You must pass a "method" option for a %s object.', get_class($this)));
  137. }
  138. return call_user_func(array($className, $this->options['method']), $this->filterParameters($parameters));
  139. }
  140. protected function getObjectsForParameters($parameters)
  141. {
  142. $className = $this->options['model'];
  143. if (!isset($this->options['method']))
  144. {
  145. throw new InvalidArgumentException(sprintf('You must pass a "method" option for a %s object.', get_class($this)));
  146. }
  147. return call_user_func(array($className, $this->options['method']), $this->filterParameters($parameters));
  148. }
  149. protected function filterParameters($parameters)
  150. {
  151. if (!is_array($parameters))
  152. {
  153. return $parameters;
  154. }
  155. $params = array();
  156. foreach (array_keys($this->variables) as $variable)
  157. {
  158. $params[$variable] = $parameters[$variable];
  159. }
  160. return $params;
  161. }
  162. protected function convertObjectToArray($object)
  163. {
  164. if (is_array($object))
  165. {
  166. if (!isset($object['sf_subject']))
  167. {
  168. return $object;
  169. }
  170. $parameters = $object;
  171. $object = $parameters['sf_subject'];
  172. unset($parameters['sf_subject']);
  173. }
  174. else
  175. {
  176. $parameters = array();
  177. }
  178. return array_merge($parameters, $this->doConvertObjectToArray($object));
  179. }
  180. protected function doConvertObjectToArray($object)
  181. {
  182. $method = isset($this->options['convert']) ? $this->options['convert'] : 'toParams';
  183. return $object->$method();
  184. }
  185. protected function getRealVariables()
  186. {
  187. $variables = array();
  188. foreach (array_keys($this->variables) as $variable)
  189. {
  190. if (0 === strpos($variable, 'sf_') || in_array($variable, array('module', 'action')))
  191. {
  192. continue;
  193. }
  194. $variables[] = $variable;
  195. }
  196. return $variables;
  197. }
  198. }