Jump to content

Two-way link between related pages


SwimToWin
 Share

Recommended Posts

The Page Reference-field seems to create a one-way link from one page to one/many other pages. This means that the related pages will only display on the referring page while not on the referred pages.

Is it possible to make a two-way link that can be displayed both on the referring page and the referred pages?

My Page Reference field is configured as follows:

  • Deference in API as: Multiple pages (PageArray).
  • Input field type: PageListSelectMultiple

(Perhaps it is necessary to use another field type?)

Link to comment
Share on other sites

On the page with the page field, output all references

foreach($page->yourpagefield as $pr){
   echo "<a href='{$pr->url}'>$pr->title</a>";
}

On another page that is referenced, search all pages that have "me" referenced:

$pa = $pages->find("yourpagefield=$page"); // $page is the current page

foreach($pa as $p){
   echo "<a href='{$p->url}'>$p->title</a>";
}

  • Like 1
Link to comment
Share on other sites

@Soma: Thanks. I did consider a similar solution but rejected it because I worry that it will be performance unfriendly on a large site? Or perhaps caching and optimized SQL queries takes care of that?

@Ryan: I was looking for a solution where page relations were stored in a single table (one table for all Page Reference fields) => dbtable RELATIONS could have this layout: id, field_id, aid, bid. SQL query to get all relations: SELECT * FROM relations WHERE aid=1 OR bid=1.

Hope this makes sense..

/Kristoffer :-)

Link to comment
Share on other sites

The site will have a few thousand pages (far from all will have relations to other pages).

1) I see your point that PW store every field in a unique table - my table field_related has the structure below:

pages_id data sort

1842 1844 0

2) Performance: My worry is not a single field or feature, but the accumulated effect when a page is rendered with SQL queries that "talk too much" with the database. It all adds up. (I worked for six years in a software house that develop, sell and support Web CMS systems to the media industry and have seen how bad SQL queries can affect overall site performance. Disclaimer: I have no idea how PW's SQL works. Just trying to be careful.)

Link to comment
Share on other sites

It's pretty well tested up to many thousands of pages, but you wouldn't actually want to output thousands of related pages into the page (or bring them into memory) so you could change Soma's find-> selector examples to have a ,limit=5 on the end, sort by newest to oldest (get the latest 5 related pages) or even grab a number of random pages.

Putting a limit on it will pretty much write in the SQL behind the scenes that you only want to get X results so it works the way you probably normally would if you were manually writing the SQL yourself.

I wouldn't worry too much about it - it's very well put together, and you can always test using Xdebug or something if you're still unsure.

Link to comment
Share on other sites

2) Performance: My worry is not a single field or feature, but the accumulated effect when a page is rendered with SQL queries that "talk too much" with the database. It all adds up. (I worked for six years in a software house that develop, sell and support Web CMS systems to the media industry and have seen how bad SQL queries can affect overall site performance. Disclaimer: I have no idea how PW's SQL works. Just trying to be careful.)

Talking too much with the database is not what slows a site down. Reducing queries doesn't necessarily improve performance. You can have a few hundred queries that execute faster than one query. It comes down to good indexing and queries that use those indexes well. When optimized well, MySQL is not likely to be a bottleneck even with a lot of chatter. Now if data isn't indexed well, things get slower as the scale increases. Combine that with more unoptimized queries and it gets even slower. But this isn't ProcessWire.

ProcessWire does support the 'autojoin' option for page references, which causes those references to be joined in the page-loading query. This has potential performance benefits if the quantity of joined page references is relatively small. But I don't recommend it unless you plan to use those page references on every single instance of the page. Otherwise, you are joining extra data for the times when you don't actually need it.

Using the FieldtypeCache is yet another way you can join more data at once. This particular Fieldtype actually keeps a separate cached copy of the page's data encoded into a one-time-load bundle, bypassing load from the field_* tables. But ProcessWire is pretty fast in the way it works natively, so the situations where one benefits from autojoin and FieldtypeCache are fairly rare. But they are there when you need them.

  • Like 1
Link to comment
Share on other sites

Thanks all.

I have summarized the solution suggested by Soma in two examples (for future reference):

Example 1: Simple example that lists your related yourpagefield articles - first pages referred by this page, next pages that refer this page.

<ul>
<?php
foreach($page->yourpagefield as $k=>$p) {
echo "<li><a href='{$p->url}'>$p->title</a></li>";
}
foreach($pages->find("yourpagefield=$page") as $p){
echo "<li><a href='{$p->url}'>$p->title</a></li>";
}
?>
</ul>

Example 2: List related pages based on the yourpagefield field (all referring pages are sorted alphabetically, the block is only output when related pages exist).

<?php if(count($page->yourpagefield)>0 || count($pages->find("yourpagefield=$page"))>0 ): ?>
<h2>Related</h2>

<ul>
<?php
$ref = array();
foreach($page->yourpagefield as $k=>$p) {
$ref[$p->url] = $p->title;
}

foreach($pages->find("yourpagefield=$page") as $p){
$ref[$p->url] = $p->title;
}
asort($ref);
#shuffle($ref);
foreach($ref as $url=>$title) {
echo "<li><a href='{$url}'>{$title}</a></li>";
}
?>
</ul>
<?php endif; ?>
  • Like 2
Link to comment
Share on other sites

You could also do it like below. I'm thinking you'd want to sort by 'title' or 'date' rather than by 'url'?

$related = $page->yourpagefield->import($pages->find("yourpagefield=$page"))->sort("title"); 

if(count($related)) {
 echo "<h2>Related</h2>";
 foreach($related as $item) echo "<li><a href='$item->url'>$item->title</a></li>";
}
  • Like 1
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

×
×
  • Create New...