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

[3.2]: Live Preview does not show changes #4542

Closed
sebastian-lenz opened this issue Jul 12, 2019 · 7 comments
Closed

[3.2]: Live Preview does not show changes #4542

sebastian-lenz opened this issue Jul 12, 2019 · 7 comments

Comments

@sebastian-lenz
Copy link
Contributor

Description

Starting with Craft 3.2 the live preview does not update in our projects after the editor changes something in the element form.

Steps to reproduce

  1. Open an entry, open the live preview and change something in the form on the left
  2. The spinner in the toolbar shows up, the preview reloads but shows the original state of the entry, any made changes are not reflected.

Additional info

  • Craft version: Craft Pro 3.2.1
  • PHP version: 7.2.19
  • Database driver & version: MySQL 5.7.19

Investigation

I've traced the actions using xDebug to check whether this is a bug in my code. This is what I've could reconstruct:

  • Live Preview requests are handled by the new PreviewController in Craft 3.2. The preview controller seems to work correctly, it picks up the incoming draft id / revision id and I can see it actually loads the changed element.
  • The preview controller then sends back the request to the normal request pipeline. This triggers the normal chain of events of a regular page request:
    • Application::handleRequest
    • Request::resolve
    • UrlManager::parseRequest
    • UrlManager::_getRequestRoute
    • UrlManager::_getMatchedElementRoute
  • The call to UrlManager::_getMatchedElementRoute seems to be the problem here as the url manager has cached the result of the initial url matching (using UrlManager::_matchedElementRoute) and this cached route contains the original, currently published version of the element. As the method does not run and opts out early, the placeholder element set by the preview controller is not being picked up here.

So, as far as I can see through this, I think that the preview controller should clear the cached route of the UrlManager before handing of the request to the normal request chain so it gets reevaluated.

Sidenote: I initially observed that the element returned by UrlManager::getMatchedElement is wrong in preview requests, it also points to the currently published element. As this call is cached too (using UrlManager::_matchedElement) it might be a good idea to clear that cache too?

@sebastian-lenz
Copy link
Contributor Author

Okay, we've found the issue in our module code, we have a listener for onBeforeAction on the application class. In this listener the code fetches the matched element from the url manager and causes the cached route and element to be set. If we remove the listener everything works as intended.

@brandonkelly
Copy link
Member

Sounds like that code needs to be revisited then.

In case it helps, here’s how previewing entries works in Craft 3.2:

  1. The token param tells Craft to initially route to PreviewController::actionPreview().
  2. PreviewController will load the entry/draft/revision associated with the token, and pass it to craft\services\Elements::setPlaceholderElement().
  3. Then it will basically remove the token from the request, and tell Craft to completely re-handle the request (this time sans-token), so it will route to wherever it would have gone if there hadn’t been a token.
  4. For the remainder of the request, if anything is trying to load the source entry, element queries should start replacing them with the entry/draft/revision that was loaded in step 2.

@sebastian-lenz
Copy link
Contributor Author

Thank you for your detailed response. Of course we did update our code after we've found the cause of our problems. I can understand that the releae of a new major version is a stressful situation for you and that the problem described here is not a major issue. However I would like to point out that:

  • Baiscally all code that accesses properties on the UrlManager before the action PreviewController has run will now:
    1. Receive wrong data as the matched element is not patched yet.
    2. Cause errors down the line as the PreviewController does not clear its caches correctly.
  • This is a breaking change, Craft 3.1 did not behave like this and there is no indication for this change in behaviour in the release notes.
  • As the PreviewController is, like the name suggests, a controller, a lot of events and hooks are executed beforehand. All userland code and all plugins therefore now have to check whether the current request is a preview request and act accordingly before accessing the UrlManager in those scenarios. To me this sounds like a major cause of pain.

I thought of creating a pull request to at least implement a cache clear method on the UrlManager which could solve the second issue. However, as this would be only a partial solution, I think it would be more sensible to look for a more wholesome solution.

@brandonkelly
Copy link
Member

In this listener the code fetches the matched element from the url manager and causes the cached route and element to be set

Can you post your code for this?

@sebastian-lenz
Copy link
Contributor Author

Sure, here is the actual code that triggered the error. Basically to reproduce the issue all you need is some custom module which does the following:

namespace acme;

use Craft;
use craft\web\Application;
use yii\base\ActionEvent;
use yii\base\Event;

class Module extends \yii\base\Module {
  public function init() {
    parent::init();
    Event::on(
      Application::class, Application::EVENT_BEFORE_ACTION,
      [$this, 'onBeforeAction']
    );
  }

  public function onBeforeAction(ActionEvent $event) {
    $element = Craft::$app->getUrlManager()->getMatchedElement();
  }
}

Calling UrlManager::getMatchedElementat this moment in the event chain triggers the described effects, $element will be the wrong element in this case and after the call UrlManager::_matchedElement will be set to the wrong element.

We meanwhile found similiar code in other projects where we use it for caching and preprocessing stuff.

@brandonkelly
Copy link
Member

Just fixed this for the next release. Going forward, preview requests will reset the matched element on UrlManager before re-routing the request.

To get the fix early, change your craftcms/cms requirement in composer.json to:

"require": {
  "craftcms/cms": "dev-develop#e2457ceece352edee53916008103bf866ed6ab3b as 3.2.2",
  "...": "..."
}

Then run composer update.

@sebastian-lenz
Copy link
Contributor Author

Awesome! Please excuse my tenacity and thank you for looking into it.

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

No branches or pull requests

2 participants