PageRenderTime 42ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/backend/models/ImportModel.php

https://gitlab.com/gideonmarked/wellmarketing
PHP | 211 lines | 129 code | 19 blank | 63 comment | 3 complexity | 4e55d6abedbdcae9d7a03e10ce7a2993 MD5 | raw file
  1. <?php namespace Backend\Models;
  2. use Model;
  3. use League\Csv\Reader as CsvReader;
  4. /**
  5. * Model used for importing data
  6. *
  7. * @package october\backend
  8. * @author Alexey Bobkov, Samuel Georges
  9. */
  10. abstract class ImportModel extends Model
  11. {
  12. use \October\Rain\Database\Traits\Validation;
  13. /**
  14. * The attributes that aren't mass assignable.
  15. * @var array
  16. */
  17. protected $guarded = [];
  18. /**
  19. * Relations
  20. */
  21. public $attachOne = [
  22. 'import_file' => ['System\Models\File']
  23. ];
  24. /**
  25. * @var array Import statistics store.
  26. */
  27. protected $resultStats = [
  28. 'updated' => 0,
  29. 'created' => 0,
  30. 'errors' => [],
  31. 'warnings' => [],
  32. 'skipped' => []
  33. ];
  34. /**
  35. * Called when data is being imported.
  36. * The $results array should be in the format of:
  37. *
  38. * [
  39. * 'db_name1' => 'Some value',
  40. * 'db_name2' => 'Another value'
  41. * ],
  42. * [...]
  43. *
  44. */
  45. abstract public function importData($results, $sessionKey = null);
  46. /**
  47. * Import data based on column names matching header indexes in the CSV.
  48. * The $matches array should be in the format of:
  49. *
  50. * [
  51. * 0 => [db_name1, db_name2],
  52. * 1 => [db_name3],
  53. * ...
  54. * ]
  55. *
  56. * The key (0, 1) is the column index in the CSV and the value
  57. * is another array of target database column names.
  58. */
  59. public function import($matches, $options = [])
  60. {
  61. $sessionKey = array_get($options, 'sessionKey');
  62. $path = $this->getImportFilePath($sessionKey);
  63. $data = $this->processImportData($path, $matches, $options);
  64. return $this->importData($data, $sessionKey);
  65. }
  66. /**
  67. * Converts column index to database column map to an array containing
  68. * database column names and values pulled from the CSV file. Eg:
  69. *
  70. * [0 => [first_name], 1 => [last_name]]
  71. *
  72. * Will return:
  73. *
  74. * [first_name => Joe, last_name => Blogs],
  75. * [first_name => Harry, last_name => Potter],
  76. * [...]
  77. *
  78. * @return array
  79. */
  80. protected function processImportData($filePath, $matches, $options)
  81. {
  82. extract(array_merge([
  83. 'firstRowTitles' => true
  84. ], $options));
  85. $reader = CsvReader::createFromPath($filePath, 'r');
  86. // Filter out empty rows
  87. $reader->addFilter(function(array $row) {
  88. return count($row) > 1 || reset($row) !== null;
  89. });
  90. if ($firstRowTitles) {
  91. $reader->setOffset(1);
  92. }
  93. $result = [];
  94. $contents = $reader->fetchAll();
  95. foreach ($contents as $row) {
  96. $result[] = $this->processImportRow($row, $matches);
  97. }
  98. return $result;
  99. }
  100. /**
  101. * Converts a single row of CSV data to the column map.
  102. * @return array
  103. */
  104. protected function processImportRow($rowData, $matches)
  105. {
  106. $newRow = [];
  107. foreach ($matches as $columnIndex => $dbNames) {
  108. $value = array_get($rowData, $columnIndex);
  109. foreach ((array) $dbNames as $dbName) {
  110. $newRow[$dbName] = $value;
  111. }
  112. }
  113. return $newRow;
  114. }
  115. /**
  116. * Explodes a string using pipes (|) to a single dimension array
  117. * @return array
  118. */
  119. protected function decodeArrayValue($value, $delimeter = '|')
  120. {
  121. if (strpos($value, $delimeter) === false) return [$value];
  122. $data = preg_split('~(?<!\\\)' . preg_quote($delimeter, '~') . '~', $value);
  123. $newData = [];
  124. foreach ($data as $_value) {
  125. $newData[] = str_replace('\\'.$delimeter, $delimeter, $_value);
  126. }
  127. return $newData;
  128. }
  129. /**
  130. * Returns an attached imported file local path, if available.
  131. * @return string
  132. */
  133. public function getImportFilePath($sessionKey = null)
  134. {
  135. $file = $this
  136. ->import_file()
  137. ->withDeferred($sessionKey)
  138. ->first();
  139. if (!$file) {
  140. return null;
  141. }
  142. return $file->getLocalPath();
  143. }
  144. //
  145. // Result logging
  146. //
  147. public function getResultStats()
  148. {
  149. $this->resultStats['errorCount'] = count($this->resultStats['errors']);
  150. $this->resultStats['warningCount'] = count($this->resultStats['warnings']);
  151. $this->resultStats['skippedCount'] = count($this->resultStats['skipped']);
  152. $this->resultStats['hasMessages'] = (
  153. $this->resultStats['errorCount'] > 0 ||
  154. $this->resultStats['warningCount'] > 0 ||
  155. $this->resultStats['skippedCount'] > 0
  156. );
  157. return (object) $this->resultStats;
  158. }
  159. protected function logUpdated()
  160. {
  161. $this->resultStats['updated']++;
  162. }
  163. protected function logCreated()
  164. {
  165. $this->resultStats['created']++;
  166. }
  167. protected function logError($rowIndex, $message)
  168. {
  169. $this->resultStats['errors'][$rowIndex] = $message;
  170. }
  171. protected function logWarning($rowIndex, $message)
  172. {
  173. $this->resultStats['warnings'][$rowIndex] = $message;
  174. }
  175. protected function logSkipped($rowIndex, $message)
  176. {
  177. $this->resultStats['skipped'][$rowIndex] = $message;
  178. }
  179. }