1919use \yii \helpers \StringHelper ;
2020use yii \helpers \ArrayHelper ;
2121
22+ /*
23+ * add this line to your Model to enable soft delete
24+ *
25+ * private $_rt_softdelete;
26+ *
27+ * function __construct(){
28+ * $this->_rt_softdelete = [
29+ * '<column>' => <undeleted row marker value>
30+ * // multiple row marker column example
31+ * 'isdeleted' => 1,
32+ * 'deleted_by' => \Yii::$app->user->id,
33+ * 'deleted_at' => date('Y-m-d H:i:s')
34+ * ];
35+ * }
36+ * add this line to your Model to enable soft restore
37+ * private $_rt_softrestore;
38+ *
39+ * function __construct(){
40+ * $this->_rt_softrestore = [
41+ * '<column>' => <undeleted row marker value>
42+ * // multiple row marker column example
43+ * 'isdeleted' => 0,
44+ * 'deleted_by' => 0,
45+ * 'deleted_at' => 'NULL'
46+ * ];
47+ * }
48+ */
49+
2250trait RelationTrait
2351{
24- /* add this line to your Model to enable soft delete
25- *
26- * private $_rt_softdelete = ['<column>' => <deleted row marker value>];
27- * example :
28- * private $_rt_softdelete = ['isdeleted' => 1];
29- * or :
30- * private $_rt_softdelete = ['deleted_by' => Yii::app()->user->id]
31- * add this line to your Model to enable soft restore
32- * private $_rt_softrestore = ['<column>' => <undeleted row marker value];
33- * example :
34- * private $_rt_softrestore = ['isdeleted' => 0];
35- * or :
36- * private $_rt_softdelete = ['deleted_by' => 0];
37- */
3852
53+ /**
54+ * Load all attribute including related attribute
55+ * @param $POST
56+ * @param array $skippedRelations
57+ * @return bool
58+ */
3959 public function loadAll ($ POST , $ skippedRelations = [])
4060 {
4161 if ($ this ->load ($ POST )) {
4262 $ shortName = StringHelper::basename (get_class ($ this ));
4363 $ relData = $ this ->getRelationData ();
44- foreach ($ POST as $ key => $ value ) {
45- if ($ key != $ shortName && strpos ($ key , '_ ' ) === false ) {
46- /* @var $AQ ActiveQuery */
47- /* @var $this ActiveRecord */
48- /* @var $relObj ActiveRecord */
49- $ isHasMany = is_array ($ value ) && is_array (current ($ value ));
50- $ relName = ($ isHasMany ) ? lcfirst (Inflector::pluralize ($ key )) : lcfirst ($ key );
64+ foreach ($ POST as $ model => $ attr ) {
65+ if (is_array ($ attr )) {
66+ if ($ model == $ shortName ) {
67+ foreach ($ attr as $ relName => $ relAttr ) {
68+ if (is_array ($ relAttr )) {
69+ $ isHasMany = !ArrayHelper::isAssociative ($ relAttr );
70+ if (in_array ($ relName , $ skippedRelations ) || !array_key_exists ($ relName , $ relData )) {
71+ continue ;
72+ }
73+
74+ return $ this ->loadToRelation ($ isHasMany , $ relName , $ relAttr );
75+ }
76+ }
77+ } else {
78+ $ isHasMany = is_array ($ attr ) && is_array (current ($ attr ));
79+ $ relName = ($ isHasMany ) ? lcfirst (Inflector::pluralize ($ model )) : lcfirst ($ model );
80+ if (in_array ($ relName , $ skippedRelations ) || !array_key_exists ($ relName , $ relData )) {
81+ continue ;
82+ }
5183
52- if (in_array ($ relName , $ skippedRelations ) || !array_key_exists ($ relName , $ relData )) {
53- continue ;
84+ return $ this ->loadToRelation ($ isHasMany , $ relName , $ attr );
5485 }
86+ }
87+ }
88+ }
89+ return false ;
90+ }
5591
56- $ AQ = $ this ->getRelation ($ relName );
57- /* @var $relModelClass ActiveRecord */
58- $ relModelClass = $ AQ ->modelClass ;
59- $ relPKAttr = $ relModelClass ::primaryKey ();
60- $ isManyMany = count ($ relPKAttr ) > 1 ;
92+ /**
93+ * Refactored from loadAll() function
94+ * @param $isHasMany
95+ * @param $relName
96+ * @param $v
97+ * @return bool
98+ */
99+ private function loadToRelation ($ isHasMany , $ relName , $ v )
100+ {
101+ /* @var $AQ ActiveQuery */
102+ /* @var $this ActiveRecord */
103+ /* @var $relObj ActiveRecord */
104+ $ AQ = $ this ->getRelation ($ relName );
105+ /* @var $relModelClass ActiveRecord */
106+ $ relModelClass = $ AQ ->modelClass ;
107+ $ relPKAttr = $ relModelClass ::primaryKey ();
108+ $ isManyMany = count ($ relPKAttr ) > 1 ;
61109
62- if ($ isManyMany ) {
63- $ container = [];
64- foreach ($ value as $ relPost ) {
65- if (array_filter ($ relPost )) {
66- $ condition = [];
67- $ condition [$ relPKAttr [0 ]] = $ this ->primaryKey ;
68- foreach ($ relPost as $ relAttr => $ relAttrVal ) {
69- if (in_array ($ relAttr , $ relPKAttr )) {
70- $ condition [$ relAttr ] = $ relAttrVal ;
71- }
72- }
73- $ relObj = $ relModelClass ::findOne ($ condition );
74- if (is_null ($ relObj )) {
75- $ relObj = new $ relModelClass ;
76- }
77- $ relObj ->load ($ relPost , '' );
78- $ container [] = $ relObj ;
79- }
80- }
81- $ this ->populateRelation ($ relName , $ container );
82- } else if ($ isHasMany ) {
83- $ container = [];
84- foreach ($ value as $ relPost ) {
85- if (array_filter ($ relPost )) {
86- /* @var $relObj ActiveRecord */
87- $ relObj = (empty ($ relPost [$ relPKAttr [0 ]])) ? new $ relModelClass () : $ relModelClass ::findOne ($ relPost [$ relPKAttr [0 ]]);
88- if (is_null ($ relObj )) {
89- $ relObj = new $ relModelClass ();
90- }
91- $ relObj ->load ($ relPost , '' );
92- $ container [] = $ relObj ;
93- }
110+ if ($ isManyMany ) {
111+ $ container = [];
112+ foreach ($ v as $ relPost ) {
113+ if (array_filter ($ relPost )) {
114+ $ condition = [];
115+ $ condition [$ relPKAttr [0 ]] = $ this ->primaryKey ;
116+ foreach ($ relPost as $ relAttr => $ relAttrVal ) {
117+ if (in_array ($ relAttr , $ relPKAttr )) {
118+ $ condition [$ relAttr ] = $ relAttrVal ;
94119 }
95- $ this ->populateRelation ($ relName , $ container );
96- } else {
97- $ relObj = (empty ($ value [$ relPKAttr [0 ]])) ? new $ relModelClass : $ relModelClass ::findOne ($ value [$ relPKAttr [0 ]]);
98- $ relObj ->load ($ value , '' );
99- $ this ->populateRelation ($ relName , $ relObj );
100120 }
121+ $ relObj = $ relModelClass ::findOne ($ condition );
122+ if (is_null ($ relObj )) {
123+ $ relObj = new $ relModelClass ;
124+ }
125+ $ relObj ->load ($ relPost , '' );
126+ $ container [] = $ relObj ;
101127 }
102128 }
103- return true ;
129+ $ this ->populateRelation ($ relName , $ container );
130+ } else if ($ isHasMany ) {
131+ $ container = [];
132+ foreach ($ v as $ relPost ) {
133+ if (array_filter ($ relPost )) {
134+ /* @var $relObj ActiveRecord */
135+ $ relObj = (empty ($ relPost [$ relPKAttr [0 ]])) ? new $ relModelClass () : $ relModelClass ::findOne ($ relPost [$ relPKAttr [0 ]]);
136+ if (is_null ($ relObj )) {
137+ $ relObj = new $ relModelClass ();
138+ }
139+ $ relObj ->load ($ relPost , '' );
140+ $ container [] = $ relObj ;
141+ }
142+ }
143+ $ this ->populateRelation ($ relName , $ container );
104144 } else {
105- return false ;
145+ $ relObj = (empty ($ v [$ relPKAttr [0 ]])) ? new $ relModelClass : $ relModelClass ::findOne ($ v [$ relPKAttr [0 ]]);
146+ $ relObj ->load ($ v , '' );
147+ $ this ->populateRelation ($ relName , $ relObj );
106148 }
149+ return true ;
107150 }
108151
152+ /**
153+ * Save model including all related model already loaded
154+ * @param array $skippedRelations
155+ * @return bool
156+ * @throws Exception
157+ */
109158 public function saveAll ($ skippedRelations = [])
110159 {
111160 /* @var $this ActiveRecord */
@@ -220,20 +269,36 @@ public function saveAll($skippedRelations = [])
220269 }
221270 }
222271 }
223- } else {
224- //No Children left
225- $ relAvail = array_keys ($ this ->relatedRecords );
226- $ relData = $ this ->getRelationData ();
227- $ allRel = array_keys ($ relData );
228- $ noChildren = array_diff ($ allRel , $ relAvail );
272+ }
273+
274+ //No Children left
275+ $ relAvail = array_keys ($ this ->relatedRecords );
276+ $ relData = $ this ->getRelationData ();
277+ $ allRel = array_keys ($ relData );
278+ $ noChildren = array_diff ($ allRel , $ relAvail );
229279
230- foreach ($ noChildren as $ relName ) {
231- /* @var $relModel ActiveRecord */
232- if (empty ($ relData [$ relName ]['via ' ]) && !in_array ($ relName , $ skippedRelations )) {
233- $ relModel = new $ relData [$ relName ]['modelClass ' ];
234- $ condition = [];
235- $ isManyMany = count ($ relModel ->primaryKey ()) > 1 ;
236- if ($ isManyMany ) {
280+ foreach ($ noChildren as $ relName ) {
281+ /* @var $relModel ActiveRecord */
282+ if (empty ($ relData [$ relName ]['via ' ]) && !in_array ($ relName , $ skippedRelations )) {
283+ $ relModel = new $ relData [$ relName ]['modelClass ' ];
284+ $ condition = [];
285+ $ isManyMany = count ($ relModel ->primaryKey ()) > 1 ;
286+ if ($ isManyMany ) {
287+ foreach ($ relData [$ relName ]['link ' ] as $ k => $ v ) {
288+ $ condition [$ k ] = $ this ->$ v ;
289+ }
290+ try {
291+ if ($ isSoftDelete ) {
292+ $ relModel ->updateAll ($ this ->_rt_softdelete , ['and ' , $ condition ]);
293+ } else {
294+ $ relModel ->deleteAll (['and ' , $ condition ]);
295+ }
296+ } catch (IntegrityException $ exc ) {
297+ $ this ->addError ($ relData [$ relName ]['name ' ], Yii::t ('mtrelt ' , "Data can't be deleted because it's still used by another data. " ));
298+ $ error = true ;
299+ }
300+ } else {
301+ if ($ relData [$ relName ]['ismultiple ' ]) {
237302 foreach ($ relData [$ relName ]['link ' ] as $ k => $ v ) {
238303 $ condition [$ k ] = $ this ->$ v ;
239304 }
@@ -247,27 +312,12 @@ public function saveAll($skippedRelations = [])
247312 $ this ->addError ($ relData [$ relName ]['name ' ], Yii::t ('mtrelt ' , "Data can't be deleted because it's still used by another data. " ));
248313 $ error = true ;
249314 }
250- } else {
251- if ($ relData [$ relName ]['ismultiple ' ]) {
252- foreach ($ relData [$ relName ]['link ' ] as $ k => $ v ) {
253- $ condition [$ k ] = $ this ->$ v ;
254- }
255- try {
256- if ($ isSoftDelete ) {
257- $ relModel ->updateAll ($ this ->_rt_softdelete , ['and ' , $ condition ]);
258- } else {
259- $ relModel ->deleteAll (['and ' , $ condition ]);
260- }
261- } catch (IntegrityException $ exc ) {
262- $ this ->addError ($ relData [$ relName ]['name ' ], Yii::t ('mtrelt ' , "Data can't be deleted because it's still used by another data. " ));
263- $ error = true ;
264- }
265- }
266315 }
267316 }
268317 }
269318 }
270319
320+
271321 if ($ error ) {
272322 $ trans ->rollback ();
273323 $ this ->isNewRecord = $ isNewRecord ;
@@ -285,6 +335,13 @@ public function saveAll($skippedRelations = [])
285335 }
286336 }
287337
338+
339+ /**
340+ * Deleted model row with all related records
341+ * @param array $skippedRelations
342+ * @return bool
343+ * @throws Exception
344+ */
288345 public function deleteWithRelated ($ skippedRelations = [])
289346 {
290347 /* @var $this ActiveRecord */
@@ -338,6 +395,12 @@ public function deleteWithRelated($skippedRelations = [])
338395 }
339396 }
340397
398+ /**
399+ * Restore soft deleted row including all related records
400+ * @param array $skippedRelations
401+ * @return bool
402+ * @throws Exception
403+ */
341404 public function restoreWithRelated ($ skippedRelations = [])
342405 {
343406 if (!isset ($ this ->_rt_softrestore )) {
@@ -381,8 +444,6 @@ public function restoreWithRelated($skippedRelations = [])
381444 }
382445 }
383446
384- // abstract protected function relationNames();
385-
386447 public function getRelationData ()
387448 {
388449 $ stack = [];
@@ -437,8 +498,28 @@ public function getRelationData()
437498 return $ stack ;
438499 }
439500
440- /* this function is deprecated */
441-
501+ /**
502+ * This function is deprecated!
503+ * Return array like this
504+ * Array
505+ * (
506+ * [MainClass] => Array
507+ * (
508+ * [attr1] => value1
509+ * [attr2] => value2
510+ * )
511+ *
512+ * [RelatedClass] => Array
513+ * (
514+ * [0] => Array
515+ * (
516+ * [attr1] => value1
517+ * [attr2] => value2
518+ * )
519+ * )
520+ * )
521+ * @return array
522+ */
442523 public function getAttributesWithRelatedAsPost ()
443524 {
444525 $ return = [];
@@ -458,6 +539,23 @@ public function getAttributesWithRelatedAsPost()
458539 return $ return ;
459540 }
460541
542+ /**
543+ * return array like this
544+ * Array
545+ * (
546+ * [attr1] => value1
547+ * [attr2] => value2
548+ * [relationName] => Array
549+ * (
550+ * [0] => Array
551+ * (
552+ * [attr1] => value1
553+ * [attr2] => value2
554+ * )
555+ * )
556+ * )
557+ * @return array
558+ */
461559 public function getAttributesWithRelated ()
462560 {
463561 /* @var $this ActiveRecord */
@@ -478,8 +576,6 @@ public function getAttributesWithRelated()
478576 /**
479577 * TranslationTrait manages methods for all translations used in Krajee extensions
480578 *
481- * @property array $i18n
482- *
483579 * @author Kartik Visweswaran <[email protected] > 484580 * @since 1.8.8
485581 * Yii i18n messages configuration for generating translations
0 commit comments