@@ -4996,8 +4996,8 @@ abstract class Model extends AbstractInjectionAware implements EntityInterface,
4996
4996
4997
4997
protected function preSaveRelatedRecords (<AdapterInterface> connection, related, <CollectionInterface> visited ) -> bool
4998
4998
{
4999
- var className, manager, type, relation, columns, referencedFields, nesting, name, record;
5000
-
4999
+ var className, manager, type, relation, columns, referencedFields, nesting, name, record, columnA, columnB ;
5000
+ int columnCount, i;
5001
5001
let nesting = false ;
5002
5002
5003
5003
/**
@@ -5034,17 +5034,6 @@ abstract class Model extends AbstractInjectionAware implements EntityInterface,
5034
5034
" Only objects can be stored as part of belongs-to relations in '" . get_class(this ) . " ' Relation " . name
5035
5035
);
5036
5036
}
5037
- let columns = relation-> getFields(),
5038
- referencedFields = relation-> getReferencedFields();
5039
- // let columns = relation->getFields(),
5040
- // referencedModel = relation->getReferencedModel(),
5041
- // referencedFields = relation->getReferencedFields();
5042
-
5043
- if unlikely typeof columns === " array" {
5044
- connection-> rollback(nesting);
5045
-
5046
- throw new Exception (" Not implemented in '" . get_class(this ) . " ' Relation " . name);
5047
- }
5048
5037
5049
5038
/**
5050
5039
* If dynamic update is enabled, saving the record must not take any action
@@ -5069,7 +5058,18 @@ abstract class Model extends AbstractInjectionAware implements EntityInterface,
5069
5058
* Read the attribute from the referenced model and assign
5070
5059
* it to the current model
5071
5060
*/
5072
- let this -> {columns} = record-> readAttribute(referencedFields);
5061
+ let columns = relation-> getFields(),
5062
+ referencedFields = relation-> getReferencedFields();
5063
+ if unlikely typeof columns === " array" {
5064
+ let columnCount = count(columns) - 1 ;
5065
+ for i in range(0 , columnCount) {
5066
+ let columnA = columns[i];
5067
+ let columnB = referencedFields[i];
5068
+ let this -> {columnA} = record-> {columnB};
5069
+ }
5070
+ } else {
5071
+ let this -> {columns} = record-> {referencedFields};
5072
+ }
5073
5073
}
5074
5074
}
5075
5075
}
@@ -5105,11 +5105,14 @@ abstract class Model extends AbstractInjectionAware implements EntityInterface,
5105
5105
protected function postSaveRelatedRecords (<AdapterInterface> connection, related, <CollectionInterface> visited ) -> bool
5106
5106
{
5107
5107
var nesting, className, manager, relation, name, record,
5108
- columns, referencedModel, referencedFields, relatedRecords, value,
5108
+ columns, referencedModel, referencedFields, relatedRecords,
5109
5109
recordAfter, intermediateModel, intermediateFields,
5110
- intermediateValue, intermediateModelName,
5111
- intermediateReferencedFields, existingIntermediateModel;
5110
+ intermediateModelName,
5111
+ intermediateReferencedFields, existingIntermediateModel, columnA, columnB ;
5112
5112
bool isThrough;
5113
+ int columnCount, referencedFieldsCount, i, j, t, h;
5114
+ string intermediateConditions;
5115
+ array conditions, placeholders;
5113
5116
5114
5117
let nesting = false ,
5115
5118
className = get_class(this ),
@@ -5144,12 +5147,6 @@ abstract class Model extends AbstractInjectionAware implements EntityInterface,
5144
5147
referencedModel = relation-> getReferencedModel(),
5145
5148
referencedFields = relation-> getReferencedFields();
5146
5149
5147
- if unlikely typeof columns === " array" {
5148
- connection-> rollback(nesting);
5149
-
5150
- throw new Exception (" Not implemented in '" . className . " ' on Relation " . name);
5151
- }
5152
-
5153
5150
/**
5154
5151
* Create an implicit array for has-many/has-one records
5155
5152
*/
@@ -5159,18 +5156,6 @@ abstract class Model extends AbstractInjectionAware implements EntityInterface,
5159
5156
let relatedRecords = record;
5160
5157
}
5161
5158
5162
- if unlikely ! fetch value, this -> {columns} {
5163
- connection-> rollback(nesting);
5164
-
5165
- throw new Exception (
5166
- " The column '" . columns . " ' needs to be present in the model '" . className . " '"
5167
- );
5168
- }
5169
-
5170
- /**
5171
- * Get the value of the field from the current model
5172
- * Check if the relation is a has-many-to-many
5173
- */
5174
5159
let isThrough = (bool) relation-> isThrough();
5175
5160
5176
5161
/**
@@ -5180,7 +5165,38 @@ abstract class Model extends AbstractInjectionAware implements EntityInterface,
5180
5165
let intermediateModelName = relation-> getIntermediateModel(),
5181
5166
intermediateFields = relation-> getIntermediateFields(),
5182
5167
intermediateReferencedFields = relation-> getIntermediateReferencedFields();
5168
+ let placeholders = [];
5169
+ let conditions = [];
5183
5170
5171
+ /**
5172
+ * Always check for existing intermediate models
5173
+ * otherwise conflicts will arise on insert instead of update
5174
+ */
5175
+ if unlikely typeof columns === " array" {
5176
+ let columnCount = count(columns) - 1 ;
5177
+ for i in range(0 , columnCount) {
5178
+ let columnA = columns[i];
5179
+ let conditions[] = " [" . intermediateFields[i] . " ] = :APR" . i . " :" ;
5180
+ let placeholders[" APR" . i] = this -> {columnA};
5181
+ }
5182
+ let i = columnCount + 1 ;
5183
+ } else {
5184
+ let conditions[] = " [" . intermediateFields . " ] = :APR0:" ;
5185
+ let placeholders[" APR0" ] = this -> {columns};
5186
+ let i = 1 ;
5187
+ }
5188
+ if relation-> getType() === Relation:: HAS_MANY_THROUGH {
5189
+ if unlikely typeof referencedFields === " array" {
5190
+ let referencedFieldsCount = count(referencedFields) - 1 ;
5191
+ for j in range(0 , referencedFieldsCount) {
5192
+ let t = j + i;
5193
+ let conditions[] = " [" . intermediateReferencedFields[j] . " ] = :APR" . t . " :" ;
5194
+ }
5195
+ } else {
5196
+ let conditions[] = " [" . intermediateReferencedFields . " ] = :APR" . i . " :" ;
5197
+ }
5198
+ }
5199
+ let intermediateConditions = join(" AND " , conditions);
5184
5200
for recordAfter in relatedRecords {
5185
5201
/**
5186
5202
* Save the record and get messages
@@ -5191,14 +5207,25 @@ abstract class Model extends AbstractInjectionAware implements EntityInterface,
5191
5207
* referenced model
5192
5208
*/
5193
5209
this -> appendMessagesFrom(recordAfter);
5194
-
5210
+
5195
5211
/**
5196
5212
* Rollback the implicit transaction
5197
5213
*/
5198
5214
connection-> rollback(nesting);
5199
-
5215
+
5200
5216
return false ;
5201
5217
}
5218
+ if relation-> getType() === Relation:: HAS_MANY_THROUGH {
5219
+ if unlikely typeof referencedFields === " array" {
5220
+ for j in range(0 , referencedFieldsCount) {
5221
+ let columnA = referencedFields[j];
5222
+ let t = j + i;
5223
+ let placeholders[" APR" . t] = recordAfter-> {columnA};
5224
+ }
5225
+ } else {
5226
+ let placeholders[" APR" . i] = recordAfter-> {referencedFields};
5227
+ }
5228
+ }
5202
5229
/**
5203
5230
* Create a new instance of the intermediate model
5204
5231
*/
@@ -5207,45 +5234,43 @@ abstract class Model extends AbstractInjectionAware implements EntityInterface,
5207
5234
);
5208
5235
5209
5236
/**
5210
- * Has-one-through relations can only use one intermediate model.
5211
5237
* If it already exist, it can be updated with the new referenced key.
5212
5238
*/
5213
- if relation-> getType() == Relation:: HAS_ONE_THROUGH {
5214
- let existingIntermediateModel = intermediateModel-> findFirst(
5215
- [
5216
- " [" . intermediateFields . " ] = ?0" ,
5217
- " bind" : [value]
5218
- ]
5219
- );
5239
+ let existingIntermediateModel = intermediateModel-> findFirst(
5240
+ [
5241
+ intermediateConditions,
5242
+ " bind" : placeholders
5243
+ ]
5244
+ );
5220
5245
5221
- if existingIntermediateModel {
5222
- let intermediateModel = existingIntermediateModel;
5246
+ if existingIntermediateModel {
5247
+ let intermediateModel = existingIntermediateModel;
5248
+ }
5249
+ if ! existingIntermediateModel || relation-> getType() === Relation:: HAS_ONE_THROUGH {
5250
+ /**
5251
+ * Write value in the intermediate model
5252
+ */
5253
+ if unlikely typeof columns === " array" {
5254
+ for h in range(0 , columnCount) {
5255
+ let columnA = columns[h];
5256
+ let columnB = intermediateFields[h];
5257
+ let intermediateModel-> {columnB} = this -> {columnA};
5258
+ }
5259
+ } else {
5260
+ let intermediateModel-> {intermediateFields} = this -> {columns};
5261
+ }
5262
+ if unlikely typeof referencedFields === " array" {
5263
+ let referencedFieldsCount = count(referencedFields) - 1 ;
5264
+ for h in range(0 , referencedFieldsCount) {
5265
+ let columnA = referencedFields[h];
5266
+ let columnB = intermediateReferencedFields[h];
5267
+ let intermediateModel-> {columnB} = recordAfter-> {columnA};
5268
+ }
5269
+ } else {
5270
+ let intermediateModel-> {intermediateReferencedFields} = recordAfter-> {referencedFields};
5223
5271
}
5224
5272
}
5225
5273
5226
- /**
5227
- * Write value in the intermediate model
5228
- */
5229
- intermediateModel-> writeAttribute(
5230
- intermediateFields,
5231
- value
5232
- );
5233
-
5234
- /**
5235
- * Get the value from the referenced model
5236
- */
5237
- let intermediateValue = recordAfter-> readAttribute(
5238
- referencedFields
5239
- );
5240
-
5241
- /**
5242
- * Write the intermediate value in the intermediate model
5243
- */
5244
- intermediateModel-> writeAttribute(
5245
- intermediateReferencedFields,
5246
- intermediateValue
5247
- );
5248
-
5249
5274
/**
5250
5275
* Save the record and get messages
5251
5276
*/
@@ -5264,27 +5289,56 @@ abstract class Model extends AbstractInjectionAware implements EntityInterface,
5264
5289
}
5265
5290
}
5266
5291
} else {
5267
- for recordAfter in relatedRecords {
5268
- /**
5269
- * Assign the value to the
5270
- */
5271
- recordAfter-> writeAttribute(referencedFields, value);
5272
- /**
5273
- * Save the record and get messages
5274
- */
5275
- if ! recordAfter-> doSave(visited) {
5292
+ if unlikely typeof columns === " array" {
5293
+ let columnCount = count(columns) - 1 ;
5294
+ for recordAfter in relatedRecords {
5295
+ for i in range(0 , columnCount) {
5296
+ let columnA = columns[i];
5297
+ let columnB = referencedFields[i];
5298
+ let recordAfter-> {columnB} = this -> {columnA};
5299
+ }
5276
5300
/**
5277
- * Get the validation messages generated by the
5278
- * referenced model
5301
+ * Save the record and get messages
5279
5302
*/
5280
- this -> appendMessagesFrom(recordAfter);
5281
-
5303
+ if ! recordAfter-> doSave(visited) {
5304
+ /**
5305
+ * Get the validation messages generated by the
5306
+ * referenced model
5307
+ */
5308
+ this -> appendMessagesFrom(recordAfter);
5309
+
5310
+ /**
5311
+ * Rollback the implicit transaction
5312
+ */
5313
+ connection-> rollback(nesting);
5314
+
5315
+ return false ;
5316
+ }
5317
+ }
5318
+ } else {
5319
+ for recordAfter in relatedRecords {
5282
5320
/**
5283
- * Rollback the implicit transaction
5321
+ * Assign the value to the
5284
5322
*/
5285
- connection-> rollback(nesting);
5323
+ let recordAfter-> {referencedFields} = this -> {columns};
5324
+ /**
5325
+ * Save the record and get messages
5326
+ */
5327
+ if ! recordAfter-> doSave(visited) {
5286
5328
5287
- return false ;
5329
+ /**
5330
+ * Get the validation messages generated by the
5331
+ * referenced model
5332
+ */
5333
+ this -> appendMessagesFrom(recordAfter);
5334
+
5335
+ /**
5336
+ * Rollback the implicit transaction
5337
+ */
5338
+ connection-> rollback(nesting);
5339
+
5340
+ return false ;
5341
+ }
5288
5342
}
5289
5343
}
5290
5344
}
0 commit comments