Jump to content
alevine

Module: Create a Pages->find(query) as a field

Recommended Posts

All:

  If you saw some of my recent postings where I was trying to solve the problem of running a query as a field, I have solved it.

I started with Hanna text, but that didn't quite get me all the way.  Then I tried just a Concat field, and that didn't get me all the way.

I modified the Concat fieldtype for my solution.  I had a need to dynamically pull Pages that were cross-referenced back:

   Product as a Page

   Photo Pages with a  multi-Page Select field that referenced Product (A photo could represent 1+ Products)

I wanted a ->photos field from Product that was updated dynamically according to what Photo entries were currently in place, and I didn't want copy/pasted code, and I wanted the selectors to be easily modifiable from the admin screens.

Usage is faily simple:

1:  Install as a normal module

2:  Create a field as a PagesSelectorQuery type

3: On the field details, enter your selector string, ie: template=my_template,select_page_field=$page

4: Add your field to whichever templates.

5: Access the PageArray results like you would any other $page->field

I hope you find it useful, at the very least as an exercise in my madness.  :)

Thanks again for all the help this community has provided.

EDIT:  Added to GitHub:  https://github.com/alevinetx/processwire-modules/blob/master/FieldtypePagesSelectorQuery.module

And Module's page:  http://modules.processwire.com/modules/fieldtype-pages-selector-query/

FieldtypePagesSelectorQuery.module

  • Like 2

Share this post


Link to post
Share on other sites

Would you mind creating a github account and put that there? :P

Share this post


Link to post
Share on other sites

Somewhere Soma did a post about github. That post did me start with git. I'm on mobile with a laggy expensive connection, so don't wanna search the forum.

Cool you created a module.

Share this post


Link to post
Share on other sites

Trying to figure out pros and cons of using this module because I'm doing something where I have a selector saved in a Page's text field and then do the find at render time. The module would have done that find, giving me a PageArray to use in the render. But I see that the find is done by runSelectorQuery() which is called by the wakeValue, sleepValue, loadPageField hooks. I'm new to a lot of this fieldType/hook stuff but isn't it redundant to do the find in the sleepValue hook?

Share this post


Link to post
Share on other sites

Trying to figure out pros and cons of using this module because I'm doing something where I have a selector saved in a Page's text field and then do the find at render time. The module would have done that find, giving me a PageArray to use in the render. But I see that the find is done by runSelectorQuery() which is called by the wakeValue, sleepValue, loadPageField hooks. I'm new to a lot of this fieldType/hook stuff but isn't it redundant to do the find in the sleepValue hook?

I think you see the pros.  As for the cons, I'm still new to PW, but the others haven't offered any comments about this being a bad thing. 

As for when the query is run, I honestly don't know either.  I pretty much did a blind copy from the Concat field, which is also a run-time/invocation-time process.  Hopefully Ryan and the others can provide some insight as to whether it should be called on all the hooks, or just some.

Share this post


Link to post
Share on other sites
Trying to figure out pros and cons of using this module because I'm doing something where I have a selector saved in a Page's text field and then do the find at render time. The module would have done that find, giving me a PageArray to use in the render. But I see that the find is done by runSelectorQuery() which is called by the wakeValue, sleepValue, loadPageField hooks. I'm new to a lot of this fieldType/hook stuff but isn't it redundant to do the find in the sleepValue hook?

The loadPageField() method is what loads the raw value from the database and returns it as an array. Most Fieldtypes just inherit the base Fieldtype method of doing this. But in this class, it has to be implemented since it doesn't actually load anything from the DB. 

The wakeupValue() method is what gets called to convert a raw value (typically an array) to an object, if required by the fieldtype. For instance, the Page reference Fieldtype converts it from an array of page IDs (numbers) to a PageArray of Page objects. If the raw value doesn't need any conversion to something else, then the function can just return the raw value. wakeupValue is typically dealing with values loaded from the database (via the loadPageField method). But wakeupValue can't assume that, it doesn't know where the value came from... it could have come from some other web service, data source or import function. That's why loadPageField and wakeupValue are separate things, and both necessary. 

The sleepValue() method is the opposite of wakeupValue(). It converts the value from a runtime representation back to a raw representation, suitable for storage in a DB or web service feed, etc. For instance, in the Page reference field type, it converts the value from a PageArray back to a regular PHP array of page IDs. 

If the page is being saved, the savePageField() receives the value generated from sleepValue() and saves it to the DB. It expects that value to either be a raw value: array of [column => value], string or integer. Like with loadPageField, most Fieldtypes inherit this from the base Fieldtype class. 

Lastly is the formatValue() function, which is what is used to provide any additional front-end, runtime formatting for a value where necessary. For example, text-based Fieldtypes use this to run formatters like Markdown or HTML entities. The formatValue function is only called if the "output formatting" mode for the page is ON. The formatValue() function is called on every access to $page->your_field_name, so the unformatted value is always still retained with the page. 

Given the above, there are a few different ways you could approach implementation of these functions. For instance: 

  • Make loadPageField return the result of runSelectorQuery
  • Make wakeupValue return the value it's given, assuming it's a PageArray. If it's a regularly array, assume it's page IDs and convert to a PageArray.
  • Make sleepValue convert the $value (PageArray) it's given to an array of Page IDs and return that. 

Another possible approach:

  • Make loadPageField return the selector string
  • Make wakeupValue convert the selector string to a PageArray (calling your runSelectorQuery)
  • Make sleepValue return the selector string

In this case of this Fieldtype, you'd basically just have to think about how you'd want the value represented externally in a CSV file or web service (for instance). Would that be an array of Page IDs or the selector string the generated them? The selector string may be more portable in that context. 

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By jds43
      Hello, I have a Page Reference by template radio button field to promote a certain page (only two options), but I'd like to target the page that isn't selected. This would be used dynamically throughout the site.
      Does anyone know how I could accomplish this? Would I use something like remove() or not()?
       
    • By Liam88
      Hi All,
      New user over at Processwire and have been rebuilding my site based on this CMS.
      I have been able to find so many answers through Google but I'm a little stuck on this one.
      I have my services page -> services categories -> category children.
      An example of those would be - domain -> services -> ppc -> management
      I also have a set of tags which have different names - services-tag -> grow-your-traffic
      Under these tags I would have multiple links to pages such as ppc, seo, social media and so on.
      A second example would be - services-tag -> convert-your-traffic
      Under here i would have multiple links to pages such as CRO
      Now the set of tags are not visible on-site as they are only created to give overview content to the main services categories.
      Using the categories and the tags I am looking to produce a layout such as (i have also attached an image as an example:
      Tag_1 headline
      Pull all services categories linking to Tag_1
      Tag_1 snippet
      Tag_2 headline
      Pull all services categories linking to Tag_2
      Tag_2 snippet
      So far I have this snippet which is pulling in the tag content but unable to get the posts to show under each of the tags. 
      If i change the if and statement to "tags" instead of "tag" then all posts show under all tags. Where as i want it to show only the posts which are linked to that tag.
      <?php namespace ProcessWire; $tags = $pages->get("/categories-services/")->children(); // Gets the tags $posts = $pages->get("/services/")->children(); // Gets the services categories $link = $tags->ref_6; // Gets the tags and services categories link - under here you have pages_id (services cat id) and data (tags id) // Tag header and summary foreach($tags as $tag) { // This breaks down the tags into sections echo '<section id="services"> <div class="container"> <div class="row"> <h2 class="heading-1"><span>'. $tag->headline.'</span></h2> <p class="mb-5">'. $tag->summary.'</p> </div> <div class="row justify-content-around services">'; // Main services categories that link to the above tags if ($posts->id === $link->pages_id && $tag->id === $link->data){ foreach($posts as $service){ // This pulls in the services categories under the tag header. echo '<div class="card flex-card" id=""> <div class="card-img"> <a href="/'. $service->name.'" title="'. $service->name .'"> <img class="card-img-top" src="../assets/files/'. $service->id.'/'. $service->img_1.'" alt="'. $service->img_1.'" title="'. $service->img_1.'"></a> </div> <div class="card-body"> <h3 class="card-title">'. $service->headline.'</h3> <p class="card-text">'. $service->summary .'</p> <div class="card-action"> <a href="" title="'. $service->name .'" role="link" class="link">View service<span></span></a> </div> </div> </div> '; } } // Grey snippet text echo '</div> </div> </section> <div class="snip-2 light-grey"> <div class="container"> <div class="row text-center">'. $tag->get('grey') .'</div> </div> </div>'; } ?> I appreciate this is a long post but i'm trying to be clear as I appreciate everyone's time.
      Any insight into where I am going wrong is greatly appreciated.
      Liam



    • By David Karich
      The Page Hit Counter module for ProcessWire implements a simple page view counter in backend. Page views of visitors are automatically tracked on defined templates, with monitoring of multiple page views. This gives you a quick overview of how many visitors have read a news or a blog post, for example, without first having to open complex tools such as Google Analytics. This module quickly provides simple information, e.g. for editors. Or, for example, to sort certain news by most page views. For example for "Trending Topics".

       
      Works with ProCache and AdBlockers. With a lightweight tracking code of only ~320 bytes (gzipped). And no code changes necessary! In addition GDPR compliant, since no personal data or IP addresses are stored. Only session cookies are stored without information. 
      In addition, there are some options, for example filtering IP addresses (for CronJobs) and filtering bots, spiders and crawlers. You can also configure the lifetime of the session cookies. Repeated page views are not counted during this period. It is also possible to exclude certain roles from tracking. For example, logged in editors who work on a page are not counted as page views.

      Sort by hits and access page views (hit value)
      Each trackable template has an additional field called phits. For example, you want to output all news sorted by the number of page views.
      // It is assumed that the template, e.g. with the name "news", has been configured for tracking. $news = $pages->find("template=news, sort=-phits"); To output the page views of a tracked page, use:
      echo $page->phits; Example: Tracking a page hit via API and jQuery
      If you want to track a template that does not represent a full page to automatically inject a tracking script, you can define allowed API templates in the module that you can track. Below is an example of how you can track a click on news tag using jQuery. This will allow you to find out which keywords are clicked the most. For example, you can sort and display a tag cloud by the number of hits. Suppose your keywords have the template "news_tag". The template "news_tag" was also configured in the Page Hit Counter Module as a trackable API template.
      Example PHP output of keywords / tags:
      // Required: the data attribute "data-pid" with the ID of the template to be tracked. echo $pages->find("template=news_tag, sort=-phits")->each("<a href='{url}' class='news_tag' data-pid='{id}'>{title}</a>"); Example Tracking Script with jQuery:
      /** * Required: Data attribute "data-pid" with the ID of the news tag template * Required: Send the POST request to the URL "location.pathname.replace(/\/?$/, '/') + 'phcv1'" * Required: The POST parameter "pid" with the ID of the template */ $(function(){ if($('a.news_tag').length > 0) { $('a.news_tag').each(function(){ var tPID = $(this).data("pid"); if(tPID) { $(this).on("click", function(){ $.post(location.pathname.replace(/\/?$/, '/') + 'phcv1', {pid: tPID}); }); } }); } }); So simply every click on a tag is counted. Including all checks as for automatic tracking. Like Bot Filtering, Session Lifetime, etc.
      Notice: Tracking with URL segments
      If the option "Allow URL Segments" is activated on a template, the hits are only counted if the base URL of the page is called. If you want the hit to be counted even when a segment is requested, you MUST configure the segments in the template configuration. How to do this can be found here. If you use dynamic segments, configure them as RegEx. There is currently no other option. The problem is that the Page Hit Counter hooked into the PageNotFound process. If URL segments are allowed but not defined, a 404 is never triggered. This means that the Page Hit Counter cannot be called.
      _______________________________________________________
      Background: This module is the result of a customer requirement, where the editors are overwhelmed with analytics or no tracking tools were allowed to be used. However, a way had to be found to at least count page views in a simple form for evaluations. Furthermore, by using ProCache, a way had to be found to count views of a page without clearing the cache.
      _______________________________________________________
      Pros
      Automatic Page View Tracking Lightweight tracking code, only ~320 bytes (gzipped) No code or frontend changes necessary Works with ProCache! Even if no PHP is executed on the cached page, the tracking works Works with browser AdBlockers No cache triggers (for example, ProCache) are triggered. The cache remains persistent GDPR compliant, session-based cookie only, no personal information Filtering of IPs and bots possible Exclude certain roles from tracking Ability to reset Page Views Works with all admin themes Counter database is created as write-optimized InnoDB API to track events for templates that are not viewable No dependencies on libraries, pure VanillaJS (Automatic tracking script) Works in all modern browsers Pages are sortable by hits Cons
      Only for ProcessWire version 3.0.80 or higher (Requires wireCount()) Only for PHP version 5.6.x or higher No support for Internet Explorer <= version 9 (Because of XMLHttpRequest()) No historical data, just simple summation (Because of GDPR) Segment URLs can only be counted if the segments are defined Planned Features / ToDos
      API access to hit values Since version 1.2.1 Possibility to sort the pages by hits (Request by @Zeka) Since version 1.2.0 Don't track logged in users with certain roles (Request by @wbmnfktr) Since version 1.1.0 Possibility to reset the counter for certain pages or templates (Request by @wbmnfktr) Since version 1.1.0 Better bot filter Since version 1.1.0 Disable session lifetime, don't store cookies to track every page view (Request by @matjazp) Since version 1.2.1 Option to hide the counter in the page tree (Request by @matjazp) Since version 1.2.1 Option to hide the counter in the page tree on certain templates Since version 1.2.1 API to track events for templates that are not viewable Since version 1.2.2 Changelog
      1.2.5
      Bug-Fix: When counting 404 hits, cookies are no longer set. The session lifetime is deactivated for the 404 page Enhancement: Documentation improvement regarding URL segments 1.2.4
      Bug-Fix: Resetting the counters on system pages (e.g. 404) does not work (Reported by wbmnfktr) Bug-Fix: Tracking endpoint is logged as 404 if module "Jumplinks" is installed (Reported by wbmnfktr) Enhancement: Corrected few typos (Merged from Sergio #6 – THX!) 1.2.3
      Bug-Fix: Tracking script triggers 404 if pages are configured without slash (#3) Reported by @maxf5 Enhancement: Reduction of the tracking script size if it's gzipped (~320 bytes) Enhancement: Documentation improvement Enhancement: Corrected few typos 1.2.2
      New feature: API to track events for templates that are not viewable Enhancement: Documentation improvement 1.2.1
      API access to hit values Use $page->phits Bug-Fix: No tracking on welcomepage (Reported by wbmnfktr; Thx to matjazp) Bug-Fix: Tracking script path on subfolders (Reported by matjazp) Bug-Fix: Tracking on pages with status "hidden" Enhancement: Change database engine to InnoDB for phits field Enhancement: Option to disable session lifetime set session lifetime to 0, no cookies Enhancement: Better installation check Enhancement: AJAX Request asyncron Enhancement: Reduction of the tracking script size by ~20% Enhancement: Option to hide the counter in the page tree You can output the counter with the field name "phits" Enhancement: Option to hide the counter in the page tree on certain templates Enhancement: Option for activate general IP validation Enhancement: Reduction of tracking overhead up to ~30ms Enhancement: Better bot list for detection 1.2.0
      New feature: Sort pages by hits – New field phits Migrate old counter data to new field 1.1.0
      New feature: Exclude tracking of certain roles New feature: Reset Page Views Better bot filter and detection 1.0.0
      Initial release Notes
      By default, the page views are stored as INT in the database. This allows a maximum counter value of 4.2 billion views (4,294,967,295) per page. If you need more, change the type to BIGINT directly in the database. But I recommend to use Google Analytics or similar tools if you have such a large number of users.
      _______________________________________________________
      Download GitHub: ProcessWire Page Hit Counter (Version 1.2.5)
      PW Module Directory: ProcessWire Page Hit Counter (Version 1.2.5)
      Install via ProcessWire (Classname): PageHitCounter
      _______________________________________________________
      Update information
      If you have used version 1.2.1 from the DEV branch, please replace it completely with the new master version.
    • By Hubris
      Hi there!
      I'm using some page reference fields to create lists of tags, categories, years, etc.. I'm able to find the pages like so:
      $pages->find("template=project, {$filter}={$page->title}"); Which dynamically does something like: 
      $pages->find("template=project, tags=Experimental"); Only if the value (the page name, like "Experimental") starts with letters. If it starts with numbers, find returns nothing.
      Why is this and how can I fix it?
    • By Elchin
      Hi.
      I want select pages where now between date and end_date or now bigger than date and end_date is empty.
      I have five tried variants:
      $start = strtotime(date('Y-m-d') . " 00:00:00"); $results = $page->children("foo=(date<$start,date_end=''),bar=(date<$start,date_end>=$start),sort=-date,limit=12"); $start = strtotime(date('Y-m-d') . " 00:00:00"); $results = $page->children("date<$start,(date_end='',date_end>=$start),sort=-date,limit=12"); $start = strtotime(date('Y-m-d') . " 00:00:00"); $results = $page->children("date_end=''|date_end>=$start,date<$start,sort=-date,limit=12"); $start = strtotime(date('Y-m-d') . " 00:00:00"); $results = $page->children("!date_end|date_end>=$start,date<$start,sort=-date,limit=12"); $start = strtotime(date('Y-m-d') . " 00:00:00"); $results = $page->children("date_end>=$start|!date_end,date<$start,sort=-date,limit=12"); All this variants not worked for me and returned zero results.
×
×
  • Create New...