Jump to content
fruid

access profields table from sibling

Recommended Posts

so I have a Profields Table on a sibling's page, trying to access it from its sibling's page (DUH!) which is $page now, cause that's where I am right now.

I do

    $toc = $page->siblings("template=tableofcontents")->tableofcontents_table;
    $toc->render();

or

    $toc = $page->siblings("template=tableofcontents");
    $toc = $toc->tableofcontents_table;
    $toc->render();

but it just won't work because Call to a member function render() on null

The table clearly is there and clearly is a sibling and its template is as stated and the table also.

Please help me quick, about to throw the computer out the window, thank you.

Share this post


Link to post
Share on other sites
1 hour ago, fruid said:

Please help me quick, about to throw the computer out the window, thank you.

Maybe a sign that it's times for a break 😄.

1 hour ago, fruid said:

$toc = $page->siblings("template=tableofcontents")->tableofcontents_table;

Sometimes it helps to 'speak out your code' to determine if it makes sense. Have a look at that line. tableofcontents_table, I assume, is your Profields Table. Reading that out loud, your code is saying, find me siblings (PLURAL) of this page and get me the value of its (SINGLE) tableofcontents_table field. Doesn't sound right, does it? It's like saying 'find me 10 oranges (PLURAL) and show me its (SINGLE) price. That's wrong. The question would be, which orange's price? We have 10 oranges here! It should read, 'find me 10 oranges (PLURAL) and show me their (PLURAL) prices.  Aha, so, we are dealing with a collection. Any collection has to be iterated (foreach) to get each members individual value. Alternatively, you could ask for the first(), last() or nth() orange to deal with one member only.

OK, so I need to get some sleep too. The docs say this about $page->siblings():

Quote

Return this Page’s sibling pages, optionally filtered by a selector.

 

Quote

 Return value

 $toc is a PageArray. 

If you run this in Tracy console: 

<?php

d($page->siblings("template=tableofcontents")->tableofcontents_table);// null

You will get the value null, hence your error message since you are doing this null->render().

Now for some unsolicited advice 😁.

  1. TracyDebugger is your best friend.
  2. The table clearly is there and clearly is a sibling and its template is as stated and the table also. Whilst this might be the case, it is best to always check if your selector/query returned something (and its type) before you start working with it.

Hope this helps.

ps: Profields Table has its own VIP support forum, accessible to you if you have a current subscription

  • Like 2

Share this post


Link to post
Share on other sites

OK got it working now, I should always never forget that even though it's just one item, it's still inside an array. 

thanks

Share this post


Link to post
Share on other sites

need to continue this thread, trying to improve my script but I'm out of ideas.

What I want is a hook that adds a row to the table-of-contents-table with certain details when a page (sibling to the table-of-content) is saved and removed when it's changing status, i.e. trashed, hidden or unpublished.

I tried in many ways already, I never get it to do what I need upon trashing a page plus the code was getting too complicated anyway so I tried something else. I just want the rows to be created or deleted when the table-of-contents-page itself is saved. Then, when I save any other siblingspage, just run a hook that saves the table-of-contents-page. Sounds like a plan? The code is quite short…

But again, I just can't seem to hook the trash event. My guess is that when I'm trashing the page, the parent and siblings change and so I cannot refer to them this way? 

$wire->addHookafter('Pages::saved', function($event) {        
    $page = $event->arguments[0];
    if ($page->parent->template == 'magazine' && $page->template != 'tableofcontents') :        
        $page->siblings("template=tableofcontents")->save("tableofcontents_table");
    endif;

    if ($page->template == 'tableofcontents') :    
        $parent = $page->parent;
        $page->of(false); 
        $toc = $page->tableofcontents_table;
        $contents = $page->siblings(true);

        foreach ($contents as $content) :

            $content_id = (int)$content->id;        
            $content_url = $content->url;
            $content_page = $content->page_number;
            $content_title = $content->title;

            if ($parent->child("id=$content_id") == false) {
                $item = $toc->get("content_id=$content_id"); 
                $toc->remove($item);
                $event->message("deleted row");
            } else {
                if ($toc->has("content_id=$content_id") == false) {
                    $item = $toc->makeBlankItem(); 
                    $item->content_url = $content_url;
                    $item->content_page = $content_page;
                    $item->content_title = $content_title;
                    $item->content_id = $content_id;
                    $toc->add($item); 
                    $event->message("create row");
                } else {
                    $item = $toc->get("content_id=$content_id"); 
                    $item->content_url = $content_url;
                    $item->content_title = $content_title;
                    $item->content_page = $content_page;
                    $event->message("update row");
                }                    
            }

        endforeach;
        
        $page->save("tableofcontents_table");
        $page->of(true);
    
    endif;
    
});

 

Share this post


Link to post
Share on other sites

OK so I made some adjustments because I figured the best way to remove a row in the table of contents when that page is deleted is to make use of a addHookBefore, because at this point the page "still knows" its parents and siblings. However, if I go that way, updating the table only when saving the table of contents page gets in the way because the script would check the pages (siblings) and since it's a addHookBefore (hooked before it's deleted) it would still exist at this point so basically would be deleted with the hook but also re-added with the hook that runs when the table of contents page is saved. Pretty confusing for newcomers I guess but not for this community, I'm sure you had dilemmas like that yourself before.

So I did the following instead:

$wire->addHookAfter('Pages::saved', function($event) {

	if ($page->parent->template == 'magazine' && $page->template != 'tableofcontents') :
            
        $parent = $page->parent;
        $toc = $parent->child("template=tableofcontents");
        $rows = $toc->tableofcontents_table;
        $contents = $page->siblings(true);

        $page_id = $page->id;        
        $page_url = $page->url;
        $page_number = $page->page_number;
        $page_title = $page->title;

        $toc->of(false); 

		// creating a new row for this page
        if ($rows->has("content_id=$page_id") == false) {
            $item = $rows->makeBlankItem(); 
            $item->content_id = $page_id;
            $item->content_url = $page_url;
            $item->content_title = $page_title;
            $item->content_page = $page_number;
            $rows->add($item); 
            $event->message("row created");
		// updating an existing row for this page
        } else {
            $item = $rows->get("content_id=$page_id"); 
            $item->content_url = $page_url;
            $item->content_title = $page_title;
            $item->content_page = $page_number;
            $event->message("row updated");
        }              
    
		// this is new: also loop through the rows and delete any which don't have a corresponding page
        foreach ($rows as $row) :
            $row_id = $row->content_id;
            if ($contents->has("id=$row_id") == false) {
                $rows->remove($row);
                $event->message("row deleted");
            }
        endforeach;
    
        $toc->save("tableofcontents_table"); 
        $toc->of(true); 
     
    endif;

});

and the other hook:

$wire->addHookBefore('Pages::trashed', function($event) {

    $page = $event->arguments[0];
    $parent = $page->parent;
    if ($parent->template == 'magazine') :
        $toc = $parent->child("template=tableofcontents");
        $rows = $toc->tableofcontents_table;
        $page_id = $page->id;        
        $toc->of(false); 
        $item = $rows->get("content_id=$page_id");
        $rows->remove($item);
        $event->message("row deleted");
        $toc->save("tableofcontents_table");
        $toc->of(true);
    endif;

});

creating, unpublishing, hiding and editing pages do change the rows of the table of contents as intended but I can't get it to delete a row when trashed.

Any ideas why? 

Share this post


Link to post
Share on other sites

OK so I had some progress, the hooks, I guess were conflicting each other. One hook's intention is to delete the page from the other page's table but it was triggered before the page is saved and also triggered the other page's save-event. See here:

so basically…
page A is the page that is saved
page B is the table of content (sibling)

page A -> addHookbefore: trash: remove from page B, save page B -> addHook: check siblings -> finds and adds page A -> page A is saved i.e. trashed but stil in the page B list.

Here's the code – if you're still interested 😅

 

// update TABLE OF CONTENTS when saving individual page :D 
// works for editing, hiding/unhiding, publishing/unpublishing and trashing :D 
// does NOT work for moving pages (to different parent) :D 
// when moving a page, save the former parent page (table of contents) :D 

$wire->addHook('Pages::saveReady', function($event) {

    $item = $event->arguments[0];
    
    if ($item->parent->template != 'magazine') return;
    if ($item->template != 'tableofcontents') return;
    
    $parent = $item->parent;

    $contents = $item->siblings(true);
    $rows = $item->tableofcontents_table;
    foreach ($contents as $content) :
        updateRows($rows, $content, $event);
    endforeach;
    cleanRows($rows, $item, $event);
            
});


// update TABLE OF CONTENTS when saving TABLE OF CONTENTS page :D 

$wire->addHookAfter('Pages::saved', function($event) {

    $item = $event->arguments[0];
    
    if ($item->parent->template != 'magazine') return;
    if ($item->template == 'tableofcontents') return;
    
    $parent = $item->parent;
    $toc = $parent->child("template=tableofcontents");

    $rows = $toc->tableofcontents_table;
    
    $toc->of(false); 

    updateRows($rows, $item, $event);
    cleanRows($rows, $item, $event);
    
    $toc->save("tableofcontents_table"); 
    $toc->of(true); 
            
});


// *update TABLE OF CONTENTS when trashing a page :D 

$wire->addHookBefore('Pages::trash', function($event) {
    
    $page = $event->arguments[0];
    $parent = $page->parent;
    if ($parent->template != 'magazine') return;
    $toc = $parent->child("template=tableofcontents");
    $rows = $toc->tableofcontents_table;
    $page_id = $page->id;        
    $toc->of(false); 
    $item = $rows->get("content_id=$page_id");
    $rows->remove($item);
    $event->message("row deleted");
    $toc->save("tableofcontents_table");
    $toc->of(true);
});

// this just won't work, thought it would work just as well as trashed but nope

//$wire->addHookBefore('Pages::moved', function(HookEvent $event) {
//    $page = $event->arguments[0];
//    $parent = $page->parent;
//    if ($parent->template != 'magazine') return;
//    $toc = $parent->child("template=tableofcontents");
//    $rows = $toc->tableofcontents_table;
//    $page_id = $page->id;        
//    $toc->of(false); 
//    $item = $rows->get("content_id=$page_id");
//    $rows->remove($item);
//    $event->message("row deleted");
//    $toc->save("tableofcontents_table");
//    $toc->of(true);
//});


// add/update details to pages that are missing from the TABLE OF CONTENTS :D

function updateRows($rows, $item, $event) {
        
    $item_id = $item->id;        
    $item_url = $item->url;
    $item_title = $item->title;
    $item_number = $item->page_number;
    if ($rows->has("content_id=$item_id") == false) {
        $row = $rows->makeBlankItem(); 
        $row->content_id = $item_id;
        $row->content_url = $item_url;
        $row->content_title = $item_title;
        $row->content_page = $item_number;
        $rows->add($row); 
        $event->message("row created");
    } else {
        $row = $rows->get("content_id=$item_id"); 
        $row->content_url = $item_url;
//        $row->content_title = $item_title;
        $row->content_page = $item_number;
        $event->message("row updated");
    }              
}


// remove obsolete pages from the TABLE OF CONTENTS :D

function cleanRows($rows, $item, $event) {

    $contents = $item->siblings(true);
    foreach ($rows as $row) :
        $row_id = $row->content_id;
        if ($row_id == '') continue;
        if ($contents->has("id=$row_id") == false) {
            $rows->remove($row);
            $event->message("row deleted");
        }
    endforeach;
    
}

 

 

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 fruid
      textformatters=TextformatterEntities in my field settings cause problems when moving data around (exporting, importing). I can't get rid of the entities in my code and so I have troubles filtering and selecting the content (I tried $sanitizer->unentities, actually need to apply that twice for it to look good but still does not filter/select properly). I figured It must be possible to change the format in the field directly and once and for all.
      What other options does "textformatters" allow? Because deleting it altogether doesn't work at all.
      thanks
       
    • By fruid
      I'm having and always have a hard time building PaginatedArrays, I never know where to put the "limit=24" selector so please enlighten me.
      Here's what I'm doing…
      $categories = $page->protable('start=0, limit=999999'); // this I need in order to retrieve the pages's "categories" and I don't know how to make use of $rows instead for that purpose because of this confusing limit-API $rows = $page->protable; // put ("limit=20") here? $custom = buildSelector($input, $rows); // this function returns an array of selectors depending on the user input $items = new PaginatedArray; // or here? // or here? $items->find("limit=20") // or like this? $items("limit=20") // or like this? $items = $items("limit=20") // or like this? $items = $items->find("limit=20") foreach ($rows as $r) : if ($custom->get('selector')->matches($r)) : $items->add($r); endif; endforeach; // or at this point? and then
      if ($items) { // or maybe somewhere here?     echo '<span class="grey">'.$items->getPaginationString(array(     'label' => 'entries',     'zeroLabel' => '0 entries', // 3.0.127+ only     'usePageNum' => false,     'count' => count($items),     'start' => $items->getStart(),     'limit' => count($items),     'total' => $items->getTotal()     )); and then of course…
      $pager = $modules->get("MarkupPagerNav"); echo '<div class="uk-flex uk-flex-center">'.$pager->render($items, $options).'</div>'; I'm out of ideas and confused cause it doesn't make sense to me either way.
      ____
      The buildSelector function above returns and array…
      $selected = new Selectors("$letter, $searchterm, $category"); $custom = new Wirearray; $custom->set('selector', $selected); $built->set('sort', $sort); return $custom; and each of the new selectors are basically strings ("category=whatever")
      Also, you cannot put the "sort=title" or whatever as a selector for the Selectors function (see above). Why, I know not.
      I don't know if that is the proper way but selecting kind of works now as opposed to many other ways I tried. Selectors always require a lot of trial and error, it seems to have a very sensitive API, always depends on double quotes, single quotes and how you concatenate.
      Thanks for help!
    • By fruid
      I have a profield table and one of its fields is called category.
      I want to use the values of this field and created a unique array which I use for the options-filter.
      $rows = $pages->get('path/to/parent/page')->protable; $categories = new WireArray; foreach ($rows as $r) :     if ($r->category != '') :         $categories->add($r->category);     endif; endforeach; $categories = $categories->unique(); So I loop through the $categories array and output an option field for each.
      This kinda works but not with the pagination. On the second page it would only show the "options" (categories) of those entries that the unfiltered array would show on a second page.
      Where's the problem?
    • By fruid
      Inside the ProFields Table I have one column of type "select" for which I entered several options to choose from. 
      How can I return these options? I mean loop through them to build my markup.
    • By Guy Incognito
      I've created a simple sports league fixture generator in a template called 'League'. Teams are added as page references then a fixture list is created as a ProFields table by hooking page save to add new rows to the table.
      The bit I need help with is that I'm trying to check a fixture doesn't already exist before adding it to the table (e.g. if a new team is added to the league). I'm trying to do this with a PW selector to filter the fixtures table and check whether Team A vs Team B already exists in the table. Then on the next line checking no fixture was found by using count().
      However as soon as I add the selector the script no longer adds any rows to the table. If I take it out, it all works fine (albeit with duplicate fixtures each time the page is saved). I've also tested the selector in a page template and it filters as expected. It's late here (UK)... I'm probably doing something stupid! Any ideas?
      <?php //Hook page save to generate league fixture lists $wire->addHookAfter("Pages::saved(template=league)", function ($event) { //Get which page has been saved $page = $event->arguments(0); $noFixturesAdded = 0; //For each team in league cycle through and add home fixtures foreach ($page->teams_in_league as $teamA) { foreach ($page->teams_in_league as $teamB) { //Check if fixture already exists $existingFixtures = $page->fixtures("team_a=$teamA,team_b=$teamB"); //Check team A is not the same as team B as you can't play yourself //Then add row to fixture table if ($teamB != $teamA && $existingFixtures->count() < 1 ) { $fixture = $page->fixtures->makeBlankItem(); $fixture->team_a = $teamA->id; $fixture->team_b = $teamB->id; $page->fixtures->add($fixture); $noFixturesAdded ++; } } } //Save updates to table $page->save('fixtures'); $message = "League saved. $noFixturesAdded new fixtures were added"; $this->message($message); });  
×
×
  • Create New...