1: <?php
2:
3: App::uses('Model', 'Model');
4: App::uses('ArrayUtil', 'Base.Lib');
5: App::uses('ArrayUtil', 'Base.Lib');
6: App::uses('AtomicOperation', 'Operations.Lib');
7: App::uses('ModelOperations', 'Operations.Lib');
8: App::uses('OperationSet', 'Operations.Lib');
9: App::uses('TransactionModel', 'Operations.Model');
10:
11: abstract class CustomDataModel extends TransactionModel {
12:
13: 14: 15: 16: 17:
18: private static $initializedModels = array();
19:
20: 21: 22: 23:
24: private static $initializingModels = array();
25: public $alwaysInitialize = false;
26:
27: public function assertInitializedData() {
28: if (!$this->_isInitialized()) {
29: $this->_setInitializing();
30: $this->_initData();
31: $this->_setInitialized();
32: }
33: }
34:
35: public function clearCache() {
36: $this->getDataSource()->truncate($this->table);
37: self::_setUnitialized($this);
38: }
39:
40: private function _setInitializing() {
41: if (array_search($this->name, self::$initializingModels)) {
42: throw new Exception("Initializing loop: " . $this->name . "\n" . print_r(self::$initializingModels, true));
43: } else {
44: array_push(self::$initializingModels, $this->name);
45: }
46: }
47:
48: private function _setInitialized() {
49: if (!file_exists($this->_initializedDirectory())) {
50: mkdir($this->_initializedDirectory());
51: }
52: touch($this->_initializedFile());
53: self::$initializedModels[$this->name] = true;
54:
55: $poped = array_pop(self::$initializingModels);
56: if ($poped != $this->name) {
57: throw new Exception("Poped: $poped / Model name: {$this->name}");
58: }
59: }
60:
61: private function _setUnitialized() {
62: if (file_exists($this->_initializedFile())) {
63: unlink($this->_initializedFile());
64: }
65: unset(self::$initializedModels[$this->name]);
66: }
67:
68: private function _initializedDirectory() {
69: return TMP . DS . 'custom_data_models';
70: }
71:
72: private function _initializedFile() {
73: return $this->_initializedDirectory() . DS . $this->name;
74: }
75:
76: private function _isInitialized() {
77: if ($this->alwaysInitialize) {
78: return !empty(self::$initializedModels[$this->name]);
79: } else {
80: return file_exists($this->_initializedFile());
81: }
82: }
83:
84: private function _initData() {
85: $this->getDataSource()->truncate($this->table);
86: $this->beforeInitData();
87: $internalModel = new Model(false, $this->table, $this->useDbConfig);
88: $internalModel->begin();
89: foreach ($this->customData() as $row) {
90: $internalModel->create();
91: $aliasedRow = array(
92: $internalModel->alias => $row
93: );
94:
95: if (!$internalModel->save($aliasedRow)) {
96: $validationErrors = $internalModel->validationErrors;
97: throw new Exception("Fail to save on data initializing: " . print_r(compact('aliasedRow', 'validationErrors'), true));
98: }
99: }
100: $internalModel->commit();
101: $this->afterInitData();
102: }
103:
104: public function beforeInitData() {
105:
106: }
107:
108: public function afterInitData() {
109:
110: }
111:
112: 113: 114:
115: protected abstract function customData();
116:
117: 118: 119: 120: 121: 122:
123: protected function customSave($oldData, $newData) {
124: throw new Exception("Must be overrided");
125: }
126:
127: 128: 129: 130:
131: protected function customDelete($row) {
132: throw new Exception("Must be overrided");
133: }
134:
135: public function find($type = 'first', $query = array()) {
136: $this->assertInitializedData();
137: return parent::find($type, $query);
138: }
139:
140: public function save($data = null, $validate = true, $fieldList = array()) {
141: $this->assertInitializedData();
142: if ($data) {
143: $this->set($data);
144: }
145:
146: if (!$this->validates()) {
147: return false;
148: }
149:
150: if (empty($data[$this->alias][$this->primaryKey])) {
151: $oldData = false;
152: } else {
153: $oldData = $this->find(
154: 'first', array(
155: 'conditions' => array(
156: $this->escapeField($this->primaryKey) => ($data[$this->alias][$this->primaryKey]
157: )
158: )
159: )
160: );
161: $oldData = empty($oldData[$this->alias]) ? false : $oldData[$this->alias];
162: }
163:
164: $newData = $this->data[$this->alias];
165:
166: try {
167: $this->customSave($oldData, $newData);
168: } catch (Exception $ex) {
169: $this->rollback();
170: throw $ex;
171: }
172:
173: return parent::save($this->data, $validate, $fieldList);
174: }
175:
176: public function cacheSave($data = null, $validate = true, $fieldList = array()) {
177: return parent::save($data, $validate, $fieldList);
178: }
179:
180: public function delete($id = null, $cascade = true) {
181: $this->assertInitializedData();
182: if ($id) {
183: $this->id = $id;
184: }
185: $row = $this->find('first', array(
186: 'conditions' => array(
187: "{$this->alias}.{$this->primaryKey}" => $this->id
188: )
189: ));
190:
191: if (empty($row)) {
192: throw new Exception("Row not found with id \"{$this->id}\"");
193: }
194:
195: try {
196: $this->customDelete($row[$this->alias]);
197: } catch (Exception $ex) {
198: $this->rollback();
199: throw $ex;
200: }
201:
202: return parent::delete($id, $cascade);
203: }
204:
205: public function cacheDelete($id = null, $cascade = true) {
206: return parent::delete($id, $cascade);
207: }
208:
209: 210: 211: 212:
213: private function _createInternalModel() {
214: $internalModel = new BaseModel(false, $this->table, $this->useDbConfig);
215: $internalModel->name = $this->name;
216: $internalModel->alias = $this->alias;
217: return $internalModel;
218: }
219:
220: public static function failOperation() {
221: return new AnonymousFunctionOperation(function() {
222: return false;
223: });
224: }
225:
226: }
227:
228: ?>
229: