Skip to content
Permalink
Browse files

Initial implementation of automatic setup of associations

  • Loading branch information...
lorenzo committed Oct 6, 2013
1 parent c6b4565 commit c04bcf9d22883d8d548fa97d0da494d121cf050b
Showing with 179 additions and 3 deletions.
  1. +1 −1 Cake/Core/App.php
  2. +15 −1 Cake/ORM/Association.php
  3. +71 −1 Cake/ORM/Entity.php
  4. +52 −0 Cake/ORM/Table.php
  5. +40 −0 Cake/Test/TestCase/ORM/TableTest.php
@@ -34,7 +34,7 @@
* You can inspect the currently loaded paths using `App::path('Controller')` for example to see loaded
* controller paths.
*
* It is also possible to inspect paths for plugin classes, for instance, to get
* It is also possible to inspect paths for plugin classes, for instance, to get
* the path to a plugin's helpers you would call `App::path('View/Helper', 'MyPlugin')`
*
* ### Locating plugins and themes
@@ -127,6 +127,14 @@ abstract class Association {
*/
protected $_strategy = self::STRATEGY_JOIN;
/**
* The name of the entity class associated to the targetTable, used to load an
* instance of this table in case it is present
*
* @return void
*/
protected $_entityClass;
/**
* Constructor. Subclasses can override _options function to get the original
* list of passed options if expecting any other special key
@@ -144,7 +152,8 @@ public function __construct($name, array $options = []) {
'sourceTable',
'targetTable',
'joinType',
'property'
'property',
'entityClass'
];
foreach ($defaults as $property) {
if (isset($options[$property])) {
@@ -204,6 +213,11 @@ public function target(Table $table = null) {
return $this->_targetTable;
}
if ($table === null && $this->_entityClass) {
$entity = $this->_entityClass;
$table = $entity::repository();
}
if ($table !== null) {
return $this->_targetTable = $table;
}
@@ -30,6 +30,36 @@ class Entity implements \ArrayAccess {
*/
protected static $_repositoryClass;
/**
* List of Belongs To associations
*
* ### Basic usage
*
* `public $belongsTo = array('Group', 'Department');`
*
* ### Detailed configuration
*
* {{{
* public $belongsTo = array(
* 'Group',
* 'Department' => array(
* 'className' => 'Department',
* 'foreignKey' => 'department_id'
* )
* );
* }}}
*
* @see \Cake\ORM\Table::belongsTo() for a list of accepted configuration keys
* @var array
*/
protected static $_belongsTo = [];
protected static $_hasOne = [];
protected static $_hasMany = [];
protected static $_belongsToMany = [];
/**
* Initializes the internal properties of this entity out of the
* keys in an array
@@ -328,7 +358,7 @@ public static function repositoryClass($class = null) {
if ($self === __CLASS__ || count($parts) < 3) {
return self::$_repositoryClass[$self] = $default;
}
$alias = array_pop($parts) . 'Table';
$class = implode('\\', array_slice($parts, 0, -1)) . '\Repository\\' . $alias;
if (!class_exists($class)) {
@@ -346,4 +376,44 @@ public static function repositoryClass($class = null) {
return $result;
}
/**
* Returns the BelongsTo associations as statically defined in the $_belongsTo
* property
*
* @return array
*/
public static function belongsTo() {
return static::$_belongsTo;
}
/**
* Returns the HasOne associations as statically defined in the $_hasOne
* property
*
* @return array
*/
public static function hasOne() {
return static::$_hasOne;
}
/**
* Returns the HasMany associations as statically defined in the $_hasMany
* property
*
* @return array
*/
public static function hasMany() {
return static::$_hasMany;
}
/**
* Returns the BelongsToMany associations as statically defined in the
* $_belongsToMany property
*
* @return array
*/
public static function belongsToMany() {
return static::$_belongsToMany;
}
}
@@ -16,6 +16,7 @@
*/
namespace Cake\ORM;
use Cake\Core\App;
use Cake\Database\Schema\Table as Schema;
use Cake\Event\EventManager;
use Cake\ORM\Association\BelongsTo;
@@ -126,6 +127,13 @@ class Table {
*/
protected $_entityClass = '\Cake\ORM\Entity';
/**
* Whether associations where copied already from the entity class or not
*
* @var boolean
*/
protected $_associationsInit = false;
/**
* Initializes a new instance
*
@@ -402,6 +410,10 @@ public function entityClass($name = null) {
* @return Cake\ORM\Association
*/
public function association($name) {
if (!$this->_associationsInit && $this->entityClass()) {
$this->_associationsInit = true;
$this->_setupAssociations();
}
if (isset($this->_associations[$name])) {
return $this->_associations[$name];
}
@@ -757,4 +769,44 @@ public function __call($method, $args) {
return $this->callFinder($method, $query, $options);
}
/**
* Copies association configuration from the entity class to the associations
* classes stored in this object
*
* @return void
*/
protected function _setupAssociations() {
$entity = $this->entityClass();
$types = [
'belongsTo' => false,
'hasOne' => false,
'hasMany' => true,
'belongsToMany' => true
];
foreach ($types as $assoc => $plural) {
$associations = $entity::$assoc();
foreach ($associations as $key => $val) {
if (is_integer($key)) {
$entityClass = App::classname($val, 'Model\Entity');
list(,$className) = pluginSplit($val);
list($key, $val) = [$className, compact('entityClass')];
}
if (!empty($val['className'])) {
$val['entityClass'] = App::classname($val['className'], 'Model\Entity');
unset($val['className']);
}
if (empty($val['property'])) {
$name = Inflector::variable($key);
$name = $plural ? Inflector::pluralize($name) : $name;
$val['property'] = $name;
}
$this->{$assoc}($key, $val);
}
}
}
}
@@ -783,4 +783,44 @@ public function testFindListHydrated() {
$this->assertSame($expected, $query->toArray());
}
/**
* Tests automatic setup of associations based on entity information
*
* @return void
*/
public function testAutoSetupBelongsToBasic() {
$table = new Table(['table' => 'users', 'connection' => $this->connection]);
$barTarget = new Table(['table' => 'bars', 'connection' => $this->connection]);
$entity = $this->getMockClass('\Cake\ORM\Entity', ['belongsTo']);
$barEntity = $this->getMockClass('\Cake\ORM\Entity', ['repository']);
class_alias($barEntity, 'App\Model\Entity\Bar');
$entity::staticExpects($this->once())->method('belongsTo')
->will($this->returnValue(['Bar', 'Foo.Department']));
$barEntity::staticExpects($this->at(0))->method('repository')
->will($this->returnValue($barTarget));
$depatmentTarget = new Table([
'table' => 'departments',
'connection' => $this->connection
]);
$departmentEntity = $this->getMockClass('\Cake\ORM\Entity', ['repository']);
class_alias($departmentEntity, 'Foo\Model\Entity\Department');
$departmentEntity::staticExpects($this->at(1))->method('repository')
->will($this->returnValue($depatmentTarget));
$table->entityClass($entity);
$association = $table->association('Bar');
$this->assertEquals('Bar', $association->name());
$this->assertEquals('bar', $association->property());
$this->assertSame($barTarget, $association->target());
$association = $table->association('Department');
$this->assertEquals('Department', $association->name());
$this->assertEquals('department', $association->property());
$this->assertSame($depatmentTarget, $association->target());
}
}

0 comments on commit c04bcf9

Please sign in to comment.
You can’t perform that action at this time.