PageRenderTime 45ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/app/code/core/Mage/Api2/Model/Resource.php

https://github.com/FiveDigital/magento2
PHP | 1082 lines | 543 code | 92 blank | 447 comment | 64 complexity | 0bb07062084420b0847a54f2d6ab6fac MD5 | raw file
Possible License(s): CC-BY-SA-3.0
  1. <?php
  2. /**
  3. * Magento
  4. *
  5. * NOTICE OF LICENSE
  6. *
  7. * This source file is subject to the Open Software License (OSL 3.0)
  8. * that is bundled with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://opensource.org/licenses/osl-3.0.php
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@magentocommerce.com so we can send you a copy immediately.
  14. *
  15. * DISCLAIMER
  16. *
  17. * Do not edit or add to this file if you wish to upgrade Magento to newer
  18. * versions in the future. If you wish to customize Magento for your
  19. * needs please refer to http://www.magentocommerce.com for more information.
  20. *
  21. * @category Mage
  22. * @package Mage_Api2
  23. * @copyright Copyright (c) 2012 Magento Inc. (http://www.magentocommerce.com)
  24. * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
  25. */
  26. /**
  27. * API2 Abstract Resource
  28. *
  29. * @category Mage
  30. * @package Mage_Api2
  31. * @author Magento Core Team <core@magentocommerce.com>
  32. * @method string _create() _create(array $filteredData) creation of an entity
  33. * @method void _multiCreate() _multiCreate(array $filteredData) processing and creation of a collection
  34. * @method array _retrieve() retrieving an entity
  35. * @method array _retrieveCollection() retrieving a collection
  36. * @method void _update() _update(array $filteredData) update of an entity
  37. * @method void _multiUpdate() _multiUpdate(array $filteredData) update of a collection
  38. * @method void _delete() deletion of an entity
  39. * @method void _multidelete() _multidelete(array $requestData) deletion of a collection
  40. */
  41. abstract class Mage_Api2_Model_Resource
  42. {
  43. /**#@+
  44. * Action types
  45. */
  46. const ACTION_TYPE_ENTITY = 'entity';
  47. const ACTION_TYPE_COLLECTION = 'collection';
  48. /**#@-*/
  49. /**#@+
  50. * Operations. Resource method names
  51. */
  52. const OPERATION_CREATE = 'create';
  53. const OPERATION_RETRIEVE = 'retrieve';
  54. const OPERATION_UPDATE = 'update';
  55. const OPERATION_DELETE = 'delete';
  56. /**#@-*/
  57. /**#@+
  58. * Common operations for attributes
  59. */
  60. const OPERATION_ATTRIBUTE_READ = 'read';
  61. const OPERATION_ATTRIBUTE_WRITE = 'write';
  62. /**#@-*/
  63. /**#@+
  64. * Default error messages
  65. */
  66. const RESOURCE_NOT_FOUND = 'Resource not found.';
  67. const RESOURCE_METHOD_NOT_ALLOWED = 'Resource does not support method.';
  68. const RESOURCE_METHOD_NOT_IMPLEMENTED = 'Resource method not implemented yet.';
  69. const RESOURCE_INTERNAL_ERROR = 'Resource internal error.';
  70. const RESOURCE_DATA_PRE_VALIDATION_ERROR = 'Resource data pre-validation error.';
  71. const RESOURCE_DATA_INVALID = 'Resource data invalid.'; //error while checking data inside method
  72. const RESOURCE_UNKNOWN_ERROR = 'Resource unknown error.';
  73. const RESOURCE_REQUEST_DATA_INVALID = 'The request data is invalid.';
  74. /**#@-*/
  75. /**#@+
  76. * Default collection resources error messages
  77. */
  78. const RESOURCE_COLLECTION_PAGING_ERROR = 'Resource collection paging error.';
  79. const RESOURCE_COLLECTION_PAGING_LIMIT_ERROR = 'The paging limit exceeds the allowed number.';
  80. const RESOURCE_COLLECTION_ORDERING_ERROR = 'Resource collection ordering error.';
  81. const RESOURCE_COLLECTION_FILTERING_ERROR = 'Resource collection filtering error.';
  82. const RESOURCE_COLLECTION_ATTRIBUTES_ERROR = 'Resource collection including additional attributes error.';
  83. /**#@-*/
  84. /**#@+
  85. * Default success messages
  86. */
  87. const RESOURCE_UPDATED_SUCCESSFUL = 'Resource updated successful.';
  88. /**#@-*/
  89. /**#@+
  90. * Collection page sizes
  91. */
  92. const PAGE_SIZE_DEFAULT = 10;
  93. const PAGE_SIZE_MAX = 100;
  94. /**#@-*/
  95. /**
  96. * Request
  97. *
  98. * @var Mage_Api2_Model_Request
  99. */
  100. protected $_request;
  101. /**
  102. * Resource type
  103. *
  104. * @var string
  105. */
  106. protected $_resourceType;
  107. /**
  108. * Api type
  109. *
  110. * @var string
  111. */
  112. protected $_apiType;
  113. /**
  114. * API Version
  115. *
  116. * @var int
  117. */
  118. protected $_version = null;
  119. /**
  120. * Response
  121. *
  122. * @var Zend_Controller_Response_Http
  123. */
  124. protected $_response;
  125. /**
  126. * Attribute Filter
  127. *
  128. * @var Mage_Api2_Model_Acl_Filter
  129. */
  130. protected $_filter;
  131. /**
  132. * Renderer
  133. *
  134. * @var Mage_Api2_Model_Renderer_Interface
  135. */
  136. protected $_renderer;
  137. /**
  138. * Api user
  139. *
  140. * @var Mage_Api2_Model_Auth_User_Abstract
  141. */
  142. protected $_apiUser;
  143. /**
  144. * User type
  145. *
  146. * @var string
  147. */
  148. protected $_userType;
  149. /**
  150. * One of Mage_Api2_Model_Resource::ACTION_TYPE_... constant
  151. *
  152. * @var string
  153. */
  154. protected $_actionType;
  155. /**
  156. * One of Mage_Api2_Model_Resource::OPERATION_... constant
  157. *
  158. * @var string
  159. */
  160. protected $_operation;
  161. /**
  162. * If TRUE - no rendering will be done and dispatch will return data. Otherwise, by default
  163. *
  164. * @var bool
  165. */
  166. protected $_returnData = false;
  167. /**
  168. * @var Mage_Api2_Model_Multicall
  169. */
  170. protected $_multicall;
  171. /**
  172. * Dispatch
  173. * To implement the functionality, you must create a method in the parent one.
  174. *
  175. * Action type is defined in api2.xml in the routes section and depends on entity (single object)
  176. * or collection (several objects).
  177. *
  178. * HTTP_MULTI_STATUS is used for several status codes in the response
  179. */
  180. public function dispatch()
  181. {
  182. switch ($this->getActionType() . $this->getOperation()) {
  183. /* Create */
  184. case self::ACTION_TYPE_ENTITY . self::OPERATION_CREATE:
  185. // Creation of objects is possible only when working with collection
  186. $this->_critical(self::RESOURCE_METHOD_NOT_IMPLEMENTED);
  187. break;
  188. case self::ACTION_TYPE_COLLECTION . self::OPERATION_CREATE:
  189. // If no of the methods(multi or single) is implemented, request body is not checked
  190. if (!$this->_checkMethodExist('_create') && !$this->_checkMethodExist('_multiCreate')) {
  191. $this->_critical(self::RESOURCE_METHOD_NOT_IMPLEMENTED);
  192. }
  193. // If one of the methods(multi or single) is implemented, request body must not be empty
  194. $requestData = $this->getRequest()->getBodyParams();
  195. if (empty($requestData)) {
  196. $this->_critical(self::RESOURCE_REQUEST_DATA_INVALID);
  197. }
  198. // The create action has the dynamic type which depends on data in the request body
  199. if ($this->getRequest()->isAssocArrayInRequestBody()) {
  200. $this->_errorIfMethodNotExist('_create');
  201. $filteredData = $this->getFilter()->in($requestData);
  202. if (empty($filteredData)) {
  203. $this->_critical(self::RESOURCE_REQUEST_DATA_INVALID);
  204. }
  205. $newItemLocation = $this->_create($filteredData);
  206. $this->getResponse()->setHeader('Location', $newItemLocation);
  207. } else {
  208. $this->_errorIfMethodNotExist('_multiCreate');
  209. $filteredData = $this->getFilter()->collectionIn($requestData);
  210. $this->_multiCreate($filteredData);
  211. $this->_render($this->getResponse()->getMessages());
  212. $this->getResponse()->setHttpResponseCode(Mage_Api2_Model_Server::HTTP_MULTI_STATUS);
  213. }
  214. break;
  215. /* Retrieve */
  216. case self::ACTION_TYPE_ENTITY . self::OPERATION_RETRIEVE:
  217. $this->_errorIfMethodNotExist('_retrieve');
  218. $retrievedData = $this->_retrieve();
  219. $filteredData = $this->getFilter()->out($retrievedData);
  220. $this->_render($filteredData);
  221. break;
  222. case self::ACTION_TYPE_COLLECTION . self::OPERATION_RETRIEVE:
  223. $this->_errorIfMethodNotExist('_retrieveCollection');
  224. $retrievedData = $this->_retrieveCollection();
  225. $filteredData = $this->getFilter()->collectionOut($retrievedData);
  226. $this->_render($filteredData);
  227. break;
  228. /* Update */
  229. case self::ACTION_TYPE_ENTITY . self::OPERATION_UPDATE:
  230. $this->_errorIfMethodNotExist('_update');
  231. $requestData = $this->getRequest()->getBodyParams();
  232. if (empty($requestData)) {
  233. $this->_critical(self::RESOURCE_REQUEST_DATA_INVALID);
  234. }
  235. $filteredData = $this->getFilter()->in($requestData);
  236. if (empty($filteredData)) {
  237. $this->_critical(self::RESOURCE_REQUEST_DATA_INVALID);
  238. }
  239. $this->_update($filteredData);
  240. break;
  241. case self::ACTION_TYPE_COLLECTION . self::OPERATION_UPDATE:
  242. $this->_errorIfMethodNotExist('_multiUpdate');
  243. $requestData = $this->getRequest()->getBodyParams();
  244. if (empty($requestData)) {
  245. $this->_critical(self::RESOURCE_REQUEST_DATA_INVALID);
  246. }
  247. $filteredData = $this->getFilter()->collectionIn($requestData);
  248. $this->_multiUpdate($filteredData);
  249. $this->_render($this->getResponse()->getMessages());
  250. $this->getResponse()->setHttpResponseCode(Mage_Api2_Model_Server::HTTP_MULTI_STATUS);
  251. break;
  252. /* Delete */
  253. case self::ACTION_TYPE_ENTITY . self::OPERATION_DELETE:
  254. $this->_errorIfMethodNotExist('_delete');
  255. $this->_delete();
  256. break;
  257. case self::ACTION_TYPE_COLLECTION . self::OPERATION_DELETE:
  258. $this->_errorIfMethodNotExist('_multiDelete');
  259. $requestData = $this->getRequest()->getBodyParams();
  260. if (empty($requestData)) {
  261. $this->_critical(self::RESOURCE_REQUEST_DATA_INVALID);
  262. }
  263. $this->_multiDelete($requestData);
  264. $this->getResponse()->setHttpResponseCode(Mage_Api2_Model_Server::HTTP_MULTI_STATUS);
  265. break;
  266. default:
  267. $this->_critical(self::RESOURCE_METHOD_NOT_IMPLEMENTED);
  268. break;
  269. }
  270. }
  271. /**
  272. * Trigger error for not-implemented operations
  273. *
  274. * @param $methodName
  275. */
  276. protected function _errorIfMethodNotExist($methodName)
  277. {
  278. if (!$this->_checkMethodExist($methodName)) {
  279. $this->_critical(self::RESOURCE_METHOD_NOT_IMPLEMENTED);
  280. }
  281. }
  282. /**
  283. * Check method exist
  284. *
  285. * @param $methodName
  286. * @return bool
  287. */
  288. protected function _checkMethodExist($methodName)
  289. {
  290. return method_exists($this, $methodName);
  291. }
  292. /**
  293. * Get request
  294. *
  295. * @throws Exception
  296. * @return Mage_Api2_Model_Request
  297. */
  298. public function getRequest()
  299. {
  300. if (!$this->_request) {
  301. throw new Exception('Request is not set.');
  302. }
  303. return $this->_request;
  304. }
  305. /**
  306. * Set request
  307. *
  308. * @param Mage_Api2_Model_Request $request
  309. * @return Mage_Api2_Model_Resource
  310. */
  311. public function setRequest(Mage_Api2_Model_Request $request)
  312. {
  313. $this->setResourceType($request->getResourceType());
  314. $this->setApiType($request->getApiType());
  315. $this->_request = $request;
  316. return $this;
  317. }
  318. /**
  319. * Get resource type
  320. * If not exists get from Request
  321. *
  322. * @return string
  323. */
  324. public function getResourceType()
  325. {
  326. if (!$this->_resourceType) {
  327. $this->setResourceType($this->getRequest()->getResourceType());
  328. }
  329. return $this->_resourceType;
  330. }
  331. /**
  332. * Set resource type
  333. *
  334. * @param string $resourceType
  335. * @return Mage_Api2_Model_Resource
  336. */
  337. public function setResourceType($resourceType)
  338. {
  339. $this->_resourceType = $resourceType;
  340. return $this;
  341. }
  342. /**
  343. * Get API type
  344. * If not exists get from Request.
  345. *
  346. * @return string
  347. */
  348. public function getApiType()
  349. {
  350. if (!$this->_apiType) {
  351. $this->setApiType($this->getRequest()->getApiType());
  352. }
  353. return $this->_apiType;
  354. }
  355. /**
  356. * Set API type
  357. *
  358. * @param string $apiType
  359. * @return Mage_Api2_Model_Resource
  360. */
  361. public function setApiType($apiType)
  362. {
  363. $this->_apiType = $apiType;
  364. return $this;
  365. }
  366. /**
  367. * Determine version from class name
  368. *
  369. * @return int
  370. */
  371. public function getVersion()
  372. {
  373. if (null === $this->_version) {
  374. if (preg_match('/^.+([1-9]\d*)$/', get_class($this), $matches) ) {
  375. $this->setVersion($matches[1]);
  376. } else {
  377. throw new Exception('Can not determine version from class name');
  378. }
  379. }
  380. return $this->_version;
  381. }
  382. /**
  383. * Set API version
  384. *
  385. * @param int $version
  386. */
  387. public function setVersion($version)
  388. {
  389. $this->_version = (int)$version;
  390. }
  391. /**
  392. * Get response
  393. *
  394. * @return Mage_Api2_Model_Response
  395. */
  396. public function getResponse()
  397. {
  398. if (!$this->_response) {
  399. throw new Exception('Response is not set.');
  400. }
  401. return $this->_response;
  402. }
  403. /**
  404. * Set response
  405. *
  406. * @param Mage_Api2_Model_Response $response
  407. */
  408. public function setResponse(Mage_Api2_Model_Response $response)
  409. {
  410. $this->_response = $response;
  411. }
  412. /**
  413. * Get filter if not exists create
  414. *
  415. * @return Mage_Api2_Model_Acl_Filter
  416. */
  417. public function getFilter()
  418. {
  419. if (!$this->_filter) {
  420. /** @var $filter Mage_Api2_Model_Acl_Filter */
  421. $filter = Mage::getModel('Mage_Api2_Model_Acl_Filter', $this);
  422. $this->setFilter($filter);
  423. }
  424. return $this->_filter;
  425. }
  426. /**
  427. * Set filter
  428. *
  429. * @param Mage_Api2_Model_Acl_Filter $filter
  430. */
  431. public function setFilter(Mage_Api2_Model_Acl_Filter $filter)
  432. {
  433. $this->_filter = $filter;
  434. }
  435. /**
  436. * Get renderer if not exists create
  437. *
  438. * @return Mage_Api2_Model_Renderer_Interface
  439. */
  440. public function getRenderer()
  441. {
  442. if (!$this->_renderer) {
  443. $renderer = Mage_Api2_Model_Renderer::factory($this->getRequest()->getAcceptTypes());
  444. $this->setRenderer($renderer);
  445. }
  446. return $this->_renderer;
  447. }
  448. /**
  449. * Set renderer
  450. *
  451. * @param Mage_Api2_Model_Renderer_Interface $renderer
  452. */
  453. public function setRenderer(Mage_Api2_Model_Renderer_Interface $renderer)
  454. {
  455. $this->_renderer = $renderer;
  456. }
  457. /**
  458. * Get user type
  459. * If not exists get from apiUser
  460. *
  461. * @return string
  462. */
  463. public function getUserType()
  464. {
  465. if (!$this->_userType) {
  466. $this->setUserType($this->getApiUser()->getType());
  467. }
  468. return $this->_userType;
  469. }
  470. /**
  471. * Set user type
  472. *
  473. * @param string $userType
  474. * @return Mage_Api2_Model_Resource
  475. */
  476. public function setUserType($userType)
  477. {
  478. $this->_userType = $userType;
  479. return $this;
  480. }
  481. /**
  482. * Get API user
  483. *
  484. * @throws Exception
  485. * @return Mage_Api2_Model_Auth_User_Abstract
  486. */
  487. public function getApiUser()
  488. {
  489. if (!$this->_apiUser) {
  490. throw new Exception('API user is not set.');
  491. }
  492. return $this->_apiUser;
  493. }
  494. /**
  495. * Set API user
  496. *
  497. * @param Mage_Api2_Model_Auth_User_Abstract $apiUser
  498. * @return Mage_Api2_Model_Resource
  499. */
  500. public function setApiUser(Mage_Api2_Model_Auth_User_Abstract $apiUser)
  501. {
  502. $this->_apiUser = $apiUser;
  503. return $this;
  504. }
  505. /**
  506. * Get action type
  507. * If not exists get from Request
  508. *
  509. * @return string One of Mage_Api2_Model_Resource::ACTION_TYPE_... constant
  510. */
  511. public function getActionType()
  512. {
  513. if (!$this->_actionType) {
  514. $this->setActionType($this->getRequest()->getActionType());
  515. }
  516. return $this->_actionType;
  517. }
  518. /**
  519. * Set route type
  520. *
  521. * @param string $actionType One of Mage_Api2_Model_Resource::ACTION_TYPE_... constant
  522. * @return Mage_Api2_Model_Resource
  523. */
  524. public function setActionType($actionType)
  525. {
  526. $this->_actionType = $actionType;
  527. return $this;
  528. }
  529. /**
  530. * Get operation
  531. * If not exists get from Request
  532. *
  533. * @return string One of Mage_Api2_Model_Resource::OPERATION_... constant
  534. */
  535. public function getOperation()
  536. {
  537. if (!$this->_operation) {
  538. $this->setOperation($this->getRequest()->getOperation());
  539. }
  540. return $this->_operation;
  541. }
  542. /**
  543. * Set operation
  544. *
  545. * @param string $operation One of Mage_Api2_Model_Resource::OPERATION_... constant
  546. * @return Mage_Api2_Model_Resource
  547. */
  548. public function setOperation($operation)
  549. {
  550. $this->_operation = $operation;
  551. return $this;
  552. }
  553. /**
  554. * Get API2 config
  555. *
  556. * @return Mage_Api2_Model_Config
  557. */
  558. public function getConfig()
  559. {
  560. return Mage::getSingleton('Mage_Api2_Model_Config');
  561. }
  562. /**
  563. * Get working model
  564. *
  565. * @return Mage_Core_Model_Abstract
  566. */
  567. public function getWorkingModel()
  568. {
  569. return Mage::getModel($this->getConfig()->getResourceWorkingModel($this->getResourceType()));
  570. }
  571. /**
  572. * Render data using registered Renderer
  573. *
  574. * @param mixed $data
  575. */
  576. protected function _render($data)
  577. {
  578. $this->getResponse()->setMimeType($this->getRenderer()->getMimeType())
  579. ->setBody($this->getRenderer()->render($data));
  580. }
  581. /**
  582. * Throw exception, critical error - stop execution
  583. *
  584. * @param string $message
  585. * @param int $code
  586. * @throws Mage_Api2_Exception
  587. */
  588. protected function _critical($message, $code = null)
  589. {
  590. if ($code === null) {
  591. $errors = $this->_getCriticalErrors();
  592. if (!isset($errors[$message])) {
  593. throw new Exception(
  594. sprintf('Invalid error "%s" or error code missed.', $message),
  595. Mage_Api2_Model_Server::HTTP_INTERNAL_ERROR
  596. );
  597. }
  598. $code = $errors[$message];
  599. }
  600. throw new Mage_Api2_Exception($message, $code);
  601. }
  602. /**
  603. * Retrieve array with critical errors mapped to HTTP codes
  604. *
  605. * @return array
  606. */
  607. protected function _getCriticalErrors()
  608. {
  609. return array(
  610. '' => Mage_Api2_Model_Server::HTTP_BAD_REQUEST,
  611. self::RESOURCE_NOT_FOUND => Mage_Api2_Model_Server::HTTP_NOT_FOUND,
  612. self::RESOURCE_METHOD_NOT_ALLOWED => Mage_Api2_Model_Server::HTTP_METHOD_NOT_ALLOWED,
  613. self::RESOURCE_METHOD_NOT_IMPLEMENTED => Mage_Api2_Model_Server::HTTP_METHOD_NOT_ALLOWED,
  614. self::RESOURCE_DATA_PRE_VALIDATION_ERROR => Mage_Api2_Model_Server::HTTP_BAD_REQUEST,
  615. self::RESOURCE_INTERNAL_ERROR => Mage_Api2_Model_Server::HTTP_INTERNAL_ERROR,
  616. self::RESOURCE_UNKNOWN_ERROR => Mage_Api2_Model_Server::HTTP_BAD_REQUEST,
  617. self::RESOURCE_REQUEST_DATA_INVALID => Mage_Api2_Model_Server::HTTP_BAD_REQUEST,
  618. self::RESOURCE_COLLECTION_PAGING_ERROR => Mage_Api2_Model_Server::HTTP_BAD_REQUEST,
  619. self::RESOURCE_COLLECTION_PAGING_LIMIT_ERROR => Mage_Api2_Model_Server::HTTP_BAD_REQUEST,
  620. self::RESOURCE_COLLECTION_ORDERING_ERROR => Mage_Api2_Model_Server::HTTP_BAD_REQUEST,
  621. self::RESOURCE_COLLECTION_FILTERING_ERROR => Mage_Api2_Model_Server::HTTP_BAD_REQUEST,
  622. self::RESOURCE_COLLECTION_ATTRIBUTES_ERROR => Mage_Api2_Model_Server::HTTP_BAD_REQUEST,
  623. );
  624. }
  625. /**
  626. * Add non-critical error
  627. *
  628. * @param string $message
  629. * @param int $code
  630. * @return Mage_Api2_Model_Resource
  631. */
  632. protected function _error($message, $code)
  633. {
  634. $this->getResponse()->setException(new Mage_Api2_Exception($message, $code));
  635. return $this;
  636. }
  637. /**
  638. * Add success message
  639. *
  640. * @param string $message
  641. * @param int $code
  642. * @param array $params
  643. * @return Mage_Api2_Model_Resource
  644. */
  645. protected function _successMessage($message, $code, $params = array())
  646. {
  647. $this->getResponse()->addMessage($message, $code, $params, Mage_Api2_Model_Response::MESSAGE_TYPE_SUCCESS);
  648. return $this;
  649. }
  650. /**
  651. * Add error message
  652. *
  653. * @param string $message
  654. * @param int $code
  655. * @param array $params
  656. * @return Mage_Api2_Model_Resource
  657. */
  658. protected function _errorMessage($message, $code, $params = array())
  659. {
  660. $this->getResponse()->addMessage($message, $code, $params, Mage_Api2_Model_Response::MESSAGE_TYPE_ERROR);
  661. return $this;
  662. }
  663. /**
  664. * Set navigation parameters and apply filters from URL params
  665. *
  666. * @param Varien_Data_Collection_Db $collection
  667. * @return Mage_Api2_Model_Resource
  668. */
  669. final protected function _applyCollectionModifiers(Varien_Data_Collection_Db $collection)
  670. {
  671. $pageNumber = $this->getRequest()->getPageNumber();
  672. if ($pageNumber != abs($pageNumber)) {
  673. $this->_critical(self::RESOURCE_COLLECTION_PAGING_ERROR);
  674. }
  675. $pageSize = $this->getRequest()->getPageSize();
  676. if (null == $pageSize) {
  677. $pageSize = self::PAGE_SIZE_DEFAULT;
  678. } else {
  679. if ($pageSize != abs($pageSize) || $pageSize > self::PAGE_SIZE_MAX) {
  680. $this->_critical(self::RESOURCE_COLLECTION_PAGING_LIMIT_ERROR);
  681. }
  682. }
  683. $orderField = $this->getRequest()->getOrderField();
  684. if (null !== $orderField) {
  685. $operation = Mage_Api2_Model_Resource::OPERATION_ATTRIBUTE_READ;
  686. if (!is_string($orderField)
  687. || !array_key_exists($orderField, $this->getAvailableAttributes($this->getUserType(), $operation))
  688. ) {
  689. $this->_critical(self::RESOURCE_COLLECTION_ORDERING_ERROR);
  690. }
  691. $collection->setOrder($orderField, $this->getRequest()->getOrderDirection());
  692. }
  693. $collection->setCurPage($pageNumber)->setPageSize($pageSize);
  694. return $this->_applyFilter($collection);
  695. }
  696. /**
  697. * Validate filter data and apply it to collection if possible
  698. *
  699. * @param Varien_Data_Collection_Db $collection
  700. * @return Mage_Api2_Model_Resource
  701. */
  702. protected function _applyFilter(Varien_Data_Collection_Db $collection)
  703. {
  704. $filter = $this->getRequest()->getFilter();
  705. if (!$filter) {
  706. return $this;
  707. }
  708. if (!is_array($filter)) {
  709. $this->_critical(self::RESOURCE_COLLECTION_FILTERING_ERROR);
  710. }
  711. if (method_exists($collection, 'addAttributeToFilter')) {
  712. $methodName = 'addAttributeToFilter';
  713. } elseif (method_exists($collection, 'addFieldToFilter')) {
  714. $methodName = 'addFieldToFilter';
  715. } else {
  716. return $this;
  717. }
  718. $allowedAttributes = $this->getFilter()->getAllowedAttributes(self::OPERATION_ATTRIBUTE_READ);
  719. foreach ($filter as $filterEntry) {
  720. if (!is_array($filterEntry)
  721. || !array_key_exists('attribute', $filterEntry)
  722. || !in_array($filterEntry['attribute'], $allowedAttributes)
  723. ) {
  724. $this->_critical(self::RESOURCE_COLLECTION_FILTERING_ERROR);
  725. }
  726. $attributeCode = $filterEntry['attribute'];
  727. unset($filterEntry['attribute']);
  728. try {
  729. $collection->$methodName($attributeCode, $filterEntry);
  730. } catch(Exception $e) {
  731. $this->_critical(self::RESOURCE_COLLECTION_FILTERING_ERROR);
  732. }
  733. }
  734. return $this;
  735. }
  736. /**
  737. * Perform multiple calls to subresources of specified resource
  738. *
  739. * @param string $resourceInstanceId
  740. * @return Mage_Api2_Model_Response
  741. */
  742. protected function _multicall($resourceInstanceId)
  743. {
  744. if (!$this->_multicall) {
  745. $this->_multicall = Mage::getModel('Mage_Api2_Model_Multicall');
  746. }
  747. $resourceName = $this->getResourceType();
  748. return $this->_multicall->call($resourceInstanceId, $resourceName, $this->getRequest());
  749. }
  750. /**
  751. * Create model of specified resource and configure it with current object attributes
  752. *
  753. * @param string $resourceId Resource identifier
  754. * @param array $requestParams Parameters to be set to request
  755. * @return Mage_Api2_Model_Resource
  756. */
  757. protected function _getSubModel($resourceId, array $requestParams)
  758. {
  759. $resourceModel = Mage_Api2_Model_Dispatcher::loadResourceModel(
  760. $this->getConfig()->getResourceModel($resourceId),
  761. $this->getApiType(),
  762. $this->getUserType(),
  763. $this->getVersion()
  764. );
  765. /** @var $request Mage_Api2_Model_Request */
  766. $request = Mage::getModel('Mage_Api2_Model_Request');
  767. $request->setParams($requestParams);
  768. $resourceModel
  769. ->setRequest($request) // request MUST be set first
  770. ->setApiUser($this->getApiUser())
  771. ->setApiType($this->getApiType())
  772. ->setResourceType($resourceId)
  773. ->setOperation($this->getOperation())
  774. ->setReturnData(true);
  775. return $resourceModel;
  776. }
  777. /**
  778. * Check ACL permission for specified resource with current other conditions
  779. *
  780. * @param string $resourceId Resource identifier
  781. * @return bool
  782. * @throws Exception
  783. */
  784. protected function _isSubCallAllowed($resourceId)
  785. {
  786. /** @var $globalAcl Mage_Api2_Model_Acl_Global */
  787. $globalAcl = Mage::getSingleton('Mage_Api2_Model_Acl_Global');
  788. try {
  789. return $globalAcl->isAllowed($this->getApiUser(), $resourceId, $this->getOperation());
  790. } catch (Mage_Api2_Exception $e) {
  791. throw new Exception('Invalid arguments for isAllowed() call');
  792. }
  793. }
  794. /**
  795. * Set 'returnData' flag
  796. *
  797. * @param boolean $flag
  798. * @return Mage_Api2_Model_Resource
  799. */
  800. public function setReturnData($flag)
  801. {
  802. $this->_returnData = $flag;
  803. return $this;
  804. }
  805. /**
  806. * Get resource location
  807. *
  808. * @param Mage_Core_Model_Abstract $resource
  809. * @return string URL
  810. */
  811. protected function _getLocation($resource)
  812. {
  813. /* @var $apiTypeRoute Mage_Api2_Model_Route_ApiType */
  814. $apiTypeRoute = Mage::getModel('Mage_Api2_Model_Route_ApiType');
  815. $chain = $apiTypeRoute->chain(
  816. new Zend_Controller_Router_Route($this->getConfig()->getRouteWithEntityTypeAction($this->getResourceType()))
  817. );
  818. $params = array(
  819. 'api_type' => $this->getRequest()->getApiType(),
  820. 'id' => $resource->getId()
  821. );
  822. $uri = $chain->assemble($params);
  823. return '/' . $uri;
  824. }
  825. /**
  826. * Resource specific method to retrieve attributes' codes. May be overriden in child.
  827. *
  828. * @return array
  829. */
  830. protected function _getResourceAttributes()
  831. {
  832. return array();
  833. }
  834. /**
  835. * Get available attributes of API resource
  836. *
  837. * @param string $userType
  838. * @param string $operation
  839. * @return array
  840. */
  841. public function getAvailableAttributes($userType, $operation)
  842. {
  843. $available = $this->getAvailableAttributesFromConfig();
  844. $excludedAttrs = $this->getExcludedAttributes($userType, $operation);
  845. $includedAttrs = $this->getIncludedAttributes($userType, $operation);
  846. $entityOnlyAttrs = $this->getEntityOnlyAttributes($userType, $operation);
  847. $resourceAttrs = $this->_getResourceAttributes();
  848. // if resource returns not-associative array - attributes' codes only
  849. if (0 === key($resourceAttrs)) {
  850. $resourceAttrs = array_combine($resourceAttrs, $resourceAttrs);
  851. }
  852. foreach ($resourceAttrs as $attrCode => $attrLabel) {
  853. if (!isset($available[$attrCode])) {
  854. $available[$attrCode] = empty($attrLabel) ? $attrCode : $attrLabel;
  855. }
  856. }
  857. foreach (array_keys($available) as $code) {
  858. if (in_array($code, $excludedAttrs) || ($includedAttrs && !in_array($code, $includedAttrs))) {
  859. unset($available[$code]);
  860. }
  861. if (in_array($code, $entityOnlyAttrs)) {
  862. $available[$code] .= ' *';
  863. }
  864. }
  865. return $available;
  866. }
  867. /**
  868. * Get excluded attributes for user type
  869. *
  870. * @param string $userType
  871. * @param string $operation
  872. * @return array
  873. */
  874. public function getExcludedAttributes($userType, $operation)
  875. {
  876. return $this->getConfig()->getResourceExcludedAttributes($this->getResourceType(), $userType, $operation);
  877. }
  878. /**
  879. * Get forced attributes
  880. *
  881. * @return array
  882. */
  883. public function getForcedAttributes()
  884. {
  885. return $this->getConfig()->getResourceForcedAttributes($this->getResourceType(), $this->getUserType());
  886. }
  887. /**
  888. * Retrieve list of included attributes
  889. *
  890. * @param string $userType API user type
  891. * @param string $operationType Type of operation: one of Mage_Api2_Model_Resource::OPERATION_ATTRIBUTE_... constant
  892. * @return array
  893. */
  894. public function getIncludedAttributes($userType, $operationType)
  895. {
  896. return $this->getConfig()->getResourceIncludedAttributes($this->getResourceType(), $userType, $operationType);
  897. }
  898. /**
  899. * Retrieve list of entity only attributes
  900. *
  901. * @param string $userType API user type
  902. * @param string $operationType Type of operation: one of Mage_Api2_Model_Resource::OPERATION_ATTRIBUTE_... constant
  903. * @return array
  904. */
  905. public function getEntityOnlyAttributes($userType, $operationType)
  906. {
  907. return $this->getConfig()->getResourceEntityOnlyAttributes($this->getResourceType(), $userType, $operationType);
  908. }
  909. /**
  910. * Get available attributes of API resource from configuration file
  911. *
  912. * @return array
  913. */
  914. public function getAvailableAttributesFromConfig()
  915. {
  916. return $this->getConfig()->getResourceAttributes($this->getResourceType());
  917. }
  918. /**
  919. * Get available attributes of API resource from data base
  920. *
  921. * @return array
  922. */
  923. public function getDbAttributes()
  924. {
  925. $available = array();
  926. $workModel = $this->getConfig()->getResourceWorkingModel($this->getResourceType());
  927. if ($workModel) {
  928. /* @var $resource Mage_Core_Model_Resource_Db_Abstract */
  929. $resource = Mage::getResourceModel($workModel);
  930. if (method_exists($resource, 'getMainTable')) {
  931. $available = array_keys($resource->getReadConnection()->describeTable($resource->getMainTable()));
  932. }
  933. }
  934. return $available;
  935. }
  936. /**
  937. * Get EAV attributes of working model
  938. *
  939. * @param bool $onlyVisible OPTIONAL Show only the attributes which are visible on frontend
  940. * @param bool $excludeSystem OPTIONAL Exclude attributes marked as system
  941. * @return array
  942. */
  943. public function getEavAttributes($onlyVisible = false, $excludeSystem = false)
  944. {
  945. $attributes = array();
  946. $model = $this->getConfig()->getResourceWorkingModel($this->getResourceType());
  947. /** @var $entityType Mage_Eav_Model_Entity_Type */
  948. $entityType = Mage::getModel('Mage_Eav_Model_Entity_Type')->load($model, 'entity_model');
  949. /** @var $attribute Mage_Eav_Model_Entity_Attribute */
  950. foreach ($entityType->getAttributeCollection() as $attribute) {
  951. if ($onlyVisible && !$attribute->getIsVisible()) {
  952. continue;
  953. }
  954. if ($excludeSystem && $attribute->getIsSystem()) {
  955. continue;
  956. }
  957. $attributes[$attribute->getAttributeCode()] = $attribute->getFrontendLabel();
  958. }
  959. return $attributes;
  960. }
  961. /**
  962. * Retrieve current store according to request and API user type
  963. *
  964. * @return Mage_Core_Model_Store
  965. */
  966. protected function _getStore()
  967. {
  968. $store = $this->getRequest()->getParam('store');
  969. try {
  970. if ($this->getUserType() != Mage_Api2_Model_Auth_User_Admin::USER_TYPE) {
  971. // customer or guest role
  972. if (!$store) {
  973. $store = Mage::app()->getDefaultStoreView();
  974. } else {
  975. $store = Mage::app()->getStore($store);
  976. }
  977. } else {
  978. // admin role
  979. if (is_null($store)) {
  980. $store = Mage_Catalog_Model_Abstract::DEFAULT_STORE_ID;
  981. }
  982. $store = Mage::app()->getStore($store);
  983. }
  984. } catch (Mage_Core_Model_Store_Exception $e) {
  985. // store does not exist
  986. $this->_critical('Requested store is invalid', Mage_Api2_Model_Server::HTTP_BAD_REQUEST);
  987. }
  988. return $store;
  989. }
  990. }