PageRenderTime 46ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/Model/Behavior/CsvImportBehavior.php

http://github.com/CakeDC/utils
PHP | 221 lines | 113 code | 21 blank | 87 comment | 22 complexity | d056cfc17d93e1a1bccada39ee35c111 MD5 | raw file
Possible License(s): MIT
  1. <?php
  2. /**
  3. * Copyright 2009 - 2013, Cake Development Corporation (http://cakedc.com)
  4. *
  5. * Licensed under The MIT License
  6. * Redistributions of files must retain the above copyright notice.
  7. *
  8. * @copyright Copyright 2009 - 2013, Cake Development Corporation (http://cakedc.com)
  9. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  10. */
  11. /**
  12. * Utils Plugin
  13. *
  14. * Utils Csv Import Behavior
  15. *
  16. * @package utils
  17. * @subpackage utils.models.behaviors
  18. */
  19. class CsvImportBehavior extends ModelBehavior {
  20. /**
  21. * Importable behavior settings
  22. *
  23. * @var array
  24. */
  25. public $settings = array();
  26. /**
  27. * List of errors generated by the import action
  28. *
  29. * @var array
  30. */
  31. public $errors = array();
  32. /**
  33. * List of objects instances or callables to notify from events on this class
  34. *
  35. * @var array
  36. */
  37. protected $_subscribers = array();
  38. /**
  39. * Initializes this behavior for the model $Model
  40. *
  41. * @param Model $Model
  42. * @param array $settings
  43. * @return void
  44. */
  45. public function setup(Model $Model, $settings = array()) {
  46. if (!isset($this->settings[$Model->alias])) {
  47. $this->settings[$Model->alias] = array(
  48. 'delimiter' => ';',
  49. 'enclosure' => '"',
  50. 'hasHeader' => true
  51. );
  52. }
  53. $this->settings[$Model->alias] = array_merge($this->settings[$Model->alias], $settings);
  54. }
  55. /**
  56. * Returns a line form the CSV file and advances the pointer to the next one
  57. *
  58. * @param Model $Model
  59. * @param SplFileObject $handle CSV file handler
  60. * @return array list of attributes fetched from the CSV file
  61. */
  62. protected function _getCSVLine(Model &$Model, SplFileObject $handle) {
  63. if ($handle->eof()) {
  64. return false;
  65. }
  66. return $handle->fgetcsv(
  67. $this->settings[$Model->alias]['delimiter'],
  68. $this->settings[$Model->alias]['enclosure']
  69. );
  70. }
  71. /**
  72. * Returns a list of keys representing the columns of the CSV file
  73. *
  74. * @param Model $Model
  75. * @param SplFileObject $handle CSV file handler
  76. * @return array list of attributes fetched from the CSV file
  77. */
  78. protected function _getHeader(Model &$Model, SplFileObject $handle) {
  79. if ($this->settings[$Model->alias]['hasHeader'] === true) {
  80. $header = $this->_getCSVLine($Model, $handle);
  81. } else {
  82. $header = array_keys($Model->schema());
  83. }
  84. return $header;
  85. }
  86. /**
  87. * Returns a list of keys representing the columns of the CSV file
  88. *
  89. * @param Model $Model
  90. * @param string $file path to the CSV file
  91. * @param array $fixed data to be merged with every row
  92. * @param boolean $returnSaved true to return
  93. * @throws RuntimeException if $file does not exists
  94. * @return mixed boolean indicating the success of the operation or list of saved records
  95. */
  96. public function importCSV(Model &$Model, $file, $fixed = array(), $returnSaved = false) {
  97. $handle = new SplFileObject($file, 'rb');
  98. $header = $this->_getHeader($Model, $handle);
  99. $db = $Model->getDataSource();
  100. $db->begin($Model);
  101. $saved = array();
  102. $i = 0;
  103. while (($row = $this->_getCSVLine($Model, $handle)) !== false) {
  104. $data = array();
  105. foreach ($header as $k => $col) {
  106. // get the data field from Model.field
  107. if (strpos($col, '.') !== false) {
  108. $keys = explode('.', $col);
  109. if (isset($keys[2])) {
  110. $data[$keys[0]][$keys[1]][$keys[2]]= (isset($row[$k])) ? $row[$k] : '';
  111. } else {
  112. $data[$keys[0]][$keys[1]]= (isset($row[$k])) ? $row[$k] : '';
  113. }
  114. } else {
  115. $data[$Model->alias][$col]= (isset($row[$k])) ? $row[$k] : '';
  116. }
  117. }
  118. $data = Set::merge($data, $fixed);
  119. $Model->create();
  120. $Model->id = isset($data[$Model->alias][$Model->primaryKey]) ? $data[$Model->alias][$Model->primaryKey] : false;
  121. //beforeImport callback
  122. if (method_exists($Model, 'beforeImport')) {
  123. $data = $Model->beforeImport($data);
  124. }
  125. $error = false;
  126. $Model->set($data);
  127. if (!$Model->validates()) {
  128. $this->errors[$Model->alias][$i]['validation'] = $Model->validationErrors;
  129. $error = true;
  130. $this->_notify($Model, 'onImportError', $this->errors[$Model->alias][$i]);
  131. }
  132. // save the row
  133. if (!$error && !$Model->saveAll($data, array('validate' => false,'atomic' => false))) {
  134. $this->errors[$Model->alias][$i]['save'] = sprintf(__d('utils', '%s for Row %d failed to save.'), $Model->alias, $i);
  135. $error = true;
  136. $this->_notify($Model, 'onImportError', $this->errors[$Model->alias][$i]);
  137. }
  138. if (!$error) {
  139. $this->_notify($Model, 'onImportRow', $data);
  140. if ($returnSaved) {
  141. $saved[] = $i;
  142. }
  143. }
  144. $i++;
  145. }
  146. $success = empty($this->errors);
  147. if (!$returnSaved && !$success) {
  148. $db->rollback($Model);
  149. return false;
  150. }
  151. $db->commit($Model);
  152. if ($returnSaved) {
  153. return $saved;
  154. }
  155. return true;
  156. }
  157. /**
  158. * Returns the errors generated by last import
  159. *
  160. * @param Model $Model
  161. * @return array
  162. */
  163. public function getImportErrors(Model &$Model) {
  164. if (empty($this->errors[$Model->alias])) {
  165. return array();
  166. }
  167. return $this->errors[$Model->alias];
  168. }
  169. /**
  170. * Attachs a new listener for the events generated by this class
  171. *
  172. * @param Model $Model
  173. * @param mixed listener instances of an object or valid php callback
  174. * @return void
  175. */
  176. public function attachImportListener(Model $Model, $listener) {
  177. $this->_subscribers[$Model->alias][] = $listener;
  178. }
  179. /**
  180. * Notifies the listeners of events generated by this class
  181. *
  182. * @param Model $Model
  183. * @param string $action the name of the event. It will be used as method name for object listeners
  184. * @param mixed $data additional information to pass to the listener callback
  185. * @return void
  186. */
  187. protected function _notify(Model $Model, $action, $data = null) {
  188. if (empty($this->_subscribers[$Model->alias])) {
  189. return;
  190. }
  191. foreach ($this->_subscribers[$Model->alias] as $object) {
  192. if (method_exists($object, $action)) {
  193. $object->{$action}($data);
  194. }
  195. if (is_callable($object)) {
  196. call_user_func($object, $action, $data);
  197. }
  198. }
  199. }
  200. }