XF 2.2 Customizing "Similar threads"

Anatoliy

Well-known member
I use xenForo's "Similar threads" add-on and I like it. However there is a problem with it as it takes in consideration keyword relevance only, and doesn't (well, because it just can't) consider pages "authority". So I exported a list of top performing pages (bring most organic traffic) from Google Search Console, and my idea is to create "topic related clusters" of 11 pages, so each of them will have a widget with 10 links to others, and those 10 Similar threads will be not just keyword related, but also with the biggest "authority".

It was not difficult to create a first cluster, a widget with links to pages, and a conditional !$xf.visitor.user_id && in_array($xf.reply.contentKey, ['thread-x', 'thread-y', ...', 'thread-z']). And I added a conditional to the XFES: Similar threads widget so the pages that have the widget with my manually selected related links would not display the XFES: Similar threads widget. !$xf.visitor.user_id AND !in_array($xf.reply.contentKey, ['thread-x', ..., 'thread-z']). Everything seems to work as expected.

So here is a question that bothers me. To "similar link" all those 1000 best performing pages I will have to create 100 widgets. (And that's fine, I can handle it. )
So the conditional in XFES: Similar threads widget will include an array with 1000 :eek: elements.

Will it slow down page load speed for all the threads as php now will have to go through that huge array to see if XFES: Similar threads widget should be displayed?
 
I guess a proper way to show my custom related links is to create an add-on with a custom db table. In this case I will need to create just one widget to display them, not 100.

But what about teaching the XFES: Similar threads widget not to appear on those threads? Is there a better way than a condition !in_array with 1000 elements?
 
Last edited:
You may answered your question already by yourself:
Since the information about "being one of special 1000" must be held somewhere, you probably can choose to enclose the list directly in your call or you can build a special call to check against a separate remote list (textvar, db) or an additional tag or even a new column to the thread DB table. Same decision as often: the more efficient, the more intrusive.
As you were willing to add 100 widgets to your system :eek:, it may also cope a list of 1000 some-digit numbers without any problems? ;)
 
So, following the "Building with xf2" tutorials I build an addon that with minor modifications serve my needs - custom entity, custom table, ability to add/edit/delete records.

Edited
I created another controller and php callback widget. And stuck.
Do I use funder here too, or I write my own php code in that another controller?

I need to get current thread id. then I need to get value 'keyword' for this thread from my custom table. then select from that table all records with that keyword.

I have no idea how to do it properly.

Please advise.
 
Last edited:
When I'm trying to create a php callback widget it shows an error:
Callback AV\STManager\Pub\Controller\STManagerWidget::render is invalid (error_method_not_static).

here is the code:

PHP:
<?php

namespace AV\STManager\Pub\Controller;

use \XF\Widget\AbstractWidget;

class STManagerWidget extends AbstractWidget
{
    public function render()
    {
        $finder = \XF::finder('AV\STManager:Thread');
        $threads = $finder
            ->limit('10')
            ->fetch();

        $viewParams = [
            'threads' => $threads
        ];
        
        return $this->renderer('av_stmanager_widget', $viewParams);
    }

}

when I add "static" (public static function render()) it shows an error in console

<b>Fatal error</b>: Cannot make non static method XF\Widget\AbstractWidget::render() static in class AV\STManager\Pub\Controller\STManagerWidget in <b>C:\xampp\htdocs\xf\src\addons\AV\STManager\Pub\Controller\STManagerWidget.php</b> on line <b>9</b><br />

Please help.
 
How do I access the value of a thread field?

I created an extra 'keyword' column in the 'threads' table and a new 'linked_threads' table. I put some test rows in the new table and created a widget that at this stage displays all of them on every thread page.

Now I need to add the final touch - so the widget would display data only if it is the thread, with non empty 'keyword' column. And I don't understand how to achieve that.

Here is my Widget.php

Code:
<?php

namespace AV\LinkedThreads\Widget;

class Widget
{ 
    public static function widget(\XF\Widget\AbstractWidget $widget)
    {   $keyword = 'trout';
        $finder = \XF::finder('AV\LinkedThreads:LinkedThread');
        $data = $finder->where('keyword', $keyword)->limit(5)->fetch();
        $viewParams = [
            'data' => $data,

        ];
        return $widget->renderer('linked_threads_widget', $viewParams);
    }
}

So far $keyword = 'trout'. But I was planning to use something like $keyword = $xf.thread.keyword; and my widget would display threads that has the same keyword as the current thread. How do I access the value of the thread's 'keyword'?
 
How do I access the value of the thread's 'keyword'?
If you extend the class XF\Widget\AbstractWidget, you have access to $this->contextParams. I guess this should include the thread.

Try:

PHP:
$context = $this->contextParams;
$thread = $context['thread'];
$keyword = $thread->keyword;

// you can try testing the outcome with:
\XF::dump($thread);
// if this does not work, then inspect this output:
\XF::dump($context);

P.S. or with your code, try $widget->contextParams.
 
Thanks! but if I try

Code:
<?php

namespace AV\LinkedThreads\Widget;
use XF\Widget\AbstractWidget;

class Widget extends AbstractWidget
{ 
    public static function widget(\XF\Widget\AbstractWidget $widget)
    {   $context = $this->contextParams;
        $thread = $context['thread'];
        $keyword = $thread->keyword;

        \XF::dump($keyword);

I'm getting an error Fatal error: Class AV\LinkedThreads\Widget\Widget contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (XF\Widget\AbstractWidget::render) in C:\xampp\htdocs\xf\src\addons\AV\LinkedThreads\Widget\Widget.php on line 6
P.S. or with your code, try $widget->contextParams.
If I try
Code:
$widget->contextParams;
        $thread = $widget['thread'];
        $keyword = $thread->keyword;

        \XF::dump($keyword);

I'm getting an error "public:thread_view - Cannot access protected property XF\Widget\PhpCallback::$contextParams in C:\xampp\htdocs\xf\src\addons\AV\LinkedThreads\Widget\Widget.php:9

"
 
did you use this exact code? Try
I have
PHP:
<?php

namespace AV\LinkedThreads\Widget;
use XF\Widget\AbstractWidget;

class Widget extends AbstractWidget
{ 
    public static function widget(\XF\Widget\AbstractWidget $widget)
    {   $context = $this->contextParams;
        $thread = $context['thread'];
        $keyword = $thread->keyword;
 
        \XF::dump($keyword);

        $finder = \XF::finder('AV\LinkedThreads:LinkedThread');
        $data = $finder->where('keyword', $keyword)->limit(5)->fetch();


        $viewParams = [
            'data' => $data,

        ];
        return $widget->renderer('linked_threads_widget', $viewParams);
    }
}

error Fatal error: Class AV\LinkedThreads\Widget\Widget contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (XF\Widget\AbstractWidget::render) in C:\xampp\htdocs\xf\src\addons\AV\LinkedThreads\Widget\Widget.php on line 6

also visualstudio code underlines with red class Widget extends AbstractWidget
'AV\LinkedThreads\Widget\Widget' does not implement method 'render'

and $this
Cannot use '$this' in non-object context.in
 
If I use $context = $widget->contextParams; instead of $context = $this->contextParams; getting the error
Fatal error: Class AV\LinkedThreads\Widget\Widget contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (XF\Widget\AbstractWidget::render) in C:\xampp\htdocs\xf\src\addons\AV\LinkedThreads\Widget\Widget.php on line 6.

(line 6 - class Widget extends AbstractWidget )
 
if I use class Widget instead of class Widget extends AbstractWidget ?

Template Compilation Error​

public:thread_view - Cannot access protected property XF\Widget\PhpCallback::$contextParams in C:\xampp\htdocs\xf\src\addons\AV\LinkedThreads\Widget\Widget.php:9

PHP:
<?php

namespace AV\LinkedThreads\Widget;

class Widget
{ 
    public static function widget(\XF\Widget\AbstractWidget $widget)
    {   $context = $widget->contextParams;
        $thread = $context['thread'];
        $keyword = $thread->keyword;
 
        \XF::dump($keyword);

        $finder = \XF::finder('AV\LinkedThreads:LinkedThread');
        $data = $finder->where('keyword', $keyword)->limit(5)->fetch();


        $viewParams = [
            'data' => $data,

        ];
        return $widget->renderer('linked_threads_widget', $viewParams);
    }
}
 
o.k. apparently I did not read too carefully and I am out of practice too..

You should add a widget definition (admin cp: admin.php?widgets/definitions/add) and not a "PHP callback" widget. After you have created this definition you can add the widget like you added the "PHP callback" widget. :)
 
Damn ) I was thinking that a widget definition is created when a new route is created so a new place for a widget is being used. And in my case I don't need it (I thought) because I use already defined place. )))

Thanks for your help. I'll try in few hours when i'm back to my comp.
 
So I added a widget definition, but when I try to add a widget I get an error

Add widget​

Template Compilation Error​

admin:widget_edit - Call to undefined method AV\LinkedThreads\Widget\Widget::renderOptions() in C:\xampp\htdocs\xf\src\XF\Entity\Widget.php:36
 
I have
PHP:
<?php

namespace AV\LinkedThreads\Widget;
use XF\Widget\AbstractWidget;

class Widget extends AbstractWidget
{
    public static function widget(\XF\Widget\AbstractWidget $widget)
    {   $context = $this->contextParams;
        $thread = $context['thread'];
        $keyword = $thread->keyword;
 
        \XF::dump($keyword);

        $finder = \XF::finder('AV\LinkedThreads:LinkedThread');
        $data = $finder->where('keyword', $keyword)->limit(5)->fetch();


        $viewParams = [
            'data' => $data,

        ];
        return $widget->renderer('linked_threads_widget', $viewParams);
    }
}

error Fatal error: Class AV\LinkedThreads\Widget\Widget contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (XF\Widget\AbstractWidget::render) in C:\xampp\htdocs\xf\src\addons\AV\LinkedThreads\Widget\Widget.php on line 6

also visualstudio code underlines with red class Widget extends AbstractWidget
'AV\LinkedThreads\Widget\Widget' does not implement method 'render'

and $this
Cannot use '$this' in non-object context.in
If the AbstractWidget class is extended, then the public function render() method must be present.

PHP:
<?php

namespace AV\LinkedThreads\Widget;

use XF\Widget\AbstractWidget;

class Widget extends AbstractWidget
{
    public function render()
    { 
        $context = $this->getContextParams();
        $thread = $context['thread'];
        $keyword = $thread->keyword;
 
        $finder = \XF::finder('AV\LinkedThreads:LinkedThread');
        $data = $finder->where('keyword', $keyword)->limit(5)->fetch();


        $viewParams = [
            'data' => $data,
        ];
        return $this->renderer('linked_threads_widget', $viewParams);  
    }
}
 
Last edited:
return $widget->renderer('linked_threads_widget', $viewParams);
or
return $this->renderer('linked_threads_widget', $viewParams);
?
because
@var unset $widget
Undefined variable '$widget'

in case return $this->renderer('linked_threads_widget', $viewParams);

Template errors​

  • Template public:thread_view: [E_USER_WARNING] Accessed unknown getter 'keyword' on XF:Thread[1] (src\XF\Mvc\Entity\Entity.php:202)
 
Last edited:
sorry, my mistake. I used a wrong keyword column name. now it looks like it works, however when I
\XF::dump($keyword); it shows ''true", not the value "steelhead". )
and if I pass it to a template it shows '1'.

if I dump $thread I can see in values "av_lt_keyword" => "steelhead", how come
$keyword = $thread->av_lt_keyword; shows 1 and not steelhead?

I'm going sligtly maad...
 
Last edited:
Top Bottom