XF 2.2 Passing data to a thread

Lee

Well-known member
I have a custom table with multiple rows. I want to add a ‘submit details’ button which will link to the thread creation page for a specific node. This is easy, I can do this - but I want to ‘link‘ the created thread with the record of the button that is pressed.

I guess the simple thing to do is to save the record ID in the thread as a column, but i'm not sure how to pass that data over when saving the thread.
 
Maybe set a query parameter in the link (ie. {{ link('forums/post-thread', $forum, {'some_id': $someId}) }}) and then extend the controllers and thread creation service to capture and store the value.
 
Maybe set a query parameter in the link (ie. {{ link('forums/post-thread', $forum, {'some_id': $someId}) }}) and then extend the controllers and thread creation service to capture and store the value.
Thanks Jeremy.

Do you know of any stock code that implements this that I could look at? :)
 
Not specifically, but virtually everything captures input, passes it around, and persists it to the database. As a rough outline:

You'd extend the thread entity with a new column, then extend \XF\Service\Thread\Creator with a new setter for it:
PHP:
public function setSomeId(int $someId)
{
    $this->thread->some_id = $someId;
}

Then extend \XF\Pub\Controller\Forum::actionPostThread to capture it and pass it to the template:
PHP:
public function actionPostThread(ParameterBag $params)
{
    $reply = parent::actionPostThread($params);

    if ($reply instanceof \XF\Mvc\Reply\View)
    {
        $someId = $this->filter('some_id', 'int');
        if ($someId)
        {
            $reply->setParam('someId', $someId);
        }
    }

    return $reply;
}

Then you'd persist it with the form data using a template modification for forum_post_thread:
HTML:
<xf:if is="$someId">
    <xf:hiddenval name="some_id" value="$someId" />
</xf:if>

Then capture it and pass it to the setter you created in \XF\Pub\Controller\Forum::setupThreadCreate:
PHP:
public function setupThreadCreate(\XF\Entity\Forum $forum)
{
    $creator = parent::setupThreadCreate($forum);

    $someId = $this->filter('some_id', 'int');
    if ($someId)
    {
        $creator->setSomeId($someId);
    }

    return $creator;
}
 
This is all now working.

What I am trying to do now is select the data from the other table. I'm guessing the best way to do this is to define a relation in the entity?
 
Not specifically, but virtually everything captures input, passes it around, and persists it to the database. As a rough outline:

You'd extend the thread entity with a new column, then extend \XF\Service\Thread\Creator with a new setter for it:
PHP:
public function setSomeId(int $someId)
{
    $this->thread->some_id = $someId;
}

Then extend \XF\Pub\Controller\Forum::actionPostThread to capture it and pass it to the template:
PHP:
public function actionPostThread(ParameterBag $params)
{
    $reply = parent::actionPostThread($params);

    if ($reply instanceof \XF\Mvc\Reply\View)
    {
        $someId = $this->filter('some_id', 'int');
        if ($someId)
        {
            $reply->setParam('someId', $someId);
        }
    }

    return $reply;
}

Then you'd persist it with the form data using a template modification for forum_post_thread:
HTML:
<xf:if is="$someId">
    <xf:hiddenval name="some_id" value="$someId" />
</xf:if>

Then capture it and pass it to the setter you created in \XF\Pub\Controller\Forum::setupThreadCreate:
PHP:
public function setupThreadCreate(\XF\Entity\Forum $forum)
{
    $creator = parent::setupThreadCreate($forum);

    $someId = $this->filter('some_id', 'int');
    if ($someId)
    {
        $creator->setSomeId($someId);
    }

    return $creator;
}

PHP:
<?php

namespace TFA\UpgradeReceipts\XF\Entity;

use XF\Mvc\Entity\Entity;
use XF\Mvc\Entity\Structure;

class Thread extends XFCP_Thread
{
    
    public static function getStructure(Structure $structure)
    {
        $structure = parent::getStructure($structure);
        
        $structure->columns['order_id'] = ['type' => self::UINT, 'maxLength' => 10, 'default' => '0',];

        $structure->relations['UserUpgradeActive'] = [
            'entity'    => 'XF:UserUpgradeActive',
            'type'      => self::TO_ONE,
            'conditions' => [
                ['user_upgrade_record_id', '=', '$order_id'],
            ]
        ];

        return $structure;
    }
}


What am I doing wrong? Doesn't seem to be finding the result even though it is in the database.
 
It's not clear what issue you're encountering from that code alone. You'd need to post the code in question, what you expect it to do, and what the unexpected behavior is.
 
It's not clear what issue you're encountering from that code alone. You'd need to post the code in question, what you expect it to do, and what the unexpected behavior is.
Doesn't matter, that code is actually working. It was late and my brain was not!

I have one small question left and them I am pretty sure I am finished with what I am trying to achieve.

PHP:
<xf:button href="{{ link('forums/post-thread', $forum, {'order_id': $activereceipts.user_upgrade_record_id}) }}">Submit Order Details</xf:button>

I have the above code to build the button to "create" the thread. The param gets passed to the url correctly, but how do I tell $forum which node ID to post in? I'd like to set it as an option in the ACP.
 
You can fetch the forum entity from the option and pass it through the template.

PHP:
$forumId = $this->options()->someOptionId;
$forum = $this->app->find('XF:Forum', $forumId);

You can check out the reportIntoForumId option for an example of how to create the option.

It's also probably worth guarding your thread creation extensions to check the forum ID (and user, if applicable) to prevent someone from manually crafting a URL to save a record that's not expected/permitted.
 
One more query, if I wanted to check that the thread owner is also the same as the someId owner, how would I do that?

I want to not show the new thread form if the user is not the thread owner.

For reference, the someId is the user_upgrade_record_id and I want to make sure the user_id is the current visitor.
 
Modify the \XF\Pub\Controller\Forum::actionPostThread extension to fetch the record and compare the user IDs:

PHP:
public function actionPostThread(ParameterBag $params)
{
    $someRecord = null;
    $someId = $this->filter('some_id', 'int');
    if ($someId)
    {
        $someRecord = $this->app->find('Some:Entity', $someId);
        if (!$someRecord || $someRecord->some_user_id !== \XF::visitor()->user_id)
        {
            return $this->noPermission();
        }
    }

    $reply = parent::actionPostThread($params);

    if ($reply instanceof \XF\Mvc\Reply\View && $someRecord)
    {
        $reply->setParam('someId', $someRecord->some_id);
    }

    return $reply;
}
 
Last edited:
Error: Call to undefined method XF\Pub\App::fetch() in src\addons\TFA\UpgradeReceipts\XF\Pub\Controller\Forum.php at line 17
  1. TFA\UpgradeReceipts\XF\Pub\Controller\Forum->actionPostThread() in src\XF\Mvc\Dispatcher.php at line 350
  2. XF\Mvc\Dispatcher->dispatchClass() in src\XF\Mvc\Dispatcher.php at line 257
  3. XF\Mvc\Dispatcher->dispatchFromMatch() in src\XF\Mvc\Dispatcher.php at line 113
  4. XF\Mvc\Dispatcher->dispatchLoop() in src\XF\Mvc\Dispatcher.php at line 55
  5. XF\Mvc\Dispatcher->run() in src\XF\App.php at line 2344
  6. XF\App->run() in src\XF.php at line 512
  7. XF::runApp() in index.php at line 20

A little error using the above code.
 
Top Bottom