Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
257 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
<?php | ||
/** | ||
* PHP Version 5.4 | ||
* | ||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org) | ||
* Copyright (c) Cake Software Foundation, Inc. (http://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. (http://cakefoundation.org) | ||
* @link http://cakephp.org CakePHP(tm) Project | ||
* @since CakePHP(tm) v 3.0.0 | ||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php) | ||
*/ | ||
namespace Cake\ORM\Association; | ||
|
||
use Cake\ORM\Association; | ||
use Cake\ORM\Query; | ||
use Cake\Utility\Inflector; | ||
|
||
/** | ||
* Represents an 1 - 1 relationship where the source side of the relation is | ||
* related to only one record in the target table and vice versa. | ||
* | ||
*/ | ||
class HasOne extends Association { | ||
|
||
/** | ||
* Whether this association can be expressed directly in a query join | ||
* | ||
* @var boolean | ||
*/ | ||
protected $_canBeJoined = true; | ||
|
||
/** | ||
* Sets the name of the field representing the foreign key to the target table. | ||
* If no parameters are passed current field is returned | ||
* | ||
* @param string $key the key to be used to link both tables together | ||
* @return string | ||
*/ | ||
public function foreignKey($key = null) { | ||
if ($key === null) { | ||
if ($this->_foreignKey === null) { | ||
$this->_foreignKey = Inflector::underscore($this->source()->alias()) . '_id'; | ||
} | ||
return $this->_foreignKey; | ||
} | ||
return parent::foreignKey($key); | ||
} | ||
|
||
/** | ||
* Alters a Query object to include the associated target table data in the final | ||
* result | ||
* | ||
* The options array accept the following keys: | ||
* | ||
* - includeFields: Whether to include target model fields in the result or not | ||
* - foreignKey: The name of the field to use as foreign key, if false none | ||
* will be sued | ||
* - conditions: array with a list of conditions to filter the join with | ||
* - fields: a list of fields in the target table to include in the result | ||
* | ||
* @param Query $query the query to be altered to include the target table data | ||
* @param array $options Any extra options or overrides to be taken in account | ||
* @return void | ||
*/ | ||
public function attachTo(Query $query, array $options = []) { | ||
$target = $this->target(); | ||
$source = $this->source(); | ||
$options += [ | ||
'includeFields' => true, | ||
'foreignKey' => $this->foreignKey(), | ||
'conditions' => [] | ||
]; | ||
$options['conditions'] = array_merge($this->conditions(), $options['conditions']); | ||
|
||
if (!empty($options['foreignKey'])) { | ||
$options['conditions'][] = sprintf('%s.%s = %s.%s', | ||
$source->alias(), | ||
$source->primaryKey(), | ||
$target->alias(), | ||
$options['foreignKey'] | ||
); | ||
} | ||
|
||
$joinOptions = ['table' => 1, 'conditions' => 1, 'type' => 1]; | ||
$query->join([$target->alias() => array_intersect_key($options, $joinOptions)]); | ||
|
||
if (empty($options['fields'])) { | ||
$f = isset($options['fields']) ? $options['fields'] : null; | ||
if ($options['includeFields'] && ($f === null || $f !== false)) { | ||
$options['fields'] = array_keys($target->schema()); | ||
} | ||
} | ||
|
||
if (!empty($options['fields'])) { | ||
$query->select($query->aliasFields($options['fields'], $target->alias())); | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
<?php | ||
/** | ||
* PHP Version 5.4 | ||
* | ||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org) | ||
* Copyright (c) Cake Software Foundation, Inc. (http://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. (http://cakefoundation.org) | ||
* @link http://cakephp.org CakePHP(tm) Project | ||
* @since CakePHP(tm) v 3.0.0 | ||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php) | ||
*/ | ||
namespace Cake\Test\TestCase\ORM\Association; | ||
|
||
use Cake\ORM\Association\HasOne; | ||
use Cake\ORM\Table; | ||
use Cake\ORM\Query; | ||
|
||
/** | ||
* Tests HasOne class | ||
* | ||
*/ | ||
class HasOneTest extends \Cake\TestSuite\TestCase { | ||
|
||
/** | ||
* Set up | ||
* | ||
* @return void | ||
*/ | ||
public function setUp() { | ||
$this->user = Table::build('User', [ | ||
'schema' => [ | ||
'id' => ['type' => 'integer'], | ||
'username' => ['type' => 'string'], | ||
] | ||
]); | ||
$this->profile = Table::build('Profile', [ | ||
'schema' => [ | ||
'id' => ['type' => 'integer'], | ||
'first_name' => ['type' => 'string'], | ||
'user_id' => ['type' => 'integer'], | ||
] | ||
]); | ||
} | ||
|
||
/** | ||
* Tear down | ||
* | ||
* @return void | ||
*/ | ||
public function tearDown() { | ||
Table::clearRegistry(); | ||
} | ||
|
||
/** | ||
* Tests that the association reports it can be joined | ||
* | ||
* @return void | ||
*/ | ||
public function testCanBeJoined() { | ||
$assoc = new HasOne('Test'); | ||
$this->assertTrue($assoc->canBeJoined()); | ||
} | ||
|
||
/** | ||
* Tests that the correct join and fields are attached to a query depending on | ||
* the association config | ||
* | ||
* @return void | ||
*/ | ||
public function testAttachTo() { | ||
$query = $this->getMock('\Cake\ORM\Query', ['join', 'select'], [null]); | ||
$config = [ | ||
'foreignKey' => 'user_id', | ||
'sourceTable' => $this->user, | ||
'targetTable' => $this->profile, | ||
'conditions' => ['Profile.is_active' => true] | ||
]; | ||
$association = new HasOne('Profile', $config); | ||
$query->expects($this->once())->method('join')->with([ | ||
'Profile' => ['conditions' => [ | ||
'Profile.is_active' => true, | ||
'User.id = Profile.user_id', | ||
]] | ||
]); | ||
$query->expects($this->once())->method('select')->with([ | ||
'Profile__id' => 'Profile.id', | ||
'Profile__first_name' => 'Profile.first_name', | ||
'Profile__user_id' => 'Profile.user_id' | ||
]); | ||
$association->attachTo($query); | ||
} | ||
|
||
/** | ||
* Tests that default config defined in the association can be overridden | ||
* | ||
* @return void | ||
*/ | ||
public function testAttachToConfigOverride() { | ||
$query = $this->getMock('\Cake\ORM\Query', ['join', 'select'], [null]); | ||
$config = [ | ||
'foreignKey' => 'user_id', | ||
'sourceTable' => $this->user, | ||
'targetTable' => $this->profile, | ||
'conditions' => ['Profile.is_active' => true] | ||
]; | ||
$association = new HasOne('Profile', $config); | ||
$query->expects($this->once())->method('join')->with([ | ||
'Profile' => ['conditions' => [ | ||
'Profile.is_active' => false | ||
]] | ||
]); | ||
$query->expects($this->once())->method('select')->with([ | ||
'Profile__first_name' => 'Profile.first_name' | ||
]); | ||
|
||
$override = [ | ||
'conditions' => ['Profile.is_active' => false], | ||
'foreignKey' => false, | ||
'fields' => ['first_name'] | ||
]; | ||
$association->attachTo($query, $override); | ||
} | ||
|
||
/** | ||
* Tests that it is possible to avoid fields inclusion for the associated table | ||
* | ||
* @return void | ||
*/ | ||
public function testAttachToNoFields() { | ||
$query = $this->getMock('\Cake\ORM\Query', ['join', 'select'], [null]); | ||
$config = [ | ||
'sourceTable' => $this->user, | ||
'targetTable' => $this->profile, | ||
'conditions' => ['Profile.is_active' => true] | ||
]; | ||
$association = new HasOne('Profile', $config); | ||
$query->expects($this->once())->method('join')->with([ | ||
'Profile' => ['conditions' => [ | ||
'Profile.is_active' => true, | ||
'User.id = Profile.user_id', | ||
]] | ||
]); | ||
$query->expects($this->never())->method('select'); | ||
$association->attachTo($query, ['includeFields' => false]); | ||
} | ||
|
||
} | ||
|