Browse Source

Merge branch 'master' into 3.next

Mark Story 9 years ago
parent
commit
3943052d35

+ 1 - 1
src/Datasource/Exception/InvalidPrimaryKeyException.php

@@ -17,7 +17,7 @@ namespace Cake\Datasource\Exception;
 use RuntimeException;
 
 /**
- * Exception raised when a particular record was not found
+ * Exception raised when the provided primary key does not match the table primary key
  *
  */
 class InvalidPrimaryKeyException extends RuntimeException

+ 1 - 1
src/ORM/Rule/IsUnique.php

@@ -91,7 +91,7 @@ class IsUnique
     {
         $aliased = [];
         foreach ($conditions as $key => $value) {
-            $aliased["$alias.$key"] = $value;
+            $aliased["$alias.$key IS"] = $value;
         }
         return $aliased;
     }

+ 3 - 0
src/Routing/Exception/MissingRouteException.php

@@ -31,6 +31,9 @@ class MissingRouteException extends Exception
      */
     public function __construct($message, $code = 404)
     {
+        if (is_array($message) && isset($message['message'])) {
+            $this->_messageTemplate = $message['message'];
+        }
         parent::__construct($message, $code);
     }
 }

+ 8 - 3
src/Routing/RouteCollection.php

@@ -238,9 +238,14 @@ class RouteCollection
             if (isset($this->_named[$name])) {
                 $route = $this->_named[$name];
                 $out = $route->match($url + $route->defaults, $context);
-            }
-            if ($out) {
-                return $out;
+                if ($out) {
+                    return $out;
+                }
+                throw new MissingRouteException([
+                    'url' => $name,
+                    'context' => $context,
+                    'message' => 'A named route was found for "%s", but matching failed.',
+                ]);
             }
             throw new MissingRouteException(['url' => $name, 'context' => $context]);
         }

+ 1 - 1
src/Template/Error/missing_route.ctp

@@ -30,7 +30,7 @@ $this->start('subheading');
 <?php $this->end() ?>
 
 <?php $this->start('file') ?>
-<p>None of the currently connected routes match the given URL or parameters.
+<p>None of the currently connected routes match the provided parameters.
 Add a matching route to <?= 'config' . DIRECTORY_SEPARATOR . 'routes.php' ?></p>
 
 <?php if (!empty($attributes['context'])): ?>

+ 4 - 2
src/Utility/Hash.php

@@ -747,9 +747,11 @@ class Hash
         while (!empty($stack)) {
             foreach ($stack as $curKey => &$curMerge) {
                 foreach ($curMerge[0] as $key => &$val) {
-                    if (!empty($curMerge[1][$key]) && (array)$curMerge[1][$key] === $curMerge[1][$key] && (array)$val === $val) {
+                    $isArray = is_array($curMerge[1]);
+                    if ($isArray && !empty($curMerge[1][$key]) && (array)$curMerge[1][$key] === $curMerge[1][$key] && (array)$val === $val) {
+                        // Recurse into the current merge data as it is an array.
                         $stack[] = [&$val, &$curMerge[1][$key]];
-                    } elseif ((int)$key === $key && isset($curMerge[1][$key])) {
+                    } elseif ((int)$key === $key && $isArray && isset($curMerge[1][$key])) {
                         $curMerge[1][] = $val;
                     } else {
                         $curMerge[1][$key] = $val;

+ 5 - 1
src/View/Helper/FormHelper.php

@@ -1021,7 +1021,11 @@ class FormHelper extends Helper
         $error = null;
         $errorSuffix = '';
         if ($options['type'] !== 'hidden' && $options['error'] !== false) {
-            $error = $this->error($fieldName, $options['error']);
+            if (is_array($options['error'])) {
+                $error = $this->error($fieldName, $options['error'], $options['error']);
+            } else {
+                $error = $this->error($fieldName, $options['error']);
+            }
             $errorSuffix = empty($error) ? '' : 'Error';
             unset($options['error']);
         }

+ 27 - 0
tests/TestCase/ORM/RulesCheckerIntegrationTest.php

@@ -430,6 +430,33 @@ class RulesCheckerIntegrationTest extends TestCase
     }
 
     /**
+     * Tests isUnique with multiple fields and a nulled field.
+     * 
+     * @group save
+     * @return void
+     */
+    public function testIsUniqueMultipleFieldsOneIsNull()
+    {
+        $entity = new Entity([
+            'author_id' => null,
+            'title' => 'First Article'
+        ]);
+        $table = TableRegistry::get('Articles');
+        $rules = $table->rulesChecker();
+        $rules->add($rules->isUnique(['title', 'author_id'], 'Nope'));
+
+        $this->assertSame($entity, $table->save($entity));
+
+        // Make a duplicate
+        $entity = new Entity([
+            'author_id' => null,
+            'title' => 'First Article'
+        ]);
+        $this->assertFalse($table->save($entity));
+        $this->assertEquals(['title' => ['_isUnique' => 'Nope']], $entity->errors());
+    }
+
+    /**
      * Tests the existsIn domain rule
      *
      * @group save

+ 21 - 3
tests/TestCase/Routing/RouteCollectionTest.php

@@ -167,8 +167,7 @@ class RouteCollectionTest extends TestCase
         $routes = new RouteBuilder($this->collection, '/b');
         $routes->connect('/', ['controller' => 'Articles']);
 
-        $result = $this->collection->match(['plugin' => null, 'controller' => 'Articles', 'action' => 'add'], $context);
-        $this->assertFalse($result, 'No matches');
+        $this->collection->match(['plugin' => null, 'controller' => 'Articles', 'action' => 'add'], $context);
     }
 
     /**
@@ -221,12 +220,31 @@ class RouteCollectionTest extends TestCase
     }
 
     /**
+     * Test match() throws an error on named routes that fail to match
+     *
+     * @expectedException \Cake\Routing\Exception\MissingRouteException
+     * @expectedExceptionMessage A named route was found for "fail", but matching failed
+     */
+    public function testMatchNamedError()
+    {
+        $context = [
+            '_base' => '/',
+            '_scheme' => 'http',
+            '_host' => 'example.org',
+        ];
+        $routes = new RouteBuilder($this->collection, '/b');
+        $routes->connect('/:lang/articles', ['controller' => 'Articles'], ['_name' => 'fail']);
+
+        $this->collection->match(['_name' => 'fail'], $context);
+    }
+
+    /**
      * Test matching routes with names and failing
      *
      * @expectedException \Cake\Routing\Exception\MissingRouteException
      * @return void
      */
-    public function testMatchNamedError()
+    public function testMatchNamedMissingError()
     {
         $context = [
             '_base' => '/',

+ 48 - 27
tests/TestCase/Utility/HashTest.php

@@ -658,9 +658,6 @@ class HashTest extends TestCase
         $result = Hash::merge(['foo'], ['bar']);
         $this->assertEquals($result, ['foo', 'bar']);
 
-        $result = Hash::merge(['foo'], ['user' => 'bob', 'no-bar'], 'bar');
-        $this->assertEquals($result, ['foo', 'user' => 'bob', 'no-bar', 'bar']);
-
         $a = ['foo', 'foo2'];
         $b = ['bar', 'bar2'];
         $expected = ['foo', 'foo2', 'bar', 'bar2'];
@@ -681,30 +678,6 @@ class HashTest extends TestCase
         $expected = ['users' => 'none'];
         $this->assertEquals($expected, Hash::merge($a, $b));
 
-        $a = ['users' => ['lisa' => ['id' => 5, 'pw' => 'secret']], 'cakephp'];
-        $b = ['users' => ['lisa' => ['pw' => 'new-pass', 'age' => 23]], 'ice-cream'];
-        $expected = [
-            'users' => ['lisa' => ['id' => 5, 'pw' => 'new-pass', 'age' => 23]],
-            'cakephp',
-            'ice-cream'
-        ];
-        $result = Hash::merge($a, $b);
-        $this->assertEquals($expected, $result);
-
-        $c = [
-            'users' => ['lisa' => ['pw' => 'you-will-never-guess', 'age' => 25, 'pet' => 'dog']],
-            'chocolate'
-        ];
-        $expected = [
-            'users' => ['lisa' => ['id' => 5, 'pw' => 'you-will-never-guess', 'age' => 25, 'pet' => 'dog']],
-            'cakephp',
-            'ice-cream',
-            'chocolate'
-        ];
-        $this->assertEquals($expected, Hash::merge($a, $b, $c));
-
-        $this->assertEquals($expected, Hash::merge($a, $b, [], $c));
-
         $a = [
             'Tree',
             'CounterCache',
@@ -737,6 +710,54 @@ class HashTest extends TestCase
     }
 
     /**
+     * Test that merge() works with variadic arguments.
+     *
+     * @return void
+     */
+    public function testMergeVariadic()
+    {
+        $result = Hash::merge(
+            ['hkuc' => ['lion']],
+            ['hkuc' => 'lion']
+        );
+        $expected = ['hkuc' => 'lion'];
+        $this->assertEquals($expected, $result);
+
+        $result = Hash::merge(
+            ['hkuc' => ['lion']],
+            ['hkuc' => ['lion']],
+            ['hkuc' => 'lion']
+        );
+        $this->assertEquals($expected, $result);
+
+        $result = Hash::merge(['foo'], ['user' => 'bob', 'no-bar'], 'bar');
+        $this->assertEquals($result, ['foo', 'user' => 'bob', 'no-bar', 'bar']);
+
+        $a = ['users' => ['lisa' => ['id' => 5, 'pw' => 'secret']], 'cakephp'];
+        $b = ['users' => ['lisa' => ['pw' => 'new-pass', 'age' => 23]], 'ice-cream'];
+        $expected = [
+            'users' => ['lisa' => ['id' => 5, 'pw' => 'new-pass', 'age' => 23]],
+            'cakephp',
+            'ice-cream'
+        ];
+        $result = Hash::merge($a, $b);
+        $this->assertEquals($expected, $result);
+
+        $c = [
+            'users' => ['lisa' => ['pw' => 'you-will-never-guess', 'age' => 25, 'pet' => 'dog']],
+            'chocolate'
+        ];
+        $expected = [
+            'users' => ['lisa' => ['id' => 5, 'pw' => 'you-will-never-guess', 'age' => 25, 'pet' => 'dog']],
+            'cakephp',
+            'ice-cream',
+            'chocolate'
+        ];
+        $this->assertEquals($expected, Hash::merge($a, $b, $c));
+        $this->assertEquals($expected, Hash::merge($a, $b, [], $c));
+    }
+
+    /**
      * test normalizing arrays
      *
      * @return void

+ 56 - 1
tests/TestCase/View/Helper/FormHelperTest.php

@@ -2355,7 +2355,10 @@ class FormHelperTest extends TestCase
     public function testErrorMessageDisplay()
     {
         $this->article['errors'] = [
-            'Article' => ['title' => 'error message']
+            'Article' => [
+                'title' => 'error message',
+                'content' => 'some <strong>test</strong> data with <a href="#">HTML</a> chars'
+            ]
         ];
         $this->Form->create($this->article);
 
@@ -2394,6 +2397,58 @@ class FormHelperTest extends TestCase
             '/div'
         ];
         $this->assertHtml($expected, $result);
+
+
+        $result = $this->Form->input('Article.content');
+        $expected = [
+            'div' => ['class' => 'input text error'],
+            'label' => ['for' => 'article-content'],
+            'Content',
+            '/label',
+            'input' => [
+                'type' => 'text', 'name' => 'Article[content]',
+                'id' => 'article-content', 'class' => 'form-error'
+            ],
+            ['div' => ['class' => 'error-message']],
+            'some &lt;strong&gt;test&lt;/strong&gt; data with &lt;a href=&quot;#&quot;&gt;HTML&lt;/a&gt; chars',
+            '/div',
+            '/div'
+        ];
+        $this->assertHtml($expected, $result);
+
+        $result = $this->Form->input('Article.content', ['error' => ['escape' => true]]);
+        $expected = [
+            'div' => ['class' => 'input text error'],
+            'label' => ['for' => 'article-content'],
+            'Content',
+            '/label',
+            'input' => [
+                'type' => 'text', 'name' => 'Article[content]',
+                'id' => 'article-content', 'class' => 'form-error'
+            ],
+            ['div' => ['class' => 'error-message']],
+            'some &lt;strong&gt;test&lt;/strong&gt; data with &lt;a href=&quot;#&quot;&gt;HTML&lt;/a&gt; chars',
+            '/div',
+            '/div'
+        ];
+        $this->assertHtml($expected, $result);
+
+        $result = $this->Form->input('Article.content', ['error' => ['escape' => false]]);
+        $expected = [
+            'div' => ['class' => 'input text error'],
+            'label' => ['for' => 'article-content'],
+            'Content',
+            '/label',
+            'input' => [
+                'type' => 'text', 'name' => 'Article[content]',
+                'id' => 'article-content', 'class' => 'form-error'
+            ],
+            ['div' => ['class' => 'error-message']],
+            'some <strong>test</strong> data with <a href="#">HTML</a> chars',
+            '/div',
+            '/div'
+        ];
+        $this->assertHtml($expected, $result);
     }
 
     /**