Mercurial > hg > Members > shoshi > webvirt
comparison cake/libs/model/cake_schema.php @ 0:261e66bd5a0c
hg init
author | Shoshi TAMAKI <shoshi@cr.ie.u-ryukyu.ac.jp> |
---|---|
date | Sun, 24 Jul 2011 21:08:31 +0900 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:261e66bd5a0c |
---|---|
1 <?php | |
2 /** | |
3 * Schema database management for CakePHP. | |
4 * | |
5 * PHP versions 4 and 5 | |
6 * | |
7 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org) | |
8 * Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org) | |
9 * | |
10 * Licensed under The MIT License | |
11 * Redistributions of files must retain the above copyright notice. | |
12 * | |
13 * @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org) | |
14 * @link http://cakephp.org CakePHP(tm) Project | |
15 * @package cake | |
16 * @subpackage cake.cake.libs.model | |
17 * @since CakePHP(tm) v 1.2.0.5550 | |
18 * @license MIT License (http://www.opensource.org/licenses/mit-license.php) | |
19 */ | |
20 App::import('Core', array('Model', 'ConnectionManager')); | |
21 | |
22 /** | |
23 * Base Class for Schema management | |
24 * | |
25 * @package cake | |
26 * @subpackage cake.cake.libs.model | |
27 */ | |
28 class CakeSchema extends Object { | |
29 | |
30 /** | |
31 * Name of the App Schema | |
32 * | |
33 * @var string | |
34 * @access public | |
35 */ | |
36 var $name = null; | |
37 | |
38 /** | |
39 * Path to write location | |
40 * | |
41 * @var string | |
42 * @access public | |
43 */ | |
44 var $path = null; | |
45 | |
46 /** | |
47 * File to write | |
48 * | |
49 * @var string | |
50 * @access public | |
51 */ | |
52 var $file = 'schema.php'; | |
53 | |
54 /** | |
55 * Connection used for read | |
56 * | |
57 * @var string | |
58 * @access public | |
59 */ | |
60 var $connection = 'default'; | |
61 | |
62 /** | |
63 * plugin name. | |
64 * | |
65 * @var string | |
66 */ | |
67 var $plugin = null; | |
68 | |
69 /** | |
70 * Set of tables | |
71 * | |
72 * @var array | |
73 * @access public | |
74 */ | |
75 var $tables = array(); | |
76 | |
77 /** | |
78 * Constructor | |
79 * | |
80 * @param array $options optional load object properties | |
81 */ | |
82 function __construct($options = array()) { | |
83 parent::__construct(); | |
84 | |
85 if (empty($options['name'])) { | |
86 $this->name = preg_replace('/schema$/i', '', get_class($this)); | |
87 } | |
88 if (!empty($options['plugin'])) { | |
89 $this->plugin = $options['plugin']; | |
90 } | |
91 | |
92 if (strtolower($this->name) === 'cake') { | |
93 $this->name = Inflector::camelize(Inflector::slug(Configure::read('App.dir'))); | |
94 } | |
95 | |
96 if (empty($options['path'])) { | |
97 if (is_dir(CONFIGS . 'schema')) { | |
98 $this->path = CONFIGS . 'schema'; | |
99 } else { | |
100 $this->path = CONFIGS . 'sql'; | |
101 } | |
102 } | |
103 | |
104 $options = array_merge(get_object_vars($this), $options); | |
105 $this->_build($options); | |
106 } | |
107 | |
108 /** | |
109 * Builds schema object properties | |
110 * | |
111 * @param array $data loaded object properties | |
112 * @return void | |
113 * @access protected | |
114 */ | |
115 function _build($data) { | |
116 $file = null; | |
117 foreach ($data as $key => $val) { | |
118 if (!empty($val)) { | |
119 if (!in_array($key, array('plugin', 'name', 'path', 'file', 'connection', 'tables', '_log'))) { | |
120 $this->tables[$key] = $val; | |
121 unset($this->{$key}); | |
122 } elseif ($key !== 'tables') { | |
123 if ($key === 'name' && $val !== $this->name && !isset($data['file'])) { | |
124 $file = Inflector::underscore($val) . '.php'; | |
125 } | |
126 $this->{$key} = $val; | |
127 } | |
128 } | |
129 } | |
130 if (file_exists($this->path . DS . $file) && is_file($this->path . DS . $file)) { | |
131 $this->file = $file; | |
132 } elseif (!empty($this->plugin)) { | |
133 $this->path = App::pluginPath($this->plugin) . 'config' . DS . 'schema'; | |
134 } | |
135 } | |
136 | |
137 /** | |
138 * Before callback to be implemented in subclasses | |
139 * | |
140 * @param array $events schema object properties | |
141 * @return boolean Should process continue | |
142 * @access public | |
143 */ | |
144 function before($event = array()) { | |
145 return true; | |
146 } | |
147 | |
148 /** | |
149 * After callback to be implemented in subclasses | |
150 * | |
151 * @param array $events schema object properties | |
152 * @access public | |
153 */ | |
154 function after($event = array()) { | |
155 } | |
156 | |
157 /** | |
158 * Reads database and creates schema tables | |
159 * | |
160 * @param array $options schema object properties | |
161 * @return array Set of name and tables | |
162 * @access public | |
163 */ | |
164 function &load($options = array()) { | |
165 if (is_string($options)) { | |
166 $options = array('path' => $options); | |
167 } | |
168 | |
169 $this->_build($options); | |
170 extract(get_object_vars($this)); | |
171 | |
172 $class = $name .'Schema'; | |
173 | |
174 if (!class_exists($class)) { | |
175 if (file_exists($path . DS . $file) && is_file($path . DS . $file)) { | |
176 require_once($path . DS . $file); | |
177 } elseif (file_exists($path . DS . 'schema.php') && is_file($path . DS . 'schema.php')) { | |
178 require_once($path . DS . 'schema.php'); | |
179 } | |
180 } | |
181 | |
182 if (class_exists($class)) { | |
183 $Schema =& new $class($options); | |
184 return $Schema; | |
185 } | |
186 $false = false; | |
187 return $false; | |
188 } | |
189 | |
190 /** | |
191 * Reads database and creates schema tables | |
192 * | |
193 * Options | |
194 * | |
195 * - 'connection' - the db connection to use | |
196 * - 'name' - name of the schema | |
197 * - 'models' - a list of models to use, or false to ignore models | |
198 * | |
199 * @param array $options schema object properties | |
200 * @return array Array indexed by name and tables | |
201 * @access public | |
202 */ | |
203 function read($options = array()) { | |
204 extract(array_merge( | |
205 array( | |
206 'connection' => $this->connection, | |
207 'name' => $this->name, | |
208 'models' => true, | |
209 ), | |
210 $options | |
211 )); | |
212 $db =& ConnectionManager::getDataSource($connection); | |
213 | |
214 App::import('Model', 'AppModel'); | |
215 if (isset($this->plugin)) { | |
216 App::import('Model', Inflector::camelize($this->plugin) . 'AppModel'); | |
217 } | |
218 | |
219 $tables = array(); | |
220 $currentTables = $db->listSources(); | |
221 | |
222 $prefix = null; | |
223 if (isset($db->config['prefix'])) { | |
224 $prefix = $db->config['prefix']; | |
225 } | |
226 | |
227 if (!is_array($models) && $models !== false) { | |
228 if (isset($this->plugin)) { | |
229 $models = App::objects('model', App::pluginPath($this->plugin) . 'models' . DS, false); | |
230 } else { | |
231 $models = App::objects('model'); | |
232 } | |
233 } | |
234 | |
235 if (is_array($models)) { | |
236 foreach ($models as $model) { | |
237 $importModel = $model; | |
238 if (isset($this->plugin)) { | |
239 $importModel = $this->plugin . '.' . $model; | |
240 } | |
241 if (!App::import('Model', $importModel)) { | |
242 continue; | |
243 } | |
244 $vars = get_class_vars($model); | |
245 if (empty($vars['useDbConfig']) || $vars['useDbConfig'] != $connection) { | |
246 continue; | |
247 } | |
248 | |
249 if (PHP5) { | |
250 $Object = ClassRegistry::init(array('class' => $model, 'ds' => $connection)); | |
251 } else { | |
252 $Object =& ClassRegistry::init(array('class' => $model, 'ds' => $connection)); | |
253 } | |
254 | |
255 if (is_object($Object) && $Object->useTable !== false) { | |
256 $fulltable = $table = $db->fullTableName($Object, false); | |
257 if ($prefix && strpos($table, $prefix) !== 0) { | |
258 continue; | |
259 } | |
260 $table = str_replace($prefix, '', $table); | |
261 | |
262 if (in_array($fulltable, $currentTables)) { | |
263 $key = array_search($fulltable, $currentTables); | |
264 if (empty($tables[$table])) { | |
265 $tables[$table] = $this->__columns($Object); | |
266 $tables[$table]['indexes'] = $db->index($Object); | |
267 $tables[$table]['tableParameters'] = $db->readTableParameters($fulltable); | |
268 unset($currentTables[$key]); | |
269 } | |
270 if (!empty($Object->hasAndBelongsToMany)) { | |
271 foreach ($Object->hasAndBelongsToMany as $Assoc => $assocData) { | |
272 if (isset($assocData['with'])) { | |
273 $class = $assocData['with']; | |
274 } | |
275 if (is_object($Object->$class)) { | |
276 $withTable = $db->fullTableName($Object->$class, false); | |
277 if ($prefix && strpos($withTable, $prefix) !== 0) { | |
278 continue; | |
279 } | |
280 if (in_array($withTable, $currentTables)) { | |
281 $key = array_search($withTable, $currentTables); | |
282 $noPrefixWith = str_replace($prefix, '', $withTable); | |
283 | |
284 $tables[$noPrefixWith] = $this->__columns($Object->$class); | |
285 $tables[$noPrefixWith]['indexes'] = $db->index($Object->$class); | |
286 $tables[$noPrefixWith]['tableParameters'] = $db->readTableParameters($withTable); | |
287 unset($currentTables[$key]); | |
288 } | |
289 } | |
290 } | |
291 } | |
292 } | |
293 } | |
294 } | |
295 } | |
296 | |
297 if (!empty($currentTables)) { | |
298 foreach ($currentTables as $table) { | |
299 if ($prefix) { | |
300 if (strpos($table, $prefix) !== 0) { | |
301 continue; | |
302 } | |
303 $table = str_replace($prefix, '', $table); | |
304 } | |
305 $Object = new AppModel(array( | |
306 'name' => Inflector::classify($table), 'table' => $table, 'ds' => $connection | |
307 )); | |
308 | |
309 $systemTables = array( | |
310 'aros', 'acos', 'aros_acos', Configure::read('Session.table'), 'i18n' | |
311 ); | |
312 | |
313 if (in_array($table, $systemTables)) { | |
314 $tables[$Object->table] = $this->__columns($Object); | |
315 $tables[$Object->table]['indexes'] = $db->index($Object); | |
316 $tables[$Object->table]['tableParameters'] = $db->readTableParameters($table); | |
317 } elseif ($models === false) { | |
318 $tables[$table] = $this->__columns($Object); | |
319 $tables[$table]['indexes'] = $db->index($Object); | |
320 $tables[$table]['tableParameters'] = $db->readTableParameters($table); | |
321 } else { | |
322 $tables['missing'][$table] = $this->__columns($Object); | |
323 $tables['missing'][$table]['indexes'] = $db->index($Object); | |
324 $tables['missing'][$table]['tableParameters'] = $db->readTableParameters($table); | |
325 } | |
326 } | |
327 } | |
328 | |
329 ksort($tables); | |
330 return compact('name', 'tables'); | |
331 } | |
332 | |
333 /** | |
334 * Writes schema file from object or options | |
335 * | |
336 * @param mixed $object schema object or options array | |
337 * @param array $options schema object properties to override object | |
338 * @return mixed false or string written to file | |
339 * @access public | |
340 */ | |
341 function write($object, $options = array()) { | |
342 if (is_object($object)) { | |
343 $object = get_object_vars($object); | |
344 $this->_build($object); | |
345 } | |
346 | |
347 if (is_array($object)) { | |
348 $options = $object; | |
349 unset($object); | |
350 } | |
351 | |
352 extract(array_merge( | |
353 get_object_vars($this), $options | |
354 )); | |
355 | |
356 $out = "class {$name}Schema extends CakeSchema {\n"; | |
357 | |
358 $out .= "\tvar \$name = '{$name}';\n\n"; | |
359 | |
360 if ($path !== $this->path) { | |
361 $out .= "\tvar \$path = '{$path}';\n\n"; | |
362 } | |
363 | |
364 if ($file !== $this->file) { | |
365 $out .= "\tvar \$file = '{$file}';\n\n"; | |
366 } | |
367 | |
368 if ($connection !== 'default') { | |
369 $out .= "\tvar \$connection = '{$connection}';\n\n"; | |
370 } | |
371 | |
372 $out .= "\tfunction before(\$event = array()) {\n\t\treturn true;\n\t}\n\n\tfunction after(\$event = array()) {\n\t}\n\n"; | |
373 | |
374 if (empty($tables)) { | |
375 $this->read(); | |
376 } | |
377 | |
378 foreach ($tables as $table => $fields) { | |
379 if (!is_numeric($table) && $table !== 'missing') { | |
380 $out .= $this->generateTable($table, $fields); | |
381 } | |
382 } | |
383 $out .= "}\n"; | |
384 | |
385 $File =& new File($path . DS . $file, true); | |
386 $header = '$Id'; | |
387 $content = "<?php \n/* {$name} schema generated on: " . date('Y-m-d H:i:s') . " : ". time() . "*/\n{$out}?>"; | |
388 $content = $File->prepare($content); | |
389 if ($File->write($content)) { | |
390 return $content; | |
391 } | |
392 return false; | |
393 } | |
394 | |
395 /** | |
396 * Generate the code for a table. Takes a table name and $fields array | |
397 * Returns a completed variable declaration to be used in schema classes | |
398 * | |
399 * @param string $table Table name you want returned. | |
400 * @param array $fields Array of field information to generate the table with. | |
401 * @return string Variable declaration for a schema class | |
402 */ | |
403 function generateTable($table, $fields) { | |
404 $out = "\tvar \${$table} = array(\n"; | |
405 if (is_array($fields)) { | |
406 $cols = array(); | |
407 foreach ($fields as $field => $value) { | |
408 if ($field != 'indexes' && $field != 'tableParameters') { | |
409 if (is_string($value)) { | |
410 $type = $value; | |
411 $value = array('type'=> $type); | |
412 } | |
413 $col = "\t\t'{$field}' => array('type' => '" . $value['type'] . "', "; | |
414 unset($value['type']); | |
415 $col .= join(', ', $this->__values($value)); | |
416 } elseif ($field == 'indexes') { | |
417 $col = "\t\t'indexes' => array("; | |
418 $props = array(); | |
419 foreach ((array)$value as $key => $index) { | |
420 $props[] = "'{$key}' => array(" . join(', ', $this->__values($index)) . ")"; | |
421 } | |
422 $col .= join(', ', $props); | |
423 } elseif ($field == 'tableParameters') { | |
424 //@todo add charset, collate and engine here | |
425 $col = "\t\t'tableParameters' => array("; | |
426 $props = array(); | |
427 foreach ((array)$value as $key => $param) { | |
428 $props[] = "'{$key}' => '$param'"; | |
429 } | |
430 $col .= join(', ', $props); | |
431 } | |
432 $col .= ")"; | |
433 $cols[] = $col; | |
434 } | |
435 $out .= join(",\n", $cols); | |
436 } | |
437 $out .= "\n\t);\n"; | |
438 return $out; | |
439 } | |
440 | |
441 /** | |
442 * Compares two sets of schemas | |
443 * | |
444 * @param mixed $old Schema object or array | |
445 * @param mixed $new Schema object or array | |
446 * @return array Tables (that are added, dropped, or changed) | |
447 * @access public | |
448 */ | |
449 function compare($old, $new = null) { | |
450 if (empty($new)) { | |
451 $new =& $this; | |
452 } | |
453 if (is_array($new)) { | |
454 if (isset($new['tables'])) { | |
455 $new = $new['tables']; | |
456 } | |
457 } else { | |
458 $new = $new->tables; | |
459 } | |
460 | |
461 if (is_array($old)) { | |
462 if (isset($old['tables'])) { | |
463 $old = $old['tables']; | |
464 } | |
465 } else { | |
466 $old = $old->tables; | |
467 } | |
468 $tables = array(); | |
469 foreach ($new as $table => $fields) { | |
470 if ($table == 'missing') { | |
471 continue; | |
472 } | |
473 if (!array_key_exists($table, $old)) { | |
474 $tables[$table]['add'] = $fields; | |
475 } else { | |
476 $diff = $this->_arrayDiffAssoc($fields, $old[$table]); | |
477 if (!empty($diff)) { | |
478 $tables[$table]['add'] = $diff; | |
479 } | |
480 $diff = $this->_arrayDiffAssoc($old[$table], $fields); | |
481 if (!empty($diff)) { | |
482 $tables[$table]['drop'] = $diff; | |
483 } | |
484 } | |
485 | |
486 foreach ($fields as $field => $value) { | |
487 if (isset($old[$table][$field])) { | |
488 $diff = $this->_arrayDiffAssoc($value, $old[$table][$field]); | |
489 if (!empty($diff) && $field !== 'indexes' && $field !== 'tableParameters') { | |
490 $tables[$table]['change'][$field] = array_merge($old[$table][$field], $diff); | |
491 } | |
492 } | |
493 | |
494 if (isset($add[$table][$field])) { | |
495 $wrapper = array_keys($fields); | |
496 if ($column = array_search($field, $wrapper)) { | |
497 if (isset($wrapper[$column - 1])) { | |
498 $tables[$table]['add'][$field]['after'] = $wrapper[$column - 1]; | |
499 } | |
500 } | |
501 } | |
502 } | |
503 | |
504 if (isset($old[$table]['indexes']) && isset($new[$table]['indexes'])) { | |
505 $diff = $this->_compareIndexes($new[$table]['indexes'], $old[$table]['indexes']); | |
506 if ($diff) { | |
507 if (!isset($tables[$table])) { | |
508 $tables[$table] = array(); | |
509 } | |
510 if (isset($diff['drop'])) { | |
511 $tables[$table]['drop']['indexes'] = $diff['drop']; | |
512 } | |
513 if ($diff && isset($diff['add'])) { | |
514 $tables[$table]['add']['indexes'] = $diff['add']; | |
515 } | |
516 } | |
517 } | |
518 if (isset($old[$table]['tableParameters']) && isset($new[$table]['tableParameters'])) { | |
519 $diff = $this->_compareTableParameters($new[$table]['tableParameters'], $old[$table]['tableParameters']); | |
520 if ($diff) { | |
521 $tables[$table]['change']['tableParameters'] = $diff; | |
522 } | |
523 } | |
524 } | |
525 return $tables; | |
526 } | |
527 | |
528 /** | |
529 * Extended array_diff_assoc noticing change from/to NULL values | |
530 * | |
531 * It behaves almost the same way as array_diff_assoc except for NULL values: if | |
532 * one of the values is not NULL - change is detected. It is useful in situation | |
533 * where one value is strval('') ant other is strval(null) - in string comparing | |
534 * methods this results as EQUAL, while it is not. | |
535 * | |
536 * @param array $array1 Base array | |
537 * @param array $array2 Corresponding array checked for equality | |
538 * @return array Difference as array with array(keys => values) from input array | |
539 * where match was not found. | |
540 * @access protected | |
541 */ | |
542 function _arrayDiffAssoc($array1, $array2) { | |
543 $difference = array(); | |
544 foreach ($array1 as $key => $value) { | |
545 if (!array_key_exists($key, $array2)) { | |
546 $difference[$key] = $value; | |
547 continue; | |
548 } | |
549 $correspondingValue = $array2[$key]; | |
550 if (is_null($value) !== is_null($correspondingValue)) { | |
551 $difference[$key] = $value; | |
552 continue; | |
553 } | |
554 if (is_bool($value) !== is_bool($correspondingValue)) { | |
555 $difference[$key] = $value; | |
556 continue; | |
557 } | |
558 $compare = strval($value); | |
559 $correspondingValue = strval($correspondingValue); | |
560 if ($compare === $correspondingValue) { | |
561 continue; | |
562 } | |
563 $difference[$key] = $value; | |
564 } | |
565 return $difference; | |
566 } | |
567 | |
568 /** | |
569 * Formats Schema columns from Model Object | |
570 * | |
571 * @param array $values options keys(type, null, default, key, length, extra) | |
572 * @return array Formatted values | |
573 * @access public | |
574 */ | |
575 function __values($values) { | |
576 $vals = array(); | |
577 if (is_array($values)) { | |
578 foreach ($values as $key => $val) { | |
579 if (is_array($val)) { | |
580 $vals[] = "'{$key}' => array('" . implode("', '", $val) . "')"; | |
581 } else if (!is_numeric($key)) { | |
582 $val = var_export($val, true); | |
583 $vals[] = "'{$key}' => {$val}"; | |
584 } | |
585 } | |
586 } | |
587 return $vals; | |
588 } | |
589 | |
590 /** | |
591 * Formats Schema columns from Model Object | |
592 * | |
593 * @param array $Obj model object | |
594 * @return array Formatted columns | |
595 * @access public | |
596 */ | |
597 function __columns(&$Obj) { | |
598 $db =& ConnectionManager::getDataSource($Obj->useDbConfig); | |
599 $fields = $Obj->schema(true); | |
600 $columns = $props = array(); | |
601 foreach ($fields as $name => $value) { | |
602 if ($Obj->primaryKey == $name) { | |
603 $value['key'] = 'primary'; | |
604 } | |
605 if (!isset($db->columns[$value['type']])) { | |
606 trigger_error(sprintf(__('Schema generation error: invalid column type %s does not exist in DBO', true), $value['type']), E_USER_NOTICE); | |
607 continue; | |
608 } else { | |
609 $defaultCol = $db->columns[$value['type']]; | |
610 if (isset($defaultCol['limit']) && $defaultCol['limit'] == $value['length']) { | |
611 unset($value['length']); | |
612 } elseif (isset($defaultCol['length']) && $defaultCol['length'] == $value['length']) { | |
613 unset($value['length']); | |
614 } | |
615 unset($value['limit']); | |
616 } | |
617 | |
618 if (isset($value['default']) && ($value['default'] === '' || $value['default'] === false)) { | |
619 unset($value['default']); | |
620 } | |
621 if (empty($value['length'])) { | |
622 unset($value['length']); | |
623 } | |
624 if (empty($value['key'])) { | |
625 unset($value['key']); | |
626 } | |
627 $columns[$name] = $value; | |
628 } | |
629 | |
630 return $columns; | |
631 } | |
632 | |
633 /** | |
634 * Compare two schema files table Parameters | |
635 * | |
636 * @param array $new New indexes | |
637 * @param array $old Old indexes | |
638 * @return mixed False on failure, or an array of parameters to add & drop. | |
639 */ | |
640 function _compareTableParameters($new, $old) { | |
641 if (!is_array($new) || !is_array($old)) { | |
642 return false; | |
643 } | |
644 $change = $this->_arrayDiffAssoc($new, $old); | |
645 return $change; | |
646 } | |
647 | |
648 /** | |
649 * Compare two schema indexes | |
650 * | |
651 * @param array $new New indexes | |
652 * @param array $old Old indexes | |
653 * @return mixed false on failure or array of indexes to add and drop | |
654 */ | |
655 function _compareIndexes($new, $old) { | |
656 if (!is_array($new) || !is_array($old)) { | |
657 return false; | |
658 } | |
659 | |
660 $add = $drop = array(); | |
661 | |
662 $diff = $this->_arrayDiffAssoc($new, $old); | |
663 if (!empty($diff)) { | |
664 $add = $diff; | |
665 } | |
666 | |
667 $diff = $this->_arrayDiffAssoc($old, $new); | |
668 if (!empty($diff)) { | |
669 $drop = $diff; | |
670 } | |
671 | |
672 foreach ($new as $name => $value) { | |
673 if (isset($old[$name])) { | |
674 $newUnique = isset($value['unique']) ? $value['unique'] : 0; | |
675 $oldUnique = isset($old[$name]['unique']) ? $old[$name]['unique'] : 0; | |
676 $newColumn = $value['column']; | |
677 $oldColumn = $old[$name]['column']; | |
678 | |
679 $diff = false; | |
680 | |
681 if ($newUnique != $oldUnique) { | |
682 $diff = true; | |
683 } elseif (is_array($newColumn) && is_array($oldColumn)) { | |
684 $diff = ($newColumn !== $oldColumn); | |
685 } elseif (is_string($newColumn) && is_string($oldColumn)) { | |
686 $diff = ($newColumn != $oldColumn); | |
687 } else { | |
688 $diff = true; | |
689 } | |
690 if ($diff) { | |
691 $drop[$name] = null; | |
692 $add[$name] = $value; | |
693 } | |
694 } | |
695 } | |
696 return array_filter(compact('add', 'drop')); | |
697 } | |
698 } |