Export.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\ImportExport\Model;
  7. /**
  8. * Export model
  9. *
  10. * @api
  11. *
  12. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  13. * @since 100.0.2
  14. */
  15. class Export extends \Magento\ImportExport\Model\AbstractModel
  16. {
  17. const FILTER_ELEMENT_GROUP = 'export_filter';
  18. const FILTER_ELEMENT_SKIP = 'skip_attr';
  19. /**
  20. * Allow multiple values wrapping in double quotes for additional attributes.
  21. */
  22. const FIELDS_ENCLOSURE = 'fields_enclosure';
  23. /**
  24. * Filter fields types.
  25. */
  26. const FILTER_TYPE_SELECT = 'select';
  27. const FILTER_TYPE_MULTISELECT = 'multiselect';
  28. const FILTER_TYPE_INPUT = 'input';
  29. const FILTER_TYPE_DATE = 'date';
  30. const FILTER_TYPE_NUMBER = 'number';
  31. /**
  32. * Entity adapter.
  33. *
  34. * @var \Magento\ImportExport\Model\Export\Entity\AbstractEntity
  35. */
  36. protected $_entityAdapter;
  37. /**
  38. * Writer object instance.
  39. *
  40. * @var \Magento\ImportExport\Model\Export\Adapter\AbstractAdapter
  41. */
  42. protected $_writer;
  43. /**
  44. * @var \Magento\ImportExport\Model\Export\ConfigInterface
  45. */
  46. protected $_exportConfig;
  47. /**
  48. * @var \Magento\ImportExport\Model\Export\Entity\Factory
  49. */
  50. protected $_entityFactory;
  51. /**
  52. * @var \Magento\ImportExport\Model\Export\Adapter\Factory
  53. */
  54. protected $_exportAdapterFac;
  55. /**
  56. * @var array
  57. */
  58. private static $backendTypeToFilterMapper = [
  59. 'datetime' => self::FILTER_TYPE_DATE,
  60. 'decimal' => self::FILTER_TYPE_NUMBER,
  61. 'int' => self::FILTER_TYPE_NUMBER,
  62. 'varchar' => self::FILTER_TYPE_INPUT,
  63. 'text' => self::FILTER_TYPE_INPUT
  64. ];
  65. /**
  66. * @param \Psr\Log\LoggerInterface $logger
  67. * @param \Magento\Framework\Filesystem $filesystem
  68. * @param \Magento\ImportExport\Model\Export\ConfigInterface $exportConfig
  69. * @param \Magento\ImportExport\Model\Export\Entity\Factory $entityFactory
  70. * @param \Magento\ImportExport\Model\Export\Adapter\Factory $exportAdapterFac
  71. * @param array $data
  72. */
  73. public function __construct(
  74. \Psr\Log\LoggerInterface $logger,
  75. \Magento\Framework\Filesystem $filesystem,
  76. \Magento\ImportExport\Model\Export\ConfigInterface $exportConfig,
  77. \Magento\ImportExport\Model\Export\Entity\Factory $entityFactory,
  78. \Magento\ImportExport\Model\Export\Adapter\Factory $exportAdapterFac,
  79. array $data = []
  80. ) {
  81. $this->_exportConfig = $exportConfig;
  82. $this->_entityFactory = $entityFactory;
  83. $this->_exportAdapterFac = $exportAdapterFac;
  84. parent::__construct($logger, $filesystem, $data);
  85. }
  86. /**
  87. * Create instance of entity adapter and return it
  88. *
  89. * @return \Magento\ImportExport\Model\Export\Entity\AbstractEntity
  90. * |\Magento\ImportExport\Model\Export\AbstractEntity
  91. * @throws \Magento\Framework\Exception\LocalizedException
  92. */
  93. protected function _getEntityAdapter()
  94. {
  95. if (!$this->_entityAdapter) {
  96. $entities = $this->_exportConfig->getEntities();
  97. if (isset($entities[$this->getEntity()])) {
  98. try {
  99. $this->_entityAdapter = $this->_entityFactory->create($entities[$this->getEntity()]['model']);
  100. } catch (\Exception $e) {
  101. $this->_logger->critical($e);
  102. throw new \Magento\Framework\Exception\LocalizedException(
  103. __('Please enter a correct entity model.')
  104. );
  105. }
  106. if (!$this->_entityAdapter instanceof \Magento\ImportExport\Model\Export\Entity\AbstractEntity &&
  107. !$this->_entityAdapter instanceof \Magento\ImportExport\Model\Export\AbstractEntity
  108. ) {
  109. throw new \Magento\Framework\Exception\LocalizedException(
  110. __(
  111. 'The entity adapter object must be an instance of %1 or %2.',
  112. \Magento\ImportExport\Model\Export\Entity\AbstractEntity::class,
  113. \Magento\ImportExport\Model\Export\AbstractEntity::class
  114. )
  115. );
  116. }
  117. // check for entity codes integrity
  118. if ($this->getEntity() != $this->_entityAdapter->getEntityTypeCode()) {
  119. throw new \Magento\Framework\Exception\LocalizedException(
  120. __('The input entity code is not equal to entity adapter code.')
  121. );
  122. }
  123. } else {
  124. throw new \Magento\Framework\Exception\LocalizedException(__('Please enter a correct entity.'));
  125. }
  126. $this->_entityAdapter->setParameters($this->getData());
  127. }
  128. return $this->_entityAdapter;
  129. }
  130. /**
  131. * Get writer object.
  132. *
  133. * @return \Magento\ImportExport\Model\Export\Adapter\AbstractAdapter
  134. * @throws \Magento\Framework\Exception\LocalizedException
  135. */
  136. protected function _getWriter()
  137. {
  138. if (!$this->_writer) {
  139. $fileFormats = $this->_exportConfig->getFileFormats();
  140. if (isset($fileFormats[$this->getFileFormat()])) {
  141. try {
  142. $this->_writer = $this->_exportAdapterFac->create($fileFormats[$this->getFileFormat()]['model']);
  143. } catch (\Exception $e) {
  144. $this->_logger->critical($e);
  145. throw new \Magento\Framework\Exception\LocalizedException(
  146. __('Please enter a correct entity model.')
  147. );
  148. }
  149. if (!$this->_writer instanceof \Magento\ImportExport\Model\Export\Adapter\AbstractAdapter) {
  150. throw new \Magento\Framework\Exception\LocalizedException(
  151. __(
  152. 'The adapter object must be an instance of %1.',
  153. \Magento\ImportExport\Model\Export\Adapter\AbstractAdapter::class
  154. )
  155. );
  156. }
  157. } else {
  158. throw new \Magento\Framework\Exception\LocalizedException(__('Please correct the file format.'));
  159. }
  160. }
  161. return $this->_writer;
  162. }
  163. /**
  164. * Export data.
  165. *
  166. * @return string
  167. * @throws \Magento\Framework\Exception\LocalizedException
  168. */
  169. public function export()
  170. {
  171. if (isset($this->_data[self::FILTER_ELEMENT_GROUP])) {
  172. $this->addLogComment(__('Begin export of %1', $this->getEntity()));
  173. $result = $this->_getEntityAdapter()->setWriter($this->_getWriter())->export();
  174. $countRows = substr_count(trim($result), "\n");
  175. if (!$countRows) {
  176. throw new \Magento\Framework\Exception\LocalizedException(__('There is no data for the export.'));
  177. }
  178. if ($result) {
  179. $this->addLogComment([__('Exported %1 rows.', $countRows), __('The export is finished.')]);
  180. }
  181. return $result;
  182. } else {
  183. throw new \Magento\Framework\Exception\LocalizedException(__('Please provide filter data.'));
  184. }
  185. }
  186. /**
  187. * Clean up already loaded attribute collection.
  188. *
  189. * @param \Magento\Framework\Data\Collection $collection
  190. * @return \Magento\Framework\Data\Collection
  191. */
  192. public function filterAttributeCollection(\Magento\Framework\Data\Collection $collection)
  193. {
  194. return $this->_getEntityAdapter()->filterAttributeCollection($collection);
  195. }
  196. /**
  197. * Determine filter type for specified attribute.
  198. *
  199. * @static
  200. * @param \Magento\Eav\Model\Entity\Attribute $attribute
  201. * @return string
  202. * @throws \Magento\Framework\Exception\LocalizedException
  203. */
  204. public static function getAttributeFilterType(\Magento\Eav\Model\Entity\Attribute $attribute)
  205. {
  206. if ($attribute->usesSource() || $attribute->getFilterOptions()) {
  207. return 'multiselect' == $attribute->getFrontendInput() ?
  208. self::FILTER_TYPE_MULTISELECT : self::FILTER_TYPE_SELECT;
  209. }
  210. if (isset(self::$backendTypeToFilterMapper[$attribute->getBackendType()])) {
  211. return self::$backendTypeToFilterMapper[$attribute->getBackendType()];
  212. }
  213. if ($attribute->isStatic()) {
  214. return self::getStaticAttributeFilterType($attribute);
  215. }
  216. throw new \Magento\Framework\Exception\LocalizedException(
  217. __('We can\'t determine the attribute filter type.')
  218. );
  219. }
  220. /**
  221. * Determine filter type for static attribute.
  222. *
  223. * @static
  224. * @param \Magento\Eav\Model\Entity\Attribute $attribute
  225. * @return string
  226. */
  227. public static function getStaticAttributeFilterType(\Magento\Eav\Model\Entity\Attribute $attribute)
  228. {
  229. if (in_array($attribute->getAttributeCode(), ['category_ids', 'media_gallery'])) {
  230. return self::FILTER_TYPE_INPUT;
  231. }
  232. $columns = $attribute->getFlatColumns();
  233. if (empty($columns)) {
  234. return self::FILTER_TYPE_INPUT;
  235. }
  236. switch ($columns[$attribute->getAttributeCode()]['type']) {
  237. case \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER:
  238. case \Magento\Framework\DB\Ddl\Table::TYPE_BIGINT:
  239. $type = self::FILTER_TYPE_NUMBER;
  240. break;
  241. case \Magento\Framework\DB\Ddl\Table::TYPE_DATE:
  242. case \Magento\Framework\DB\Ddl\Table::TYPE_DATETIME:
  243. case \Magento\Framework\DB\Ddl\Table::TYPE_TIMESTAMP:
  244. $type = self::FILTER_TYPE_DATE;
  245. break;
  246. default:
  247. $type = self::FILTER_TYPE_INPUT;
  248. }
  249. return $type;
  250. }
  251. /**
  252. * MIME-type for 'Content-Type' header.
  253. *
  254. * @return string
  255. */
  256. public function getContentType()
  257. {
  258. return $this->_getWriter()->getContentType();
  259. }
  260. /**
  261. * Override standard entity getter.
  262. *
  263. * @return string
  264. * @throws \Magento\Framework\Exception\LocalizedException
  265. */
  266. public function getEntity()
  267. {
  268. if (empty($this->_data['entity'])) {
  269. throw new \Magento\Framework\Exception\LocalizedException(__('Entity is unknown'));
  270. }
  271. return $this->_data['entity'];
  272. }
  273. /**
  274. * Entity attributes collection getter.
  275. *
  276. * @return \Magento\Framework\Data\Collection
  277. */
  278. public function getEntityAttributeCollection()
  279. {
  280. return $this->_getEntityAdapter()->getAttributeCollection();
  281. }
  282. /**
  283. * Override standard entity getter.
  284. *
  285. * @return string
  286. * @throws \Magento\Framework\Exception\LocalizedException
  287. */
  288. public function getFileFormat()
  289. {
  290. if (empty($this->_data['file_format'])) {
  291. throw new \Magento\Framework\Exception\LocalizedException(__('We can\'t identify this file format.'));
  292. }
  293. return $this->_data['file_format'];
  294. }
  295. /**
  296. * Return file name for downloading.
  297. *
  298. * @return string
  299. */
  300. public function getFileName()
  301. {
  302. $fileName = null;
  303. $entityAdapter = $this->_getEntityAdapter();
  304. if ($entityAdapter instanceof \Magento\ImportExport\Model\Export\AbstractEntity) {
  305. $fileName = $entityAdapter->getFileName();
  306. }
  307. if (!$fileName) {
  308. $fileName = $this->getEntity();
  309. }
  310. return $fileName . '_' . date('Ymd_His') . '.' . $this->_getWriter()->getFileExtension();
  311. }
  312. }