Skip to content

Commit 6bb4ee9

Browse files
committed
1. loadAll now support relation form
POST array : [MainModel] => [ [attr1] => 'value1', // has many [relationName] => [ [0] => [ [rel0Attr1] => 'rel0Value1' ], [1] => [ [rel1Attr1] => 'rel1Value1' ] ], //has one [relationName] => [ [hasOneAttr1] => 'value' ] ] 2. Implement PR #43 by @mzglinski
1 parent f30bad6 commit 6bb4ee9

File tree

1 file changed

+198
-102
lines changed

1 file changed

+198
-102
lines changed

RelationTrait.php

Lines changed: 198 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -19,93 +19,142 @@
1919
use \yii\helpers\StringHelper;
2020
use 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+
2250
trait 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

Comments
 (0)