Export.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Backend\Block\Widget\Grid;
  7. use Magento\Framework\App\Filesystem\DirectoryList;
  8. /**
  9. * @api
  10. * @deprecated 100.2.0 in favour of UI component implementation
  11. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  12. * @since 100.0.2
  13. */
  14. class Export extends \Magento\Backend\Block\Widget implements \Magento\Backend\Block\Widget\Grid\ExportInterface
  15. {
  16. /**
  17. * Grid export types
  18. *
  19. * @var \Magento\Framework\DataObject[]
  20. */
  21. protected $_exportTypes = [];
  22. /**
  23. * Rows per page for import
  24. *
  25. * @var int
  26. */
  27. protected $_exportPageSize = 1000;
  28. /**
  29. * Template file name
  30. *
  31. * @var string
  32. */
  33. protected $_template = "Magento_Backend::widget/grid/export.phtml";
  34. /**
  35. * @var \Magento\Framework\Data\CollectionFactory
  36. */
  37. protected $_collectionFactory;
  38. /**
  39. * @var \Magento\Framework\Filesystem\Directory\WriteInterface
  40. */
  41. protected $_directory;
  42. /**
  43. * Additional path to folder
  44. *
  45. * @var string
  46. */
  47. protected $_path = 'export';
  48. /**
  49. * @param \Magento\Backend\Block\Template\Context $context
  50. * @param \Magento\Framework\Data\CollectionFactory $collectionFactory
  51. * @param array $data
  52. */
  53. public function __construct(
  54. \Magento\Backend\Block\Template\Context $context,
  55. \Magento\Framework\Data\CollectionFactory $collectionFactory,
  56. array $data = []
  57. ) {
  58. $this->_collectionFactory = $collectionFactory;
  59. parent::__construct($context, $data);
  60. }
  61. /**
  62. * @return void
  63. * @throws \Magento\Framework\Exception\LocalizedException
  64. */
  65. protected function _construct()
  66. {
  67. parent::_construct();
  68. if ($this->hasData('exportTypes')) {
  69. foreach ($this->getData('exportTypes') as $type) {
  70. if (!isset($type['urlPath']) || !isset($type['label'])) {
  71. throw new \Magento\Framework\Exception\LocalizedException(
  72. __('Invalid export type supplied for grid export block')
  73. );
  74. }
  75. $this->addExportType($type['urlPath'], $type['label']);
  76. }
  77. }
  78. $this->_directory = $this->_filesystem->getDirectoryWrite(DirectoryList::VAR_DIR);
  79. }
  80. /**
  81. * Retrieve grid columns
  82. *
  83. * @return \Magento\Backend\Block\Widget\Grid\Column[]
  84. */
  85. protected function _getColumns()
  86. {
  87. return $this->getParentBlock()->getColumns();
  88. }
  89. /**
  90. * Retrieve totals
  91. *
  92. * @return \Magento\Framework\DataObject
  93. */
  94. protected function _getTotals()
  95. {
  96. return $this->getParentBlock()->getColumnSet()->getTotals();
  97. }
  98. /**
  99. * Return count totals
  100. *
  101. * @return bool
  102. * @SuppressWarnings(PHPMD.BooleanGetMethodName)
  103. */
  104. public function getCountTotals()
  105. {
  106. return $this->getParentBlock()->getColumnSet()->shouldRenderTotal();
  107. }
  108. /**
  109. * Get collection object
  110. *
  111. * @return \Magento\Framework\Data\Collection
  112. */
  113. protected function _getCollection()
  114. {
  115. return $this->getParentBlock()->getCollection();
  116. }
  117. /**
  118. * Retrieve grid export types
  119. *
  120. * @return \Magento\Framework\DataObject[]|false
  121. */
  122. public function getExportTypes()
  123. {
  124. return empty($this->_exportTypes) ? false : $this->_exportTypes;
  125. }
  126. /**
  127. * Retrieve grid id
  128. *
  129. * @return string
  130. */
  131. public function getId()
  132. {
  133. return $this->getParentBlock()->getId();
  134. }
  135. /**
  136. * Prepare export button
  137. *
  138. * @return $this
  139. */
  140. protected function _prepareLayout()
  141. {
  142. $this->setChild(
  143. 'export_button',
  144. $this->getLayout()->createBlock(
  145. \Magento\Backend\Block\Widget\Button::class
  146. )->setData(
  147. [
  148. 'label' => __('Export'),
  149. 'onclick' => $this->getParentBlock()->getJsObjectName() . '.doExport()',
  150. 'class' => 'task',
  151. ]
  152. )
  153. );
  154. return parent::_prepareLayout();
  155. }
  156. /**
  157. * Render export button
  158. *
  159. * @return string
  160. */
  161. public function getExportButtonHtml()
  162. {
  163. return $this->getChildHtml('export_button');
  164. }
  165. /**
  166. * Add new export type to grid
  167. *
  168. * @param string $url
  169. * @param string $label
  170. * @return $this
  171. */
  172. public function addExportType($url, $label)
  173. {
  174. $this->_exportTypes[] = new \Magento\Framework\DataObject(
  175. ['url' => $this->getUrl($url, ['_current' => true]), 'label' => $label]
  176. );
  177. return $this;
  178. }
  179. /**
  180. * Retrieve file content from file container array
  181. *
  182. * @param array $fileData
  183. * @return string
  184. */
  185. protected function _getFileContainerContent(array $fileData)
  186. {
  187. return $this->_directory->readFile('export/' . $fileData['value']);
  188. }
  189. /**
  190. * Retrieve Headers row array for Export
  191. *
  192. * @return string[]
  193. */
  194. protected function _getExportHeaders()
  195. {
  196. $row = [];
  197. foreach ($this->_getColumns() as $column) {
  198. if (!$column->getIsSystem()) {
  199. $row[] = $column->getExportHeader();
  200. }
  201. }
  202. return $row;
  203. }
  204. /**
  205. * Retrieve Totals row array for Export
  206. *
  207. * @return string[]
  208. */
  209. protected function _getExportTotals()
  210. {
  211. $totals = $this->_getTotals();
  212. $row = [];
  213. foreach ($this->_getColumns() as $column) {
  214. if (!$column->getIsSystem()) {
  215. $row[] = $column->hasTotalsLabel() ? $column->getTotalsLabel() : $column->getRowFieldExport($totals);
  216. }
  217. }
  218. return $row;
  219. }
  220. /**
  221. * Iterate collection and call callback method per item
  222. * For callback method first argument always is item object
  223. *
  224. * @param string $callback
  225. * @param array $args additional arguments for callback method
  226. * @return void
  227. */
  228. public function _exportIterateCollection($callback, array $args)
  229. {
  230. /** @var $originalCollection \Magento\Framework\Data\Collection */
  231. $originalCollection = $this->getParentBlock()->getPreparedCollection();
  232. $count = null;
  233. $page = 1;
  234. $lPage = null;
  235. $break = false;
  236. while ($break !== true) {
  237. $originalCollection->clear();
  238. $originalCollection->setPageSize($this->getExportPageSize());
  239. $originalCollection->setCurPage($page);
  240. $originalCollection->load();
  241. if ($count === null) {
  242. $count = $originalCollection->getSize();
  243. $lPage = $originalCollection->getLastPageNumber();
  244. }
  245. if ($lPage == $page) {
  246. $break = true;
  247. }
  248. $page++;
  249. $collection = $this->_getRowCollection($originalCollection);
  250. foreach ($collection as $item) {
  251. call_user_func_array([$this, $callback], array_merge([$item], $args));
  252. }
  253. }
  254. }
  255. /**
  256. * Write item data to csv export file
  257. *
  258. * @param \Magento\Framework\DataObject $item
  259. * @param \Magento\Framework\Filesystem\File\WriteInterface $stream
  260. * @return void
  261. */
  262. protected function _exportCsvItem(
  263. \Magento\Framework\DataObject $item,
  264. \Magento\Framework\Filesystem\File\WriteInterface $stream
  265. ) {
  266. $row = [];
  267. foreach ($this->_getColumns() as $column) {
  268. if (!$column->getIsSystem()) {
  269. $row[] = $column->getRowFieldExport($item);
  270. }
  271. }
  272. $stream->writeCsv($row);
  273. }
  274. /**
  275. * Retrieve a file container array by grid data as CSV
  276. *
  277. * Return array with keys type and value
  278. *
  279. * @return array
  280. */
  281. public function getCsvFile()
  282. {
  283. $name = md5(microtime());
  284. $file = $this->_path . '/' . $name . '.csv';
  285. $this->_directory->create($this->_path);
  286. $stream = $this->_directory->openFile($file, 'w+');
  287. $stream->writeCsv($this->_getExportHeaders());
  288. $stream->lock();
  289. $this->_exportIterateCollection('_exportCsvItem', [$stream]);
  290. if ($this->getCountTotals()) {
  291. $stream->writeCsv($this->_getExportTotals());
  292. }
  293. $stream->unlock();
  294. $stream->close();
  295. return [
  296. 'type' => 'filename',
  297. 'value' => $file,
  298. 'rm' => true // can delete file after use
  299. ];
  300. }
  301. /**
  302. * Retrieve Grid data as CSV
  303. *
  304. * @return string
  305. */
  306. public function getCsv()
  307. {
  308. $csv = '';
  309. $collection = $this->_getPreparedCollection();
  310. $data = [];
  311. foreach ($this->_getColumns() as $column) {
  312. if (!$column->getIsSystem()) {
  313. $data[] = '"' . $column->getExportHeader() . '"';
  314. }
  315. }
  316. $csv .= implode(',', $data) . "\n";
  317. foreach ($collection as $item) {
  318. $data = [];
  319. foreach ($this->_getColumns() as $column) {
  320. if (!$column->getIsSystem()) {
  321. $data[] = '"' . str_replace(
  322. ['"', '\\'],
  323. ['""', '\\\\'],
  324. $column->getRowFieldExport($item)
  325. ) . '"';
  326. }
  327. }
  328. $csv .= implode(',', $data) . "\n";
  329. }
  330. if ($this->getCountTotals()) {
  331. $data = [];
  332. foreach ($this->_getColumns() as $column) {
  333. if (!$column->getIsSystem()) {
  334. $data[] = '"' . str_replace(
  335. ['"', '\\'],
  336. ['""', '\\\\'],
  337. $column->getRowFieldExport($this->_getTotals())
  338. ) . '"';
  339. }
  340. }
  341. $csv .= implode(',', $data) . "\n";
  342. }
  343. return $csv;
  344. }
  345. /**
  346. * Retrieve data in xml
  347. *
  348. * @return string
  349. */
  350. public function getXml()
  351. {
  352. $collection = $this->_getPreparedCollection();
  353. $indexes = [];
  354. foreach ($this->_getColumns() as $column) {
  355. if (!$column->getIsSystem()) {
  356. $indexes[] = $column->getIndex();
  357. }
  358. }
  359. $xml = '<?xml version="1.0" encoding="UTF-8"?>';
  360. $xml .= '<items>';
  361. foreach ($collection as $item) {
  362. $xml .= $item->toXml($indexes);
  363. }
  364. if ($this->getCountTotals()) {
  365. $xml .= $this->_getTotals()->toXml($indexes);
  366. }
  367. $xml .= '</items>';
  368. return $xml;
  369. }
  370. /**
  371. * Get a row data of the particular columns
  372. *
  373. * @param \Magento\Framework\DataObject $data
  374. * @return string[]
  375. */
  376. public function getRowRecord(\Magento\Framework\DataObject $data)
  377. {
  378. $row = [];
  379. foreach ($this->_getColumns() as $column) {
  380. if (!$column->getIsSystem()) {
  381. $row[] = $column->getRowFieldExport($data);
  382. }
  383. }
  384. return $row;
  385. }
  386. /**
  387. * Retrieve a file container array by grid data as MS Excel 2003 XML Document
  388. *
  389. * Return array with keys type and value
  390. *
  391. * @param string $sheetName
  392. * @return array
  393. */
  394. public function getExcelFile($sheetName = '')
  395. {
  396. $collection = $this->_getRowCollection();
  397. $convert = new \Magento\Framework\Convert\Excel($collection->getIterator(), [$this, 'getRowRecord']);
  398. $name = md5(microtime());
  399. $file = $this->_path . '/' . $name . '.xml';
  400. $this->_directory->create($this->_path);
  401. $stream = $this->_directory->openFile($file, 'w+');
  402. $stream->lock();
  403. $convert->setDataHeader($this->_getExportHeaders());
  404. if ($this->getCountTotals()) {
  405. $convert->setDataFooter($this->_getExportTotals());
  406. }
  407. $convert->write($stream, $sheetName);
  408. $stream->unlock();
  409. $stream->close();
  410. return [
  411. 'type' => 'filename',
  412. 'value' => $file,
  413. 'rm' => true // can delete file after use
  414. ];
  415. }
  416. /**
  417. * Retrieve grid data as MS Excel 2003 XML Document
  418. *
  419. * @return string
  420. */
  421. public function getExcel()
  422. {
  423. $collection = $this->_getPreparedCollection();
  424. $headers = [];
  425. $data = [];
  426. foreach ($this->_getColumns() as $column) {
  427. if (!$column->getIsSystem()) {
  428. $headers[] = $column->getHeader();
  429. }
  430. }
  431. $data[] = $headers;
  432. foreach ($collection as $item) {
  433. $row = [];
  434. foreach ($this->_getColumns() as $column) {
  435. if (!$column->getIsSystem()) {
  436. $row[] = $column->getRowField($item);
  437. }
  438. }
  439. $data[] = $row;
  440. }
  441. if ($this->getCountTotals()) {
  442. $row = [];
  443. foreach ($this->_getColumns() as $column) {
  444. if (!$column->getIsSystem()) {
  445. $row[] = $column->getRowField($this->_getTotals());
  446. }
  447. }
  448. $data[] = $row;
  449. }
  450. $convert = new \Magento\Framework\Convert\Excel(new \ArrayIterator($data));
  451. return $convert->convert('single_sheet');
  452. }
  453. /**
  454. * Reformat base collection into collection without sub-collection in items
  455. *
  456. * @param \Magento\Framework\Data\Collection $baseCollection
  457. * @return \Magento\Framework\Data\Collection
  458. */
  459. protected function _getRowCollection(\Magento\Framework\Data\Collection $baseCollection = null)
  460. {
  461. if (null === $baseCollection) {
  462. $baseCollection = $this->getParentBlock()->getPreparedCollection();
  463. }
  464. $collection = $this->_collectionFactory->create();
  465. /** @var $item \Magento\Framework\DataObject */
  466. foreach ($baseCollection as $item) {
  467. if ($item->getIsEmpty()) {
  468. continue;
  469. }
  470. if ($item->hasChildren() && count($item->getChildren()) > 0) {
  471. /** @var $subItem \Magento\Framework\DataObject */
  472. foreach ($item->getChildren() as $subItem) {
  473. $tmpItem = clone $item;
  474. $tmpItem->unsChildren();
  475. $tmpItem->addData($subItem->getData());
  476. $collection->addItem($tmpItem);
  477. }
  478. } else {
  479. $collection->addItem($item);
  480. }
  481. }
  482. return $collection;
  483. }
  484. /**
  485. * Return prepared collection as row collection with additional conditions
  486. *
  487. * @return \Magento\Framework\Data\Collection
  488. */
  489. public function _getPreparedCollection()
  490. {
  491. /** @var $collection \Magento\Framework\Data\Collection */
  492. $collection = $this->getParentBlock()->getPreparedCollection();
  493. $collection->setPageSize(0);
  494. $collection->load();
  495. return $this->_getRowCollection($collection);
  496. }
  497. /**
  498. * @return int
  499. */
  500. public function getExportPageSize()
  501. {
  502. return $this->_exportPageSize;
  503. }
  504. }