nurkka Posted May 14 Share Posted May 14 Hello everyone, I use ProcessWire with multilangage support (5 languages) to provide a shop with content as JSON files. To match the specifications regarding the structure of those JSON files, I wrote a module that exports the files on page save. It renders a page and outputs the language content in different JSON files, within different folders. The module hooks into the Pages::saved hook. As soon as a page is saved in the admin area, the module loops over the languages, renders the page content in each language and writes a JSON file for each language. The following phenomenon now occurs: If one edits individual fields of the page in the admin area and then saves it, the JSON files are output correctly. If one saves the page again – without having changed a field on the page – $page->render() does not return the text content of the multilanguage fields, but objects with the text content of all languages. These are e.g. LanguagesPageFieldValue objects or ComboLanguagesValue objects. I believe this must have something to do with the trackChanges behaviour. ProcessWire tracks the changes that have been made to fields. If no changes have been detected, the behaviour appears to be different when saving the page and with $page->render within the Pages::saved hook. Here is the code (simplified) I am using: <?php namespace ProcessWire; class ExportAsJson extends WireData implements Module { public function init() { $this->addHookAfter('Pages::saved', $this, 'hookPageSaved', ['priority' => 200]); } protected function hookPageSaved(HookEvent $event) { $page = $event->arguments(0); // get the saved page // save current settings $saved_lang = $this->wire->user->language; $saved_user = $this->wire->user; $saved_output_formatting = $page->of(); // set current user to guest user $this->wire->users->setCurrentUser($this->wire->users->getGuestUser()); $page->of(true); $json_data = []; foreach ($this->wire->languages as $l) { $lang_name = $l->name; // set current language for rendering the page $this->wire->user->language = $l; // get rendered json data as string $markup = $page->render(); $json_data[$lang_name] = json_decode($markup); } // restore saved settings $page->of($saved_output_formatting); $this->wire->users->setCurrentUser($saved_user); $this->wire->user->language = $saved_lang; // write the JSON data to separate files on the remote shop server $this->writeJsonToSftpAsSeparateFiles($page, $json_data); } } The correct result in JSON would be like this: [ { "featureHeadline": "Lorem ipsum dolor" } ] But when I save the page without changing a field beforehand, I get this: [ { "featureHeadline": {} } ] Does anyone have an idea how to fix this behaviour? Thanks and best regards! Link to comment Share on other sites More sharing options...
poljpocket Posted May 21 Share Posted May 21 Can you also provide the code for your template file (the one which gets used by the render()-function)? Also just a side note: Is there a reason you are generating JSON in the render file, decoding it just to pass it along and re-encode it again? Link to comment Share on other sites More sharing options...
nurkka Posted May 22 Author Share Posted May 22 23 hours ago, poljpocket said: Can you also provide the code for your template file (the one which gets used by the render()-function)? Here is an example of the template file: $json_data = []; foreach ($page->contentblocks as $cb) { // "contentblocks" is a repeatermatrix field $json_data_block = $cb->render(); // this returns an actual json array array_push($json_data, $json_data_block); } header('Content-Type: application/json'); echo json_encode($json_data); And here is an examle of a repeatermatrix field template file: $json_data = []; $json_data["headline"] = $page->text; // "text" is a multilanguage text field $json_data["text"] = $page->body; // "body" is a multilanguage textarea/tinymce field return $json_data; // no json_encode needed 23 hours ago, poljpocket said: Also just a side note: Is there a reason you are generating JSON in the render file, decoding it just to pass it along and re-encode it again? Returning JSON data from the template did not work, because the $page->render() method threw an error ("strlen(): Argument #1 ($string) must be of type string, array given"). So I had to convert the JSON data to a string in the template and convert it back in the module. The advantage is, that one can view the page in the browser and check the JSON output. Link to comment Share on other sites More sharing options...
poljpocket Posted May 22 Share Posted May 22 (edited) Ok, I tried to reproduce your situation locally on Docker. There are two languages "default" and "other". I have used site-blank as template and just added a TinyMCE ML field for body and added it to basic-page. Also, I changed the title field to be ML. PW version is 3.0.227 and PHP version is 8.2.18. Here's the result after I have changed nothing and just hit "Save" (same result when I change something): And here is the relevant code I used. I tried to stay as true to your code as possible. ready.php <?php namespace ProcessWire; if(!defined("PROCESSWIRE")) die(); /** @var ProcessWire $wire */ $wire->pages->addHookAfter('Pages::saved(template=basic-page)', 'hookPageSaved', ['priority' => 200]); function hookPageSaved(HookEvent $event) { $wire = $event->wire; $page = $event->arguments(0); // get the saved page // save current settings $saved_lang = $wire->user->language; $saved_user = $wire->user; $saved_output_formatting = $page->of(); // set current user to guest user $wire->users->setCurrentUser($wire->users->getGuestUser()); $page->of(true); $json_data = []; foreach ($wire->languages as $l) { $lang_name = $l->name; // set current language for rendering the page $wire->user->language = $l; // get rendered json data as string $markup = $page->render(); $json_data[$lang_name] = json_decode($markup); } // restore saved settings $page->of($saved_output_formatting); $wire->users->setCurrentUser($saved_user); $wire->user->language = $saved_lang; // write the JSON data to Tracy bd($json_data); } basic-page.php <?php namespace ProcessWire; /** @var Page $page */ $json_data = []; $json_data['headline'] = $page->title; $json_data['text'] = $page->body; header('Content-Type: application/json'); echo json_encode($json_data); Note two differences: I have restricted the hook to only fire for pages with template basic-page (as this would throw a bunch of errors in the Admin otherwise) I am not using a module to add the hook Maybe this sheds some light into the situation. Edited May 22 by poljpocket 2 Link to comment Share on other sites More sharing options...
nurkka Posted May 22 Author Share Posted May 22 Thanks for testing. I was using the latest stable version of ProcessWire (3.0.229) and upgraded now to the latest dev version (3.0.239). The issue seems to be gone (for now), i.e. I get the same data on saving the page – no matter if I change something in the page editor or not. Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now