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?
 

Anatoliy

Well-known member
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:

hstammlerj

Active member
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? ;)
 

Anatoliy

Well-known member
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:

Anatoliy

Well-known member
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.
 

Anatoliy

Well-known member
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'?
 

nocte

Well-known member
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.
 

Anatoliy

Well-known member
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

"
 

Anatoliy

Well-known member
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
 

Anatoliy

Well-known member
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 )
 

Anatoliy

Well-known member
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);
    }
}
 

nocte

Well-known member
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. :)
 

Anatoliy

Well-known member
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.
 

Anatoliy

Well-known member
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
 

DimmmCom

Active member
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:

Anatoliy

Well-known member
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:

Anatoliy

Well-known member
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