PageRenderTime 48ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/library/Zend/Mvc/Controller/RestfulController.php

http://github.com/zendframework/zf2
PHP | 385 lines | 211 code | 30 blank | 144 comment | 24 complexity | fdc52590b5e4f2675b8bf1d91687e199 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. namespace Zend\Mvc\Controller;
  3. use Zend\Di\Locator,
  4. Zend\EventManager\EventCollection,
  5. Zend\EventManager\EventDescription as Event,
  6. Zend\EventManager\EventManager,
  7. Zend\Http\Request as HttpRequest,
  8. Zend\Http\PhpEnvironment\Response as HttpResponse,
  9. Zend\Loader\Broker,
  10. Zend\Loader\Pluggable,
  11. Zend\Stdlib\Dispatchable,
  12. Zend\Stdlib\RequestDescription as Request,
  13. Zend\Stdlib\ResponseDescription as Response,
  14. Zend\Mvc\InjectApplicationEvent,
  15. Zend\Mvc\LocatorAware,
  16. Zend\Mvc\MvcEvent;
  17. /**
  18. * Abstract RESTful controller
  19. */
  20. abstract class RestfulController implements Dispatchable, InjectApplicationEvent, LocatorAware, Pluggable
  21. {
  22. protected $broker;
  23. protected $request;
  24. protected $response;
  25. protected $event;
  26. protected $events;
  27. /**
  28. * Return list of resources
  29. *
  30. * @return mixed
  31. */
  32. abstract public function getList();
  33. /**
  34. * Return single resource
  35. *
  36. * @param mixed $id
  37. * @return mixed
  38. */
  39. abstract public function get($id);
  40. /**
  41. * Create a new resource
  42. *
  43. * @param mixed $data
  44. * @return mixed
  45. */
  46. abstract public function create($data);
  47. /**
  48. * Update an existing resource
  49. *
  50. * @param mixed $id
  51. * @param mixed $data
  52. * @return mixed
  53. */
  54. abstract public function update($id, $data);
  55. /**
  56. * Delete an existing resource
  57. *
  58. * @param mixed $id
  59. * @return mixed
  60. */
  61. abstract public function delete($id);
  62. /**
  63. * Basic functionality for when a page is not available
  64. *
  65. * @return array
  66. */
  67. public function notFoundAction()
  68. {
  69. $this->response->setStatusCode(404);
  70. return array('content' => 'Page not found');
  71. }
  72. /**
  73. * Dispatch a request
  74. *
  75. * If the route match includes an "action" key, then this acts basically like
  76. * a standard action controller. Otherwise, it introspects the HTTP method
  77. * to determine how to handle the request, and which method to delegate to.
  78. *
  79. * @events dispatch.pre, dispatch.post
  80. * @param Request $request
  81. * @param null|Response $response
  82. * @return mixed|Response
  83. */
  84. public function dispatch(Request $request, Response $response = null)
  85. {
  86. if (!$request instanceof HttpRequest) {
  87. throw new \InvalidArgumentException('Expected an HTTP request');
  88. }
  89. $this->request = $request;
  90. if (!$response) {
  91. $response = new HttpResponse();
  92. }
  93. $this->response = $response;
  94. $e = $this->getEvent();
  95. $e->setRequest($request)
  96. ->setResponse($response)
  97. ->setTarget($this);
  98. $result = $this->events()->trigger('dispatch', $e, function($test) {
  99. return ($test instanceof Response);
  100. });
  101. if ($result->stopped()) {
  102. return $result->last();
  103. }
  104. return $e->getResult();
  105. }
  106. public function execute(MvcEvent $e)
  107. {
  108. $routeMatch = $e->getRouteMatch();
  109. if (!$routeMatch) {
  110. /**
  111. * @todo Determine requirements for when route match is missing.
  112. * Potentially allow pulling directly from request metadata?
  113. */
  114. throw new \DomainException('Missing route matches; unsure how to retrieve action');
  115. }
  116. $request = $e->getRequest();
  117. $action = $routeMatch->getParam('action', false);
  118. if ($action) {
  119. // Handle arbitrary methods, ending in Action
  120. $method = static::getMethodFromAction($action);
  121. if (!method_exists($this, $method)) {
  122. $method = 'notFoundAction';
  123. }
  124. $return = $this->$method();
  125. } else {
  126. // RESTful methods
  127. switch (strtolower($request->getMethod())) {
  128. case 'get':
  129. if (null !== $id = $routeMatch->getParam('id')) {
  130. $return = $this->get($id);
  131. break;
  132. }
  133. if (null !== $id = $request->query()->get('id')) {
  134. $return = $this->get($id);
  135. break;
  136. }
  137. $return = $this->getList();
  138. break;
  139. case 'post':
  140. $return = $this->create($request->post()->toArray());
  141. break;
  142. case 'put':
  143. if (null === $id = $routeMatch->getParam('id')) {
  144. if (!($id = $request->query()->get('id', false))) {
  145. throw new \DomainException('Missing identifier');
  146. }
  147. }
  148. $content = $request->getContent();
  149. parse_str($content, $parsedParams);
  150. $return = $this->update($id, $parsedParams);
  151. break;
  152. case 'delete':
  153. if (null === $id = $routeMatch->getParam('id')) {
  154. if (!($id = $request->query()->get('id', false))) {
  155. throw new \DomainException('Missing identifier');
  156. }
  157. }
  158. $return = $this->delete($id);
  159. break;
  160. default:
  161. throw new \DomainException('Invalid HTTP method!');
  162. }
  163. }
  164. // Emit post-dispatch signal, passing:
  165. // - return from method, request, response
  166. // If a listener returns a response object, return it immediately
  167. $e->setResult($return);
  168. return $return;
  169. }
  170. /**
  171. * Get request object
  172. *
  173. * @return Request
  174. */
  175. public function getRequest()
  176. {
  177. if (!$this->request) {
  178. $this->setRequest(new HttpRequest());
  179. }
  180. return $this->request;
  181. }
  182. /**
  183. * Get response object
  184. *
  185. * @return Response
  186. */
  187. public function getResponse()
  188. {
  189. if (!$this->response) {
  190. $this->setResponse(new HttpResponse());
  191. }
  192. return $this->response;
  193. }
  194. /**
  195. * Set the event manager instance used by this context
  196. *
  197. * @param EventCollection $events
  198. * @return AppContext
  199. */
  200. public function setEventManager(EventCollection $events)
  201. {
  202. $this->events = $events;
  203. return $this;
  204. }
  205. /**
  206. * Retrieve the event manager
  207. *
  208. * Lazy-loads an EventManager instance if none registered.
  209. *
  210. * @return EventCollection
  211. */
  212. public function events()
  213. {
  214. if (!$this->events) {
  215. $this->setEventManager(new EventManager(array(
  216. 'Zend\Stdlib\Dispatchable',
  217. __CLASS__,
  218. get_called_class(),
  219. )));
  220. $this->attachDefaultListeners();
  221. }
  222. return $this->events;
  223. }
  224. /**
  225. * Set an event to use during dispatch
  226. *
  227. * By default, will re-cast to MvcEvent if another event type is provided.
  228. *
  229. * @param Event $e
  230. * @return void
  231. */
  232. public function setEvent(Event $e)
  233. {
  234. if ($e instanceof Event && !$e instanceof MvcEvent) {
  235. $eventParams = $e->getParams();
  236. $e = new MvcEvent();
  237. $e->setParams($eventParams);
  238. unset($eventParams);
  239. }
  240. $this->event = $e;
  241. }
  242. /**
  243. * Get the attached event
  244. *
  245. * Will create a new MvcEvent if none provided.
  246. *
  247. * @return Event
  248. */
  249. public function getEvent()
  250. {
  251. if (!$this->event) {
  252. $this->setEvent(new MvcEvent());
  253. }
  254. return $this->event;
  255. }
  256. /**
  257. * Set locator instance
  258. *
  259. * @param Locator $locator
  260. * @return void
  261. */
  262. public function setLocator(Locator $locator)
  263. {
  264. $this->locator = $locator;
  265. }
  266. /**
  267. * Retrieve locator instance
  268. *
  269. * @return Locator
  270. */
  271. public function getLocator()
  272. {
  273. return $this->locator;
  274. }
  275. /**
  276. * Get plugin broker instance
  277. *
  278. * @return Zend\Loader\Broker
  279. */
  280. public function getBroker()
  281. {
  282. if (!$this->broker) {
  283. $this->setBroker(new PluginBroker());
  284. }
  285. return $this->broker;
  286. }
  287. /**
  288. * Set plugin broker instance
  289. *
  290. * @param string|Broker $broker Plugin broker to load plugins
  291. * @return Zend\Loader\Pluggable
  292. */
  293. public function setBroker($broker)
  294. {
  295. if (!$broker instanceof Broker) {
  296. throw new Exception\InvalidArgumentException('Broker must implement Zend\Loader\Broker');
  297. }
  298. $this->broker = $broker;
  299. if (method_exists($broker, 'setController')) {
  300. $this->broker->setController($this);
  301. }
  302. return $this;
  303. }
  304. /**
  305. * Get plugin instance
  306. *
  307. * @param string $plugin Name of plugin to return
  308. * @param null|array $options Options to pass to plugin constructor (if not already instantiated)
  309. * @return mixed
  310. */
  311. public function plugin($name, array $options = null)
  312. {
  313. return $this->getBroker()->load($name, $options);
  314. }
  315. /**
  316. * Method overloading: return plugins
  317. *
  318. * @param mixed $method
  319. * @param mixed $params
  320. * @return void
  321. */
  322. public function __call($method, $params)
  323. {
  324. $options = null;
  325. if (0 < count($params)) {
  326. $options = array_shift($params);
  327. }
  328. return $this->plugin($method, $options);
  329. }
  330. /**
  331. * Register the default events for this controller
  332. *
  333. * @return void
  334. */
  335. protected function attachDefaultListeners()
  336. {
  337. $events = $this->events();
  338. $events->attach('dispatch', array($this, 'execute'));
  339. }
  340. /**
  341. * Transform an "action" token into a method name
  342. *
  343. * @param string $action
  344. * @return string
  345. */
  346. public static function getMethodFromAction($action)
  347. {
  348. $method = str_replace(array('.', '-', '_'), ' ', $action);
  349. $method = ucwords($method);
  350. $method = str_replace(' ', '', $method);
  351. $method = lcfirst($method);
  352. $method .= 'Action';
  353. return $method;
  354. }
  355. }