AbstractMapper.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Framework\DB;
  7. use Magento\Framework\Api\CriteriaInterface;
  8. use Magento\Framework\Data\Collection\Db\FetchStrategyInterface;
  9. use Magento\Framework\Data\ObjectFactory;
  10. use Magento\Framework\DB\Adapter\AdapterInterface;
  11. use Psr\Log\LoggerInterface as Logger;
  12. /**
  13. * Class AbstractMapper
  14. * @package Magento\Framework\DB
  15. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  16. */
  17. abstract class AbstractMapper implements MapperInterface
  18. {
  19. /**
  20. * Resource model name
  21. *
  22. * @var string
  23. */
  24. protected $resourceModel;
  25. /**
  26. * Resource instance
  27. *
  28. * @var \Magento\Framework\Model\ResourceModel\Db\AbstractDb
  29. */
  30. protected $resource;
  31. /**
  32. * Store joined tables here
  33. *
  34. * @var array
  35. */
  36. protected $joinedTables = [];
  37. /**
  38. * DB connection
  39. *
  40. * @var AdapterInterface
  41. */
  42. protected $connection;
  43. /**
  44. * Select object
  45. *
  46. * @var Select
  47. */
  48. protected $select;
  49. /**
  50. * @var Logger
  51. */
  52. protected $logger;
  53. /**
  54. * @var FetchStrategyInterface
  55. */
  56. protected $fetchStrategy;
  57. /**
  58. * @var ObjectFactory
  59. */
  60. protected $objectFactory;
  61. /**
  62. * @var MapperFactory
  63. */
  64. protected $mapperFactory;
  65. /**
  66. * Fields and filters map
  67. *
  68. * @var array
  69. */
  70. protected $map = [];
  71. /**
  72. * @param Logger $logger
  73. * @param FetchStrategyInterface $fetchStrategy
  74. * @param ObjectFactory $objectFactory
  75. * @param MapperFactory $mapperFactory
  76. * @param Select $select
  77. */
  78. public function __construct(
  79. Logger $logger,
  80. FetchStrategyInterface $fetchStrategy,
  81. ObjectFactory $objectFactory,
  82. MapperFactory $mapperFactory,
  83. Select $select = null
  84. ) {
  85. $this->logger = $logger;
  86. $this->fetchStrategy = $fetchStrategy;
  87. $this->objectFactory = $objectFactory;
  88. $this->mapperFactory = $mapperFactory;
  89. $this->select = $select;
  90. $this->init();
  91. }
  92. /**
  93. * Set initial conditions
  94. *
  95. * @return void
  96. */
  97. abstract protected function init();
  98. /**
  99. * Map criteria to Select Query Object
  100. *
  101. * @param CriteriaInterface $criteria
  102. * @return Select
  103. */
  104. public function map(CriteriaInterface $criteria)
  105. {
  106. $criteriaParts = $criteria->toArray();
  107. foreach ($criteriaParts as $key => $value) {
  108. $camelCaseKey = \Magento\Framework\Api\SimpleDataObjectConverter::snakeCaseToUpperCamelCase($key);
  109. $mapperMethod = 'map' . $camelCaseKey;
  110. if (method_exists($this, $mapperMethod)) {
  111. if (!is_array($value)) {
  112. throw new \InvalidArgumentException('Wrong type of argument, expecting array for '. $mapperMethod);
  113. }
  114. call_user_func_array([$this, $mapperMethod], $value);
  115. }
  116. }
  117. return $this->select;
  118. }
  119. /**
  120. * Add attribute expression (SUM, COUNT, etc)
  121. * Example: ('sub_total', 'SUM({{attribute}})', 'revenue')
  122. * Example: ('sub_total', 'SUM({{revenue}})', 'revenue')
  123. * For some functions like SUM use groupByAttribute.
  124. *
  125. * @param string $alias
  126. * @param string $expression
  127. * @param array|string $fields
  128. * @return void
  129. */
  130. public function addExpressionFieldToSelect($alias, $expression, $fields)
  131. {
  132. // validate alias
  133. if (!is_array($fields)) {
  134. $fields = [$fields => $fields];
  135. }
  136. $fullExpression = $expression;
  137. foreach ($fields as $fieldKey => $fieldItem) {
  138. $fullExpression = str_replace('{{' . $fieldKey . '}}', $fieldItem, $fullExpression);
  139. }
  140. $this->getSelect()->columns([$alias => $fullExpression]);
  141. }
  142. /**
  143. * @inheritdoc
  144. */
  145. public function addFieldToFilter($field, $condition = null)
  146. {
  147. if (is_array($field)) {
  148. $conditions = [];
  149. foreach ($field as $key => $value) {
  150. $conditions[] = $this->translateCondition($value, isset($condition[$key]) ? $condition[$key] : null);
  151. }
  152. $resultCondition = '(' . implode(') ' . \Magento\Framework\DB\Select::SQL_OR . ' (', $conditions) . ')';
  153. } else {
  154. $resultCondition = $this->translateCondition($field, $condition);
  155. }
  156. $this->select->where($resultCondition, null, Select::TYPE_CONDITION);
  157. }
  158. /**
  159. * @inheritdoc
  160. */
  161. public function reset()
  162. {
  163. $this->getSelect()->reset();
  164. }
  165. /**
  166. * Set resource model name
  167. *
  168. * @param string $model
  169. * @return void
  170. */
  171. protected function setResourceModelName($model)
  172. {
  173. $this->resourceModel = $model;
  174. }
  175. /**
  176. * Retrieve resource model name
  177. *
  178. * @return string
  179. */
  180. protected function getResourceModelName()
  181. {
  182. return $this->resourceModel;
  183. }
  184. /**
  185. * Get resource instance
  186. *
  187. * @return \Magento\Framework\Model\ResourceModel\Db\AbstractDb
  188. */
  189. public function getResource()
  190. {
  191. if (empty($this->resource)) {
  192. $this->resource = \Magento\Framework\App\ObjectManager::getInstance()->create(
  193. $this->getResourceModelName()
  194. );
  195. }
  196. return $this->resource;
  197. }
  198. /**
  199. * Standard query builder initialization
  200. *
  201. * @param string $resourceInterface
  202. * @return void
  203. */
  204. protected function initResource($resourceInterface)
  205. {
  206. $this->setResourceModelName($resourceInterface);
  207. $this->setConnection($this->getResource()->getConnection());
  208. if (!$this->select) {
  209. $this->select = $this->getConnection()->select();
  210. $this->initSelect();
  211. }
  212. }
  213. /**
  214. * Init collection select
  215. *
  216. * @return void
  217. */
  218. protected function initSelect()
  219. {
  220. $this->getSelect()->from(['main_table' => $this->getResource()->getMainTable()]);
  221. }
  222. /**
  223. * Join table to collection select
  224. *
  225. * @param string $table
  226. * @param string $condition
  227. * @param string $cols
  228. * @return void
  229. */
  230. protected function join($table, $condition, $cols = '*')
  231. {
  232. if (is_array($table)) {
  233. foreach ($table as $k => $v) {
  234. $alias = $k;
  235. $table = $v;
  236. break;
  237. }
  238. } else {
  239. $alias = $table;
  240. }
  241. if (!isset($this->joinedTables[$table])) {
  242. $this->getSelect()->join([$alias => $this->getTable($table)], $condition, $cols);
  243. $this->joinedTables[$alias] = true;
  244. }
  245. }
  246. /**
  247. * Retrieve connection object
  248. *
  249. * @return AdapterInterface
  250. */
  251. protected function getConnection()
  252. {
  253. return $this->connection;
  254. }
  255. /**
  256. * Set database connection adapter
  257. *
  258. * @param AdapterInterface $connection
  259. * @return void
  260. * @throws \InvalidArgumentException
  261. */
  262. protected function setConnection($connection)
  263. {
  264. if (!$connection instanceof \Magento\Framework\DB\Adapter\AdapterInterface) {
  265. throw new \InvalidArgumentException(
  266. (string)new \Magento\Framework\Phrase(
  267. 'dbModel read resource does not implement \Magento\Framework\DB\Adapter\AdapterInterface'
  268. )
  269. );
  270. }
  271. $this->connection = $connection;
  272. }
  273. /**
  274. * Build sql where condition part
  275. *
  276. * @param string|array $field
  277. * @param null|string|array $condition
  278. * @return string
  279. */
  280. protected function translateCondition($field, $condition)
  281. {
  282. $field = $this->getMappedField($field);
  283. return $this->getConditionSql($this->getConnection()->quoteIdentifier($field), $condition);
  284. }
  285. /**
  286. * Try to get mapped field name for filter to collection
  287. *
  288. * @param string $field
  289. * @return string
  290. */
  291. protected function getMappedField($field)
  292. {
  293. $mapper = $this->getMapper();
  294. if (isset($mapper['fields'][$field])) {
  295. $mappedField = $mapper['fields'][$field];
  296. } else {
  297. $mappedField = $field;
  298. }
  299. return $mappedField;
  300. }
  301. /**
  302. * Retrieve mapper data
  303. *
  304. * @return array|bool|null
  305. */
  306. protected function getMapper()
  307. {
  308. if (isset($this->map)) {
  309. return $this->map;
  310. } else {
  311. return false;
  312. }
  313. }
  314. /**
  315. * Build SQL statement for condition
  316. *
  317. * If $condition integer or string - exact value will be filtered ('eq' condition)
  318. *
  319. * If $condition is array - one of the following structures is expected:
  320. * - array("from" => $fromValue, "to" => $toValue)
  321. * - array("eq" => $equalValue)
  322. * - array("neq" => $notEqualValue)
  323. * - array("like" => $likeValue)
  324. * - array("in" => array($inValues))
  325. * - array("nin" => array($notInValues))
  326. * - array("notnull" => $valueIsNotNull)
  327. * - array("null" => $valueIsNull)
  328. * - array("moreq" => $moreOrEqualValue)
  329. * - array("gt" => $greaterValue)
  330. * - array("lt" => $lessValue)
  331. * - array("gteq" => $greaterOrEqualValue)
  332. * - array("lteq" => $lessOrEqualValue)
  333. * - array("finset" => $valueInSet)
  334. * - array("regexp" => $regularExpression)
  335. * - array("seq" => $stringValue)
  336. * - array("sneq" => $stringValue)
  337. *
  338. * If non matched - sequential array is expected and OR conditions
  339. * will be built using above mentioned structure
  340. *
  341. * @param string $fieldName
  342. * @param integer|string|array $condition
  343. * @return string
  344. */
  345. protected function getConditionSql($fieldName, $condition)
  346. {
  347. return $this->getConnection()->prepareSqlCondition($fieldName, $condition);
  348. }
  349. /**
  350. * Return the field name for the condition.
  351. *
  352. * @param string $fieldName
  353. * @return string
  354. */
  355. protected function getConditionFieldName($fieldName)
  356. {
  357. return $fieldName;
  358. }
  359. /**
  360. * Hook for operations before rendering filters
  361. * @return void
  362. */
  363. protected function renderFiltersBefore()
  364. {
  365. }
  366. /**
  367. * Retrieve table name
  368. *
  369. * @param string $table
  370. * @return string
  371. */
  372. protected function getTable($table)
  373. {
  374. return $this->getResource()->getTable($table);
  375. }
  376. /**
  377. * Get \Magento\Framework\DB\Select object instance
  378. *
  379. * @return Select
  380. */
  381. protected function getSelect()
  382. {
  383. return $this->select;
  384. }
  385. }