New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HasMany to HasOne relation with inverseOf and indexBy #7316

Closed
thejahweh opened this Issue Feb 17, 2015 · 9 comments

Comments

Projects
None yet
9 participants
@thejahweh
Copy link
Contributor

thejahweh commented Feb 17, 2015

There is a problem in the one to many relation, with inverseOf and indexBy. The problem is relatively simple: $object->article->objects (returns array but only with the related object).

I have the following two Tables

  • Article
    • articleID
  • Object
    • objectID
    • articleID

The relations are like this:

// Object Model
public function getArticle() {
    return $this->hasOne(Article::className(), ['articleID' => 'articleID'])
        ->inverseOf('objects');
}
// Article Model
public function getObjects()
{
    return $this->hasMany(Object::className(), ['articleID' => 'articleID'])
        ->indexBy('objectID')
        ->inverseOf('article');
}

And now the problem:

$object = Object::findOne(['objectID' => 1]);
// $object has one Article and the article of the object has many objects.
var_dump($object->article->objects);
// Returns a array with only the one related object indexed by an integer 
// and not every related object of the article.
@Faryshta

This comment has been minimized.

Copy link
Contributor

Faryshta commented Feb 17, 2015

I think you are missusing the inverseOf method.

When you call that method you are telling to the active records that the caller is the inverse of the defined relation, so that the active records doesn't have to search for them on the database.

// Object Model
public function getArticle() {
    return $this->hasOne(Article::className(), ['articleID' => 'articleID']);
}

Try this please

@thejahweh

This comment has been minimized.

Copy link
Contributor

thejahweh commented Sep 18, 2015

The inverseOf method should not be used with the hasOne method. It would make sense to add this to the docs.

@samdark samdark added the type:docs label Sep 19, 2015

@samdark

This comment has been minimized.

Copy link
Member

samdark commented Sep 19, 2015

@thejahweh any suggestions about where to add it?

@PowerGamer1

This comment has been minimized.

Copy link
Contributor

PowerGamer1 commented Jan 26, 2016

From a comment before public function inverseOf($relationName)

Sets the name of the relation that is the inverse of this relation.
For example, an order has a customer, which means the inverse of the "customer" relation
is the "orders"
, and the inverse of the "orders" relation is the "customer".

Part in bold directly suggests to use the inverseOf together with hasOne. That comment should be fixed too.

@lynicidn

This comment has been minimized.

Copy link
Contributor

lynicidn commented Jan 27, 2016

not correct code

public function getArticle() {
    return $this->hasOne(Article::className(), ['articleID' => 'articleID'])

public function getObjects()
{
    return $this->hasMany(Objects

Article has one Article
Objects has many Objects ?
relation populated with indexBy

@allush

This comment has been minimized.

Copy link

allush commented Aug 10, 2017

inverseOf may be used when using hasOne on both models. For example: Contract has a Result. Result has one contract, and contract has one resuls. in database: table contract (id, ...) and result (id, contract_id, ...) . one contract may have many results when using such scheme, but by logic, may have only one result, so, in the Contract model may be defined two relations: $result (hasOne) and $results (hasMany).

/**
 * @property Result $result
 * @property Result[] $results
 */
class Contract
{
  publc function getResult()
  {
    return $this->hasOne(Result::className(), ['contract_id' => 'id'])->inverseOf('contract');
  }

  publc function getResults()
  {
    return $this->hasMany(Result::className(), ['contract_id' => 'id'])->inverseOf('contract');
  }
}

/**
 *@property Contract $contract
 */
class Result
{
  public function getContract()
  {
    // inverse of result, not results.
    retutn $this->hasOne(Contract::className(), ['id' => 'contract_id')->inverseOf('result');
  }
}

is such using of inverseOf correct? or are there any mistakes?

@dmitry-kulikov

This comment has been minimized.

Copy link
Contributor

dmitry-kulikov commented Sep 29, 2017

Improved documentation in this PR #14892.

However I think that inverseOf for "multiple" relation ("many" in 1-to-many or many-to-many) is too dangerous for practical usage, for this reason I created another PR #14893, dangerous usage is ignored in this version. Unfortunately it breaks backward compatibility for people who used this, so unlikely to be accepted, however I would like to share my thoughts how developer-friendly inverseOf should be implemented.

My personal rules to stay on safe side with current Yii behavior:

  1. Name of relation must take into consideration cardinality of relation, so if Customer has many Orders then method will have name User::getOrders(), not User::getOrder().
  2. inverseOf is useful, but if argument of this method is plural name, e.g. inverseOf('orders'), better don't use it for this case.

Of course it also can be checked by models analysis.
Thus it is safe to use inverseOf for 1-to-1 on both sides of relation, on one side of 1-to-many, and not at all for many-to-many.

@samdark samdark added this to the 2.0.14 milestone Sep 30, 2017

@rob006

This comment has been minimized.

Copy link
Member

rob006 commented Oct 7, 2017

I'm for improving docs only. inverseOf() for "multiple" relations still can be useful, if you know what you're doing, so I'm against making this feature more foolproof at the cost of flexibility.

@samdark

This comment has been minimized.

Copy link
Member

samdark commented Oct 7, 2017

Agree.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment