1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
|
4
|
|
|
class CopyFactory extends Object |
5
|
|
|
{ |
6
|
|
|
|
7
|
|
|
/** |
8
|
|
|
* saves all the changes to the debug::log file.. |
9
|
|
|
* needs to be set manually |
10
|
|
|
* @var Boolean |
11
|
|
|
*/ |
12
|
|
|
private static $debug = false; |
13
|
|
|
|
14
|
|
|
/** |
15
|
|
|
* is this a real copy event? |
16
|
|
|
* @var Boolean |
17
|
|
|
*/ |
18
|
|
|
private static $for_real = false; |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* the time the copy was started. |
22
|
|
|
* @var Int |
23
|
|
|
*/ |
24
|
|
|
private static $start_time = 0; |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* list of classes and their preferred title field |
28
|
|
|
* e.g. |
29
|
|
|
* MyDataObject => "FancyTitle" |
30
|
|
|
* |
31
|
|
|
* @var Array |
32
|
|
|
*/ |
33
|
|
|
private static $title_map_for_display_of_record_name = array(); |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* The part at the start of the copy field |
37
|
|
|
* e.g. MyDataObject becomes CopyMyDataObject. |
38
|
|
|
* |
39
|
|
|
* @var String |
40
|
|
|
*/ |
41
|
|
|
private static $copy_fields_prefix = "Copy"; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* the field where we save the ID of the completed copy |
45
|
|
|
* action should be the same of the field where we saved the original |
46
|
|
|
* dataobject to be copied + an appendix. |
47
|
|
|
* e.g. CopyMyDataObject becomes CopyMyDataObject_Completed. |
48
|
|
|
* |
49
|
|
|
* @var String |
50
|
|
|
*/ |
51
|
|
|
private static $completed_field_appendix = "_Completed"; |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* holds all the singletons |
55
|
|
|
* @var array |
56
|
|
|
*/ |
57
|
|
|
private static $singleton_holder = array(); |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* base name for session variables... |
61
|
|
|
* @var String |
62
|
|
|
*/ |
63
|
|
|
private static $dry_run_for_session_base_name = "CopyFactorySessionVariable"; |
64
|
|
|
|
65
|
|
|
/** |
66
|
|
|
* static variable that holds the details for the current dry run... |
67
|
|
|
* @var string |
68
|
|
|
*/ |
69
|
|
|
private static $dry_run_for_class_name = ""; |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* static variable that holds the details for the current dry run... |
73
|
|
|
* @var string |
74
|
|
|
*/ |
75
|
|
|
private static $dry_run_for_id = ""; |
76
|
|
|
|
77
|
|
|
/** |
78
|
|
|
* returns a CopyFactory specific to a Class. |
79
|
|
|
* You must provide an object class name. |
80
|
|
|
* |
81
|
|
|
* @param DataObject $obj |
|
|
|
|
82
|
|
|
* |
83
|
|
|
* @return CopyFactory |
84
|
|
|
*/ |
85
|
|
|
public static function create() |
86
|
|
|
{ |
87
|
|
|
//allow up to ten minutes for copy ... |
88
|
|
|
increase_time_limit_to(600); |
89
|
|
|
$args = func_get_args(); |
90
|
|
|
if (!$args || count($args) != 1) { |
|
|
|
|
91
|
|
|
user_error("Please provide an Object for your CopyFactory - like this: CopyFactory::create($myDataObject);"); |
|
|
|
|
92
|
|
|
} |
93
|
|
|
$obj = array_shift($args); |
94
|
|
|
if (!($obj instanceof DataObject)) { |
95
|
|
|
user_error("First argument provided should be a DataObject."); |
96
|
|
|
} |
97
|
|
|
//set basic details if no copy has run thus far ... |
98
|
|
|
if (count(self::$singleton_holder) == 0) { |
99
|
|
|
$startTime = time(); |
100
|
|
|
Config::inst()->update("CopyFactory", "dry_run_for_class_name", $obj->ClassName); |
101
|
|
|
Config::inst()->update("CopyFactory", "dry_run_for_id", $obj->ID); |
102
|
|
|
Config::inst()->update("CopyFactory", "start_time", $startTime); |
103
|
|
|
// dry run only... |
104
|
|
View Code Duplication |
if (SiteConfig::current_site_config()->AllowCopyingOfRecords == 1) { |
|
|
|
|
105
|
|
|
Config::inst()->update("CopyFactory", "for_real", false); |
106
|
|
|
self::add_to_session(" |
107
|
|
|
=========================================================== |
108
|
|
|
Starting dry run: ".date("r", $startTime)." |
109
|
|
|
=========================================================== |
110
|
|
|
=========================================================== |
111
|
|
|
"); |
112
|
|
|
} |
113
|
|
|
// for real ... |
114
|
|
View Code Duplication |
if (SiteConfig::current_site_config()->AllowCopyingOfRecords == 2) { |
|
|
|
|
115
|
|
|
Config::inst()->update("CopyFactory", "for_real", true); |
116
|
|
|
// we cant do too many writes ... in a live environment |
117
|
|
|
self::add_to_session(" |
118
|
|
|
=========================================================== |
119
|
|
|
Starting real copy task: ".date("r", $startTime)." |
120
|
|
|
=========================================================== |
121
|
|
|
=========================================================== |
122
|
|
|
"); |
123
|
|
|
} |
124
|
|
|
} |
125
|
|
|
if (!isset(self::$singleton_holder[$obj->ClassName])) { |
126
|
|
|
self::$singleton_holder[$obj->ClassName] = new CopyFactory(); |
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
self::$singleton_holder[$obj->ClassName]->myClassName = $obj->ClassName; |
130
|
|
|
self::$singleton_holder[$obj->ClassName]->isForReal = Config::inst()->get("CopyFactory", "for_real") ? true : false; |
131
|
|
|
if (Director::isDev()) { |
132
|
|
|
self::$singleton_holder[$obj->ClassName]->recordSession = true; |
133
|
|
|
} else { |
134
|
|
|
self::$singleton_holder[$obj->ClassName]->recordSession = self::$singleton_holder[$obj->ClassName]->isForReal; |
135
|
|
|
} |
136
|
|
|
return self::$singleton_holder[$obj->ClassName]; |
137
|
|
|
} |
138
|
|
|
|
139
|
|
|
|
140
|
|
|
/** |
141
|
|
|
* adds additional info to current session |
142
|
|
|
* @param String $action |
143
|
|
|
* @param DataObject $copyFrom |
|
|
|
|
144
|
|
|
* @param DataObject $copyInto |
|
|
|
|
145
|
|
|
*/ |
146
|
|
|
protected static function add_to_session($action, $copyFrom = null, $copyInto = null) |
147
|
|
|
{ |
148
|
|
|
$obj = new CopyFactoryLog(); |
149
|
|
|
$obj->Type = Config::inst()->get("CopyFactory", "for_real") ? "Real" : "Fake"; |
|
|
|
|
150
|
|
|
$obj->StartTime = Config::inst()->get("CopyFactory", "start_time"); |
|
|
|
|
151
|
|
|
$obj->CopyCausingClassName = Config::inst()->get("CopyFactory", "dry_run_for_class_name"); |
|
|
|
|
152
|
|
|
$obj->CopyCausingClassNameID = Config::inst()->get("CopyFactory", "dry_run_for_id"); |
|
|
|
|
153
|
|
|
if ($copyFrom) { |
154
|
|
|
$obj->CopyFromClassNameID = $copyFrom->ID; |
|
|
|
|
155
|
|
|
} |
156
|
|
|
if ($copyInto) { |
157
|
|
|
$obj->CopyIntoClassName = $copyInto->ClassName; |
|
|
|
|
158
|
|
|
$obj->CopyIntoClassNameID = $copyInto->ID; |
|
|
|
|
159
|
|
|
} |
160
|
|
|
$obj->Action = $action; |
|
|
|
|
161
|
|
|
$obj->write(); |
162
|
|
|
if (Config::inst()->get("CopyFactory", "debug")) { |
163
|
|
|
$copyFromLine = ""; |
164
|
|
View Code Duplication |
if ($copyFrom && $copyFrom->exists()) { |
|
|
|
|
165
|
|
|
$copyFromLine = "FROM: ".self::title_for_object($copyFrom)." - ".$copyFrom->ClassName.".".$copyFrom->ID."\n"; |
166
|
|
|
} |
167
|
|
|
$copyIntoLine = ""; |
168
|
|
View Code Duplication |
if ($copyInto && $copyInto->exists()) { |
|
|
|
|
169
|
|
|
$copyIntoLine = "INTO: ".self::title_for_object($copyInto)." - ".$copyInto->ClassName.".".$copyInto->ID."\n"; |
170
|
|
|
} |
171
|
|
|
debug::log( |
172
|
|
|
$copyFromLine. |
173
|
|
|
$copyIntoLine. |
174
|
|
|
$action |
175
|
|
|
); |
176
|
|
|
} |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
|
180
|
|
|
/** |
181
|
|
|
* provides a meaningful title for an object |
182
|
|
|
* @return String ... |
183
|
|
|
*/ |
184
|
|
View Code Duplication |
public static function title_for_object($obj) |
|
|
|
|
185
|
|
|
{ |
186
|
|
|
$methodOrField = self::preferred_title_field($obj); |
187
|
|
|
if ($obj->hasMethod($methodOrField)) { |
188
|
|
|
return $obj->$methodOrField(); |
189
|
|
|
} elseif ($obj->hasMethod("get".$methodOrField)) { |
190
|
|
|
$methodName = "get".$methodOrField; |
191
|
|
|
return $obj->$methodName(); |
192
|
|
|
} else { |
193
|
|
|
return $obj->$methodOrField; |
194
|
|
|
} |
195
|
|
|
} |
196
|
|
|
|
197
|
|
|
/** |
198
|
|
|
* returns the name of a method / db field |
199
|
|
|
* that can be used to describe the object ... |
200
|
|
|
* |
201
|
|
|
* @param DataObject $object |
|
|
|
|
202
|
|
|
* |
203
|
|
|
* @return String |
204
|
|
|
*/ |
205
|
|
|
public static function preferred_title_field($obj) |
206
|
|
|
{ |
207
|
|
|
$titleMap = Config::inst()->get("CopyFactory", "title_map_for_display_of_record_name"); |
208
|
|
|
if (isset($titleMap[$obj->ClassName])) { |
209
|
|
|
return $titleMap[$obj->ClassName]; |
210
|
|
|
} |
211
|
|
|
return "Title"; |
212
|
|
|
} |
213
|
|
|
|
214
|
|
|
/** |
215
|
|
|
* are records actually been written ... |
216
|
|
|
* |
217
|
|
|
* @var Boolean |
218
|
|
|
*/ |
219
|
|
|
protected $isForReal = ""; |
220
|
|
|
|
221
|
|
|
/** |
222
|
|
|
* used for reference only |
223
|
|
|
* saves the class of the current singleton instance |
224
|
|
|
* that initiated the copy |
225
|
|
|
* |
226
|
|
|
* @var String |
227
|
|
|
*/ |
228
|
|
|
protected $recordSession = ""; |
229
|
|
|
|
230
|
|
|
/** |
231
|
|
|
* used for reference only |
232
|
|
|
* saves the class of the current singleton instance |
233
|
|
|
* that initiated the copy |
234
|
|
|
* |
235
|
|
|
* @var String |
236
|
|
|
*/ |
237
|
|
|
protected $myClassName = ""; |
238
|
|
|
|
239
|
|
|
/** |
240
|
|
|
* fields that are never copied |
241
|
|
|
* @var array |
242
|
|
|
*/ |
243
|
|
|
protected $baseIgnoreFields = array( |
244
|
|
|
"Created", |
245
|
|
|
"LastEdited", |
246
|
|
|
"ID" |
247
|
|
|
); |
248
|
|
|
|
249
|
|
|
/** |
250
|
|
|
* set a list of fields that should always be ignored ... |
251
|
|
|
* we add the base array to it at the same time ... |
252
|
|
|
* @param Array $array |
253
|
|
|
*/ |
254
|
|
|
public function setIgnoreFields($array) |
255
|
|
|
{ |
256
|
|
|
$this->ignoreFields = array_merge($this->baseIgnoreFields, $array); |
|
|
|
|
257
|
|
|
} |
258
|
|
|
|
259
|
|
|
/** |
260
|
|
|
* add one field to ignore |
261
|
|
|
* @param String $fieldName |
262
|
|
|
*/ |
263
|
|
|
public function addIgnoreField($fieldName) |
264
|
|
|
{ |
265
|
|
|
$this->ignoreFields[] = $fieldName; |
|
|
|
|
266
|
|
|
} |
267
|
|
|
|
268
|
|
|
/** |
269
|
|
|
* |
270
|
|
|
* @param Boolean $b |
271
|
|
|
* |
272
|
|
|
* @return CopyFactory |
273
|
|
|
*/ |
274
|
|
|
public function setIsForReal($b) |
275
|
|
|
{ |
276
|
|
|
return $this->isForReal = $b; |
277
|
|
|
return $this; |
|
|
|
|
278
|
|
|
} |
279
|
|
|
|
280
|
|
|
/** |
281
|
|
|
* |
282
|
|
|
* @return Boolean |
283
|
|
|
*/ |
284
|
|
|
public function getIsForReal() |
285
|
|
|
{ |
286
|
|
|
return $this->isForReal; |
287
|
|
|
} |
288
|
|
|
|
289
|
|
|
/** |
290
|
|
|
* |
291
|
|
|
* @return String |
292
|
|
|
*/ |
293
|
|
|
protected function getCopyFactorySessionName() |
294
|
|
|
{ |
295
|
|
|
return |
296
|
|
|
$this->Config()->get("dry_run_for_session_base_name") |
297
|
|
|
."_". |
298
|
|
|
$this->Config()->get("dry_run_for_class_name") |
299
|
|
|
."_". |
300
|
|
|
$this->Config()->get("dry_run_for_id"); |
301
|
|
|
} |
302
|
|
|
|
303
|
|
|
|
304
|
|
|
|
305
|
|
|
/***************************************************** |
306
|
|
|
* COPYING METHODS |
307
|
|
|
*****************************************************/ |
308
|
|
|
|
309
|
|
|
/** |
310
|
|
|
* Copy one object into another including has-one fields |
311
|
|
|
* |
312
|
|
|
* @param DataObject $copyFrom |
313
|
|
|
* @param DataObject $newObject |
314
|
|
|
* @param Boolean $overwriteValues - overwrite values that exist |
315
|
|
|
* |
316
|
|
|
* @return CopyFactory |
317
|
|
|
*/ |
318
|
|
|
public function copyObject($copyFrom, $newObject, $overwriteValues = true) |
319
|
|
|
{ |
320
|
|
|
|
321
|
|
|
//get ignore fields |
322
|
|
|
if ($newObject->hasMethod("getIgnoreInCopyFields")) { |
323
|
|
|
$this->setIgnoreFields($newObject->getIgnoreInCopyFields()); |
324
|
|
|
} elseif ($array = Config::inst()->get($newObject->ClassName, "ignore_in_copy_fields")) { |
325
|
|
|
if (is_array($array) && count($array)) { |
326
|
|
|
$this->setIgnoreFields($array); |
327
|
|
|
} |
328
|
|
|
} |
329
|
|
|
|
330
|
|
|
//get copy field |
331
|
|
|
$this->addIgnoreField($newObject->CopyFromFieldName($withID = true)); |
332
|
|
|
$this->addIgnoreField($newObject->CopiedFromFieldName($withID = true)); |
333
|
|
|
|
334
|
|
|
//get copy-able fields |
335
|
|
|
$dbFields = Config::inst()->get($copyFrom->ClassName, "db"); |
336
|
|
|
$hasOneFields = Config::inst()->get($copyFrom->ClassName, "has_one"); |
337
|
|
|
|
338
|
|
|
//has-one fields fixup |
339
|
|
|
foreach ($hasOneFields as $key => $field) { |
|
|
|
|
340
|
|
|
$hasOneFields[$key."ID"] = $field; |
341
|
|
|
unset($hasOneFields[$key]); |
342
|
|
|
} |
343
|
|
|
$fields = $dbFields + $hasOneFields; |
344
|
|
|
//remove ignore fields ... |
345
|
|
|
foreach ($this->ignoreFields as $ignoreField) { |
|
|
|
|
346
|
|
|
unset($fields[$ignoreField]); |
347
|
|
|
} |
348
|
|
|
//flip |
349
|
|
|
$fields = array_keys($fields); |
350
|
|
|
if ($this->recordSession) { |
351
|
|
|
self::add_to_session( |
352
|
|
|
" |
353
|
|
|
==================================== |
354
|
|
|
*** COPYING INTO |
355
|
|
|
".($this->myClassName != $copyFrom->ClassName ? "ERROR: ClassName mismatch: ".$this->myClassName." != ".$copyFrom->ClassName : "")." |
356
|
|
|
Values will ".($overwriteValues ? "" : "NOT")." be overridden. |
357
|
|
|
DB Fields: ".implode(", ", array_keys($dbFields))." |
358
|
|
|
HAS ONE Fields: ".implode(", ", array_keys($hasOneFields))." |
359
|
|
|
IGNORE Fields: ".implode(", ", $this->ignoreFields)." |
|
|
|
|
360
|
|
|
FINAL Fields: ".implode(", ", $fields)." |
361
|
|
|
==================================== |
362
|
|
|
", |
363
|
|
|
$copyFrom, |
364
|
|
|
$newObject |
365
|
|
|
); |
366
|
|
|
} |
367
|
|
|
if ($this->recordSession) { |
368
|
|
|
$copySessionRecording = ""; |
369
|
|
|
} |
370
|
|
|
foreach ($fields as $field) { |
371
|
|
|
if (!$newObject->$field || $overwriteValues) { |
372
|
|
|
$value = $copyFrom->$field; |
373
|
|
|
if (is_object($value)) { |
374
|
|
|
$value = $value->raw(); |
375
|
|
|
} |
376
|
|
|
if ($this->recordSession) { |
377
|
|
|
$copySessionRecording .= "\r\nSetting '$field' to '$value'"; |
|
|
|
|
378
|
|
|
} |
379
|
|
|
if ($this->isForReal) { |
380
|
|
|
$newObject->$field = $value; |
381
|
|
|
} |
382
|
|
|
} |
383
|
|
|
} |
384
|
|
|
$copyField = $newObject->CopyFromFieldName($withID = true); |
385
|
|
|
$copyFieldCompleted = $newObject->CopiedFromFieldName($withID = true); |
386
|
|
|
//important - reset, so that it does not get into a loop. |
387
|
|
|
if ($this->recordSession) { |
388
|
|
|
self::add_to_session( |
389
|
|
|
" |
390
|
|
|
$copySessionRecording |
391
|
|
|
setting '$copyField' to zero |
392
|
|
|
setting '$copyFieldCompleted' to '".$copyFrom->ID."'", |
393
|
|
|
$copyFrom, |
394
|
|
|
$newObject |
395
|
|
|
); |
396
|
|
|
} |
397
|
|
|
if ($this->isForReal) { |
398
|
|
|
$newObject->$copyFieldCompleted = $copyFrom->ID; |
399
|
|
|
if ($newObject->exists()) { |
400
|
|
|
$newObject->$copyField = 0; |
401
|
|
|
} |
402
|
|
|
$newObject->write(); |
403
|
|
|
} |
404
|
|
|
//now we do all the other methods ... |
405
|
|
|
if ($newObject->hasMethod("doCopyFactory")) { |
406
|
|
|
if ($this->recordSession) { |
407
|
|
|
self::add_to_session("Now calling doCopyFactory ... ", $copyFrom, $newObject); |
408
|
|
|
} |
409
|
|
|
$newObject->doCopyFactory($this, $copyFrom); |
410
|
|
|
} |
411
|
|
|
return $this; |
412
|
|
|
} |
413
|
|
|
|
414
|
|
|
/** |
415
|
|
|
* From a copied object, copy the has-one of the copyFrom Object to the newObject. |
416
|
|
|
* Old and new copy point to the same has-one record. |
417
|
|
|
* |
418
|
|
|
* @param DataObject $copyFromParent |
419
|
|
|
* @param DataObject $newObjectParent |
420
|
|
|
* @param String $relationalFieldForChildWithoutID - e.g. MyImage |
421
|
|
|
* |
422
|
|
|
* @return CopyFactory |
423
|
|
|
*/ |
424
|
|
|
public function copyOriginalHasOneItem($copyFromParent, $newObjectParent, $relationalFieldForChildWithoutID) |
425
|
|
|
{ |
426
|
|
|
if ($this->recordSession) { |
427
|
|
|
self::add_to_session( |
428
|
|
|
" |
429
|
|
|
==================================== |
430
|
|
|
COPY ORIGINAL HAS-ONE RELATION: $relationalFieldForChildWithoutID |
431
|
|
|
Children will link to original record (will not be copied) |
432
|
|
|
==================================== |
433
|
|
|
", |
434
|
|
|
$copyFromParent, |
435
|
|
|
$newObjectParent |
436
|
|
|
); |
437
|
|
|
} |
438
|
|
|
$relationalFieldForChildWithID = $relationalFieldForChildWithoutID . "ID"; |
439
|
|
|
if ($this->isForReal) { |
440
|
|
|
$newObjectParent->$relationalFieldForChildWithID = $copyFromParent->$relationalFieldForChildWithID; |
441
|
|
|
$newObjectParent->write(); |
442
|
|
|
} |
443
|
|
|
return $this; |
444
|
|
|
} |
445
|
|
|
|
446
|
|
|
/** |
447
|
|
|
* Usage: an object has one child |
448
|
|
|
* and we want to also copy the child and add it to the |
449
|
|
|
* copied into parent ... |
450
|
|
|
* |
451
|
|
|
* @param DataObject $copyFromParent |
452
|
|
|
* @param DataObject $newObjectParent |
453
|
|
|
* @param String $relationalFieldForChildWithoutID - |
454
|
|
|
* @param String $relationalFieldForParentWithoutID - if it is a has_one to has_one relationship only!!! |
455
|
|
|
* this is the field on the parent that provides |
456
|
|
|
* its child (the single-child / has one child )WITHOUT the ID part. |
457
|
|
|
* |
458
|
|
|
* @return CopyFactory |
459
|
|
|
*/ |
460
|
|
|
public function copyHasOneRelation($copyFromParent, $newObjectParent, $relationalFieldForChildWithoutID, $relationalFieldForParentWithoutID = "") |
461
|
|
|
{ |
462
|
|
|
if ($this->recordSession) { |
463
|
|
|
self::add_to_session(" |
464
|
|
|
==================================== |
465
|
|
|
COPY HAS-ONE RELATION: $relationalFieldForChildWithoutID |
466
|
|
|
==================================== |
467
|
|
|
", $copyFromParent, $newObjectParent); |
468
|
|
|
} |
469
|
|
|
if ($copyFromChildObject = $copyFromParent->$relationalFieldForChildWithoutID()) { |
470
|
|
|
if ($copyFromChildObject->exists()) { |
471
|
|
|
$className = $copyFromChildObject->ClassName; |
472
|
|
|
// what are we going to do? |
473
|
|
|
if ($this->recordSession) { |
474
|
|
|
self::add_to_session("Creating a new object '$className' linking to '$relationalFieldForChildWithoutID' in parent.", $copyFromParent, $newObjectParent); |
475
|
|
|
} |
476
|
|
|
|
477
|
|
|
//create object and set parent ... |
478
|
|
|
$newObjectChildObject = $className::create(); |
479
|
|
|
$newObjectChildObject->ClassName = $className; |
480
|
|
|
//does the child also copy ... |
481
|
|
|
//we copy the data here so that we dont run into validation errors |
482
|
|
|
$obj = CopyFactory::create($newObjectChildObject); |
483
|
|
|
$obj->copyObject($copyFromChildObject, $newObjectChildObject); |
484
|
|
|
|
485
|
|
|
if ($this->isForReal) { |
486
|
|
|
if ($relationalFieldForParentWithoutID) { |
487
|
|
|
$relationalFieldForParentWithID = $relationalFieldForParentWithoutID."ID"; |
488
|
|
|
$newObjectChildObject->$relationalFieldForParentWithID = $newObjectParent->ID; |
489
|
|
|
} |
490
|
|
|
$newObjectChildObject->write(); |
491
|
|
|
$newObjectChildObject = $className::get()->byID($newObjectChildObject->ID); |
492
|
|
|
//attach to parent ... |
493
|
|
|
$relationalFieldForChildWithID = $relationalFieldForChildWithoutID."ID"; |
494
|
|
|
$newObjectParent->$relationalFieldForChildWithID = $newObjectChildObject->ID; |
495
|
|
|
$newObjectParent->write(); |
496
|
|
|
} |
497
|
|
|
|
498
|
|
|
// setting child again - just in case ... |
499
|
|
|
if ($this->isForReal) { |
500
|
|
|
$newObjectParent->$relationalFieldForChildWithID = $newObjectChildObject->ID; |
|
|
|
|
501
|
|
|
$newObjectParent->write(); |
502
|
|
|
} |
503
|
|
View Code Duplication |
if ($this->recordSession) { |
|
|
|
|
504
|
|
|
if (!$newObjectChildObject) { |
505
|
|
|
self::add_to_session("ERROR: did not create object listed above", $copyFromChildObject, $newObjectChildObject); |
506
|
|
|
} else { |
507
|
|
|
self::add_to_session("CREATED object", $copyFromChildObject, $newObjectChildObject); |
508
|
|
|
} |
509
|
|
|
if ($newObjectParent->$relationalFieldForChildWithID != $newObjectChildObject->ID) { |
510
|
|
|
self::add_to_session( |
511
|
|
|
"ERROR: broken link ... '".$newObjectParent->$relationalFieldForChildWithID."' is not equal to '".$newObjectChildObject->ID."'", |
512
|
|
|
$copyFromChildObject, |
513
|
|
|
$newObjectChildObject |
514
|
|
|
); |
515
|
|
|
//hack fix |
516
|
|
|
} else { |
517
|
|
|
self::add_to_session("Saved with correct new parent field ($relationFieldForParentWithID) ID: ".$newObjectChildObject->$relationFieldForParentWithID, $copyFromChildObject, $newObjectChildObject); |
|
|
|
|
518
|
|
|
} |
519
|
|
|
} |
520
|
|
|
} |
521
|
|
|
} |
522
|
|
|
return $this; |
523
|
|
|
} |
524
|
|
|
|
525
|
|
|
/** |
526
|
|
|
* Usage: Find the copied ("NEW") equivalent of the old has-one relation and attach it to the newObject ... |
527
|
|
|
* @param DataObject $copyFrom |
528
|
|
|
* @param DataObject $newObject |
529
|
|
|
* @param String $hasOneMethod - the fieldname (method) of the has one relations (e.g. MyOtherDataObject) |
530
|
|
|
* @param DataList $dataListToChooseFrom |
531
|
|
|
* |
532
|
|
|
* @return CopyFactory |
533
|
|
|
*/ |
534
|
|
|
public function attachToMoreRelevantHasOne($copyFrom, $newObject, $hasOneMethod, $dataListToChooseFrom) |
535
|
|
|
{ |
536
|
|
|
$fieldNameWithID = $hasOneMethod."ID"; |
537
|
|
|
if ($this->recordSession) { |
538
|
|
|
self::add_to_session( |
539
|
|
|
" |
540
|
|
|
==================================== |
541
|
|
|
ATTACH TO MORE RELEVANT HAS-ONE |
542
|
|
|
FIELD $hasOneMethod |
543
|
|
|
OBJECTS TO CHOOSE FROM: ".$dataListToChooseFrom->sql()." |
544
|
|
|
==================================== |
545
|
|
|
", |
546
|
|
|
$copyFrom, |
547
|
|
|
$newObject |
548
|
|
|
); |
549
|
|
|
} |
550
|
|
|
if ($copyFrom->$fieldNameWithID) { |
551
|
|
|
//find out field to choose from |
552
|
|
|
$firstObject = $dataListToChooseFrom->first(); |
553
|
|
|
if ($firstObject) { |
554
|
|
|
$dataListToChooseFrom = $dataListToChooseFrom |
555
|
|
|
->filter(array($firstObject->CopiedFromFieldName($withID = true) => $copyFrom->$fieldNameWithID)) |
556
|
|
|
->Sort("Created DESC"); |
557
|
|
|
$count = $dataListToChooseFrom->count(); |
558
|
|
|
if ($count == 1 && $newAttachment = $dataListToChooseFrom->First()) { |
559
|
|
|
if ($this->recordSession) { |
560
|
|
|
self::add_to_session("Found Matching record.", $copyFrom, $newObject); |
561
|
|
|
} |
562
|
|
|
if ($this->isForReal) { |
563
|
|
|
$newObject->$fieldNameWithID = $newAttachment->ID; |
564
|
|
|
$newObject->write(); |
565
|
|
|
} |
566
|
|
|
} else { |
567
|
|
|
if ($this->recordSession) { |
568
|
|
|
if ($count > 1) { |
569
|
|
|
self::add_to_session("ERROR: found too many Matching records.", $copyFrom, $newObject); |
570
|
|
|
} elseif ($count == 0) { |
571
|
|
|
self::add_to_session("ERROR: Could not find any Matching records.", $copyFrom, $newObject); |
572
|
|
|
} else { |
573
|
|
|
self::add_to_session("ERROR: There was an error retrieving the matching record.", $copyFrom, $newObject); |
574
|
|
|
} |
575
|
|
|
} |
576
|
|
|
if ($this->isForReal) { |
577
|
|
|
$newObject->$fieldNameWithID = 0; |
578
|
|
|
$newObject->write(); |
579
|
|
|
} |
580
|
|
|
} |
581
|
|
|
} else { |
582
|
|
|
self::add_to_session("ERROR: Could not find any Matching records from base DataList.", $copyFrom, $newObject); |
583
|
|
|
$newObject->$fieldNameWithID = 0; |
584
|
|
|
$newObject->write(); |
585
|
|
|
} |
586
|
|
|
} else { |
587
|
|
|
if ($this->recordSession) { |
588
|
|
|
self::add_to_session("copyFrom object does not have a value for: $fieldNameWithID", $copyFrom, $newObject); |
589
|
|
|
} |
590
|
|
|
} |
591
|
|
|
if ($this->recordSession) { |
592
|
|
|
self::add_to_session("*** END OF attachToMoreRelevantHasOne ***", $copyFrom, $newObject); |
593
|
|
|
} |
594
|
|
|
return $this; |
595
|
|
|
} |
596
|
|
|
|
597
|
|
|
/** |
598
|
|
|
* Usage: an object has many children but we do NOT copy the children ... |
599
|
|
|
* |
600
|
|
|
* @param DataObject $copyFromParent |
601
|
|
|
* @param DataObject $newObjectParent |
602
|
|
|
* @param String $relationalFieldForChildren - this is the field on the parent that provides the children (e.g. Children or Images) WITHOUT the ID part. |
603
|
|
|
* @param String $relationFieldForParent - this is the field on the children that links them back to the parent. |
|
|
|
|
604
|
|
|
* |
605
|
|
|
* @return CopyFactory |
|
|
|
|
606
|
|
|
*/ |
607
|
|
|
public function copyOriginalHasManyItems($copyFromParent, $newObjectParent, $relationalFieldForChildren, $relationFieldForParentWithoutID) |
|
|
|
|
608
|
|
|
{ |
609
|
|
|
user_error("This is not possible, because the many-side can't link to two objects (the copyFrom and copyInto part."); |
610
|
|
|
} |
611
|
|
|
|
612
|
|
|
/** |
613
|
|
|
* Usage: an object has many children |
614
|
|
|
* and we want to also copy the children and add them to the |
615
|
|
|
* copied into parent ... |
616
|
|
|
* |
617
|
|
|
* @param DataObject $copyFromParent |
618
|
|
|
* @param DataObject $newObjectParent |
619
|
|
|
* @param String $relationalFieldForChildren - this is the field on the parent that provides the children (e.g. Children or Images) WITHOUT the ID part. |
620
|
|
|
* @param String $relationFieldForParentWithoutID - this is the field on the children that links them back to the parent. |
621
|
|
|
* |
622
|
|
|
* @return CopyFactory |
623
|
|
|
*/ |
624
|
|
|
public function copyHasManyRelation($copyFromParent, $newObjectParent, $relationalFieldForChildren, $relationFieldForParentWithoutID) |
625
|
|
|
{ |
626
|
|
|
if ($this->recordSession) { |
627
|
|
|
self::add_to_session( |
628
|
|
|
" |
629
|
|
|
==================================== |
630
|
|
|
COPY HAS-MANY RELATION: |
631
|
|
|
CHILDREN METHOD: '$relationalFieldForChildren' and |
632
|
|
|
PARENT METHOD: '$relationFieldForParentWithoutID' |
633
|
|
|
==================================== |
634
|
|
|
", |
635
|
|
|
$copyFromParent, |
636
|
|
|
$newObjectParent |
637
|
|
|
); |
638
|
|
|
} |
639
|
|
|
foreach ($copyFromParent->$relationalFieldForChildren() as $copyFromChildObject) { |
640
|
|
|
$className = $copyFromChildObject->ClassName; |
641
|
|
|
$relationFieldForParentWithID = $relationFieldForParentWithoutID."ID"; |
642
|
|
|
$childCopyField = $copyFromChildObject->CopyFromFieldName($withID = true); |
|
|
|
|
643
|
|
|
if ($this->recordSession) { |
644
|
|
|
self::add_to_session("Creating a new object '$className'; adding parent field ($relationFieldForParentWithID) ID: ".$newObjectParent->ID, $copyFromParent, $newObjectParent); |
645
|
|
|
} |
646
|
|
|
//create object and set parent ... |
647
|
|
|
$newObjectChildObject = $className::create(); |
648
|
|
|
if ($this->isForReal) { |
649
|
|
|
$newObjectChildObject->$relationFieldForParentWithID = $newObjectParent->ID; |
650
|
|
|
|
651
|
|
|
//does the child also copy ... |
652
|
|
|
//we copy the data here so that we dont run into validation errors |
653
|
|
|
$obj = CopyFactory::create($newObjectChildObject); |
654
|
|
|
$obj->copyObject($copyFromChildObject, $newObjectChildObject); |
655
|
|
|
//we reset the copy field here so that the copy can run another |
656
|
|
|
//time and do the has-many and many-many parts |
657
|
|
|
//$newObjectChildObject->$childCopyField = intval($copyFromChildObject->ID); |
658
|
|
|
//$newObjectChildObject->write(); |
659
|
|
|
//retrieve it again to set more details ... |
660
|
|
|
//$newObjectChildObject = $className::get()->byID($newObjectChildObject->ID); |
661
|
|
|
|
662
|
|
|
// setting parent again - just in case ... |
663
|
|
|
$newObjectChildObject->$relationFieldForParentWithID = $newObjectParent->ID; |
664
|
|
|
$newObjectChildObject->write(); |
665
|
|
|
} |
666
|
|
View Code Duplication |
if ($this->recordSession) { |
|
|
|
|
667
|
|
|
if (!$newObjectChildObject) { |
668
|
|
|
self::add_to_session("ERROR: did not create object listed above", $copyFromChildObject, $newObjectChildObject); |
669
|
|
|
} else { |
670
|
|
|
self::add_to_session("CREATED object", $copyFromChildObject, $newObjectChildObject); |
671
|
|
|
} |
672
|
|
|
if ($newObjectChildObject->$relationFieldForParentWithID != $newObjectParent->ID) { |
673
|
|
|
self::add_to_session( |
674
|
|
|
" |
675
|
|
|
ERROR: broken link ... '".$newObjectChildObject->$relationFieldForParentWithID."' is not equal to '".$newObjectParent->ID."'", |
676
|
|
|
$copyFromChildObject, |
677
|
|
|
$newObjectChildObject |
678
|
|
|
); |
679
|
|
|
//hack fix |
680
|
|
|
} else { |
681
|
|
|
self::add_to_session("Saved with correct new parent field ($relationFieldForParentWithID) ID: ".$newObjectChildObject->$relationFieldForParentWithID, $copyFromChildObject, $newObjectChildObject); |
682
|
|
|
} |
683
|
|
|
} |
684
|
|
|
} |
685
|
|
|
if ($this->recordSession) { |
686
|
|
|
self::add_to_session("*** END OF copyHasManyRelation ***", $copyFromParent, $newObjectParent); |
687
|
|
|
} |
688
|
|
|
return $this; |
689
|
|
|
} |
690
|
|
|
|
691
|
|
|
/** |
692
|
|
|
* Usage: an object has many children, the children have already been copied, but they are not pointing at the new parent object. |
693
|
|
|
* |
694
|
|
|
* @param DataObject $copyFromParent |
695
|
|
|
* @param DataObject $newObjectParent |
696
|
|
|
* @param String $relationalFieldForChildren - this is the field on the parent that provides the children (e.g. Children or Images) WITHOUT the ID part. |
697
|
|
|
* @param String $relationFieldForParentWithoutID - this is the field on the children that links them back to the parent. |
698
|
|
|
* @param DataList $dataListToChooseFrom - selection of children that are best matches ... |
699
|
|
|
* |
700
|
|
|
* @return CopyFactory |
701
|
|
|
*/ |
702
|
|
|
public function attachToMoreRelevantHasMany($copyFromParent, $newObjectParent, $relationalFieldForChildren, $relationFieldForParentWithoutID, $dataListToChooseFrom) |
|
|
|
|
703
|
|
|
{ |
704
|
|
|
user_error("The attachToMoreRelevantHasMany method is be completed."); |
705
|
|
|
return $this; |
706
|
|
|
} |
707
|
|
|
|
708
|
|
|
/** |
709
|
|
|
* copies Many-Many relationship Without copying the items linked to ... |
710
|
|
|
* @param DataObject $copyFrom |
711
|
|
|
* @param DataObject $newObject |
712
|
|
|
* @param String $manyManyMethod |
713
|
|
|
* @param Array $extraFields - e..g Field1, Field2 |
714
|
|
|
* |
715
|
|
|
* @return CopyFactory |
716
|
|
|
*/ |
717
|
|
|
public function copyOriginalManyManyItems($copyFrom, $newObject, $manyManyMethod, $extraFields = array()) |
718
|
|
|
{ |
719
|
|
View Code Duplication |
if ($this->recordSession) { |
|
|
|
|
720
|
|
|
self::add_to_session( |
721
|
|
|
" |
722
|
|
|
==================================== |
723
|
|
|
COPY Original Many Many Items |
724
|
|
|
MANY-MANY METHOD: '$manyManyMethod' |
725
|
|
|
EXTRAFIELDS: '".implode(", ", $extraFields)."' |
726
|
|
|
==================================== |
727
|
|
|
", |
728
|
|
|
$copyFrom, |
729
|
|
|
$newObject |
730
|
|
|
); |
731
|
|
|
} |
732
|
|
|
//remove current ones on NewObject |
733
|
|
|
if ($this->isForReal) { |
734
|
|
|
$newObject->$manyManyMethod()->removeAll(); |
735
|
|
|
} |
736
|
|
|
if ($this->isForReal) { |
737
|
|
|
if (count($extraFields) == 0) { |
738
|
|
|
$ids = $copyFrom->$manyManyMethod()->Column("ID"); |
739
|
|
|
if ($this->recordSession) { |
740
|
|
|
self::add_to_session("copying ".count($ids)." items into $manyManyMethod.", $copyFrom, $newObject); |
741
|
|
|
} |
742
|
|
|
$newObject->$manyManyMethod()->addMany($ids); |
743
|
|
|
} else { |
744
|
|
|
$count = 0; |
745
|
|
|
foreach ($copyFrom->$manyManyMethod() as $itemToAdd) { |
746
|
|
|
$count++; |
747
|
|
|
unset($newExtraFieldsArray); |
748
|
|
|
$newExtraFieldsArray = array(); |
749
|
|
|
foreach ($extraFields as $extraField) { |
750
|
|
|
$newExtraFieldsArray[$extraField] = $itemToAdd->$extraField; |
751
|
|
|
} |
752
|
|
|
$newObject->$manyManyMethod()->add($itemToAdd, $newExtraFieldsArray); |
753
|
|
|
} |
754
|
|
|
if ($this->recordSession) { |
755
|
|
|
self::add_to_session("copying ".$count." items into $manyManyMethod, with extra Fields.", $copyFrom, $newObject); |
756
|
|
|
} |
757
|
|
|
} |
758
|
|
|
} |
759
|
|
|
return $this; |
760
|
|
|
} |
761
|
|
|
|
762
|
|
|
/*** |
763
|
|
|
* finds copied items for a many-many relationship |
764
|
|
|
* make sure the many-many relation is also copy-able |
765
|
|
|
* @param DataObject $copyFrom |
766
|
|
|
* @param DataObject $newObject |
767
|
|
|
* @param String $manyManyMethod |
768
|
|
|
* @param DataList $dataListToChooseFrom |
769
|
|
|
* @param Array $extraFields - many_many_ExtraFields |
770
|
|
|
* |
771
|
|
|
* @return CopyFactory |
772
|
|
|
*/ |
773
|
|
|
public function attachToMoreRelevantManyMany($copyFrom, $newObject, $manyManyMethod, $dataListToChooseFrom, $extraFields = array()) |
774
|
|
|
{ |
775
|
|
View Code Duplication |
if ($this->recordSession) { |
|
|
|
|
776
|
|
|
self::add_to_session( |
777
|
|
|
" |
778
|
|
|
==================================== |
779
|
|
|
ATTACH TO MORE RELEVANT MANY-MANY |
780
|
|
|
MANY-MANY METHOD: $manyManyMethod |
781
|
|
|
OBJECTS TO CHOOSE FROM: ".$dataListToChooseFrom->count()." |
782
|
|
|
EXTRA-FIELDS: '".implode(", ", $extraFields)."' |
783
|
|
|
==================================== |
784
|
|
|
", |
785
|
|
|
$copyFrom, |
786
|
|
|
$newObject |
787
|
|
|
); |
788
|
|
|
} |
789
|
|
|
//remove current ones on NewObject |
790
|
|
|
if ($this->isForReal) { |
791
|
|
|
$newObject->$manyManyMethod()->removeAll(); |
792
|
|
|
} |
793
|
|
|
if ($copyFrom->$manyManyMethod()->count()) { |
794
|
|
|
foreach ($copyFrom->$manyManyMethod() as $manyManyRelation) { |
795
|
|
|
$myDataListToChooseFrom = $dataListToChooseFrom |
796
|
|
|
->filter(array($manyManyRelation->CopiedFromFieldName($withID = true) => $manyManyRelation->ID)) |
797
|
|
|
->Sort("Created DESC"); |
798
|
|
|
$count = $myDataListToChooseFrom->count(); |
799
|
|
|
if ($count == 1 && $newAttachment = $myDataListToChooseFrom->First()) { |
800
|
|
|
if ($this->recordSession) { |
801
|
|
|
self::add_to_session("Found Matching record using ".$manyManyRelation->CopiedFromFieldName($withID = true)." with ID: ".$manyManyRelation->ID, $copyFrom, $newObject); |
802
|
|
|
} |
803
|
|
|
if ($extraFields && count($extraFields)) { |
|
|
|
|
804
|
|
|
unset($newExtraFieldsArray); |
805
|
|
|
$newExtraFieldsArray = array(); |
806
|
|
|
foreach ($extraFields as $extraField) { |
807
|
|
|
$newExtraFieldsArray[$extraField] = $newAttachment->$extraField; |
808
|
|
|
} |
809
|
|
|
if ($this->isForReal) { |
810
|
|
|
$newObject->$manyManyMethod()->add($newAttachment, $newExtraFieldsArray); |
811
|
|
|
} |
812
|
|
|
} else { |
813
|
|
|
if ($this->isForReal) { |
814
|
|
|
$newObject->$manyManyMethod()->add($newAttachment); |
815
|
|
|
} |
816
|
|
|
} |
817
|
|
|
} else { |
818
|
|
|
if ($this->recordSession) { |
819
|
|
|
$useStatement = ", using '".$manyManyRelation->CopiedFromFieldName($withID = true)."' with ID: '".$manyManyRelation->ID."'."; |
820
|
|
|
if ($count > 1) { |
821
|
|
|
self::add_to_session("ERROR: found too many Matching records".$useStatement, $copyFrom, $newObject); |
822
|
|
|
} elseif ($count == 0) { |
823
|
|
|
self::add_to_session("ERROR: Could not find any Matching records".$useStatement, $copyFrom, $newObject); |
824
|
|
|
} else { |
825
|
|
|
self::add_to_session("ERROR: There was an error retrieving the matching record".$useStatement, $copyFrom, $newObject); |
826
|
|
|
} |
827
|
|
|
} |
828
|
|
|
if ($this->isForReal) { |
829
|
|
|
//nothing to do here as they have already been deleted. |
830
|
|
|
} |
831
|
|
|
} |
832
|
|
|
} |
833
|
|
|
} else { |
834
|
|
|
if ($this->recordSession) { |
835
|
|
|
self::add_to_session("copyFrom object does not have a value for", $copyFrom, $newObject); |
836
|
|
|
} |
837
|
|
|
} |
838
|
|
|
if ($this->recordSession) { |
839
|
|
|
self::add_to_session("*** END OF attachToMoreRelevantManyMany ***", $copyFrom, $newObject); |
840
|
|
|
} |
841
|
|
|
return $this; |
842
|
|
|
} |
843
|
|
|
} |
844
|
|
|
|
This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.
Consider the following example. The parameter
$italy
is not defined by the methodfinale(...)
.The most likely cause is that the parameter was removed, but the annotation was not.