Skip to content
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

Deferred calls on TActiveRecordRelation objects collect foreign objects globally #554

Open
emkael opened this issue Sep 16, 2015 · 1 comment

Comments

@emkael
Copy link
Contributor

emkael commented Sep 16, 2015

Hi,

I'm having trouble with chaining multiple ->with_xxx() calls for TActiveRecord finders that are not executed immediately.

The basic setup is:

  • TActiveRecords: FirstRecord and SecondRecord
  • FirstRecord has foreign objects FieldOne and FieldTwo
  • SecondRecord has foreign objects FieldThree and FieldFour

I'm trying to prepare a finder (with chained with_xxx() calls) for FirstRecord, but execute the appropriate find*() method later in application's lifecycle. The call fails if there's another chain of ->with_xxx() between the creation of the first finder and find*() call. For example:

$preparedFinder = FirstRecord::finder()->withFieldOne()->withFieldTwo();
$otherFinder = SecondRecord::finder()->withFieldThree()->withFieldFour();
$preparedFinder->findAll(); // throws "FirstRecord.FieldFourID is not defined" TInvalidOperationException

As a matter of fact, replacing the last line with:

$otherFinder->findAll();

(so not deferring the second finder, just the first), throws the same exception, only trying to access FieldTwo in SecondRecord.

If there's no chaining (i.e. there's no ->with_xxx() call on TActiveRecordRelation instance, just on TActiveRecord instance), deferring the find*() call works as expected.

This is caused by a static stack used in the TActiveRecordRelation::__call method - which causes the result of withFieldFour() call to be appended to the stack, which is later processed by the findAll() call on TActiveRecordRelation object returned by withFieldTwo() (or vice versa).

Getting rid of multiple with_xxx() chaining does not trigger the same behavior, as the static stack in TActiveRecordRelation is not used. I can also see that design present all the way since 2007, so I'm guessing I'm just trying to use the framework in a way it wasn't supposed to be used. I couldn't find any code examples in documentation that would show either deferring finder() use or multiple with_xxx() chains - is that something that's achievable at all, somehow?

For now I think I can be able to patch my copy of the framework, for example by splitting the static stack per SourceRecord class name, but I'm not sure how unintrusive that workaround would be (it would certainly not work on multiple finders for the same record class, but from what I can see in TActiveRecord::finder implementation, that's impossible anyway).

Best regards,
Michal K.

@ctrlaltca
Copy link
Member

Nice analysis of the problem, thank you. Imho this is a technological limit of how TActiveRecordRelations has been implemented.
Yii's ActiveRecord implementation has been worked as a replacement of Prado's ActiveRecord, and for some time a backport also existed in Testing: d4b1971
It worked fine, but has been dropped since we had no active maintainer for it. I hope it could help you in find a solution to the problem.

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

No branches or pull requests

2 participants