Jump to content

Mixing pages and fields in a page list


rash
 Share

Recommended Posts

Hi Guys,

I’m working on kind of an encyclopedic website and there is a problem, I’m trying to find an intelligent way to solve it. Until now, I can only think of a less intelligent way and would be glad to hear your ideas, whether and how it could be done.

It’s about listing pages. Each encyclopedic term is represented by a page, so basic listing of term entries is easy. What I would like to get managed is also listing synonyms or similarities to terms. Let’s say we have the main terms 'car', 'ship' and 'plane' and a page for each of them. For 'car' let’s take 'automobile' and 'vehicle' as synonyms and for 'plane' we have 'jet' and 'aircraft'. Differently to terms, synonyms don’t have pages but only references to terms/pages. The alphatbetic list would be:

Of course I could manage it with a page type 'synonym'. Pages of that type would exist without content and just do redirects or similar. That would be the less intelligent way I mentioned above. As there will be several thousand of main terms/pages, there might soon be three oder four times more pages, just for handling synonyms of minor importance. At least to me this seems to be a bit confused.

I’m dreaming of solving it with a field 'synonyms' that contains a synonym array of arbitrary length. The alphabtetic list would be a mixture of pages and fields showing to their parent page. Sadly I have no idea if this is doable and where to start, if so.

Any hints anybody?

Thanks for your time and help,
Ralf

Link to comment
Share on other sites

Mh,

The synonyms field could be a simple textarea field "synonyms" on the "root word" with a synonym in each line. And when you are looking for the root page for a synonym:

<?php
$pages->find("synonyms*=requested_synonym") // or ->get()

Would this do it? 

 

But I'd say having synonym pages might give you more flexibility - maybe you want to add properties to synonyms later .. who knows ^^. I don't think it is a downside having lots of "empty" pages.

I probably would store all synonyms in a synonyms folder page as a storage and having a page field on the actual entry with that inputfield: http://modules.processwire.com/modules/inputfield-page-autocomplete/ 

It'll basically just be like tagging and one doesn't have to think much about those tag / synonym pages. They can be hidden somewhere. Finding correlating pages would work like above. (Not sure about the *= though, works with just = on a setup I have). This might also prevent you from some errors since every synonym is an actual entity in the system.

  • Like 2
Link to comment
Share on other sites

2 hours ago, blynx said:

<?php
$pages->find("synonyms*=requested_synonym") // or ->get()

It'll basically just be like tagging and one doesn't have to think much about those tag / synonym pages. They can be hidden somewhere. Finding correlating pages would work like above. (Not sure about the *= though, works with just = on a setup I have). This might also prevent you from some errors since every synonym is an actual entity in the system.

Thank you very much bIynx,

'tags' is a very good keyword, as I work with them already in the way you described. Indeed I could organize the synonyms with a field that actually creates pages. The result would be the load of additional pages I wanted to avoid, but they get built automatically and always stay 'connected' with their root page. Means: I’ll seldomly see them and so they don’t have to bother me, beside the fact they are there. You’re sure there is no downside keeping thousands of extra pages?

Another question regarding the list structure of my opening post: My list selector would be posts of types 'entries' and 'synonyms' in alphabetical order. Depending on the type, the list item would either be a direct link or a reference link synonym → entry page. But how does the synonym page know where to link as I never touched it? In other words: I don’t fully understand what your code line exactly does.

Link to comment
Share on other sites

Hm, well - regarding "thousands of pages": There was this blogpost once: https://processwire.com/blog/posts/find-and-iterate-many-pages-at-once/ about the new findmany() function: https://processwire.com/api/ref/pages/find-many/ - but from my understanding, this only applies if you really want to retrieve and do something with that many pages.

In an encyclopaedia application you will of course search inside of manymany pages but you mostly only retrieve one or a small subset. I would assume (though I am not an expert) that the findMany() case does not apply here.

The actual link will actually be the other way around. The synonyms don't know anything about their relation to someone else. That's why I was referring to the tagging: You have the entries and they point to synonyms: plane → aircraft, ufo, flying saucer, … but with your application logic you can create that link. 

Assuming:

entries
	plane
	car
	fork
	...

synonyms
	airplane
	automobile
	ufo
	flying saucer
	...

Then, for example you get a query for "airplane":

<?php

$incoming_query_term = "airplane";

$entry = $pages->get("name=$incoming_query_term, parent=entries"); // see if there is an entry

if($entry instanceof NullPage) { // no entry?
  	$synonym = $pages->get("name=$incoming_query_term, parent=synonyms"); // get the synonym
	$entry = $pages->get("synonyms=$synonym, parent=entries"); // get the entry which has that synonym in its synonyms page field
  
 	// eh, should also work, combined and short:
  	$entry = $pages->get("synonyms=[name=$incoming_query_term, parent=synonyms], parent=entries");
}

I guess your tagging should work kind of the same?

edit:

Mh, reread your original post - I think it would be more complicated to have the synonyms in a field. Maybe it feels stupid to have extra pages for the synonyms but I think it is the better solution.

The downside is maybe that database queries take longer - but since processwire has the findMany() method now I don't think it is any problem to handle that many pages.

 

One consideration might be helpful for rendering the list:

Without this you would have to retrieve the entry for a synonym for every synonym by code like $pages->get("synonyms=THIS_SYNONYM.id") ...

You could link from the synonyms to the entries by giving them a page field, too, where you store the linked entry. You could create that link with a hook automatically every time a synonym is created, so that every synonym has an entry-link. The problem here is to keep the links coherent.
But then it would be very easy to render the list:

  • Find all entries and all synonyms to one PageArray (probably paginated then, findMany?).
  • Sort by name.
  • Render them accordingly to their type: Entries become direkt links to the entry, synonyms become that "aircraft → plane" output.

 

  • Like 3
Link to comment
Share on other sites

Okay, I’m nearly convinced now. The steps you outlined in your last paragraph seem to be the most plausible way to get where I want to. Sadly, I found another stumbling stone in the meantime: the tags. There will hardly be the need to list all database entries, the most frequent rendered lists will be search results and entries matching tag X. Approximately I will exclude synonym pages from the search, so the main problem will be the tags. If 'plane' is tagged with 'traffic' and 'transport', the synonyms 'ufo' and 'flying saucer' should be tagged with both tags too. So I should better not only give the synonym page a parent field, but a tags field too. Which leads me to hopefully my last questions:

Is it possible to sync the content of the tag field like the following?

  • Creating a new synonym creates the according synonym page and copies the content of the root entry’s tags field to the synonym’s tag field.
  • When editing the tags field content of the root entry, all connected synonym tag fields get updated too.

You mentioned the option to create links to parent pages automatically via hook. How do I do this? In case of the tags I work with the 'page' field type and unfotunately I don’t know how to run automated tasks when filling a field with content. Where should the hook function reside?

Edit – a few hours later

Meanwhile I’ve read (nearly) everything about hooks and where to place them. I think I will get the autolinks done as well as the tag syncs, so both questions are answered. I want to thank you again, blynx – you showed a lot of patience and helped me a whole piece further, that was very friendly.

  • Like 1
Link to comment
Share on other sites

Hej,

I think thats no problem. When you have the link between synonyms and entries like I described, it is easy to retrieve the tags from the entry when you query a synonym:

<?php
#
#  "linked_entry" is the page field on the synonym holding the 
#  reference to the entry.
#  synonyms have no tags field.
#

// get the tags of synonyms

$a_synonym = $pages->get("parent=synonyms, name=airplane");
$synonyms_tags_via_its_linked_entry = $a_synonym->linked_entry->tags;

// edit: in one line:

$synonyms_tags_via_its_linked_entry = $pages->get("parent=synonyms, name=airplane")->linked_entry->tags;

// find synonyms with tags

$those_synonyms = $pages->find("parent=synonym, linked_entry.tags=some_tag|another_tag");

You just need to tag your entries and through the link in the synonym its very easy to handle anything tag related.

But the syncing you describe would still be possible ;) - though I don't think you need that if the tags of the synonym and the entry will always be the same.

 

Regarding hooks:
Have a close look at https://processwire.com/api/hooks/ and this when you got the idea: https://processwire.com/api/hooks/captain-hook/ and also search the forum for more examples!

This is a rough sketch of a hook that might sit in the init.php / ready.php file.
It may work out of the box, but I am still in the trial and error phase when it comes to hooks and haven't teste this one ;) but it should work more or less somehow like this:

<?php

# keep synonyms linked_entry in snyc with entries synonyms

wire()->addHookAfter('Pages::saved', function($event) {
  
  $page = $event->arguments(0); // what page was the hook called on?
  $template = $page->template; // what template has that page
  
  // see if the saved page is an entry because this hook is called on every page save
  if($template == "entry") { 
	
    /*
    $related_synonym_pages = $pages->find("parent=synonyms, name={$page->synonyms}");
    foreach($related_synonym_pages as $syn) {
      $syn->setAndSave("linked_entry", $page); 
    }
    */
    
    // haha, this happens to me all the time, the following should be simplier =)
    
    foreach($page->synonyms as $syn) { // get the set synonyms directly from the page
      $syn->setAndSave("linked_entry", $page);  // set this page to that synonym
    }
  }
});

So, this is the hook for: Edit synonyms on entry page, save it → hook updates the related synonyms which are set on the entry page

enjoy :) 

Edited by blynx
added "in one line" in first example
  • Like 4
Link to comment
Share on other sites

blynx,

your solution is definitely ingenious – in total and detail! I played around with all parts and everything was working immediately without any issues. Standing ovations!

What I like the most is the relation of maximum result with minimum effort – this reveals true masters. To get the synonym and sync problem solved with a few lines of code in very short time is more than I dared to dream of.  So another giant thank you!

  • Like 2
Link to comment
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
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...