Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
CsvImportHandler.php
Go to the documentation of this file.
1 <?php
7 
15 {
21  protected $_publicStores;
22 
30  protected $_regionCollection;
31 
37  protected $_countryFactory;
38 
44  protected $_taxRateFactory;
45 
51  protected $csvProcessor;
52 
60  public function __construct(
61  \Magento\Store\Model\ResourceModel\Store\Collection $storeCollection,
62  \Magento\Directory\Model\ResourceModel\Region\Collection $regionCollection,
63  \Magento\Directory\Model\CountryFactory $countryFactory,
64  \Magento\Tax\Model\Calculation\RateFactory $taxRateFactory,
65  \Magento\Framework\File\Csv $csvProcessor
66  ) {
67  // prevent admin store from loading
68  $this->_publicStores = $storeCollection->setLoadDefault(false);
69  $this->_regionCollection = $regionCollection;
70  $this->_countryFactory = $countryFactory;
71  $this->_taxRateFactory = $taxRateFactory;
72  $this->csvProcessor = $csvProcessor;
73  }
74 
80  public function getRequiredCsvFields()
81  {
82  // indexes are specified for clarity, they are used during import
83  return [
84  0 => __('Code'),
85  1 => __('Country'),
86  2 => __('State'),
87  3 => __('Zip/Post Code'),
88  4 => __('Rate'),
89  5 => __('Zip/Post is Range'),
90  6 => __('Range From'),
91  7 => __('Range To')
92  ];
93  }
94 
102  public function importFromCsvFile($file)
103  {
104  if (!isset($file['tmp_name'])) {
105  throw new \Magento\Framework\Exception\LocalizedException(__('Invalid file upload attempt.'));
106  }
107  $ratesRawData = $this->csvProcessor->getData($file['tmp_name']);
108  // first row of file represents headers
109  $fileFields = $ratesRawData[0];
110  $validFields = $this->_filterFileFields($fileFields);
111  $invalidFields = array_diff_key($fileFields, $validFields);
112  $ratesData = $this->_filterRateData($ratesRawData, $invalidFields, $validFields);
113  // store cache array is used to quickly retrieve store ID when handling locale-specific tax rate titles
114  $storesCache = $this->_composeStoreCache($validFields);
115  $regionsCache = [];
116  foreach ($ratesData as $rowIndex => $dataRow) {
117  // skip headers
118  if ($rowIndex == 0) {
119  continue;
120  }
121  $regionsCache = $this->_importRate($dataRow, $regionsCache, $storesCache);
122  }
123  }
124 
131  protected function _filterFileFields(array $fileFields)
132  {
133  $filteredFields = $this->getRequiredCsvFields();
134  $requiredFieldsNum = count($this->getRequiredCsvFields());
135  $fileFieldsNum = count($fileFields);
136 
137  // process title-related fields that are located right after required fields with store code as field name)
138  for ($index = $requiredFieldsNum; $index < $fileFieldsNum; $index++) {
139  $titleFieldName = $fileFields[$index];
140  if ($this->_isStoreCodeValid($titleFieldName)) {
141  // if store is still valid, append this field to valid file fields
142  $filteredFields[$index] = $titleFieldName;
143  }
144  }
145 
146  return $filteredFields;
147  }
148 
159  protected function _filterRateData(array $rateRawData, array $invalidFields, array $validFields)
160  {
161  $validFieldsNum = count($validFields);
162  foreach ($rateRawData as $rowIndex => $dataRow) {
163  // skip empty rows
164  if (count($dataRow) <= 1) {
165  unset($rateRawData[$rowIndex]);
166  continue;
167  }
168  // unset invalid fields from data row
169  foreach ($dataRow as $fieldIndex => $fieldValue) {
170  if (isset($invalidFields[$fieldIndex])) {
171  unset($rateRawData[$rowIndex][$fieldIndex]);
172  }
173  }
174  // check if number of fields in row match with number of valid fields
175  if (count($rateRawData[$rowIndex]) != $validFieldsNum) {
176  throw new \Magento\Framework\Exception\LocalizedException(__('Invalid file format.'));
177  }
178  }
179  return $rateRawData;
180  }
181 
190  protected function _composeStoreCache($validFields)
191  {
192  $storesCache = [];
193  $requiredFieldsNum = count($this->getRequiredCsvFields());
194  $validFieldsNum = count($validFields);
195  // title related fields located right after required fields
196  for ($index = $requiredFieldsNum; $index < $validFieldsNum; $index++) {
197  foreach ($this->_publicStores as $store) {
198  $storeCode = $validFields[$index];
199  if ($storeCode === $store->getCode()) {
200  $storesCache[$index] = $store->getId();
201  }
202  }
203  }
204  return $storesCache;
205  }
206 
213  protected function _isStoreCodeValid($storeCode)
214  {
215  $isStoreCodeValid = false;
216  foreach ($this->_publicStores as $store) {
217  if ($storeCode === $store->getCode()) {
218  $isStoreCodeValid = true;
219  break;
220  }
221  }
222  return $isStoreCodeValid;
223  }
224 
234  protected function _importRate(array $rateData, array $regionsCache, array $storesCache)
235  {
236  // data with index 1 must represent country code
237  $countryCode = $rateData[1];
238  $country = $this->_countryFactory->create()->loadByCode($countryCode, 'iso2_code');
239  if (!$country->getId()) {
240  throw new \Magento\Framework\Exception\LocalizedException(__('Country code is invalid: %1', $countryCode));
241  }
242  $regionsCache = $this->_addCountryRegionsToCache($countryCode, $regionsCache);
243 
244  // data with index 2 must represent region code
245  $regionCode = $rateData[2];
246  if (!empty($regionsCache[$countryCode][$regionCode])) {
247  $regionId = $regionsCache[$countryCode][$regionCode] == '*' ? 0 : $regionsCache[$countryCode][$regionCode];
248  // data with index 3 must represent postcode
249  $postCode = empty($rateData[3]) ? null : $rateData[3];
250  $modelData = [
251  'code' => $rateData[0],
252  'tax_country_id' => $rateData[1],
253  'tax_region_id' => $regionId,
254  'tax_postcode' => $postCode,
255  'rate' => $rateData[4],
256  'zip_is_range' => $rateData[5],
257  'zip_from' => $rateData[6],
258  'zip_to' => $rateData[7],
259  ];
260 
261  // try to load existing rate
263  $rateModel = $this->_taxRateFactory->create()->loadByCode($modelData['code']);
264  $rateModel->addData($modelData);
265 
266  // compose titles list
267  $rateTitles = [];
268  foreach ($storesCache as $fileFieldIndex => $storeId) {
269  $rateTitles[$storeId] = $rateData[$fileFieldIndex];
270  }
271 
272  $rateModel->setTitle($rateTitles);
273  $rateModel->save();
274  }
275 
276  return $regionsCache;
277  }
278 
286  protected function _addCountryRegionsToCache($countryCode, array $regionsCache)
287  {
288  if (!isset($regionsCache[$countryCode])) {
289  $regionsCache[$countryCode] = [];
290  // add 'All Regions' to the list
291  $regionsCache[$countryCode]['*'] = '*';
292  $regionCollection = clone $this->_regionCollection;
293  $regionCollection->addCountryFilter($countryCode);
294  if ($regionCollection->getSize()) {
295  foreach ($regionCollection as $region) {
296  $regionsCache[$countryCode][$region->getCode()] = $region->getRegionId();
297  }
298  }
299  }
300  return $regionsCache;
301  }
302 }
_addCountryRegionsToCache($countryCode, array $regionsCache)
__()
Definition: __.php:13
$storeCode
Definition: indexer.php:15
__construct(\Magento\Store\Model\ResourceModel\Store\Collection $storeCollection, \Magento\Directory\Model\ResourceModel\Region\Collection $regionCollection, \Magento\Directory\Model\CountryFactory $countryFactory, \Magento\Tax\Model\Calculation\RateFactory $taxRateFactory, \Magento\Framework\File\Csv $csvProcessor)
_filterRateData(array $rateRawData, array $invalidFields, array $validFields)
$index
Definition: list.phtml:44