controller.php 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. <?php namespace Laravel\Routing;
  2. use Laravel\IoC;
  3. use Laravel\Str;
  4. use Laravel\View;
  5. use Laravel\Bundle;
  6. use Laravel\Request;
  7. use Laravel\Redirect;
  8. use Laravel\Response;
  9. abstract class Controller {
  10. /**
  11. * The layout being used by the controller.
  12. *
  13. * @var string
  14. */
  15. public $layout;
  16. /**
  17. * Indicates if the controller uses RESTful routing.
  18. *
  19. * @var bool
  20. */
  21. public $restful = false;
  22. /**
  23. * The filters assigned to the controller.
  24. *
  25. * @var array
  26. */
  27. protected $filters = array();
  28. /**
  29. * Call an action method on a controller.
  30. *
  31. * <code>
  32. * // Call the "show" method on the "user" controller
  33. * $response = Controller::call('user@show');
  34. *
  35. * // Call the "profile" method on the "user/admin" controller and pass parameters
  36. * $response = Controller::call('user.admin@profile', array($username));
  37. * </code>
  38. *
  39. * @param string $destination
  40. * @param array $parameters
  41. * @return Response
  42. */
  43. public static function call($destination, $parameters = array())
  44. {
  45. list($bundle, $destination) = Bundle::parse($destination);
  46. // We will always start the bundle, just in case the developer is pointing
  47. // a route to another bundle. This allows us to lazy load the bundle and
  48. // improve performance since the bundle is not loaded on every request.
  49. Bundle::start($bundle);
  50. list($controller, $method) = explode('@', $destination);
  51. $controller = static::resolve($bundle, $controller);
  52. // If the controller could not be resolved, we're out of options and will
  53. // return the 404 error response. Of course, if we found the controller,
  54. // we can go ahead and execute the requested method on the instance.
  55. if (is_null($controller)) return Response::error('404');
  56. return $controller->execute($method, $parameters);
  57. }
  58. /**
  59. * Resolve a bundle and controller name to a controller instance.
  60. *
  61. * @param string $bundle
  62. * @param string $controller
  63. * @return Controller
  64. */
  65. public static function resolve($bundle, $controller)
  66. {
  67. if ( ! static::load($bundle, $controller)) return;
  68. // If the controller is registered in the IoC container, we will resolve
  69. // it out of the container. Using constructor injection on controllers
  70. // via the container allows more flexible and testable applications.
  71. $resolver = 'controller: '.Bundle::identifier($bundle, $controller);
  72. if (IoC::registered($resolver))
  73. {
  74. return IoC::resolve($resolver);
  75. }
  76. $controller = static::format($bundle, $controller);
  77. $controller = new $controller;
  78. // If the controller has specified a layout to be used when rendering
  79. // views, we will instantiate the layout instance and set it to the
  80. // layout property, replacing the string layout name.
  81. if ( ! is_null($controller->layout))
  82. {
  83. $controller->layout = $controller->layout();
  84. }
  85. return $controller;
  86. }
  87. /**
  88. * Load the file for a given controller.
  89. *
  90. * @param string $bundle
  91. * @param string $controller
  92. * @return bool
  93. */
  94. protected static function load($bundle, $controller)
  95. {
  96. $controller = strtolower(str_replace('.', '/', $controller));
  97. if (file_exists($path = Bundle::path($bundle).'controllers/'.$controller.EXT))
  98. {
  99. require_once $path;
  100. return true;
  101. }
  102. return false;
  103. }
  104. /**
  105. * Format a bundle and controller identifier into the controller's class name.
  106. *
  107. * @param string $bundle
  108. * @param string $controller
  109. * @return string
  110. */
  111. protected static function format($bundle, $controller)
  112. {
  113. // If the controller's bundle is not the application bundle, we will
  114. // prepend the bundle to the identifier so the bundle is prefixed to
  115. // the class name when it is formatted. Bundle controllers are
  116. // always prefixed with the bundle name.
  117. if ($bundle !== DEFAULT_BUNDLE) $controller = $bundle.'.'.$controller;
  118. return Str::classify($controller).'_Controller';
  119. }
  120. /**
  121. * Execute a controller method with the given parameters.
  122. *
  123. * @param string $method
  124. * @param array $parameters
  125. * @return Response
  126. */
  127. public function execute($method, $parameters = array())
  128. {
  129. // Again, as was the case with route closures, if the controller "before"
  130. // filters return a response, it will be considered the response to the
  131. // request and the controller method will not be used to handle the
  132. // request to the application.
  133. $response = Filter::run($this->filters('before', $method), array(), true);
  134. if (is_null($response))
  135. {
  136. $this->before();
  137. $response = $this->response($method, $parameters);
  138. }
  139. $response = Response::prepare($response);
  140. // The "after" function on the controller is simply a convenient hook
  141. // so the developer can work on the response before it's returned to
  142. // the browser. This is useful for setting partials on the layout.
  143. $this->after($response);
  144. Filter::run($this->filters('after', $method), array($response));
  145. return $response;
  146. }
  147. /**
  148. * Execute a controller action and return the response.
  149. *
  150. * Unlike the "execute" method, no filters will be run and the response
  151. * from the controller action will not be changed in any way before it
  152. * is returned to the consumer.
  153. *
  154. * @param string $method
  155. * @param array $parameters
  156. * @return mixed
  157. */
  158. public function response($method, $parameters = array())
  159. {
  160. // The developer may mark the controller as being "RESTful" which
  161. // indicates that the controller actions are prefixed with the
  162. // HTTP verb they respond to rather than the word "action".
  163. if ($this->restful)
  164. {
  165. $action = strtolower(Request::method()).'_'.$method;
  166. }
  167. else
  168. {
  169. $action = "action_{$method}";
  170. }
  171. $response = call_user_func_array(array($this, $action), $parameters);
  172. // If the controller has specified a layout view. The response
  173. // returned by the controller method will be bound to that
  174. // view and the layout will be considered the response.
  175. if (is_null($response) and ! is_null($this->layout))
  176. {
  177. $response = $this->layout;
  178. }
  179. return $response;
  180. }
  181. /**
  182. * Register filters on the controller's methods.
  183. *
  184. * <code>
  185. * // Set a "foo" after filter on the controller
  186. * $this->filter('before', 'foo');
  187. *
  188. * // Set several filters on an explicit group of methods
  189. * $this->filter('after', 'foo|bar')->only(array('user', 'profile'));
  190. * </code>
  191. *
  192. * @param string $event
  193. * @param string|array $filters
  194. * @param mixed $parameters
  195. * @return Filter_Collection
  196. */
  197. protected function filter($event, $filters, $parameters = null)
  198. {
  199. $this->filters[$event][] = new Filter_Collection($filters, $parameters);
  200. return $this->filters[$event][count($this->filters[$event]) - 1];
  201. }
  202. /**
  203. * Get an array of filter names defined for the destination.
  204. *
  205. * @param string $event
  206. * @param string $method
  207. * @return array
  208. */
  209. protected function filters($event, $method)
  210. {
  211. if ( ! isset($this->filters[$event])) return array();
  212. $filters = array();
  213. foreach ($this->filters[$event] as $collection)
  214. {
  215. if ($collection->applies($method))
  216. {
  217. $filters[] = $collection;
  218. }
  219. }
  220. return $filters;
  221. }
  222. /**
  223. * Create the layout that is assigned to the controller.
  224. *
  225. * @return View
  226. */
  227. public function layout()
  228. {
  229. return View::make($this->layout);
  230. }
  231. /**
  232. * This function is called before the action is executed.
  233. *
  234. * @return void
  235. */
  236. public function before() {}
  237. /**
  238. * This function is called after the action is executed.
  239. *
  240. * @param Response $response
  241. * @return void
  242. */
  243. public function after($response) {}
  244. /**
  245. * Magic Method to handle calls to undefined controller functions.
  246. */
  247. public function __call($method, $parameters)
  248. {
  249. return Response::error('404');
  250. }
  251. /**
  252. * Dynamically resolve items from the application IoC container.
  253. *
  254. * <code>
  255. * // Retrieve an object registered in the container as "mailer"
  256. * $mailer = $this->mailer;
  257. *
  258. * // Equivalent call using the IoC container instance
  259. * $mailer = IoC::resolve('mailer');
  260. * </code>
  261. */
  262. public function __get($key)
  263. {
  264. if (IoC::registered($key)) return IoC::resolve($key);
  265. throw new \Exception("Accessing undefined property [$key] on controller.");
  266. }
  267. }