Browse Source

Merge branch 'master' into 3.next

Mark Story 9 years ago
parent
commit
032c154b6f

+ 1 - 1
src/Http/ControllerFactory.php

@@ -67,7 +67,7 @@ class ControllerFactory
             return $this->missingController($request);
         }
 
-        $className = App::classname($pluginPath . $controller, $namespace, 'Controller');
+        $className = App::className($pluginPath . $controller, $namespace, 'Controller');
         if (!$className) {
             return $this->missingController($request);
         }

+ 2 - 2
src/Http/ResponseEmitter.php

@@ -194,8 +194,8 @@ class ResponseEmitter implements EmitterInterface
 
             list($name, $value) = explode('=', array_shift($parts), 2);
             $data = [
-                'name' => $name,
-                'value' => $value,
+                'name' => urldecode($name),
+                'value' => urldecode($value),
                 'expires' => 0,
                 'path' => '',
                 'domain' => '',

+ 3 - 2
src/Network/Response.php

@@ -67,7 +67,7 @@ class Response
         405 => 'Method Not Allowed',
         406 => 'Not Acceptable',
         407 => 'Proxy Authentication Required',
-        408 => 'Request Time-out',
+        408 => 'Request Timeout',
         409 => 'Conflict',
         410 => 'Gone',
         411 => 'Length Required',
@@ -78,6 +78,7 @@ class Response
         416 => 'Requested range not satisfiable',
         417 => 'Expectation Failed',
         418 => 'I\'m a teapot',
+        421 => 'Misdirected Request',
         422 => 'Unprocessable Entity',
         423 => 'Locked',
         424 => 'Failed Dependency',
@@ -93,7 +94,7 @@ class Response
         501 => 'Not Implemented',
         502 => 'Bad Gateway',
         503 => 'Service Unavailable',
-        504 => 'Gateway Time-out',
+        504 => 'Gateway Timeout',
         505 => 'Unsupported Version',
         506 => 'Variant Also Negotiates',
         507 => 'Insufficient Storage',

+ 8 - 4
src/ORM/Query.php

@@ -450,7 +450,8 @@ class Query extends DatabaseQuery implements JsonSerializable, QueryInterface
      */
     public function matching($assoc, callable $builder = null)
     {
-        $this->eagerLoader()->matching($assoc, $builder);
+        $result = $this->eagerLoader()->matching($assoc, $builder);
+        $this->_addAssociationsToTypeMap($this->repository(), $this->typeMap(), $result);
         $this->_dirty();
 
         return $this;
@@ -521,10 +522,11 @@ class Query extends DatabaseQuery implements JsonSerializable, QueryInterface
      */
     public function leftJoinWith($assoc, callable $builder = null)
     {
-        $this->eagerLoader()->matching($assoc, $builder, [
+        $result = $this->eagerLoader()->matching($assoc, $builder, [
             'joinType' => 'LEFT',
             'fields' => false
         ]);
+        $this->_addAssociationsToTypeMap($this->repository(), $this->typeMap(), $result);
         $this->_dirty();
 
         return $this;
@@ -567,10 +569,11 @@ class Query extends DatabaseQuery implements JsonSerializable, QueryInterface
      */
     public function innerJoinWith($assoc, callable $builder = null)
     {
-        $this->eagerLoader()->matching($assoc, $builder, [
+        $result = $this->eagerLoader()->matching($assoc, $builder, [
             'joinType' => 'INNER',
             'fields' => false
         ]);
+        $this->_addAssociationsToTypeMap($this->repository(), $this->typeMap(), $result);
         $this->_dirty();
 
         return $this;
@@ -628,11 +631,12 @@ class Query extends DatabaseQuery implements JsonSerializable, QueryInterface
      */
     public function notMatching($assoc, callable $builder = null)
     {
-        $this->eagerLoader()->matching($assoc, $builder, [
+        $result = $this->eagerLoader()->matching($assoc, $builder, [
             'joinType' => 'LEFT',
             'fields' => false,
             'negateMatch' => true
         ]);
+        $this->_addAssociationsToTypeMap($this->repository(), $this->typeMap(), $result);
         $this->_dirty();
 
         return $this;

+ 1 - 1
src/ORM/composer.json

@@ -23,6 +23,6 @@
         "cakephp/validation": "~3.0"
     },
     "suggest": {
-        "cakephp/i18n": "~3.0"
+        "cakephp/i18n": "If you are using Translate / Timestamp Behavior."
     }
 }

+ 1 - 0
src/TestSuite/TestCase.php

@@ -670,6 +670,7 @@ abstract class TestCase extends PHPUnit_Framework_TestCase
         }
 
         TableRegistry::set($baseClass, $mock);
+        TableRegistry::set($alias, $mock);
 
         return $mock;
     }

+ 1 - 1
src/Validation/Validator.php

@@ -302,7 +302,7 @@ class Validator implements ArrayAccess, IteratorAggregate, Countable
      *      ]);
      * ```
      *
-     * @param string $field The name of the field from which the rule will be removed
+     * @param string $field The name of the field from which the rule will be added
      * @param array|string $name The alias for a single rule or multiple rules array
      * @param array|\Cake\Validation\ValidationRule $rule the rule to add
      * @return $this

+ 18 - 3
src/View/Helper/PaginatorHelper.php

@@ -468,16 +468,31 @@ class PaginatorHelper extends Helper
     /**
      * Merges passed URL options with current pagination state to generate a pagination URL.
      *
+     * ### Url options:
+     *
+     * - `escape`: If false, the URL will be returned unescaped, do only use if it is manually
+     *    escaped afterwards before being displayed.
+     * - `fullBase`: If true, the full base URL will be prepended to the result
+     *
      * @param array $options Pagination/URL options array
      * @param string|null $model Which model to paginate on
-     * @param bool $full If true, the full base URL will be prepended to the result
+     * @param array|bool $urlOptions Array of options or bool `fullBase` for BC reasons.
      * @return string By default, returns a full pagination URL string for use in non-standard contexts (i.e. JavaScript)
      * @link http://book.cakephp.org/3.0/en/views/helpers/paginator.html#generating-pagination-urls
      */
-    public function generateUrl(array $options = [], $model = null, $full = false)
+    public function generateUrl(array $options = [], $model = null, $urlOptions = false)
     {
         $paging = $this->params($model);
         $paging += ['page' => null, 'sort' => null, 'direction' => null, 'limit' => null];
+
+        if (!is_array($urlOptions)) {
+            $urlOptions = ['fullBase' => $urlOptions];
+        }
+        $urlOptions += [
+            'escape' => true,
+            'fullBase' => false
+        ];
+
         $url = [
             'page' => $paging['page'],
             'limit' => $paging['limit'],
@@ -518,7 +533,7 @@ class PaginatorHelper extends Helper
             }
         }
 
-        return $this->Url->build($url, $full);
+        return $this->Url->build($url, $urlOptions);
     }
 
     /**

+ 10 - 0
tests/TestCase/Http/ResponseEmitterTest.php

@@ -75,6 +75,7 @@ class ResponseEmitterTest extends TestCase
             ->withAddedHeader('Set-Cookie', 'people=jim,jack,jonny";";Path=/accounts')
             ->withAddedHeader('Set-Cookie', 'google=not=nice;Path=/accounts; HttpOnly')
             ->withAddedHeader('Set-Cookie', 'a=b;  Expires=Wed, 13 Jan 2021 22:23:01 GMT; Domain=www.example.com;')
+            ->withAddedHeader('Set-Cookie', 'list%5B%5D=a%20b%20c')
             ->withHeader('Content-Type', 'text/plain');
         $response->getBody()->write('ok');
 
@@ -125,6 +126,15 @@ class ResponseEmitterTest extends TestCase
                 'secure' => false,
                 'httponly' => false
             ],
+            [
+                'name' => 'list[]',
+                'value' => 'a b c',
+                'path' => '',
+                'expire' => 0,
+                'domain' => '',
+                'secure' => false,
+                'httponly' => false
+            ],
         ];
         $this->assertEquals($expected, $GLOBALS['mockedCookies']);
     }

+ 3 - 3
tests/TestCase/Network/ResponseTest.php

@@ -459,7 +459,7 @@ class ResponseTest extends TestCase
     {
         $response = new Response();
         $result = $response->httpCodes();
-        $this->assertEquals(64, count($result));
+        $this->assertEquals(65, count($result));
 
         $result = $response->httpCodes(100);
         $expected = [100 => 'Continue'];
@@ -472,7 +472,7 @@ class ResponseTest extends TestCase
 
         $result = $response->httpCodes($codes);
         $this->assertTrue($result);
-        $this->assertEquals(66, count($response->httpCodes()));
+        $this->assertEquals(67, count($response->httpCodes()));
 
         $result = $response->httpCodes(381);
         $expected = [381 => 'Unicorn Moved'];
@@ -481,7 +481,7 @@ class ResponseTest extends TestCase
         $codes = [404 => 'Sorry Bro'];
         $result = $response->httpCodes($codes);
         $this->assertTrue($result);
-        $this->assertEquals(66, count($response->httpCodes()));
+        $this->assertEquals(67, count($response->httpCodes()));
 
         $result = $response->httpCodes(404);
         $expected = [404 => 'Sorry Bro'];

+ 143 - 0
tests/TestCase/ORM/QueryRegressionTest.php

@@ -1210,6 +1210,149 @@ class QueryRegressionTest extends TestCase
     }
 
     /**
+     * Test that matching queries map types correctly.
+     *
+     * @return void
+     */
+    public function testComplexTypesInJoinedWhereWithMatching()
+    {
+        $this->loadFixtures('Comments', 'Users', 'Articles');
+        $table = TableRegistry::get('Users');
+        $table->hasOne('Comments', [
+            'foreignKey' => 'user_id',
+        ]);
+        $table->Comments->belongsTo('Articles');
+        $table->Comments->Articles->belongsTo('Authors', [
+            'className' => 'Users',
+            'foreignKey' => 'author_id'
+        ]);
+
+        $query = $table->find()
+            ->matching('Comments')
+            ->where([
+                'Comments.updated >' => new \DateTime('2007-03-18 10:55:00')
+            ]);
+
+        $result = $query->first();
+        $this->assertNotEmpty($result);
+        $this->assertInstanceOf('Cake\I18n\Time', $result->_matchingData['Comments']->updated);
+
+
+        $query = $table->find()
+            ->matching('Comments.Articles.Authors')
+            ->where([
+                'Authors.created >' => new \DateTime('2007-03-17 01:16:00')
+            ]);
+
+        $result = $query->first();
+        $this->assertNotEmpty($result);
+        $this->assertInstanceOf('Cake\I18n\Time', $result->_matchingData['Authors']->updated);
+    }
+
+    /**
+     * Test that notMatching queries map types correctly.
+     *
+     * @return void
+     */
+    public function testComplexTypesInJoinedWhereWithNotMatching()
+    {
+        $this->loadFixtures('Articles', 'Tags', 'ArticlesTags');
+        $Tags = TableRegistry::get('Tags');
+        $Tags->belongsToMany('Articles');
+
+        $query = $Tags->find()
+            ->notMatching('Articles', function ($q) {
+                return $q ->where(['ArticlesTags.tag_id !=' => 3 ]);
+            })
+            ->where([
+                'Tags.created <' => new \DateTime('2016-01-02 00:00:00')
+            ]);
+
+        $result = $query->first();
+        $this->assertNotEmpty($result);
+        $this->assertEquals(3, $result->id);
+        $this->assertInstanceOf('Cake\I18n\Time', $result->created);
+    }
+
+    /**
+     * Test that innerJoinWith queries map types correctly.
+     *
+     * @return void
+     */
+    public function testComplexTypesInJoinedWhereWithInnerJoinWith()
+    {
+        $this->loadFixtures('Comments', 'Users', 'Articles');
+        $table = TableRegistry::get('Users');
+        $table->hasOne('Comments', [
+            'foreignKey' => 'user_id',
+        ]);
+        $table->Comments->belongsTo('Articles');
+        $table->Comments->Articles->belongsTo('Authors', [
+            'className' => 'Users',
+            'foreignKey' => 'author_id'
+        ]);
+
+        $query = $table->find()
+            ->innerJoinWith('Comments')
+            ->where([
+                'Comments.updated >' => new \DateTime('2007-03-18 10:55:00')
+            ]);
+
+        $result = $query->first();
+        $this->assertNotEmpty($result);
+        $this->assertInstanceOf('Cake\I18n\Time', $result->updated);
+
+        $query = $table->find()
+            ->innerJoinWith('Comments.Articles.Authors')
+            ->where([
+                'Authors.created >' => new \DateTime('2007-03-17 01:16:00')
+            ]);
+
+        $result = $query->first();
+        $this->assertNotEmpty($result);
+        $this->assertInstanceOf('Cake\I18n\Time', $result->updated);
+    }
+
+    /**
+     * Test that leftJoinWith queries map types correctly.
+     *
+     * @return void
+     */
+    public function testComplexTypesInJoinedWhereWithLeftJoinWith()
+    {
+        $this->loadFixtures('Comments', 'Users', 'Articles');
+        $table = TableRegistry::get('Users');
+        $table->hasOne('Comments', [
+            'foreignKey' => 'user_id',
+        ]);
+        $table->Comments->belongsTo('Articles');
+        $table->Comments->Articles->belongsTo('Authors', [
+            'className' => 'Users',
+            'foreignKey' => 'author_id'
+        ]);
+
+        $query = $table->find()
+            ->leftJoinWith('Comments')
+            ->where([
+                'Comments.updated >' => new \DateTime('2007-03-18 10:55:00')
+            ]);
+
+        $result = $query->first();
+        $this->assertNotEmpty($result);
+        $this->assertInstanceOf('Cake\I18n\Time', $result->updated);
+
+        $query = $table->find()
+            ->leftJoinWith('Comments.Articles.Authors')
+            ->where([
+                'Authors.created >' => new \DateTime('2007-03-17 01:16:00')
+            ]);
+
+        $result = $query->first();
+        $this->assertNotEmpty($result);
+        $this->assertInstanceOf('Cake\I18n\Time', $result->updated);
+    }
+
+    /**
      * Tests that it is possible to contain to fetch
      * associations off of a junction table.
      *

+ 1 - 1
tests/TestCase/TestSuite/IntegrationTestCaseTest.php

@@ -649,7 +649,7 @@ class IntegrationTestCaseTest extends IntegrationTestCase
     {
         $test = new AssertIntegrationTestCase('testBadAssertNoRedirect');
         $result = $test->run();
-        ob_start();
+
         $this->assertFalse($result->wasSuccessful());
         $this->assertEquals(1, $result->failureCount());
     }

+ 2 - 7
tests/TestCase/TestSuite/TestCaseTest.php

@@ -130,7 +130,7 @@ class TestCaseTest extends TestCase
     {
         $test = new AssertHtmlTestCase('testAssertHtmlQuotes');
         $result = $test->run();
-        ob_start();
+
         $this->assertEquals(0, $result->errorCount());
         $this->assertTrue($result->wasSuccessful());
         $this->assertEquals(0, $result->failureCount());
@@ -225,7 +225,6 @@ class TestCaseTest extends TestCase
     {
         $test = new AssertHtmlTestCase('testNumericValuesInExpectationForAssertHtml');
         $result = $test->run();
-        ob_start();
         $this->assertEquals(0, $result->errorCount());
         $this->assertTrue($result->wasSuccessful());
         $this->assertEquals(0, $result->failureCount());
@@ -240,14 +239,12 @@ class TestCaseTest extends TestCase
     {
         $test = new AssertHtmlTestCase('testBadAssertHtml');
         $result = $test->run();
-        ob_start();
         $this->assertEquals(0, $result->errorCount());
         $this->assertFalse($result->wasSuccessful());
         $this->assertEquals(1, $result->failureCount());
 
         $test = new AssertHtmlTestCase('testBadAssertHtml2');
         $result = $test->run();
-        ob_start();
         $this->assertEquals(0, $result->errorCount());
         $this->assertFalse($result->wasSuccessful());
         $this->assertEquals(1, $result->failureCount());
@@ -267,7 +264,6 @@ class TestCaseTest extends TestCase
         $test->fixtureManager = $manager;
         $manager->expects($this->once())->method('loadSingle');
         $result = $test->run();
-        ob_start();
 
         $this->assertEquals(0, $result->errorCount());
     }
@@ -281,12 +277,10 @@ class TestCaseTest extends TestCase
     {
         $test = new FixturizedTestCase('testSkipIfTrue');
         $result = $test->run();
-        ob_start();
         $this->assertEquals(1, $result->skippedCount());
 
         $test = new FixturizedTestCase('testSkipIfFalse');
         $result = $test->run();
-        ob_start();
         $this->assertEquals(0, $result->skippedCount());
     }
 
@@ -490,6 +484,7 @@ class TestCaseTest extends TestCase
 
         $result = TableRegistry::get('TestPlugin.TestPluginComments');
         $this->assertInstanceOf('TestPlugin\Model\Table\TestPluginCommentsTable', $result);
+        $this->assertSame($TestPluginComment, $result);
 
         $TestPluginComment = $this->getMockForModel('TestPlugin.TestPluginComments', ['save']);
 

+ 21 - 0
tests/TestCase/View/Helper/PaginatorHelperTest.php

@@ -29,6 +29,11 @@ class PaginatorHelperTest extends TestCase
 {
 
     /**
+     * @var string
+     */
+    protected $locale;
+
+    /**
      * setUp method
      *
      * @return void
@@ -704,6 +709,22 @@ class PaginatorHelperTest extends TestCase
         $options = ['sort' => 'Article.name', 'direction' => 'desc'];
         $result = $this->Paginator->generateUrl($options);
         $this->assertEquals('/index?page=3&amp;sort=Article.name&amp;direction=desc', $result);
+
+        $this->Paginator->request->params['paging']['Article']['page'] = 3;
+        $options = ['sort' => 'Article.name', 'direction' => 'desc'];
+        $result = $this->Paginator->generateUrl($options, null, ['escape' => false]);
+        $this->assertEquals('/index?page=3&sort=Article.name&direction=desc', $result);
+
+        $this->Paginator->request->params['paging']['Article']['page'] = 3;
+        $options = ['sort' => 'Article.name', 'direction' => 'desc'];
+        $result = $this->Paginator->generateUrl($options, null, ['fullBase' => true]);
+        $this->assertEquals('http://localhost/index?page=3&amp;sort=Article.name&amp;direction=desc', $result);
+
+        // @deprecated 3.3.5 Use fullBase array option instead.
+        $this->Paginator->request->params['paging']['Article']['page'] = 3;
+        $options = ['sort' => 'Article.name', 'direction' => 'desc'];
+        $result = $this->Paginator->generateUrl($options, null, true);
+        $this->assertEquals('http://localhost/index?page=3&amp;sort=Article.name&amp;direction=desc', $result);
     }
 
     /**