Browse Source

Merge pull request #15983 from cakephp/backport-15995

backport: Allow association binding key to be null during cascadeDelete
Mark Story 4 years ago
parent
commit
aac3e09a0f

+ 5 - 1
src/ORM/Association/DependentDeleteHelper.php

@@ -42,7 +42,11 @@ class DependentDeleteHelper
         $table = $association->getTarget();
         $foreignKey = array_map([$association, 'aliasField'], (array)$association->getForeignKey());
         $bindingKey = (array)$association->getBindingKey();
-        $conditions = array_combine($foreignKey, $entity->extract($bindingKey));
+        $bindingValue = $entity->extract($bindingKey);
+        if (in_array(null, $bindingValue, true)) {
+            return true;
+        }
+        $conditions = array_combine($foreignKey, $bindingValue);
 
         if ($association->getCascadeCallbacks()) {
             foreach ($association->find()->where($conditions)->all()->toList() as $related) {

+ 41 - 0
tests/Fixture/NullableAuthorsFixture.php

@@ -0,0 +1,41 @@
+<?php
+/**
+ * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
+ * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the LICENSE.txt
+ * Redistributions of files must retain the above copyright notice
+ *
+ * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
+ * @link          https://cakephp.org CakePHP(tm) Project
+ * @since         4.2.10
+ * @license       https://opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Cake\Test\Fixture;
+
+use Cake\TestSuite\Fixture\TestFixture;
+
+class NullableAuthorsFixture extends TestFixture
+{
+    /**
+     * fields property
+     *
+     * @var array
+     */
+    public $fields = [
+        'id' => ['type' => 'integer'],
+        'author_id' => ['type' => 'integer', 'null' => true],
+        '_constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]],
+    ];
+
+    /**
+     * records property
+     *
+     * @var array
+     */
+    public $records = [
+        ['author_id' => 3],
+        ['author_id' => null],
+    ];
+}

+ 33 - 2
tests/TestCase/ORM/Association/HasOneTest.php

@@ -31,7 +31,7 @@ class HasOneTest extends TestCase
      *
      * @var array
      */
-    public $fixtures = ['core.Users', 'core.Profiles'];
+    public $fixtures = ['core.Articles', 'core.NullableAuthors', 'core.Users', 'core.Profiles'];
 
     /**
      * @var bool
@@ -371,7 +371,38 @@ class HasOneTest extends TestCase
     }
 
     /**
-     * Test cascading delete with has many.
+     * Tests cascading deletes on entities with null binding and foreign key.
+     */
+    public function testCascadeDeleteNullBindingNullForeign()
+    {
+        $Articles = $this->getTableLocator()->get('Articles');
+        $Authors = $this->getTableLocator()->get('NullableAuthors');
+
+        $config = [
+            'dependent' => true,
+            'sourceTable' => $Authors,
+            'targetTable' => $Articles,
+            'bindingKey' => 'author_id',
+            'foreignKey' => 'author_id',
+            'cascadeCallbacks' => false,
+        ];
+        $association = $Authors->hasOne('Articles', $config);
+
+        // create article with null foreign key
+        $entity = new Entity(['author_id' => null, 'title' => 'this has no author', 'body' => 'I am abandoned', 'published' => 'N']);
+        $Articles->save($entity);
+
+        // get author with null binding key
+        $entity = $Authors->get(2, ['contain' => 'Articles']);
+        $this->assertNull($entity->article);
+        $this->assertTrue($association->cascadeDelete($entity));
+
+        $query = $Articles->query();
+        $this->assertSame(4, $query->count(), 'No articles should be deleted');
+    }
+
+    /**
+     * Test cascading delete with has one.
      *
      * @return void
      */