XF 2.2 Stuck with "Building with XenForo 2"

Anatoliy

Well-known member
notes/edit gives me "The requested page could not be found." error. But actionEdit is exist in a controller. 🤷‍♂️
notes/add works just fine - displays a form, then saves the data, and redirects to notes listing.

where did I make an error?


PHP:
<?php

namespace Demo\Pad\Pub\Controller;

use XF\Mvc\ParameterBag;
use XF\Pub\Controller\AbstractController;

class Note extends AbstractController{
    protected function preDispatchController($action, ParameterBag $params)
    {
        $this->assertRegistrationRequired();
    }

    public function actionIndex(ParameterBag $params){
        $noteFinder = $this->finder('Demo\Pad:Note')
        ->where('user_id', \XF::visitor()->user_id)
        ->order('post_date', 'desc');

        $page = $params->page;
        $perPage = 20;

        $noteFinder->limitByPage($page, $perPage);


        $viewParams = [
            'notes' => $noteFinder->fetch(),
            'page' => $page,
            'perPage' => $perPage,
            'total' => $noteFinder->total()
        ];
        return $this->view('Demo\Pad:Note\Index', 'demo_pad_index', $viewParams);
        
    }

    public function actionAdd()
    {
        $note = $this->em()->create('Demo\Pad:Note');
        return $this->actionAddEdit($note);
    }

    public function actionEdit(ParameterBag $params)
    {
        $note = $this->assertNoteExists($params->note_id);
        return $this->actionAddEdit($note);
    }

    protected function actionAddEdit(\Demo\Pad\Entity\Note $note)
    {
        $viewParams = [
            'note' => $note
        ];

        return $this->view('Demo\Pad:Note\Edit', 'demo_pad_edit', $viewParams);
    }

    public function actionSave(ParameterBag $params)
    {
        if($params->note_id)
        {
            $note = $this->assertNoteExists($params->note_id);           
        }
        else
        {
            $note = $this->em()->create('Demo\Pad:Note');
        }

        $this->noteSaveProcess($note)->run();
        return $this->redirect($this->buildLink('notes'));
    }

    protected function noteSaveProcess(\Demo\Pad\Entity\Note $note)
    {
        $input = $this->filter([
            'title' => 'str',
            'content' => 'str'
        ]);

        $form = $this->formAction();
        $form->basicEntitySave($note, $input);

        return $form;
    }

    /**
     * @param $id
     * @param null $with
     * @param null $phraseKey
     *
     * return \Demo\Pad\Entity\Note
     * return \XF\Mvc\Reply\Exception
     */

    protected function assertNoteExists($id, $with = null, $phraseKey = null)
    {
        return $this->assertRecordExists('Demo\Pad:Note', $id, $with, $phraseKey);
    }

}
 

Anatoliy

Well-known member
notes/edit won't work, it's notes/<id>/edit
actually it does work, because of route format :int<note_id,title>/:page.
but after your reply I went to check the route format and discovered that it's "thread_id" instead of "note_id".
I fixed that and now it works.

so, thanks, you helped! )
 

Anatoliy

Well-known member
Episode 17 is killing me. Spent several days trying to figure it out...
I wrote a search bar macros but a search bar doesn't appear.
(my route is "av-ttm" so I changed "notes" with "av-ttm")
The form is showing just fine by url http://localhost/xf/admin.php?av-ttm/search
Please help to understand what I'm doing wrong.


Code:
        <xf:macro name="filter_macros::filter_bar"
        arg-route="av-ttm"
        arg-params="{$pageParams}"
        arg-displayValues="{$displayValues}"
        arg-menue="av-ttm/search" />
 

Anatoliy

Well-known member
I'm stuck with Refine search.

Trying to find an error I removed everything from the form but the state selection. When I select Hidden it just shows the entire list, and that little rectangle thingy with name of applied filter does not appear in a bar.

Code:
<xf:title>Find titles where...</xf:title>

<xf:form action="{{ link('av-ttm/filter') }}" class="block">
    <div class="block-container">
        <div class="block-body">

            <!-- <xf:textboxrow name="q[title]" value="{$pageParams.title}" label="{{ phrase('title') }}" /> -->

            <xf:selectrow name="q['state']" value="{$pageParams.state}"
            label="{{ phrase('visibility') }}">
                <xf:option value="">{{ phrase('(any)') }}</xf:option>
                <xf:option value="visible">{{ phrase('visible') }}</xf:option>
                <xf:option value="hidden">{{ phrase('hidden') }}</xf:option>
            </xf:selectrow>

            <xf:submitrow submit="{{ phrase('search') }}" fa="fa-search" icon="" />
        </div>
    </div>
</xf:form>

Code:
    protected function getFilterTypeMap(): array
    {
        return [
            'state' => 'str',
        ];
    }

Code:
            case 'state':
            {
                if($value === ''){
                    return false;
                }

                $this->finder->where('discussion_state', "=", $value);
                return true;

            }

PHP:
public function actionFilter(ParameterBag $params)
{

    $this->setSectionContext('av_ttm_filter');
    $page = $this->filterPage();
    $perPage = 20;

    $noteFilterer = $this->setupNoteFilterer();
    $finder = $noteFilterer->apply();
    $finder
    // ->where('discussion_state', 'visible')
    // ->where('discussion_type', '!=', 'redirect')
    ->limitByPage($page, $perPage);

    $viewParams = [
        'notes' => $finder->fetch(),
        'total' => $finder->total(),
        'page' => $page,
        'perPage' => $perPage,
        'pageParams' => ['q' => $noteFilterer->getLinkParams()],
        'filterDisplay' => $noteFilterer->getDisplayValues()
    ];

        return $this->view('AV\ThreadTitles:TitleController\Filter', 'av_ttm_filter', $viewParams);               

 }

I apply filtererer ))) to Thread entity, not Note as in the tutorial.
 

Anatoliy

Well-known member
also the state dropdown on video displays (All), Visible, Hidden, when mine are (All), Visible, hidden (lowercase).
 

Anatoliy

Well-known member
I fetch from Thread entity, not from Note, so I replaced return 'Demo\Pad:Note'; with return '\XF:Thread';
can that cause my problem?

Code:
<?php
namespace AV\ThreadTitles\Filterer;
use \XF\Filterer\AbstractFilterer;
use XF\Mvc\Entity\Finder;

class Note extends AbstractFilterer
{
     /** @inheritDoc */
    
    protected function getFinderType(): string
    {
        return '\XF:Thread';
    }

...
 

Anatoliy

Well-known member
I had to quit it looong time ago, but my brain just refuses to give up, so please help before it explodes. )

PHP:
<?php

namespace AV\ThreadTitles\Admin\Controller;
use XF\Admin\Controller\AbstractController;
use XF\Mvc\ParameterBag;

class TitleController extends AbstractController
    {
        public function actionIndex()
        {
          ...         
        }

public function actionFilter(ParameterBag $params)
{
    $this->setSectionContext('av_ttm_filter');
    $page = $this->filterPage();
    $perPage = 20;

    $noteFilterer = $this->setupNoteFilterer();
    $finder = $noteFilterer->apply();
    $finder
    ->where('discussion_state', 'visible')
    ->where('discussion_type', '!=', 'redirect')
    ->limitByPage($page, $perPage);

    $viewParams = [
        'notes' => $finder->fetch(),
        'total' => $finder->total(),
        'page' => $page,
        'perPage' => $perPage,
        'pageParams' => ['q' => $noteFilterer->getLinkParams()],
        'filterDisplay' => $noteFilterer->getDisplayValues()
    ]; 

    return $this->view('AV\ThreadTitles:TitleController\Filter', 'av_ttm_filter', $viewParams);

 }

 protected function setupNoteFilterer(): \AV\ThreadTitles\Filterer\Note
 {
    /** @var \AV\ThreadTitles\Filterer\Note $filterer */
    $filterer = $this->app()->filterer('AV\ThreadTitles:Note');
    $filterer->addFilters(
        $this->filter('q', 'array'),
        $this->filter('_skipFilter', 'str')
    );

    return $filterer;

 }

 public function actionSearch()
 {
    $viewParams = [
        'pageParams' => $this->setupNoteFilterer()->getFiltersForForm()
    ];

    return $this->view('AV\ThreadTitles:TitleController\Search', 'av_ttm_search', $viewParams);               

 }

}

PHP:
<?php

namespace AV\ThreadTitles\Filterer;
use \XF\Filterer\AbstractFilterer;
use XF\Mvc\Entity\Finder;

class Note extends AbstractFilterer
{
     /** @inheritDoc */
    
    protected function getFinderType(): string
    {
        return '\XF:Thread';
    }

    protected function initFinder(Finder $finder, array $setupData)
    {
        $finder
        ->where('discussion_state', 'visible')
        ->where('discussion_type', '!=', 'redirect')
        ->setDefaultOrder('post_date');
    }

    /**
     * @inheritDoc
    */

    protected function getFilterTypeMap(): array
    {
        return [
            'order' => 'str',
        ];
    }   

    /**
     * @inheritDoc
    */

    protected function getLookupTypeList(): array
    {
        return [];
    }

        /**
     * @inheritDoc
    */

    protected function applyFilter(string $filterName, &$value, &$displayValue): bool
    {
        switch($filterName)
        {
            case 'order':
            {
                if(!$value){
                    return false;
                }
                else{
                    $this->finder->order($value, 'desc');                   
                    return true;
                }

            }           

            default:
            return false;
        }
        
    }

}

Code:
<xf:title>Thread Titles Filter</xf:title>

<div class="block">
    <div class="block-outer">
        <xf:macro name="filter_macros::quick_filter" arg-key="notes" arg-class="block-outer-opposite" />
    </div>   
    <div class="block-container">
        <xf:macro name="filter_macros::filter_bar"
        arg-route="av-ttm/filter"
        arg-params="{$pageParams}"
        arg-displayValues="{$displayValues}"
        arg-menu="av-ttm/search" />
        <div class="block-body">
            <xf:datalist data-xf-init="responsive-datalist">
                <xf:foreach loop="$notes" key="$noteId" value="$note">
                    <xf:datarow>

                        <xf:cell class="dataList-cell--min dataList-cell--image dataList-cell--imageSmall">
                            <xf:avatar user="$note.User" size="s" href="{{ link_type('public', 'members', $note.User) }}" />
                        </xf:cell>
                                            
                        <xf:main>
                            
                            <xf:label>
                                <a href="{{ link_type('public', 'threads', $note) }}" target="new">{$note.title}</a>
                            </xf:label>

                            <xf:explain>
                                <ul class="listInline listInline--bullet">
                                    <li><xf:username user="$note.User" href="{{ link_type('public', 'members', $note.User) }}" /></li>
                                    <li>{{ phrase('av_ttm_replies') }} {$note.reply_count}</li>
                                    <li>{{ phrase('av_ttm_views') }} {$note.view_count}</li>
                                    <li><xf:date time="$note.post_date" /></li>
                                    <li><xf:date time="$note.last_post_date" /></li>

                                </ul>
                            </xf:explain>
                        
                        </xf:main>


                    </xf:datarow>
                </xf:foreach>
            </xf:datalist>
        </div>
    </div>
</div>

<xf:pagenav page="{$page}" perpage="{$perPage}" total="{$total}"
link="av-ttm/filter" params="{$linkParams}" wrapperclass="block-outer block-outer-after" />

Code:
<xf:title>Find titles where...</xf:title>

<xf:form action="{{ link('av-ttm/filter') }}" class="block">
    <div class="block-container">
        <div class="block-body">

            <xf:selectrow name="q['order']" value="{$pageParams.order}"
            label="Order By">
            <xf:option value="thread_id">Thread ID</xf:option>
            <xf:option value="reply_count">Reply count</xf:option>
            <xf:option value="view_count">View count</xf:option>
            </xf:selectrow>

            <xf:submitrow submit="{{ phrase('search') }}" fa="fa-search" icon="" />
        </div>
    </div>
</xf:form>
 

Kirby

Well-known member
  1. A phrase hidden does not exist, this is why you get the phrase name (hidden) instead
  2. discussion_state only has values visible, moderatedand deleted - not hidden
 

Anatoliy

Well-known member
  1. A phrase hidden does not exist, this is why you get the phrase name (hidden) instead
  2. discussion_state only has values visible, moderatedand deleted - not hidden
I don't use discussion state in a form anymore. Just one select - 'order'.

 

Anatoliy

Well-known member
I added \XF::dump($viewParams); to controller. I go to admin.php?av-ttm/filter and see a list. I select in filterer dropdown "by reply count" and hit search. Page shows a list in the same default order. q in $pageParams is empty, so it's not sorted in a different way. But why form doesn't pass q value to controller?

array:6 [▼
"notes" => ArrayCollection {#263 ▶}
"total" => 13
"page" => 1
"perPage" => 20
"pageParams" => array:1 [▼
"q" => []
]
"filterDisplay" => []
]
 

Anatoliy

Well-known member
Did you try
Code:
<xf:selectrow name="q[order]" value="{$pageParams.order}" label="Order By">
?
just tried but it didn't change anything.
does it pass params by url? because it doesn't (there is no something like &order=reply_count in url)
can it be related to route format?
 

Kirby

Well-known member
Judging by your code it's probably just order.

The view params might need some adjustments as well.

PHP:
$viewParams = [
'notes' => $finder->fetch(),
'total' => $finder->total(),
'page' => $page,
'perPage' => $perPage,
'pageParams' => $noteFilterer->getFiltersForForm(),
'linkParams' => $noteFilterer->getLinkParams(),
'displayValues' => $noteFilterer->getDisplayValues()
];

PHP:
protected function setupNoteFilterer(): \AV\ThreadTitles\Filterer\Note
 {
/** @var \AV\ThreadTitles\Filterer\Note $filterer */
$filterer = $this->app()->filterer('AV\ThreadTitles:Note');
$filterer->addFilters(
$this->request,
$this->filter('_skipFilter', 'str')
);
 
Last edited:

Anatoliy

Well-known member
Thanks!

PHP:
$viewParams = [
'notes' => $finder->fetch(),
'total' => $finder->total(),
'page' => $page,
'perPage' => $perPage,
'pageParams' => $noteFilterer->getFiltersForForm(),
'linkParams' => $noteFilterer->getLinkParams(),
'displayValues' => $noteFilterer->getDisplayValues()
];

After rewriting this dump() shows entities and their values!

PHP:
protected function setupNoteFilterer(): \AV\ThreadTitles\Filterer\Note
 {
/** @var \AV\ThreadTitles\Filterer\Note $filterer */
$filterer = $this->app()->filterer('AV\ThreadTitles:Note');
$filterer->addFilters(
$this->request,
$this->filter('_skipFilter', 'str')
);
added here
PHP:
return $filterer;
 }

but it didn't change anything. the order is still default, the little square with filter name doesn't appear in the "refine search" bar.
 

Anatoliy

Well-known member
av-ttm/filter is the page. the others are somewhat ruined by my tries. )
and thank you very much for helping me with this thingy.
 

Attachments

  • AV-ThreadTitles-10.0.33 Release Candidate 3.zip
    15.8 KB · Views: 2
Top