@@ -41,6 +41,8 @@ use Phalcon\Mvc\Model\TransactionInterface;
41
41
use Phalcon\Mvc\Model\ValidationFailed;
42
42
use Phalcon\Mvc\ModelInterface;
43
43
use Phalcon\Filter\Validation\ValidationInterface;
44
+ use Phalcon\Support\Collection;
45
+ use Phalcon\Support\Collection\CollectionInterface;
44
46
use Serializable;
45
47
46
48
/**
@@ -2579,12 +2581,35 @@ abstract class Model extends AbstractInjectionAware implements EntityInterface,
2579
2581
* $robot->save();
2580
2582
*```
2581
2583
*/
2584
+
2582
2585
public function save () -> bool
2586
+ {
2587
+ var visited;
2588
+ let visited = new Collection ();
2589
+ return this -> doSave(visited);
2590
+ }
2591
+
2592
+ /**
2593
+ * Inserted or updates model instance, expects a visited list of objects.
2594
+ *
2595
+ * @param CollectionInterface $visited
2596
+ *
2597
+ * @return bool
2598
+ */
2599
+ public function doSave (<CollectionInterface> visited ) -> bool
2583
2600
{
2584
2601
var metaData, schema, writeConnection, readConnection, source, table,
2585
- identityField, exists, success, relatedToSave;
2602
+ identityField, exists, success, relatedToSave, objId ;
2586
2603
bool hasRelatedToSave;
2587
2604
2605
+ let objId = spl_object_id(this );
2606
+
2607
+ if true === visited-> has(objId) {
2608
+ return true ;
2609
+ }
2610
+
2611
+ visited-> set (objId, this );
2612
+
2588
2613
let metaData = this -> getModelsMetaData();
2589
2614
2590
2615
/**
@@ -2610,7 +2635,7 @@ abstract class Model extends AbstractInjectionAware implements EntityInterface,
2610
2635
let hasRelatedToSave = count(relatedToSave) > 0 ;
2611
2636
2612
2637
if hasRelatedToSave {
2613
- if this -> preSaveRelatedRecords(writeConnection, relatedToSave) === false {
2638
+ if this -> preSaveRelatedRecords(writeConnection, relatedToSave, visited ) === false {
2614
2639
return false ;
2615
2640
}
2616
2641
}
@@ -2695,7 +2720,7 @@ abstract class Model extends AbstractInjectionAware implements EntityInterface,
2695
2720
/**
2696
2721
* Change the dirty state to persistent
2697
2722
*/
2698
- if success {
2723
+ if true === success {
2699
2724
let this -> dirtyState = self :: DIRTY_STATE_PERSISTENT ;
2700
2725
}
2701
2726
@@ -2711,7 +2736,8 @@ abstract class Model extends AbstractInjectionAware implements EntityInterface,
2711
2736
*/
2712
2737
let success = this -> postSaveRelatedRecords(
2713
2738
writeConnection,
2714
- relatedToSave
2739
+ relatedToSave,
2740
+ visited
2715
2741
);
2716
2742
}
2717
2743
}
@@ -4964,9 +4990,11 @@ abstract class Model extends AbstractInjectionAware implements EntityInterface,
4964
4990
* Saves related records that must be stored prior to save the master record
4965
4991
*
4966
4992
* @param ModelInterface[] related
4993
+ * @param CollectionInterface visited
4967
4994
* @return bool
4968
4995
*/
4969
- protected function preSaveRelatedRecords (<AdapterInterface> connection, related ) -> bool
4996
+
4997
+ protected function preSaveRelatedRecords (<AdapterInterface> connection, related, <CollectionInterface> visited ) -> bool
4970
4998
{
4971
4999
var className, manager, type, relation, columns, referencedFields, nesting, name, record;
4972
5000
@@ -5006,7 +5034,6 @@ abstract class Model extends AbstractInjectionAware implements EntityInterface,
5006
5034
" Only objects can be stored as part of belongs-to relations in '" . get_class(this ) . " ' Relation " . name
5007
5035
);
5008
5036
}
5009
-
5010
5037
let columns = relation-> getFields(),
5011
5038
referencedFields = relation-> getReferencedFields();
5012
5039
// let columns = relation->getFields(),
@@ -5023,7 +5050,7 @@ abstract class Model extends AbstractInjectionAware implements EntityInterface,
5023
5050
* If dynamic update is enabled, saving the record must not take any action
5024
5051
* Only save if the model is dirty to prevent circular relations causing an infinite loop
5025
5052
*/
5026
- if record-> dirtyState !== Model:: DIRTY_STATE_PERSISTENT && ! record-> save( ) {
5053
+ if record-> dirtyState !== Model:: DIRTY_STATE_PERSISTENT && ! record-> doSave(visited ) {
5027
5054
/**
5028
5055
* Get the validation messages generated by the
5029
5056
* referenced model
@@ -5072,9 +5099,10 @@ abstract class Model extends AbstractInjectionAware implements EntityInterface,
5072
5099
* Save the related records assigned in the has-one/has-many relations
5073
5100
*
5074
5101
* @param ModelInterface[] related
5102
+ * @param CollectionInterface visited
5075
5103
* @return bool
5076
5104
*/
5077
- protected function postSaveRelatedRecords (<AdapterInterface> connection, related ) -> bool
5105
+ protected function postSaveRelatedRecords (<AdapterInterface> connection, related, <CollectionInterface> visited ) -> bool
5078
5106
{
5079
5107
var nesting, className, manager, relation, name, record,
5080
5108
columns, referencedModel, referencedFields, relatedRecords, value,
@@ -5157,7 +5185,7 @@ abstract class Model extends AbstractInjectionAware implements EntityInterface,
5157
5185
/**
5158
5186
* Save the record and get messages
5159
5187
*/
5160
- if ! recordAfter-> save( ) {
5188
+ if ! recordAfter-> doSave(visited ) {
5161
5189
/**
5162
5190
* Get the validation messages generated by the
5163
5191
* referenced model
@@ -5221,7 +5249,7 @@ abstract class Model extends AbstractInjectionAware implements EntityInterface,
5221
5249
/**
5222
5250
* Save the record and get messages
5223
5251
*/
5224
- if ! intermediateModel-> save( ) {
5252
+ if ! intermediateModel-> doSave(visited ) {
5225
5253
/**
5226
5254
* Get the validation messages generated by the referenced model
5227
5255
*/
@@ -5244,7 +5272,7 @@ abstract class Model extends AbstractInjectionAware implements EntityInterface,
5244
5272
/**
5245
5273
* Save the record and get messages
5246
5274
*/
5247
- if ! recordAfter-> save( ) {
5275
+ if ! recordAfter-> doSave(visited ) {
5248
5276
/**
5249
5277
* Get the validation messages generated by the
5250
5278
* referenced model
0 commit comments