Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Implementing Model::relationships() #575

Closed
wants to merge 1 commit into from

2 participants

@jails
Collaborator

Currently relation names are "Inflectorized" in datas, i.e. if you have the following relation : "Post HasMany Comment" the comment's data are available using :

$entity->comments

comments is the fieldname associated to the "Comment" relation but there's no way to reach the relation name without iterating over all Model's relations.

Model::relationship() allow here to get back to the relation name.

    echo Model::relationship('comments') // => 'Comment'

PS:
This will help to support the following syntax in the next futur :

$entity->comments = array(
    array('body' => 'comment1'),
    array('body' => 'comment1')
)
@nateabele
Owner

I'm not sure I understand why this is needed. Entity already contains all the mapping information it needs to support that syntax in $_relationships.

@jails
Collaborator

Currently the syntax :

$document->comments = array(
    array('body' => 'comment1'),
    array('body' => 'comment1')
)

only works with Document if comments is correctly defined in the document's Schema class. comments can't be a relation like belongsTo/hasMany. And it doesn't work with Record, well i mean it will not be instantiated as a RecordSet.

$_relationships is only populated on finds, not on updates. It only contains loaded datas. So currently there's no way to get back to a relation name thanks to a field name. This PR result from #572.

It imo this 2 PR show another way to address relationships based on detection more than splits.

@jails
Collaborator

Maybe a peace of code will be clearer..
The following is a draft of what I'm trying to reach for Record:
https://gist.github.com/3114191

@jails
Collaborator

To summarize 572 & 573, these PR are a suggestion for changing the actual behavior where :

$_relationships = array(
    'fieldname' => //datas
    'fieldname' => //datas
    ...
);

$_updated = array(
    'fieldname' => //datas
    'fieldname' => //datas
    ...
);

(Note : $_relationships are only populated with datas if the 'with' option is used in finds).

To the following:

//Contain only the mapping between a fieldname and it's corresponding relation
$_relationships = array(
    'fieldname' => //its corresponding relation name
    'fieldname' => //its corresponding relation name
    ...
);

//All datas are in $_updated
$_updated = array(
    'fieldname' => //datas
    'fieldname' => //datas
    ...
);

(Note : here $_relationships is an exhausitve mapping between fieldnames & relations no matter this datas exists or not $_updated).

If we need to iterate over relation's data a simple array_keys() should does the job. Same behavior can be used for embeded

@nateabele
Owner

Any reason this can't be consolidated with Model::relations()?

@jails
Collaborator

Imo it's just for performance since you can indeed did a foreach on all Model::relations() using a if ($fieldName == $relation->fieldName()) to know if a field name is a relation.
But currently I don't see any other alternative but may i miss the best one ;-)

@nateabele nateabele closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jul 13, 2012
  1. @jails
This page is out of date. Refresh to see the latest.
Showing with 38 additions and 1 deletion.
  1. +24 −0 data/Model.php
  2. +14 −1 tests/cases/data/ModelTest.php
View
24 data/Model.php
@@ -165,6 +165,13 @@ class Model extends \lithium\core\StaticObject {
protected $_relations = array();
/**
+ * An array containing the match between fieldname and their corresponding relation name.
+ *
+ * @var array
+ */
+ protected $_relationships = array();
+
+ /**
* List of relation types.
*
* Valid relation types are:
@@ -642,6 +649,22 @@ public static function relations($name = null) {
}
/**
+ * Returns the relation name associated to a field name, or a list of field name associated
+ * to their to corresponding relation name if `$fieldname` is null.
+ *
+ * @param string $fieldname A fieldname.
+ * @return mixed A relation name or an array of associations.
+ */
+ public static function relationships($fieldname = null) {
+ $self = static::_object();
+
+ if (!$fieldname) {
+ return $self->_relationships;
+ }
+ return isset($self->_relationships[$fieldname]) ? $self->_relationships[$fieldname] : null;
+ }
+
+ /**
* Creates a relationship binding between this model and another.
*
* @see lithium\data\model\Relationship
@@ -661,6 +684,7 @@ public static function bind($type, $name, array $config = array()) {
throw new ConfigException("Invalid relationship type `{$type}` specified.");
}
$rel = static::connection()->relationship(get_called_class(), $type, $name, $config);
+ $self->_relationships[$rel->fieldName()] = $name;
return $self->_relations[$name] = $rel;
}
View
15 tests/cases/data/ModelTest.php
@@ -790,6 +790,19 @@ public function testLiveConfiguration() {
$result = MockBadConnection::meta('connection');
$this->assertFalse($result);
}
+
+ public function testRelationships() {
+ MockPost::bind('hasMany', 'MockTag');
+ $relationships = MockPost::relationships();
+ $this->assertEqual(array(
+ 'mock_comments' => 'MockComment',
+ 'mock_tags' => 'MockTag'
+ ), $relationships);
+
+ $this->assertEqual('MockComment', MockPost::relationships('mock_comments'));
+ $this->assertEqual('MockTag', MockPost::relationships('mock_tags'));
+ $this->assertNull(MockPost::relationships('undefined'));
+ }
}
-?>
+?>
Something went wrong with that request. Please try again.