bmacnaughton

How to capture page containing a pagetable?

Recommended Posts

When a PageTable field in a specific template is being edited I need to know the Page that contains the PageTable so I can fill in hidden fields in the PageTable.

I can capture the page being edited via:

// $this->addHookBefore('ProcessPageEdit::execute', ...

public function pageEditExecuteBefore(HookEvent $e) {
    $p = $e->object->getPage();
    if ($p->template !== 'rtw-product') return;
    // $p is the page being edited
}

I can intercept PageTable entry being saved:

// $this->addHookBefore('Pages::save'

public function savePageBefore (HookEvent $e) {
    $p = $this->wire('page')->id;
    $page = $e->arguments('page');
    $obj = $e->object;
    $name = $page->name;                       // page name of PageTable page
    $template = $page->template->name;         // template of PageTable page
    $parent = $page->parent->name;             // parent directory for PageTable items

What I am trying to find is the page in which the PageTable field is located.

I've also tried having pageEditExecuteBefore() saving $p in $this->context and then accessing that in savePageBefore() but it's a different instance of the class because $this->context is null when it gets to savePageBefore(). I could save the page ID in session, but that seems error prone.

Does anyone know how to achieve this?

Share this post


Link to post
Share on other sites

Try this

$fieldList = $fields->find('type%=PageTable');
foreach($fieldList as $f) {
    if($page->template->hasField($f)) {
        // page has the field
    }
}

Or simply:

if($page->fields->find('type%=PageTable')->count)) {
	// page has a PageTable field
}

// check for a specific field
if($myField = $page->fields->get('type%=PageTable')) {
	// $myField of PageTable type exists on this page.
}

Here I used find() method with a selector for matching type. You can be more specific, or use any other WireArray methods of course.

https://processwire.com/api/ref/wire-array/

Edited by abdus
Added a simpler way
  • Like 2

Share this post


Link to post
Share on other sites

Thanks Abdus. I have a little different problem - I am looking for the specific page instance that has the field being edited. There are multiple pages based on the template that contains that field. I need to know which specific page instance is the "logical parent", not the set of pages that contain that field, i.e., are instances of the template that includes that PageTable field.

To try to make it more concrete:

PageA, PageB, and PageC all have the PageTable field Variations.

When in the Admin interface PageA is being edited and the user wants to add a variation then they click "Add New" under Variations.

At that point an iFrame overlay pops up to enter data for the new PageTable page in Variations.

When that PageTable page is saved I need to know that it is associated with PageA.

If the PageTable pages were in their default location (children of PageA) then I could just look at parent. But the PageTable pages are all kept in a single directory for grouping purposes.

Make sense?

Share this post


Link to post
Share on other sites

usually what i do in these cases is add a page field to the template of the pagetable page, and set that field to the page that it is being edited from. I used a save hook on the main page to look at the items in the page table and for any where that page field is not set, it assigns that field to the page being edited (so setting the page field on the pagetable item to PageA). that field is also hidden using CSS.

  • Like 4

Share this post


Link to post
Share on other sites

Another way perhaps:

When you look in the database at field_pagetablefieldname you will see 3 columns -

pages_id = the page on which the PageTable appears (this is what you need)

data = the id of the PageTable page

sort = sort order for the items to appear on the pages_id page

You could hook after the new PageTable entry is saved (ie Pages::saved), and do a $database call to select the pages_id that has the newly created page id in the data field.

 

Screenshot 2017-05-04 09.37.05.png

Share this post


Link to post
Share on other sites

@bmacnaughton, give the following hook a try (must be in /site/init.php or in init method of module). Seems to work well for both PageTable additions and edits of existing items.

$this->addHookBefore('InputfieldPageTableAjax::checkAjax', function($event) {
    $page_id = (int) $this->input->get('id');
    $item_id = (int) $this->input->get('InputfieldPageTableAdd');
    if($page_id) $page = $this->pages->get($page_id); // $page is the container page
    if($item_id) $item = $this->pages->get($item_id); // $item is the PageTable item
    // ...
});

 

Edit Nov 2017: more information in this post...

 

Edited by Robin S
Link to related post
  • Like 2

Share this post


Link to post
Share on other sites

Thank you @Robin S. I am going to give this a try - I solved this particular problem by forcing the new entries under the page they are contained by but that is a suboptimal solution. 

Share this post


Link to post
Share on other sites

You're all wrong and most only work with entries already added and saved to the pagetable. It doesn't work for when adding a new item, cause the new entry page isn't saved yet to the pagetable field... Only easy way is to store them as children, so you can check for the parent. But storing the entries in a different place there's no way to tell in the newly created entry what source page it is going to land in. 

I created a github issue 3! years ago https://github.com/ryancramerdesign/ProcessWire/issues/699 and never got a single response from Ryan...

The solution I came up with was to store the id in the session and transfer that over to the page edit of the entry, Since I had not autoname feature enabled there, there was a step in between where the entry page isn't even created yet. Kinda hacky but worked.

The other issue I got with PageTable also never got answered https://github.com/ryancramerdesign/ProcessWire/issues/700

  • Like 1

Share this post


Link to post
Share on other sites
7 hours ago, Soma said:

The solution I came up with was to store the id in the session and transfer that over to the page edit of the entry,

I tried the same thing - it was the last way I could think of. I'm not sure why it didn't work but will try again.

Share this post


Link to post
Share on other sites

yeah, to achieve what i was referring to above, i had a hook on the save of the main page, which iterated through all of the items in the pagetable where the field was blank, and set that value...

Has anyone come up with a reliable way to get the ID of the page that contains the page table field, either through hook, database query, or session vars? would be very helpful to know if this has been solved - maybe @Soma could you post an example of how to set the id of the page in the session; my issue with this is i have a page in a page table, which then has another page table in it, so the system is having trouble with any get var because it ends up being the root/bottom page, not the 2nd level up modal which is the page id i need...

Share this post


Link to post
Share on other sites

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 account

Sign in

Already have an account? Sign in here.

Sign In Now


  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By Noel Boss
      Page Query Boss
      Build complex nested queries containing multiple fields and pages and return an array or JSON. This is useful to fetch data for SPA and PWA.
      You can use the Module to transform a ProcessWire Page or PageArray – even RepeaterMatrixPageArrays – into an array or JSON. Queries can be nested and contain closures as callback functions. Some field-types are transformed automatically, like Pageimages or MapMarker.
      Installation
      Via ProcessWire Backend
      It is recommended to install the Module via the ProcessWire admin "Modules" > "Site" > "Add New" > "Add Module from Directory" using the PageQueryBoss class name.
      Manually
      Download the files from Github or the ProcessWire repository: https://modules.processwire.com/modules/page-query-builder/
      Copy all of the files for this module into /site/modules/PageQueryBoss/ Go to “Modules > Refresh” in your admin, and then click “install” for the this module. Module Methods
      There are two main methods:
      Return query as JSON
      $page->pageQueryJson($query); Return query as Array
      $page->pageQueryArray($query); Building the query
      The query can contain key and value pairs, or only keys. It can be nested and 
      contain closures for dynamic values. To illustrate a short example:
      // simple query: $query = [ 'height', 'floors', ]; $pages->find('template=skyscraper')->pageQueryJson($query); Queries can be nested, contain page names, template names or contain functions and ProcessWire selectors:
      // simple query: $query = [ 'height', 'floors', 'images', // < some fileds contain default sub-queries to return data 'files' => [ // but you can also overrdide these defaults: 'filename' 'ext', 'url', ], // Assuming there are child pages with the architec template, or a // field name with a page relation to architects 'architect' => [ // sub-query 'name', 'email' ], // queries can contain closure functions that return dynamic content 'querytime' => function($parent){ return "Query for $parent->title was built ".time(); } ]; $pages->find('template=skyscraper')->pageQueryJson($query); Keys:
      A single fieldname; height or floors or architects 
      The Module can handle the following fields:
      Strings, Dates, Integer… any default one-dimensional value Page references Pageimages Pagefiles PageArray MapMarker FieldtypeFunctional A template name; skyscraper or city
      Name of a child page (page.child.name=pagename); my-page-name A ProcessWire selector; template=building, floors>=25
      A new name for the returned index passed by a # delimiter:
      // the field skyscraper will be renamed to "building": $query = ["skyscraper`#building`"]  
      Key value pars:
      Any of the keys above (1-5) with an new nested sub-query array:
      $query = [ 'skyscraper' => [ 'height', 'floors' ], 'architect' => [ 'title', 'email' ], ]  
      A named key and a closure function to process and return a query. The closure gets the parent object as argument:
      $query = [ 'architecs' => function($parent) { $architects = $parent->find('template=architect'); return $architects->arrayQuery(['name', 'email']); // or return $architects->explode('name, email'); } ] Real life example:
      $query = [ 'title', 'subtitle', // naming the key invitation 'template=Invitation, limit=1#invitation' => [ 'title', 'subtitle', 'body', ], // returns global speakers and local ones... 'speakers' => function($page){ $speakers = $page->speaker_relation; $speakers = $speakers->prepend(wire('pages')->find('template=Speaker, global=1, sort=-id')); // build a query of the speakers with return $speakers->arrayQuery([ 'title#name', // rename title field to name 'subtitle#ministry', // rename subtitle field to ministry 'links' => [ 'linklabel#label', // rename linklabel field to minlabelistry 'link' ], ]); }, 'Program' => [ // Child Pages with template=Program 'title', 'summary', 'start' => function($parent){ // calculate the startdate from timetables return $parent->children->first->date; }, 'end' => function($parent){ // calculate the endate from timetables return $parent->children->last->date; }, 'Timetable' => [ 'date', // date 'timetable#entry'=> [ 'time#start', // time 'time_until#end', // time 'subtitle#description', // entry title ], ], ], // ProcessWire selector, selecting children > name result "location" 'template=Location, limit=1#location' => [ 'title#city', // summary title field to city 'body', 'country', 'venue', 'summary#address', // rename summary field to address 'link#tickets', // rename ticket link 'map', // Mapmarker field, automatically transformed 'images', 'infos#categories' => [ // repeater matrix! > rename to categories 'title#name', // rename title field to name 'entries' => [ // nested repeater matrix! 'title', 'body' ] ], ], ]; if ($input->urlSegment1 === 'json') { header('Content-type: application/json'); echo $page->pageQueryJson($query); exit(); } Module default settings
      The modules settings are public. They can be directly modified, for example:
      $modules->get('PageQueryBoss')->debug = true; $modules->get('PageQueryBoss')->defaults = []; // reset all defaults Default queries for fields:
      Some field-types or templates come with default selectors, like Pageimages etc. These are the default queries:
      // Access and modify default queries: $modules->get('PageQueryBoss')->defaults['queries'] … public $defaults = [ 'queries' => [ 'Pageimages' => [ 'basename', 'url', 'httpUrl', 'description', 'ext', 'focus', ], 'Pagefiles' => [ 'basename', 'url', 'httpUrl', 'description', 'ext', 'filesize', 'filesizeStr', 'hash', ], 'MapMarker' => [ 'lat', 'lng', 'zoom', 'address', ], 'User' => [ 'name', 'email', ], ], ]; These defaults will only be used if there is no nested sub-query for the respective type. If you query a field with complex data and do not provide a sub-query, it will be transformed accordingly:
      $page->pageQueryArry(['images']); // returns something like this 'images' => [ 'basename', 'url', 'httpUrl', 'description', 'ext', 'focus'=> [ 'top', 'left', 'zoom', 'default', 'str', ] ]; You can always provide your own sub-query, so the defaults will not be used:
      $page->pageQueryArry([ 'images' => [ 'filename', 'description' ], ]); Overriding default queries:
      You can also override the defaults, for example
      $modules->get('PageQueryBoss')->defaults['queries']['Pageimages'] = [ 'basename', 'url', 'description', ]; Index of nested elements
      The index for nested elements can be adjusted. This is also done with defaults. There are 3 possibilities:
      Nested by name (default) Nested by ID Nested by numerical index Named index (default):
      This is the default setting. If you have a field that contains sub-items, the name will be the key in the results:
      // example $pagesByName = [ 'page-1-name' => [ 'title' => "Page one title", 'name' => 'page-1-name', ], 'page-2-name' => [ 'title' => "Page two title", 'name' => 'page-2-name', ] ] ID based index:
      If an object is listed in $defaults['index-id'] the id will be the key in the results. Currently, no items are listed as defaults for id-based index:
      // Set pages to get ID based index: $modules->get('PageQueryBoss')->defaults['index-id']['Page']; // Example return array: $pagesById = [ 123 => [ 'title' => "Page one title", 'name' => 123, ], 124 => [ 'title' => "Page two title", 'name' => 124, ] ] Number based index
      By default, a couple of fields are transformed automatically to contain numbered indexes:
      // objects or template names that should use numerical indexes for children instead of names $defaults['index-n'] => [ 'Pageimage', 'Pagefile', 'RepeaterMatrixPage', ]; // example $images = [ 0 => [ 'filename' => "image1.jpg", ], 1 => [ 'filename' => "image2.jpg", ] ] Tipp: When you remove the key 'Pageimage' from $defaults['index-n'], the index will again be name-based.
       
      Debug
      The module respects wire('config')->debug. It integrates with TracyDebug. You can override it like so:
      // turns on debug output no mather what: $modules->get('PageQueryBoss')->debug = true; Todos
      Make defaults configurable via Backend. How could that be done in style with the default queries?
    • By karian

      Hi, based on the work of @microcipcip and @gebeer  (see their posts here and here), I put together a Processwire + React boilerplate (profile).

      Here is the Github repository: https://github.com/lapico/process-react

      Cheers,
      K
       
    • By louisstephens
      So I recently upgraded my sandbox environment to the latest master version from .62(?). I logged in and received the following error:
      "There appear to be multiple copies of module "AdminThemeUikit" on the file system."
      Unfortunately, in my haste, I removed the theme from site->modules so it doesnt exist anymore, and the theme has defaulted somewhat to the old theme. Is there a way to restore UIkit as the theme for processwire natively, or do I need to completely re-install?
    • By Noel Boss
      Hi, I'm stuck since hours and don't know what to do. Here is my Problem:
      I try to generate Previews of PDF using imagick. I have 4 PDF, I generate a preview of the first page of the pdf, save it to a temporary file and want to import it using the api into an image field. It works for the later 3 pdf but not the first. I add it to the image field and save it. Inside the function that saves it, the image is stored in 'data' as well as in 'itemsAdded' but as soon as i leave the function, its nowhere to be found.
      Process:
      1. create previewimage using imagick and create Pageimage > works
      2. add image to filed 'filepreviews', returns Pageimages array with image added > okay
      3. save page > returns true
      4. Outside renderPreview method, image is not anymore in 'filepreviews'
      // mymodule // … // foreach($files as $file){ $preview = wire('page')->filepreviews->get('name*='.$file->basename(false)); // if there is no preview image… if (!$preview instanceof Pageimage) { $this->renderPreview($file) // we create one using $this->renderPreview > should return true on successful save dump(wire('page')->filepreviews); // my Image is nowhere to be found $preview = wire('page')->filepreviews->get('name*='.$file->basename(false)); } // end for each // render a preview of an otherwhise not supported file format // return true if sucessfull save private function renderPreview($file) { $page = wire('page'); // get path to temporary image $tempFile = $path.$file->basename(false).'-preview.jpg'; // … some imagick code // … and save it: $imagick->writeImage($tempFile); $img = new Pageimage($page->filepreviews, $tempFile); $img->description = $file->basename(false); // destroy temp image unlink($tempFile); // this is my Pageimage, all good… dump($img); // save image, my Pageimage can be found in data and itemsAdded – all good dump($page->filepreviews->add($img)); $page->of(false); $success = $page->save(); // sucess = true dump($success); return $success; }
       
      Second question:
      Would there a generally better approach? Like using pageFiles somehow. Goal is to be able to use the image api like scale etc – I don't generally need the images to be stored in an image field.
       
    • By Kevin C. McCarthy
      I am desperate to learn how to you ProcessWire to it's fullest potential, and while the documentation is great and always appreciated, I simply can't follow along because it gets way too technical without really showing how applicable and versatile it can be. Then again, I'm almost a moderate understanding of PHP and no experience with APIs or programming JavaScript—so it's probably leagues ahead of where I am at the moment. That said, I learn best by watching and the doing. Think Codecademy or FreeCodeCamp. I was wonder if there are any video tutorials or walk-through lessons to give me a greater understanding of ProcessWire and how to utilize it effectively.
      For some background, I'm great with WordPress and I'm great with writing websites by hand with Notepad only. The biggest hurdles I have with PW is the phrasing is so far left of WP at times that it's a massive hurdle for me to get over. Like in WP, themes, templates, etc are totally different things. And as someone who builds WP sites for a living, it gets hard to kill those old preconceived meanings.
      I want to start building out PW sites for numerous reasons. For one, most of my clients they would benefit from it vs the Bloated Beast. Two, it would allow me to differentiate me in a market saturated by WP devs. I know I have a long ways to go until I reach that point of considering myself a "PW dev", but I am desperate for resources to help me wrap my head around it.
      I've built my own website in PW but TBH it only handles some of the data while most of the text has been hard-coded into the PHP template files because I couldn't get my my head around the "best practice" of structuring the data.
      Anyway, enough rambling, I'm just hoping those of you in the community can point me to easily-digestible sources out there that can help move me along so I can actually benefit from using the platform. Thank you!