Automatically add to related pages field when page is added added to current one

Hi,

it might sound confusing but the result is quite simple:
Is it possible to add the current page automatically to the related one when the related one is added to the current pages field.

The goal ist to add the connection between the pages by only adding the relation once instead of “from both sides”.
Bildschirmfoto 2020-09-13 um 13.54.33


See screenshot and blueprint part attached:

  related:
    label: Designer
    type: pages
    query: site.find('bibliothek/designers')

Yes, you can do that in your template by filtering the siblings. There is no need to actually store this information in the content file.

But is it visible in the panel then (from both sides)?

No, it won’t be. If you want it to be visible in the Panel, you would have to use a page.update:after hook and actually store the information in the other page.

I’m a hook-newbie and a little lost at this point.

This is my hook inside the config.php, but after saving i get the error “Exception: Error
Call to a member function related() on null”:

'hooks' => [
    'page.update:after' => function ($page, $relatedPage) {

        foreach ($page->related() as $relatedPage) {

            try {

                $relatedPage->update([
                  'related' => add($page),
                ]);
              
                echo 'The page has been updated:';
              
              } catch(Exception $e) {
              
                echo $e->getMessage();
              
              }
                  
          }   

    }
]

Kirby hooks have named parameters since Kirby 3.4.Idontknowwhat.

So unless you are on an older version, you have to use $newPage and $oldPage.

And somehow you use a foreach on a field which won’t work either. If $page->related() is a pages field, then you have to convert to pages first:

foreach ( $newPages->related()->toPages() as $p ) {
  // do stuff
}

And don’t override your orginal variable…

Echo and catch in a route are also quite useless.

But I don’t really understand what you are trying to do here.

If the current page is updated, you want to update a field on the same page with the current page? That somehow doesn’t make sense.

Maybe you can explain in words what you are trying to achieve, because I think you probably rather want to update another page.

Yes, i need to update the page i added to the “related” field.

Probably something like this, but that only makes sense if something was added, not removed.

'hooks' => [
    'page.update:after' => function ($newPage, $oldPage) {
       
        $oldRelatedPages = $oldPage->related()->toPages();
        $newRelatedPages = $newPage->related->toPages();
        $relatedPages    = $newRelatedPages->not($oldRelatedPages);
        foreach ($relatedPages as $relatedPage) {
           $relatedPageRelatedPages = $relatedPage->related()->toPages();
           $relatedPageRelatedPages = $relatedPageRelatedPages->add($newPage);
           $relatedPage->update([
                  'related' => Data::encode($relatedPageRelatedPages->toArray(), 'yaml'),
                ]);           
          }   
    }
]

(Not tested).

To be honest, I don’t really think this is the best way to go about this. If a page A is related to page B, that automatically means that page B is also related to Page A and IMO it is not necessary to store this information in both pages.

Thanks @texnixe, it works! But you’re right: if I can’t remove the connection the same way (which is impossible, i guess) it would be better to simply display the connection inside the template and store the information in only one page, right?

My initial thought was to keep the overwiew over the connections in the panel, but i think it’s more important to have the right information in the frontend.

I didn’t mean to say it is not possible, it’s just a lot of stuff to handle to keep things up to date and I really wonder what happens if hooks update potentially many pages while they are at the same time edited by someone. So maybe hook updates fail in this case, but I cannot really answer this question.

To achieve removal of removed connections you would have to do mainly the same as in the above snippet, just the other way round:

  • compare oldpage state with newpage state to find out what has been removed
  • loop through these removed pages
  • get the pages in the related field
  • unset the current page from the array
  • update the formerly related page

maybe this plugin can be of help if it’s really only about showing related pages in the panel? As texnixe said:

:point_down:

Add an author page on a post:

Showing posts by this author on the author’s page:

// models/author.php
<?php

class AuthorPage extends Page {
    public function contributions() {
        return page('archive')->children()->filter(function ($page) {
            foreach($page->author()->toPages() as $author) {
                if ($author->uid() === $this->uid()) {
                    return $page->author()->toPage()->uid() === $this->uid();
                }
            }
        });
    }
}
blueprints/pages/author.yml
...
sidebar:
  pagesdisplay:
    headline: Contributions
    query: page.contributions
    text: '{{ page.title }}'