Skip to content

Commit bd014cc

Browse files
committed
fix delete related many-many
TODO : fix delete related many-many when no relation left add feature for has one relation
1 parent 9f5dbfd commit bd014cc

File tree

1 file changed

+158
-78
lines changed

1 file changed

+158
-78
lines changed

RelationTrait.php

Lines changed: 158 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@
1515
use \yii\helpers\Inflector;
1616
use \yii\helpers\StringHelper;
1717

18-
trait RelationTrait {
18+
trait RelationTrait
19+
{
1920

20-
public function loadAll($POST) {
21+
public function loadAll($POST)
22+
{
2123
if ($this->load($POST)) {
2224
$shortName = StringHelper::basename(get_class($this));
2325
foreach ($POST as $key => $value) {
@@ -31,28 +33,28 @@ public function loadAll($POST) {
3133
$relModelClass = $rel->modelClass;
3234
$relPKAttr = $relModelClass::primaryKey();
3335
$isManyMany = count($relPKAttr) > 1;
34-
if($isManyMany){
35-
foreach($value as $relPost){
36-
if(!empty(array_filter($relPost))){
36+
if ($isManyMany) {
37+
foreach ($value as $relPost) {
38+
if (!empty(array_filter($relPost))) {
3739
$condition = [];
3840
$condition[$this->primaryKey()[0]] = $this->primaryKey;
39-
foreach($relPost as $relAttr => $relAttrVal){
40-
if(in_array($relAttr, $relPKAttr))
41+
foreach ($relPost as $relAttr => $relAttrVal) {
42+
if (in_array($relAttr, $relPKAttr))
4143
$condition[$relAttr] = $relAttrVal;
4244
}
4345
$relObj = $relModelClass::findOne($condition);
44-
if(is_null($relObj)){
46+
if (is_null($relObj)) {
4547
$relObj = new $relModelClass;
4648
}
4749
$relObj->load($relPost, '');
4850
$container[] = $relObj;
4951
}
5052
}
5153
$this->populateRelation($relName, $container);
52-
}else if ($isHasMany) {
54+
} else if ($isHasMany) {
5355
$container = [];
5456
foreach ($value as $relPost) {
55-
if(!empty(array_filter($relPost))) {
57+
if (!empty(array_filter($relPost))) {
5658
/* @var $relObj ActiveRecord */
5759
$relObj = (empty($relPost[$relPKAttr[0]])) ? new $relModelClass : $relModelClass::findOne($relPost[$relPKAttr[0]]);
5860
$relObj->load($relPost, '');
@@ -73,82 +75,140 @@ public function loadAll($POST) {
7375
}
7476
}
7577

76-
public function saveAll() {
78+
public function saveAll()
79+
{
7780
/* @var $this ActiveRecord */
7881
$db = $this->getDb();
7982
$trans = $db->beginTransaction();
8083
try {
8184
if ($this->save()) {
8285
$error = 0;
83-
if(!empty($this->relatedRecords)){
86+
if (!empty($this->relatedRecords)) {
8487
foreach ($this->relatedRecords as $name => $records) {
85-
if(!empty($records)){
88+
if (!empty($records)) {
89+
$isHasMany = is_array($records);
8690
$AQ = $this->getRelation($name);
8791
$link = $AQ->link;
8892
$notDeletedPK = [];
8993
$relPKAttr = $records[0]->primaryKey();
90-
$isCompositePK = (count($relPKAttr) > 1);
91-
/* @var $relModel ActiveRecord */
92-
foreach ($records as $index => $relModel) {
93-
$notDeletedFK = [];
94-
foreach ($link as $key => $value) {
95-
$relModel->$key = $this->$value;
96-
$notDeletedFK[$key] = "$key = '{$this->$value}'";
97-
}
98-
$relSave = $relModel->save();
99-
if (!$relSave || !empty($relModel->errors)) {
100-
$relModelWords = Inflector::camel2words(StringHelper::basename($AQ->modelClass));
101-
$index++;
102-
foreach ($relModel->errors as $validation) {
103-
foreach ($validation as $errorMsg) {
104-
$this->addError($name, "$relModelWords #$index : $errorMsg");
105-
}
94+
$isManyMany = (count($relPKAttr) > 1);
95+
if ($isHasMany) {
96+
/* @var $relModel ActiveRecord */
97+
$i = 0;
98+
foreach ($records as $index => $relModel) {
99+
$notDeletedFK = [];
100+
foreach ($link as $key => $value) {
101+
$relModel->$key = $this->$value;
102+
if($isManyMany) $notDeletedFK[$key] = $this->$value;
103+
elseif($isHasMany) $notDeletedFK[$key] = "$key = '{$this->$value}'";
106104
}
107-
$error = 1;
108-
} else {
109-
//GET PK OF REL MODEL
110-
if ($isCompositePK) {
111-
foreach ($relModel->primaryKey as $attr => $value) {
112-
$notDeletedPK[$attr][] = "'$value'";
105+
$relSave = $relModel->save();
106+
107+
if (!$relSave || !empty($relModel->errors)) {
108+
$relModelWords = Inflector::camel2words(StringHelper::basename($AQ->modelClass));
109+
$index++;
110+
foreach ($relModel->errors as $validation) {
111+
foreach ($validation as $errorMsg) {
112+
$this->addError($name, "$relModelWords #$index : $errorMsg");
113+
}
113114
}
115+
$error = 1;
114116
} else {
115-
$notDeletedPK[] = "'$relModel->primaryKey'";
117+
//GET PK OF REL MODEL
118+
if ($isManyMany) {
119+
foreach ($relModel->primaryKey as $attr => $value) {
120+
$notDeletedPK[$i][$attr] = "'$value'";
121+
$fields[$attr] = "";
122+
}
123+
} else {
124+
$notDeletedPK[] = "'$relModel->primaryKey'";
125+
}
116126
}
127+
$i++;
117128
}
118-
}
119-
if (!$this->isNewRecord) {
120-
//DELETE WITH 'NOT IN' PK MODEL & REL MODEL
121-
$notDeletedFK = implode(' AND ', $notDeletedFK);
122-
if ($isCompositePK) {
123-
echo "composite pk\n";
124-
$compiledNotDeletedPK = [];
125-
foreach ($notDeletedPK as $attr => $pks) {
126-
$compiledNotDeletedPK[$attr] = "$attr NOT IN(" . implode(', ', $pks) . ")";
127-
if (!empty($compiledNotDeletedPK[$attr])) {
128-
$relModel->deleteAll("$notDeletedFK AND " . implode(' AND ', $compiledNotDeletedPK));
129+
if (!$this->isNewRecord) {
130+
//DELETE WITH 'NOT IN' PK MODEL & REL MODEL
131+
if ($isManyMany) {
132+
// echo "composite pk\n";
133+
// echo "not deleted PK : \n";
134+
// print_r($notDeletedPK);
135+
// echo "not deleted FK : \n";
136+
// print_r($notDeletedFK);
137+
// echo "\nfields : \n";
138+
// print_r($fields) . "\n";
139+
$contoh = ['and',['actor_id' => 1],['not in', new \yii\db\Expression('(actor_id, film_id)'),
140+
[
141+
new \yii\db\Expression('(1,1)'),
142+
new \yii\db\Expression('(1,23)'),
143+
new \yii\db\Expression('(1,25)'),
144+
new \yii\db\Expression('(1,980)'),
145+
new \yii\db\Expression('(1,970)'),
146+
new \yii\db\Expression('(1,939)')
147+
]]];
148+
// echo $relModel::find()->where(['and',['actor_id' => 1],['not in', new \yii\db\Expression('(actor_id, film_id)'),
149+
// [
150+
// new \yii\db\Expression('(1,1)'),
151+
// new \yii\db\Expression('(1,23)'),
152+
// new \yii\db\Expression('(1,25)'),
153+
// new \yii\db\Expression('(1,980)'),
154+
// new \yii\db\Expression('(1,970)'),
155+
// new \yii\db\Expression('(1,939)')
156+
// ]]])->createCommand()->rawSql;
157+
$compiledFields = implode(", ", array_keys($fields));
158+
$compiledNotDeletedPK = ['and',$notDeletedFK];
159+
$notIn = ['not in', new \yii\db\Expression("($compiledFields)")];
160+
foreach($notDeletedPK as $value){
161+
$v = [];
162+
foreach($fields as $key => $f){
163+
$v[] = $value[$key];
164+
}
165+
$c = implode(',',$v);
166+
$content[] = new \yii\db\Expression("($c)");
167+
}
168+
array_push($notIn,$content);
169+
array_push($compiledNotDeletedPK,$notIn);
170+
try{
171+
$relModel->deleteAll($compiledNotDeletedPK);
172+
} catch (\yii\db\IntegrityException $exc) {
173+
$this->addError($name, "Data can't be deleted because it's still used by another data.");
174+
}
175+
} else {
176+
$notDeletedFK = implode(' AND ', $notDeletedFK);
177+
$compiledNotDeletedPK = implode(',', $notDeletedPK);
178+
if (!empty($compiledNotDeletedPK)) {
179+
try {
180+
$relModel->deleteAll($notDeletedFK . ' AND ' . $relPKAttr[0] . " NOT IN ($compiledNotDeletedPK)");
181+
182+
} catch (\yii\db\IntegrityException $exc) {
183+
$this->addError($name, "Data can't be deleted because it's still used by another data.");
184+
}
129185
}
130-
print_r("$notDeletedFK AND " . implode(' AND ', $compiledNotDeletedPK)."\n");
131-
}
132-
} else {
133-
$compiledNotDeletedPK = implode(',', $notDeletedPK);
134-
if (!empty($compiledNotDeletedPK)) {
135-
$relModel->deleteAll($notDeletedFK . ' AND ' . $relPKAttr[0] . " NOT IN ($compiledNotDeletedPK)");
136186
}
137187
}
188+
} else {
189+
//Has One
138190
}
191+
139192
}
140193
}
141-
}else{
194+
} else {
195+
//No Children left
142196
if (!$this->isNewRecord) {
143197
$relData = $this->getRelationData();
144-
foreach($relData as $rel){
198+
foreach ($relData as $rel) {
145199
/* @var $relModel ActiveRecord */
146200
$relModel = new $rel['modelClass'];
147201
$condition = [];
148-
foreach($rel['link'] as $k => $v){
149-
$condition[] = $k . " = ". $this->$v;
202+
foreach ($rel['link'] as $k => $v) {
203+
if(property_exists($this, $v))
204+
$condition[] = $k . " = " . $this->$v;
205+
}
206+
try {
207+
$relModel->deleteAll(implode(" AND ", $condition));
208+
} catch (\yii\db\IntegrityException $exc) {
209+
$this->addError($rel['name'], "Data can't be deleted because it's still used by another data.");
210+
$error = 1;
150211
}
151-
$relModel->deleteAll(implode(" AND ",$condition));
152212
}
153213
}
154214
}
@@ -168,21 +228,22 @@ public function saveAll() {
168228
}
169229
}
170230

171-
public function deleteWithRelated() {
231+
public function deleteWithRelated()
232+
{
172233
/* @var $this ActiveRecord */
173234
$db = $this->getDb();
174235
$trans = $db->beginTransaction();
175236
try {
176237
$error = 0;
177238
$relData = $this->getRelationData();
178239
foreach ($relData as $data) {
179-
if($data['ismultiple']){
240+
if ($data['ismultiple']) {
180241
$link = $data['link'];
181-
if(count($this->{$data['name']})){
242+
if (count($this->{$data['name']})) {
182243
$relPKAttr = $this->{$data['name']}[0]->primaryKey();
183244
$isCompositePK = (count($relPKAttr) > 1);
184245
foreach ($link as $key => $value) {
185-
if(isset($this->$value)){
246+
if (isset($this->$value)) {
186247
$array[$key] = $key . ' = ' . $this->$value;
187248
}
188249
}
@@ -194,7 +255,7 @@ public function deleteWithRelated() {
194255
$trans->rollback();
195256
return false;
196257
}
197-
if($this->delete()){
258+
if ($this->delete()) {
198259
$trans->commit();
199260
return true;
200261
}
@@ -206,8 +267,8 @@ public function deleteWithRelated() {
206267
}
207268

208269

209-
210-
public function getRelationData() {
270+
public function getRelationData()
271+
{
211272
$ARMethods = get_class_methods('\yii\db\ActiveRecord');
212273
$modelMethods = get_class_methods('\yii\base\Model');
213274
$reflection = new \ReflectionClass($this);
@@ -218,32 +279,50 @@ public function getRelationData() {
218279
if (in_array($method->name, $ARMethods) || in_array($method->name, $modelMethods)) {
219280
continue;
220281
}
221-
if($method->name === 'bindModels') {continue;}
222-
if($method->name === 'attachBehaviorInternal') {continue;}
223-
if($method->name === 'loadAll') {continue;}
224-
if($method->name === 'saveAll') {continue;}
225-
if($method->name === 'getRelationData') {continue;}
226-
if($method->name === 'getAttributesWithRelatedAsPost') {continue;}
227-
if($method->name === 'getAttributesWithRelated') {continue;}
228-
if($method->name === 'deleteWithRelated') {continue;}
282+
if ($method->name === 'bindModels') {
283+
continue;
284+
}
285+
if ($method->name === 'attachBehaviorInternal') {
286+
continue;
287+
}
288+
if ($method->name === 'loadAll') {
289+
continue;
290+
}
291+
if ($method->name === 'saveAll') {
292+
continue;
293+
}
294+
if ($method->name === 'getRelationData') {
295+
continue;
296+
}
297+
if ($method->name === 'getAttributesWithRelatedAsPost') {
298+
continue;
299+
}
300+
if ($method->name === 'getAttributesWithRelated') {
301+
continue;
302+
}
303+
if ($method->name === 'deleteWithRelated') {
304+
continue;
305+
}
229306
try {
230-
$rel = call_user_func(array($this,$method->name));
231-
if($rel instanceof \yii\db\ActiveQuery){
307+
$rel = call_user_func(array($this, $method->name));
308+
if ($rel instanceof \yii\db\ActiveQuery) {
232309
$stack[$i]['name'] = lcfirst(str_replace('get', '', $method->name));
233310
$stack[$i]['method'] = $method->name;
234311
$stack[$i]['ismultiple'] = $rel->multiple;
235312
$stack[$i]['modelClass'] = $rel->modelClass;
236313
$stack[$i]['link'] = $rel->link;
237314
$i++;
238315
}
239-
} catch (\yii\base\ErrorException $exc) {}
316+
} catch (\yii\base\ErrorException $exc) {
317+
}
240318
}
241319
return $stack;
242320
}
243321

244322
/* this function is deprecated */
245323

246-
public function getAttributesWithRelatedAsPost() {
324+
public function getAttributesWithRelatedAsPost()
325+
{
247326
$return = [];
248327
$shortName = StringHelper::basename(get_class($this));
249328
$return[$shortName] = $this->attributes;
@@ -256,7 +335,8 @@ public function getAttributesWithRelatedAsPost() {
256335
return $return;
257336
}
258337

259-
public function getAttributesWithRelated() {
338+
public function getAttributesWithRelated()
339+
{
260340
$return = $this->attributes;
261341
foreach ($this->relatedRecords as $name => $records) {
262342
foreach ($records as $index => $record) {

0 commit comments

Comments
 (0)