Browse Source

Merge branch 'master' into 3.2

Mark Story 10 years ago
parent
commit
d5ebbcfc6c

+ 8 - 1
.travis.yml

@@ -19,11 +19,16 @@ services:
   - memcached
   - redis-server
 
+cache:
+  directories:
+    - vendor
+    - $HOME/.composer/cache
+
 matrix:
   fast_finish: true
 
   include:
-    - php: 5.5
+    - php: 5.6
       env: COVERALLS=1 DEFAULT=0
 
     - php: 7.0
@@ -41,7 +46,9 @@ matrix:
     - php: hhvm
 
 before_script:
+  - rm -f ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini
   - composer self-update
+  - if [ -n "$GH_TOKEN" ]; then composer config github-oauth.github.com ${GH_TOKEN}; fi;
   - composer install --prefer-dist --no-interaction
 
   - sh -c "if [ '$DB' = 'mysql' ]; then mysql -e 'CREATE DATABASE cakephp_test;'; fi"

+ 1 - 1
LICENSE.txt

@@ -1,7 +1,7 @@
 The MIT License (MIT)
 
 CakePHP(tm) : The Rapid Development PHP Framework (http://cakephp.org)
-Copyright (c) 2005-2015, Cake Software Foundation, Inc. (http://cakefoundation.org)
+Copyright (c) 2005-2016, Cake Software Foundation, Inc. (http://cakefoundation.org)
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal

+ 4 - 0
appveyor.yml

@@ -3,6 +3,10 @@ shallow_clone: false
 platform: 'x86'
 clone_folder: c:\projects\cakephp
 
+cache:
+  - '%LOCALAPPDATA%\Composer'
+  - '%APPDATA%\Composer'
+
 branches:
   only:
     - master

+ 1 - 2
src/Database/Query.php

@@ -1430,7 +1430,7 @@ class Query implements ExpressionInterface, IteratorAggregate
     {
         $this->_dirty();
         $this->_type = 'delete';
-        if ($table) {
+        if ($table !== null) {
             $this->from($table);
         }
         return $this;
@@ -1796,7 +1796,6 @@ class Query implements ExpressionInterface, IteratorAggregate
     protected function _dirty()
     {
         $this->_dirty = true;
-        $this->_transformedQuery = null;
 
         if ($this->_iterator && $this->_valueBinder) {
             $this->valueBinder()->reset();

+ 1 - 1
src/Database/Type/StringType.php

@@ -52,7 +52,7 @@ class StringType extends Type implements OptionalConvertInterface
     }
 
     /**
-     * Convert string values to PHP integers
+     * Convert string values to PHP strings.
      *
      * @param mixed $value The value to convert.
      * @param Driver $driver The driver instance to convert with.

+ 3 - 3
src/Datasource/RulesChecker.php

@@ -279,11 +279,11 @@ class RulesChecker
 
     /**
      * Used by top level functions checkDelete, checkCreate and checkUpdate, this function
-     * iterates an array containing the rules to be checked and check them all.
+     * iterates an array containing the rules to be checked and checks them all.
      *
      * @param \Cake\Datasource\EntityInterface $entity The entity to check for validity.
      * @param array $options Extra options to pass to checker functions.
-     * @param array $rules the list of rules that must be checked
+     * @param array $rules The list of rules that must be checked.
      * @return bool
      */
     protected function _checkRules(EntityInterface $entity, array $options = [], array $rules = [])
@@ -302,7 +302,7 @@ class RulesChecker
      *
      * @param callable $rule The rule to decorate
      * @param string $name The alias for a rule.
-     * @param array $options The options containing the error message and field
+     * @param array $options The options containing the error message and field.
      * @return callable
      */
     protected function _addError($rule, $name, $options)

+ 1 - 1
src/Network/Exception/BadRequestException.php

@@ -22,7 +22,7 @@ class BadRequestException extends HttpException
     /**
      * Constructor
      *
-     * @param string $message If no message is given 'Bad Request' will be the message
+     * @param string|null $message If no message is given 'Bad Request' will be the message
      * @param int $code Status code, defaults to 400
      */
     public function __construct($message = null, $code = 400)

+ 35 - 0
src/Network/Exception/ConflictException.php

@@ -0,0 +1,35 @@
+<?php
+/**
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @since         3.1.7
+ * @license       http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Cake\Network\Exception;
+
+/**
+ * Represents an HTTP 409 error.
+ *
+ */
+class ConflictException extends HttpException
+{
+
+    /**
+     * Constructor
+     *
+     * @param string|null $message If no message is given 'Conflict' will be the message
+     * @param int $code Status code, defaults to 409
+     */
+    public function __construct($message = null, $code = 409)
+    {
+        if (empty($message)) {
+            $message = 'Conflict';
+        }
+        parent::__construct($message, $code);
+    }
+}

+ 1 - 1
src/Network/Exception/ForbiddenException.php

@@ -22,7 +22,7 @@ class ForbiddenException extends HttpException
     /**
      * Constructor
      *
-     * @param string $message If no message is given 'Forbidden' will be the message
+     * @param string|null $message If no message is given 'Forbidden' will be the message
      * @param int $code Status code, defaults to 403
      */
     public function __construct($message = null, $code = 403)

+ 35 - 0
src/Network/Exception/GoneException.php

@@ -0,0 +1,35 @@
+<?php
+/**
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @since         3.1.7
+ * @license       http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Cake\Network\Exception;
+
+/**
+ * Represents an HTTP 410 error.
+ *
+ */
+class GoneException extends HttpException
+{
+
+    /**
+     * Constructor
+     *
+     * @param string|null $message If no message is given 'Gone' will be the message
+     * @param int $code Status code, defaults to 410
+     */
+    public function __construct($message = null, $code = 410)
+    {
+        if (empty($message)) {
+            $message = 'Gone';
+        }
+        parent::__construct($message, $code);
+    }
+}

+ 1 - 2
src/Network/Exception/InternalErrorException.php

@@ -10,7 +10,6 @@
  * @since         3.0.0
  * @license       http://www.opensource.org/licenses/mit-license.php MIT License
  */
-
 namespace Cake\Network\Exception;
 
 /**
@@ -23,7 +22,7 @@ class InternalErrorException extends HttpException
     /**
      * Constructor
      *
-     * @param string $message If no message is given 'Internal Server Error' will be the message
+     * @param string|null $message If no message is given 'Internal Server Error' will be the message
      * @param int $code Status code, defaults to 500
      */
     public function __construct($message = null, $code = 500)

+ 1 - 1
src/Network/Exception/InvalidCsrfTokenException.php

@@ -22,7 +22,7 @@ class InvalidCsrfTokenException extends HttpException
     /**
      * Constructor
      *
-     * @param string $message If no message is given 'Invalid  CSRF Token' will be the message
+     * @param string|null $message If no message is given 'Invalid  CSRF Token' will be the message
      * @param int $code Status code, defaults to 403
      */
     public function __construct($message = null, $code = 403)

+ 1 - 1
src/Network/Exception/MethodNotAllowedException.php

@@ -22,7 +22,7 @@ class MethodNotAllowedException extends HttpException
     /**
      * Constructor
      *
-     * @param string $message If no message is given 'Method Not Allowed' will be the message
+     * @param string|null $message If no message is given 'Method Not Allowed' will be the message
      * @param int $code Status code, defaults to 405
      */
     public function __construct($message = null, $code = 405)

+ 35 - 0
src/Network/Exception/NotAcceptableException.php

@@ -0,0 +1,35 @@
+<?php
+/**
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @since         3.1.7
+ * @license       http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Cake\Network\Exception;
+
+/**
+ * Represents an HTTP 406 error.
+ *
+ */
+class NotAcceptableException extends HttpException
+{
+
+    /**
+     * Constructor
+     *
+     * @param string|null $message If no message is given 'Not Acceptable' will be the message
+     * @param int $code Status code, defaults to 406
+     */
+    public function __construct($message = null, $code = 406)
+    {
+        if (empty($message)) {
+            $message = 'Not Acceptable';
+        }
+        parent::__construct($message, $code);
+    }
+}

+ 1 - 1
src/Network/Exception/NotFoundException.php

@@ -22,7 +22,7 @@ class NotFoundException extends HttpException
     /**
      * Constructor
      *
-     * @param string $message If no message is given 'Not Found' will be the message
+     * @param string|null $message If no message is given 'Not Found' will be the message
      * @param int $code Status code, defaults to 404
      */
     public function __construct($message = null, $code = 404)

+ 35 - 0
src/Network/Exception/ServiceUnavailableException.php

@@ -0,0 +1,35 @@
+<?php
+/**
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @since         3.1.7
+ * @license       http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Cake\Network\Exception;
+
+/**
+ * Represents an HTTP 503 error.
+ *
+ */
+class ServiceUnavailableException extends HttpException
+{
+
+    /**
+     * Constructor
+     *
+     * @param string|null $message If no message is given 'Service Unavailable' will be the message
+     * @param int $code Status code, defaults to 503
+     */
+    public function __construct($message = null, $code = 503)
+    {
+        if (empty($message)) {
+            $message = 'Service Unavailable';
+        }
+        parent::__construct($message, $code);
+    }
+}

+ 1 - 1
src/Network/Exception/UnauthorizedException.php

@@ -22,7 +22,7 @@ class UnauthorizedException extends HttpException
     /**
      * Constructor
      *
-     * @param string $message If no message is given 'Unauthorized' will be the message
+     * @param string|null $message If no message is given 'Unauthorized' will be the message
      * @param int $code Status code, defaults to 401
      */
     public function __construct($message = null, $code = 401)

+ 14 - 2
src/ORM/Association/BelongsToMany.php

@@ -443,7 +443,7 @@ class BelongsToMany extends Association
         $table = $this->junction();
         $hasMany = $this->source()->association($table->alias());
         if ($this->_cascadeCallbacks) {
-            foreach ($hasMany->find('all')->where($conditions) as $related) {
+            foreach ($hasMany->find('all')->where($conditions)->toList() as $related) {
                 $table->delete($related, $options);
             }
             return true;
@@ -1095,11 +1095,23 @@ class BelongsToMany extends Association
     protected function _buildQuery($options)
     {
         $name = $this->_junctionAssociationName();
+        $assoc = $this->target()->association($name);
+        $queryBuilder = false;
+
+        if (!empty($options['queryBuilder'])) {
+            $queryBuilder = $options['queryBuilder'];
+            unset($options['queryBuilder']);
+        }
+
         $query = $this->_buildBaseQuery($options);
+        $query->addDefaultTypes($assoc->target());
+
+        if ($queryBuilder) {
+            $query = $queryBuilder($query);
+        }
 
         $keys = $this->_linkField($options);
         $query = $this->_appendJunctionJoin($query, $keys);
-        $assoc = $this->target()->association($name);
 
         $query->autoFields($query->clause('select') === [])
             ->select($query->aliasFields((array)$assoc->foreignKey(), $name));

+ 1 - 2
src/ORM/Association/DependentDeleteTrait.php

@@ -44,8 +44,7 @@ trait DependentDeleteTrait
         $conditions = array_combine($foreignKey, $entity->extract($bindingKey));
 
         if ($this->_cascadeCallbacks) {
-            $query = $this->find('all')->where($conditions);
-            foreach ($query as $related) {
+            foreach ($this->find()->where($conditions)->toList() as $related) {
                 $table->delete($related, $options);
             }
             return true;

+ 3 - 11
src/ORM/Association/SelectableAssociationTrait.php

@@ -43,16 +43,7 @@ trait SelectableAssociationTrait
     public function eagerLoader(array $options)
     {
         $options += $this->_defaultOptions();
-        $queryBuilder = false;
-        if (!empty($options['queryBuilder'])) {
-            $queryBuilder = $options['queryBuilder'];
-            unset($options['queryBuilder']);
-        }
-
         $fetchQuery = $this->_buildQuery($options);
-        if ($queryBuilder) {
-            $fetchQuery = $queryBuilder($fetchQuery);
-        }
         $resultMap = $this->_buildResultMap($fetchQuery, $options);
         return $this->_resultInjector($fetchQuery, $resultMap, $options);
     }
@@ -123,7 +114,7 @@ trait SelectableAssociationTrait
         }
 
         if (!empty($options['queryBuilder'])) {
-            $options['queryBuilder']($fetchQuery);
+            $fetchQuery = $options['queryBuilder']($fetchQuery);
         }
 
         return $fetchQuery;
@@ -304,7 +295,8 @@ trait SelectableAssociationTrait
 
         $sourceKeys = [];
         foreach ((array)$keys as $key) {
-            $sourceKeys[] = key($fetchQuery->aliasField($key, $sAlias));
+            $f = $fetchQuery->aliasField($key, $sAlias);
+            $sourceKeys[] = key($f);
         }
 
         $nestKey = $options['nestKey'];

+ 6 - 4
src/ORM/Query.php

@@ -305,11 +305,11 @@ class Query extends DatabaseQuery implements JsonSerializable, QueryInterface
             $this->_dirty();
         }
 
-        $result = $loader->contain($associations);
         if ($associations === null) {
-            return $result;
+            return $loader->contain();
         }
 
+        $result = $loader->contain($associations);
         $this->_addAssociationsToTypeMap($this->repository(), $this->typeMap(), $result);
         return $this;
     }
@@ -326,7 +326,6 @@ class Query extends DatabaseQuery implements JsonSerializable, QueryInterface
      */
     protected function _addAssociationsToTypeMap($table, $typeMap, $associations)
     {
-        $typeMap = $this->typeMap();
         foreach ($associations as $name => $nested) {
             $association = $table->association($name);
             if (!$association) {
@@ -775,7 +774,10 @@ class Query extends DatabaseQuery implements JsonSerializable, QueryInterface
      * The callback will receive as first argument a clone of this query and not this
      * query itself.
      *
-     * @param callable $counter The counter value
+     * If the first param is a null value, the built-in counter function will be called
+     * instead
+     *
+     * @param callable|null $counter The counter value
      * @return $this
      */
     public function counter($counter)

+ 1 - 0
src/ORM/Table.php

@@ -1814,6 +1814,7 @@ class Table implements RepositoryInterface, EventListenerInterface, EventDispatc
             );
         }
 
+        $conditions = [];
         if ($hasOr === false && $hasAnd === false) {
             $conditions = $makeConditions([$fields], $args);
         } elseif ($hasOr !== false) {

+ 2 - 0
tests/TestCase/Controller/Component/PaginatorComponentTest.php

@@ -964,6 +964,8 @@ class PaginatorComponentTest extends TestCase
      */
     public function testPaginateQueryWithBindValue()
     {
+        $config = ConnectionManager::config('test');
+        $this->skipIf(strpos($config['driver'], 'Sqlserver') !== false, 'Test temporarily broken in SQLServer');
         $this->loadFixtures('Posts');
         $table = TableRegistry::get('PaginatorPosts');
         $query = $table->find()

+ 0 - 11
tests/TestCase/Database/QueryTest.php

@@ -337,17 +337,6 @@ class QueryTest extends TestCase
             ->innerJoin(['c' => 'comments'], ['created <' => $time], $types)
             ->execute();
         $this->assertCount(0, $result->fetchAll());
-
-        $query = new Query($this->connection);
-        $result = $query
-            ->select(['title', 'name' => 'c.comment'])
-            ->from('articles')
-            ->leftJoin(['c' => 'comments'], ['created >' => $time], $types)
-            ->execute();
-        $this->assertEquals(
-            ['title' => 'First Article', 'name' => 'Second Comment for First Article'],
-            $result->fetch('assoc')
-        );
     }
 
     /**

+ 22 - 0
tests/TestCase/ORM/Association/BelongsToManyTest.php

@@ -913,4 +913,26 @@ class BelongsToManyTest extends TestCase
         $association = new BelongsToMany('Contacts.Tags', $config);
         $this->assertEquals('tags', $association->property());
     }
+
+    /**
+     * Tests that fetching belongsToMany association will not force
+     * all fields being returned, but intead will honor the select() clause
+     *
+     * @see https://github.com/cakephp/cakephp/issues/7916
+     * @return void
+     */
+    public function testEagerLoadingBelongsToManyLimitedFields()
+    {
+        $table = TableRegistry::get('Articles');
+        $table->belongsToMany('Tags');
+        $result = $table
+            ->find()
+            ->contain(['Tags' => function ($q) {
+                return $q->select(['id']);
+            }])
+            ->first();
+
+        $this->assertNotEmpty($result->tags[0]->id);
+        $this->assertEmpty($result->tags[0]->name);
+    }
 }