Skip to content
This repository has been archived by the owner on Jan 8, 2020. It is now read-only.

partialLoop/partial View Helper can not be nested when using setObjectKey #3758

Closed
tylkomat opened this issue Feb 12, 2013 · 11 comments
Closed
Assignees
Milestone

Comments

@tylkomat
Copy link

Since always the same partial(Loop) instance is used, setting a new object key inside a partial(Loop) overwrites also the current object key (because same instance). Maybe it is a better way to provide the objectKey as optional parameter for the __invoke methods of partial and partialLoop.

@ThaDafinser
Copy link
Contributor

@tylkomat i haven't tested the following, but because the execute is immediately, there shouldn't be any problems?

$this->partial('module/view/header')->setObjectKey('myVar1'); //assigned to myVar1, because immediately execute
$this->partial('module/view/footer')->setObjectKey('myVar2'); //assignet to myVar2, because immediately execute

Haven't ever used PartialLoop so maybe here is the real problem?

@tylkomat
Copy link
Author

tylkomat commented Nov 5, 2013

@ThaDafinser The thing is the ViewHelpers in general are shared instances. When you change the objectKey you change it everywhere where the function is called. This is only a problem when you nest these calls because the value is also changed for the parent, as they use the same instance. This results in the parent not working as expected, because it keeps the objectKey that was set in a deeper level of nesting.

@ThaDafinser
Copy link
Contributor

@tylkomat I understand that there is only one instance. But could you maybe provide a "use-case" there you need this to be seperated?

@tylkomat
Copy link
Author

tylkomat commented Nov 5, 2013

@ThaDafinser Let's say I have my index.phtml that renders a list of events. So beside other html and php I call:

<?php
    $this->partialLoop()->setObjectKey('event');
    echo $this->partialLoop('event/partial', $this->events);
?>

to call another template. In $this->events is an array of events. Each of them will be assigned to the $this->event variable in the called template (event.phtml).
This template itself can have multiple offers to I would call:

<?php
    $this->partialLoop()->setObjectKey('offer');
    echo $this->partialLoop('event/offer', $this->event->offers)
?>

to call an offers template (offer.phtml) to render each offer. Since I can set the ObjectKey only globally I also change it in the context of event.phtml where the expected $this->event variable is now $this->offer, because they use the same partialLoop instance.

@weierophinney
Copy link
Member

@tylkomat Try cloning the partialLoop view helper within your partial view script:

$partialLoop = $this->plugin('partialLoop');
$cloned       = clone $partialLoop;

$cloned->setObjectKey('offer');
echo $cloned('event/offer', $this->event->offers);

That should work; if it doesn't, we'll need to add a __clone method to make it work. Let me know!

@tylkomat
Copy link
Author

tylkomat commented Nov 6, 2013

@weierophinney this works partially, I can access $this->offer in the offer.pthml as expected, so clone works fine, but I have no way to access the $cloned variable from inside offer.phtml to use getPartialCounter() function.

@weierophinney
Copy link
Member

@tylkomat Hmm.. Yes, for that to work, we'd need to pass the PartialLoop instance in as well. For that to work, we'd need a third argument to __invoke on both the PartialLoop and Partial helpers, to allow passing additional variables. That might look like this:

$partialLoop = $this->plugin('partialLoop');
$cloned       = clone $partialLoop;

$cloned->setObjectKey('offer');
echo $cloned('event/offer', $this->event->offers, array('partialLoop' => $cloned));

// inside your partial:
$partialLoop = $this->partialLoop;
$count = $partialLoop->getPartialCounter();

Do either you or @ThaDafinser want to take a stab at this feature?

@tylkomat
Copy link
Author

tylkomat commented Nov 7, 2013

@weierophinney @ThaDafinser This feature might not be as simple as it seems. I was exploring a little of what should be changed and it seems it is a lot. Maybe I'm wrong, but I see two options here.

  1. The partial can not inject itself into the view directly. The partial must tell the PhpRenderer to update its instance in the HelperPluginManager that is injected into the view that is currently rendered without touching the global state.
  2. The ServiceManager->get() function may receive a parameter or option to enable the possibility to force a return of a new Instance of the desired service. This would be a huge change, as many Classes on the way will have to be touched. On the other hand this may provide a great benefit for all Services.

Maybe I'm thinking too complicated here.

@weierophinney
Copy link
Member

@tylkomat Well, adding another optional parameter to get() starts to get problematic, as we already have one optional parameter (and, in the case of plugin managers, such as the view helper manager, two).

We could make either partial() or partialLoop unshared (services may be configured to be shared or not shared), but I suspect that would have further ramifications.

Keep thinking, and if you come with any concrete ideas, feel free to create a PR so we can discuss them.

@larsnystrom
Copy link
Contributor

Since I just encountered this problem I thought I'd chip in.

As a user it's easy to get around this problem by saving the current objectKey to a temporary variable and restoring when you're done:

<?php
    $prevKey = $this->partialLoop()->getObjectKey();
    $this->partialLoop()->setObjectKey('newkey');
    echo $this->partialLoop('partial.phtml', $arrayWithObjects);
    $this->partialLoop()->setObjectKey($prevKey);
?>

This could be implemented by the framework using a stack of objectKeys in the PartialLoop helper, letting setObjectKey() push a key to the stack and __invoke() pop a key from the stack when the loop is completed.

That would of course cause a BC break for those relying on the objectKey staying set during multiple invocations of PartialLoop.

A somewhat more complex idea is to use a nesting-level bound objectKey, meaning if partialLoop() is called from within another partialLoop() the inner objectKey would be different from the outer object key, but default to the outer objectKey. I have created a pull request which implements this.

larsnystrom added a commit to larsnystrom/zf2 that referenced this issue Jan 2, 2015
@Ocramius Ocramius added this to the 2.3.4 milestone Jan 2, 2015
@Ocramius Ocramius self-assigned this Jan 2, 2015
Ocramius pushed a commit that referenced this issue Jan 3, 2015
Ocramius added a commit that referenced this issue Jan 3, 2015
Ocramius added a commit that referenced this issue Jan 3, 2015
Ocramius added a commit that referenced this issue Jan 3, 2015
Ocramius added a commit that referenced this issue Jan 3, 2015
Ocramius added a commit that referenced this issue Jan 3, 2015
Ocramius added a commit that referenced this issue Jan 3, 2015
Ocramius added a commit that referenced this issue Jan 3, 2015
…op-calls' into develop

Close #7093
Close #3758
Forward port #7093
Forward port #3758
@Ocramius
Copy link
Member

Ocramius commented Jan 3, 2015

This is handled in #7093

gianarb pushed a commit to zendframework/zend-view that referenced this issue May 15, 2015
gianarb pushed a commit to zendframework/zend-view that referenced this issue May 15, 2015
gianarb pushed a commit to zendframework/zend-view that referenced this issue May 15, 2015
…aking scope emulation variables `private`
gianarb pushed a commit to zendframework/zend-view that referenced this issue May 15, 2015
gianarb pushed a commit to zendframework/zend-view that referenced this issue May 15, 2015
gianarb pushed a commit to zendframework/zend-view that referenced this issue May 15, 2015
gianarb pushed a commit to zendframework/zend-view that referenced this issue May 15, 2015
…ixed return type (`self`), leveraging parent class API
gianarb pushed a commit to zendframework/zend-view that referenced this issue May 15, 2015
gianarb pushed a commit to zendframework/zend-view that referenced this issue May 15, 2015
gianarb pushed a commit to zendframework/zend-view that referenced this issue May 15, 2015
gianarb pushed a commit to zendframework/zend-view that referenced this issue May 15, 2015
…efactoring `PartialLoop` `$values` extraction logic into its own private method
gianarb pushed a commit to zendframework/zend-view that referenced this issue May 15, 2015
…dding `@group` annotation for newly introduced tests
gianarb pushed a commit to zendframework/zend-view that referenced this issue May 15, 2015
…dding test for non-iterable data exception message
gianarb pushed a commit to zendframework/zend-view that referenced this issue May 15, 2015
…etter exception message in case of non-iterable values given to the PartialLop helper
gianarb pushed a commit to zendframework/zend-view that referenced this issue May 15, 2015
…dding test for non-iterable object data exception message
gianarb pushed a commit to zendframework/zend-view that referenced this issue May 15, 2015
gianarb pushed a commit to zendframework/zend-view that referenced this issue May 15, 2015
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants