Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
Downloadable.php
Go to the documentation of this file.
1 <?php
9 
12 use \Magento\Store\Model\Store;
13 
19 class Downloadable extends \Magento\CatalogImportExport\Model\Import\Product\Type\AbstractType
20 {
24  const PAIR_VALUE_SEPARATOR = '=';
25 
29  const DEFAULT_SORT_ORDER = 0;
30 
35 
40 
44  const DEFAULT_WEBSITE_ID = 0;
45 
49  const DOWNLOADABLE_PATCH_SAMPLES = 'downloadable/files/samples';
50 
54  const DOWNLOADABLE_PATCH_LINKS = 'downloadable/files/links';
55 
59  const DOWNLOADABLE_PATCH_LINK_SAMPLES = 'downloadable/files/link_samples';
60 
64  const URL_OPTION_VALUE = 'url';
65 
69  const FILE_OPTION_VALUE = 'file';
70 
74  const COL_DOWNLOADABLE_SAMPLES = 'downloadable_samples';
75 
79  const COL_DOWNLOADABLE_LINKS = 'downloadable_links';
80 
84  const DEFAULT_GROUP_TITLE = '';
85 
90 
94  const ERROR_OPTIONS_NOT_FOUND = 'optionsNotFound';
95 
96  const ERROR_GROUP_TITLE_NOT_FOUND = 'groupTitleNotFound';
97 
98  const ERROR_OPTION_NO_TITLE = 'optionNoTitle';
99 
100  const ERROR_MOVE_FILE = 'moveFile';
101 
102  const ERROR_COLS_IS_EMPTY = 'emptyOptions';
103 
109  protected $_messageTemplates = [
110  self::ERROR_OPTIONS_NOT_FOUND => 'Options for downloadable products not found',
111  self::ERROR_GROUP_TITLE_NOT_FOUND => 'Group titles not found for downloadable products',
112  self::ERROR_OPTION_NO_TITLE => 'Option no title',
113  self::ERROR_MOVE_FILE => 'Error move file',
114  self::ERROR_COLS_IS_EMPTY => 'Missing sample and links data for the downloadable product'
115  ];
116 
122  protected $parameters = [];
123 
129  protected $productIds = [];
130 
136  protected $cachedOptions = [
137  'link' => [],
138  'sample' => []
139  ];
140 
146  protected $dataSample = [
147  'sample_id' => null,
148  'product_id' => null,
149  'sample_url' => null,
150  'sample_file' => null,
151  'sample_type' => null,
152  'sort_order' => self::DEFAULT_SORT_ORDER
153  ];
154 
160  protected $dataSampleTitle = [
161  'sample_id' => null,
162  'store_id' => Store::DEFAULT_STORE_ID,
163  'title' => null
164  ];
165 
171  protected $dataLink = [
172  'link_id' => null,
173  'product_id' => null,
174  'sort_order' => self::DEFAULT_SORT_ORDER,
175  'number_of_downloads' => self::DEFAULT_NUMBER_OF_DOWNLOADS,
176  'is_shareable' => self::DEFAULT_IS_SHAREABLE,
177  'link_url' => null,
178  'link_file' => null,
179  'link_type' => null,
180  'sample_url' => null,
181  'sample_file' => null,
182  'sample_type' => null
183  ];
184 
190  protected $dataLinkTitle = [
191  'link_id' => null,
192  'store_id' => Store::DEFAULT_STORE_ID,
193  'title' => null
194  ];
195 
201  protected $dataLinkPrice = [
202  'price_id' => null,
203  'link_id' => null,
204  'website_id' => self::DEFAULT_WEBSITE_ID,
205  'price' => null
206  ];
207 
213  protected $optionLinkMapping = [
214  'sortorder' => 'sort_order',
215  'downloads' => 'number_of_downloads',
216  'shareable' => 'is_shareable',
217  'url' => 'link_url',
218  'file' => 'link_file',
219  ];
220 
226  protected $optionSampleMapping = [
227  'sortorder' => 'sort_order',
228  'url' => 'sample_url',
229  'file' => 'sample_file',
230  ];
231 
235  protected $rowNum;
236 
240  protected $uploaderHelper;
241 
246 
258  public function __construct(
259  \Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\CollectionFactory $attrSetColFac,
260  \Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory $prodAttrColFac,
261  \Magento\Framework\App\ResourceConnection $resource,
262  array $params,
263  \Magento\DownloadableImportExport\Helper\Uploader $uploaderHelper,
264  \Magento\DownloadableImportExport\Helper\Data $downloadableHelper,
266  ) {
267  parent::__construct($attrSetColFac, $prodAttrColFac, $resource, $params, $metadataPool);
268  $this->parameters = $this->_entityModel->getParameters();
269  $this->_resource = $resource;
270  $this->uploaderHelper = $uploaderHelper;
271  $this->downloadableHelper = $downloadableHelper;
272  }
273 
279  public function saveData()
280  {
281  $newSku = $this->_entityModel->getNewSku();
282  while ($bunch = $this->_entityModel->getNextBunch()) {
283  foreach ($bunch as $rowNum => $rowData) {
284  if (!$this->_entityModel->isRowAllowedToImport($rowData, $rowNum)) {
285  continue;
286  }
287  $rowSku = strtolower($rowData[ImportProduct::COL_SKU]);
288  $productData = $newSku[$rowSku];
289  if ($this->_type != $productData['type_id']) {
290  continue;
291  }
292  $this->parseOptions($rowData, $productData[$this->getProductEntityLinkField()]);
293  }
294  if (!empty($this->cachedOptions['sample']) || !empty($this->cachedOptions['link'])) {
295  $this->saveOptions();
296  $this->clear();
297  }
298  }
299  return $this;
300  }
301 
313  public function isRowValid(array $rowData, $rowNum, $isNewProduct = true)
314  {
315  $this->rowNum = $rowNum;
316  $error = false;
317  if (!$this->downloadableHelper->isRowDownloadableNoValid($rowData)) {
318  $this->_entityModel->addRowError(self::ERROR_OPTIONS_NOT_FOUND, $this->rowNum);
319  $error = true;
320  }
321  if ($this->downloadableHelper->isRowDownloadableEmptyOptions($rowData)) {
322  $this->_entityModel->addRowError(self::ERROR_COLS_IS_EMPTY, $this->rowNum);
323  $error = true;
324  }
325  if ($this->isRowValidSample($rowData) || $this->isRowValidLink($rowData)) {
326  $error = true;
327  }
328  return !$error;
329  }
330 
337  protected function isRowValidSample(array $rowData)
338  {
339  $result = false;
340  if (isset($rowData[self::COL_DOWNLOADABLE_SAMPLES])
341  && $rowData[self::COL_DOWNLOADABLE_SAMPLES] != ''
342  && $this->sampleGroupTitle($rowData) == '') {
343  $this->_entityModel->addRowError(self::ERROR_GROUP_TITLE_NOT_FOUND, $this->rowNum);
344  $result = true;
345  }
346  if (isset($rowData[self::COL_DOWNLOADABLE_SAMPLES])
347  && $rowData[self::COL_DOWNLOADABLE_SAMPLES] != '') {
348  $result = $this->isTitle($this->prepareSampleData($rowData[self::COL_DOWNLOADABLE_SAMPLES]));
349  }
350  return $result;
351  }
352 
359  protected function isRowValidLink(array $rowData)
360  {
361  $result = false;
362  if (isset($rowData[self::COL_DOWNLOADABLE_LINKS]) &&
363  $rowData[self::COL_DOWNLOADABLE_LINKS] != '' &&
364  $this->linksAdditionalAttributes($rowData, 'group_title', self::DEFAULT_GROUP_TITLE) == ''
365  ) {
366  $this->_entityModel->addRowError(self::ERROR_GROUP_TITLE_NOT_FOUND, $this->rowNum);
367  $result = true;
368  }
369  if (isset($rowData[self::COL_DOWNLOADABLE_LINKS]) &&
370  $rowData[self::COL_DOWNLOADABLE_LINKS] != ''
371  ) {
372  $result = $this->isTitle($this->prepareLinkData($rowData[self::COL_DOWNLOADABLE_LINKS]));
373  }
374  return $result;
375  }
376 
383  protected function isTitle(array $options)
384  {
385  $result = false;
386  foreach ($options as $option) {
387  if (!array_key_exists('title', $option)) {
388  $this->_entityModel->addRowError(self::ERROR_OPTION_NO_TITLE, $this->rowNum);
389  $result = true;
390  }
391  }
392  return $result;
393  }
394 
402  public function prepareAttributesWithDefaultValueForSave(array $rowData, $withDefaultValue = true)
403  {
404  $resultAttrs = parent::prepareAttributesWithDefaultValueForSave($rowData, $withDefaultValue);
405  $resultAttrs = array_merge($resultAttrs, $this->addAdditionalAttributes($rowData));
406  return $resultAttrs;
407  }
408 
415  protected function addAdditionalAttributes(array $rowData)
416  {
417  return [
418  'samples_title' => $this->sampleGroupTitle($rowData),
419  'links_title' => $this->linksAdditionalAttributes($rowData, 'group_title', self::DEFAULT_GROUP_TITLE),
420  'links_purchased_separately' => $this->linksAdditionalAttributes(
421  $rowData,
422  'purchased_separately',
423  self::DEFAULT_PURCHASED_SEPARATELY
424  )
425  ];
426  }
427 
436  protected function linksAdditionalAttributes(array $rowData, $attribute, $defaultValue)
437  {
438  $result = $defaultValue;
439  if (isset($rowData[self::COL_DOWNLOADABLE_LINKS])) {
440  $options = explode(
441  ImportProduct::PSEUDO_MULTI_LINE_SEPARATOR,
442  $rowData[self::COL_DOWNLOADABLE_LINKS]
443  );
444  foreach ($options as $option) {
445  $arr = $this->parseLinkOption(explode($this->_entityModel->getMultipleValueSeparator(), $option));
446  if (isset($arr[$attribute])) {
447  $result = $arr[$attribute];
448  break;
449  }
450  }
451  }
452  return $result;
453  }
454 
461  protected function sampleGroupTitle(array $rowData)
462  {
463  $result = '';
464  if (isset($rowData[self::COL_DOWNLOADABLE_SAMPLES])) {
465  $options = explode(
466  ImportProduct::PSEUDO_MULTI_LINE_SEPARATOR,
467  $rowData[self::COL_DOWNLOADABLE_SAMPLES]
468  );
469  foreach ($options as $option) {
470  $arr = $this->parseSampleOption(explode($this->_entityModel->getMultipleValueSeparator(), $option));
471  if (isset($arr['group_title'])) {
472  $result = $arr['group_title'];
473  break;
474  }
475  }
476  }
477  return $result;
478  }
479 
487  protected function parseOptions(array $rowData, $entityId)
488  {
489  $this->productIds[] = $entityId;
490  if (isset($rowData[self::COL_DOWNLOADABLE_LINKS])) {
491  $this->cachedOptions['link'] = array_merge(
492  $this->cachedOptions['link'],
493  $this->prepareLinkData($rowData[self::COL_DOWNLOADABLE_LINKS], $entityId)
494  );
495  }
496  if (isset($rowData[self::COL_DOWNLOADABLE_SAMPLES])) {
497  $this->cachedOptions['sample'] = array_merge(
498  $this->prepareSampleData($rowData[self::COL_DOWNLOADABLE_SAMPLES], $entityId),
499  $this->cachedOptions['sample']
500  );
501  }
502  return $this;
503  }
504 
512  protected function fillDataSample(array $base, array $options)
513  {
514  $result = [];
515  $existingOptions = $this->connection->fetchAll(
516  $this->connection->select()->from(
517  $this->_resource->getTableName('downloadable_sample')
518  )->where(
519  'product_id in (?)',
520  $this->productIds
521  )
522  );
523  foreach ($options as $option) {
524  $isExist = false;
525  foreach ($existingOptions as $existingOption) {
526  if ($option['sample_url'] == $existingOption['sample_url']
527  && $option['sample_file'] == $existingOption['sample_file']
528  && $option['sample_type'] == $existingOption['sample_type']
529  && $option['product_id'] == $existingOption['product_id']) {
530  $result[] = array_replace($this->dataSampleTitle, $option, $existingOption);
531  $isExist = true;
532  }
533  }
534  if (!$isExist) {
535  $result[] = array_replace($base, $option);
536  }
537  }
538  return $result;
539  }
540 
548  protected function fillDataLink(array $base, array $options)
549  {
550  $result = [];
551  $existingOptions = $this->connection->fetchAll(
552  $this->connection->select()->from(
553  $this->_resource->getTableName('downloadable_link')
554  )->where(
555  'product_id in (?)',
556  $this->productIds
557  )
558  );
559  foreach ($options as $option) {
560  $existOption = $this->downloadableHelper->fillExistOptions($base, $option, $existingOptions);
561  if (empty($existOption)) {
562  $result[] = array_replace($base, $option);
563  } else {
564  $result[] = $existOption;
565  }
566  }
567  return $result;
568  }
569 
576  protected function fillDataTitleLink(array $options)
577  {
578  $result = [];
579  $existingOptions = $this->connection->fetchAll(
580  $this->connection->select()->from(
581  ['dl' => $this->_resource->getTableName('downloadable_link')],
582  [
583  'link_id',
584  'product_id',
585  'sort_order',
586  'number_of_downloads',
587  'is_shareable',
588  'link_url',
589  'link_file',
590  'link_type',
591  'sample_url',
592  'sample_file',
593  'sample_type'
594  ]
595  )->joinLeft(
596  ['dlp' => $this->_resource->getTableName('downloadable_link_price')],
597  'dl.link_id = dlp.link_id AND dlp.website_id=' . self::DEFAULT_WEBSITE_ID,
598  ['price_id', 'website_id']
599  )->where(
600  'product_id in (?)',
601  $this->productIds
602  )
603  );
604  foreach ($options as $option) {
605  $existOption = $this->downloadableHelper->fillExistOptions($this->dataLinkTitle, $option, $existingOptions);
606  if (!empty($existOption)) {
607  $result['title'][] = $existOption;
608  }
609  $existOption = $this->downloadableHelper->fillExistOptions($this->dataLinkPrice, $option, $existingOptions);
610  if (!empty($existOption)) {
611  $result['price'][] = $existOption;
612  }
613  }
614  return $result;
615  }
616 
623  protected function uploadSampleFiles(array $options)
624  {
625  $result = [];
626  foreach ($options as $option) {
627  if ($option['sample_file'] !== null) {
628  $option['sample_file'] = $this->uploadDownloadableFiles($option['sample_file'], 'samples', true);
629  }
630  $result[] = $option;
631  }
632  return $result;
633  }
634 
641  protected function uploadLinkFiles(array $options)
642  {
643  $result = [];
644  foreach ($options as $option) {
645  if ($option['sample_file'] !== null) {
646  $option['sample_file'] = $this->uploadDownloadableFiles($option['sample_file'], 'link_samples', true);
647  }
648  if ($option['link_file'] !== null) {
649  $option['link_file'] = $this->uploadDownloadableFiles($option['link_file'], 'links', true);
650  }
651  $result[] = $option;
652  }
653  return $result;
654  }
655 
661  protected function saveOptions()
662  {
664  if (!empty($options['sample'])) {
665  $this->saveSampleOptions();
666  }
667  if (!empty($options['link'])) {
668  $this->saveLinkOptions();
669  }
670  return $this;
671  }
672 
678  protected function saveSampleOptions()
679  {
680  $options['sample'] = $this->uploadSampleFiles($this->cachedOptions['sample']);
681  $dataSample = $this->fillDataSample($this->dataSample, $options['sample']);
682  $this->connection->insertOnDuplicate(
683  $this->_resource->getTableName('downloadable_sample'),
684  $this->downloadableHelper->prepareDataForSave($this->dataSample, $dataSample)
685  );
686  $titleSample = $this->fillDataSample($this->dataSampleTitle, $options['sample']);
687  $this->connection->insertOnDuplicate(
688  $this->_resource->getTableName('downloadable_sample_title'),
689  $this->downloadableHelper->prepareDataForSave($this->dataSampleTitle, $titleSample)
690  );
691  return $this;
692  }
693 
699  protected function saveLinkOptions()
700  {
701  $options['link'] = $this->uploadLinkFiles($this->cachedOptions['link']);
702  $dataLink = $this->fillDataLink($this->dataLink, $options['link']);
703  $this->connection->insertOnDuplicate(
704  $this->_resource->getTableName('downloadable_link'),
705  $this->downloadableHelper->prepareDataForSave($this->dataLink, $dataLink)
706  );
707  $dataLink = $this->fillDataTitleLink($options['link']);
708  $this->connection->insertOnDuplicate(
709  $this->_resource->getTableName('downloadable_link_title'),
710  $this->downloadableHelper->prepareDataForSave($this->dataLinkTitle, $dataLink['title'])
711  );
712  if (count($dataLink['price'])) {
713  $this->connection->insertOnDuplicate(
714  $this->_resource->getTableName('downloadable_link_price'),
715  $this->downloadableHelper->prepareDataForSave($this->dataLinkPrice, $dataLink['price'])
716  );
717  }
718  return $this;
719  }
720 
728  protected function prepareSampleData($rowCol, $entityId = null)
729  {
730  $result = [];
731  $options = explode(
732  ImportProduct::PSEUDO_MULTI_LINE_SEPARATOR,
733  $rowCol
734  );
735  foreach ($options as $option) {
736  $result[] = array_merge(
737  $this->dataSample,
738  ['product_id' => $entityId],
739  $this->parseSampleOption(explode($this->_entityModel->getMultipleValueSeparator(), $option))
740  );
741  }
742  return $result;
743  }
744 
752  protected function prepareLinkData($rowCol, $entityId = null)
753  {
754  $result = [];
755  $options = explode(
756  ImportProduct::PSEUDO_MULTI_LINE_SEPARATOR,
757  $rowCol
758  );
759  foreach ($options as $option) {
760  $result[] = array_merge(
761  $this->dataLink,
762  ['product_id' => $entityId],
763  $this->parseLinkOption(explode($this->_entityModel->getMultipleValueSeparator(), $option))
764  );
765  }
766  return $result;
767  }
768 
775  protected function parseLinkOption(array $values)
776  {
777  $option = [];
778  foreach ($values as $keyValue) {
779  $keyValue = trim($keyValue);
780  $pos = strpos($keyValue, self::PAIR_VALUE_SEPARATOR);
781  if ($pos !== false) {
782  $key = substr($keyValue, 0, $pos);
783  $value = substr($keyValue, $pos + 1);
784  if ($key == 'sample') {
785  $option['sample_type'] = $this->downloadableHelper->getTypeByValue($value);
786  $option['sample_' . $option['sample_type']] = $value;
787  }
788  if ($key == self::URL_OPTION_VALUE || $key == self::FILE_OPTION_VALUE) {
789  $option['link_type'] = $key;
790  }
791  if ($key == 'downloads' && $value == 'unlimited') {
792  $value = 0;
793  }
794  if (isset($this->optionLinkMapping[$key])) {
795  $key = $this->optionLinkMapping[$key];
796  }
797  $option[$key] = $value;
798  }
799  }
800  return $option;
801  }
802 
809  protected function parseSampleOption($values)
810  {
811  $option = [];
812  foreach ($values as $keyValue) {
813  $keyValue = trim($keyValue);
814  $pos = strpos($keyValue, self::PAIR_VALUE_SEPARATOR);
815  if ($pos !== false) {
816  $key = substr($keyValue, 0, $pos);
817  $value = substr($keyValue, $pos + 1);
818  if ($key == self::URL_OPTION_VALUE || $key == self::FILE_OPTION_VALUE) {
819  $option['sample_type'] = $key;
820  }
821  if (isset($this->optionSampleMapping[$key])) {
822  $key = $this->optionSampleMapping[$key];
823  }
824  $option[$key] = $value;
825  }
826  }
827  return $option;
828  }
829 
839  protected function uploadDownloadableFiles($fileName, $type = 'links', $renameFileOff = false)
840  {
841  try {
842  $res = $this->uploaderHelper->getUploader($type, $this->parameters)->move($fileName, $renameFileOff);
843  return $res['file'];
844  } catch (\Exception $e) {
845  $this->_entityModel->addRowError(self::ERROR_MOVE_FILE, $this->rowNum);
846  return '';
847  }
848  }
849 
855  protected function clear()
856  {
857  $this->cachedOptions = [
858  'link' => [],
859  'sample' => []
860  ];
861  $this->productIds = [];
862  return $this;
863  }
864 }
uploadDownloadableFiles($fileName, $type='links', $renameFileOff=false)
linksAdditionalAttributes(array $rowData, $attribute, $defaultValue)
$values
Definition: options.phtml:88
$resource
Definition: bulk.php:12
$type
Definition: item.phtml:13
$fileName
Definition: translate.phtml:15
$value
Definition: gender.phtml:16
$pos
Definition: list.phtml:42
$productData
prepareAttributesWithDefaultValueForSave(array $rowData, $withDefaultValue=true)
$params[\Magento\Store\Model\StoreManager::PARAM_RUN_CODE]
Definition: website.php:18
__construct(\Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\CollectionFactory $attrSetColFac, \Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory $prodAttrColFac, \Magento\Framework\App\ResourceConnection $resource, array $params, \Magento\DownloadableImportExport\Helper\Uploader $uploaderHelper, \Magento\DownloadableImportExport\Helper\Data $downloadableHelper, MetadataPool $metadataPool=null)