Move templateHook code to using MVC approach

LPH

Well-known member
Within a template, there was a single callback to a single PHP file. An event listener was created as well as a Listener file. The event Listener was for the templateHook.

Listener file.

PHP:
/**
* Class TRN_XenLate_Listener_Index
*/
class TRN_XenLate_Listener_Index
{
   /**
    * @param $hookName
    * @param $contents
    * @param array $hookParams
    * @param XenForo_Template_Abstract $template
    */
   public static function templateHook($hookName, &$contents, array $hookParams, XenForo_Template_Abstract $template)
   {
      if ($hookName == 'ad_sidebar_below_visitor_panel')
      {
          $trn_xenword_contents = $template->create('trn_xenlate_latestposts', $template->getParams());
          $contents = $trn_xenword_contents . $contents;
      }
   }
}
This all worked but the single PHP file used in the callback was a mixture of HTML and code. I'm wanting to separate things out -- as well as learn more about templates, etc.

This is my new model:

PHP:
/**
* Class TRN_XenLate_Model_WPPosts
*/
class TRN_XenLate_Model_WPPosts extends XenForo_Model
{
    /**
     * @return array|false
     */
    public function getPosts()
    {

        define('WP_USE_THEMES', false);
        define('DOCUMENT_ROOT', $_SERVER['DOCUMENT_ROOT']);

        // Change the path to the correct location
        require(DOCUMENT_ROOT . '/wp-blog-header.php');

        $args = array(
            'numberposts' => '5',
            'offset'      => 0,
            'post_type'   => 'post',
            'post_status' => 'publish'
        );

        $LatestWPPosts = wp_get_recent_posts($args);

        return $LatestWPPosts;
    }
}
I also built the following view:

PHP:
/**
* Class TRN_XenLate_ViewPublic_WPPosts
*/
class TRN_XenLate_ViewPublic_WPPosts extends XenForo_ViewPublic_Base
{
    public function renderHtml()
    {

        echo '<div class="section">
           <div class="secondaryContent">
               <div class="visitorText">
                   <h3>Latest Articles</h3>
                   <div class="LatestWPPosts">';

        foreach ($LatestWPPosts as $LatestWPPost) {

            $excerpt   = apply_filters('the_excerpt', get_post_field('post_excerpt', $LatestWPPost['ID']));
            $thumbnail = get_the_post_thumbnail($LatestWPPost['ID'], array(50, 50));

            if (empty($thumbnail)) {

                echo "<h4><a href='" . esc_url(get_permalink($LatestWPPost['ID'])) . "'>" . $LatestWPPost['post_title'] . '</a></h4>';

            } else {

                echo '<div class="recent_post">';
                echo '<div class="thumbnail">' . $thumbnail . '</div>';
                echo "<h4><a href='" . esc_url(get_permalink($LatestWPPost['ID'])) . "'>" . $LatestWPPost['post_title'] . '</a></h4><br />';
                echo '<div class="excerpt">' . $excerpt . '</div>';
                echo '</div>';

            }

        }

        echo '</div>
                   </div>
               </div>
            </div>
        ';
    }
}
I know from other work that a ControllerPublic extending XenForo_ControllerPublic_Abstract can return a responseView.

PHP:
$viewParams = array(
   'latestWPPosts' => $latestWPPosts,
); // Pass variables to template

return $this->responseView('TRN_XenLate_ViewPublic_WPPosts', 'trn_xenlate_latestposts', $viewParams);
I am not sure of the interaction between the Listener and ControllerPublic. I started to build this file:

PHP:
/**
* Class TRN_XenLate_ControllerPublic_Index
*/
class TRN_XenLate_ControllerPublic_Index extends XenForo_ControllerPublic_Abstract
{
    /**
     * @return XenForo_ControllerResponse_View
     */
    public function actionIndex()
    {

        $LatestWPPostsModel = XenForo_Model::create('TRN_XenLate_Model_WPPosts');
        $LatestWPPosts      = $LatestWPPostsModel->getPosts();

        $viewParams = array(
            'latestposts' => $LatestWPPosts,
        ); // Pass variables to template

        return $this->responseView('TRN_XenLate_ViewPublic_WPPosts', 'trn_xenlate_latestposts', $viewParams);
    }
}
But wasn't sure how the Listener and Controller would interact. I'm used to a route prefix and using getRouteMatch. But since this is a templateHook then I'm confused on how to get these to interact.

I hope that is clear. It's basically a question of the flow ... Listener to Controller and then making sure I'm passing the parameters out of the template and to the view.
 

LPH

Well-known member
I really don't know how to connect the Controller but went ahead and put the code to create the model within the listener.

PHP:
/**
* Class TRN_XenLate_Listener_Index
*/
class TRN_XenLate_Listener_Index
{
   /**
    * @param $hookName
    * @param $contents
    * @param array $hookParams
    * @param XenForo_Template_Abstract $template
    */
   public static function templateHook($hookName, &$contents, array $hookParams, XenForo_Template_Abstract $template)
   {
      if ($hookName == 'ad_sidebar_below_visitor_panel')
      {
         /** @var  $LatestWPPostsModel TRN_XenLate_Model_WPPosts */
         $LatestWPPostsModel = XenForo_Model::create('TRN_XenLate_Model_WPPosts');
         $LatestWPPosts      = $LatestWPPostsModel->getPosts();

         $viewParams = array(
            'latestposts' => $LatestWPPosts,
         ); // Pass variables to template

         $trn_xenword_contents = $template->create('trn_xenlate_latestposts', $viewParams);
         $contents = $trn_xenword_contents . $contents;
      }
   }
}
Next, I added the following into the template:

Rich (BB code):
<xen:require css="trn_xenlate_latestposts.css" />

<div class="section">
    <div class="secondaryContent">
        <div class="visitorText">
        <h3>Latest Articles</h3>
            <div class="LatestWPPosts">

            <xen:foreach loop="$latestposts" value="$latestpost">
                <xen:include template="trn_xenlate_latestpost_items" />
            </xen:foreach>

            </div>
        </div>
    </div>
</div>
This returns the title of the sidebar widget and the title of the post.

XenLate Using New Listener.png

I'm building the trn_xenlate_latestpost_items template right now but am struggling with getting the following three items since they rely on the ID value returned in the foreach loop.

esc_url(get_permalink($latestpost.ID))
$excerpt = apply_filters('the_excerpt', get_post_field('post_excerpt', $latestpost.ID));
$thumbnail = get_the_post_thumbnail($latestpost.ID, array(50, 50));

Obviously this doesn't work but I'm stumped at the moment.
Code:
<h4><a href="esc_url(get_permalink({$latestpost.ID}))">{$latestpost.post_title}</a></h4>
 
Last edited:

Mike

XenForo developer
Staff member
Since you're not calling a page directly, you don't want a controller, route or view class. You basically need to do the rough equivalents within your listener. This is roughly what you're doing in your latest post.

You need to move your function calls from a template back into the listener. You would loop through your array and write the results of each function call (that you want to use in the template) back into the array. Conceptually, this is what XF does in its various "prepare" functions. (Look at XenForo_Model_User::prepareUser for example.).
 

LPH

Well-known member
Since you're not calling a page directly, you don't want a controller, route or view class. You basically need to do the rough equivalents within your listener.
Thank you !!

You need to move your function calls from a template back into the listener. You would loop through your array and write the results of each function call (that you want to use in the template) back into the array.
Duh. This makes sense. I'll look at the prepareUser.
 

LPH

Well-known member
Huge thank you for clarifying the need for only a Listener and Model. I'm now working through the template syntax.

This is the array that I built and am passing through to the template.

Rich (BB code):
array (size=5)
  0 =>
   array (size=4)
     'postTitle' => string 'Default Forum & Fallback' (length=24)
     'postUrl' => string 'http://lph/2017/01/02/default-forum-fallback/' (length=45)
     'postExcerpt' => string 'This is a post excerpt for the default forum & fallback WordPress post. ' (length=72)
     'postThumbnail' => string '<img width="50" height="50" src="http://lph/wp-content/uploads/2017/01/fireworks-150x150.jpg" class="attachment-50x50 size-50x50 wp-post-image" alt="" srcset="http://lph/wp-content/uploads/2017/01/fireworks-150x150.jpg 150w, http://lph/wp-content/uploads/2017/01/fireworks-356x364.jpg 356w, http://lph/wp-content/uploads/2017/01/fireworks-16x16.jpg 16w" sizes="(max-width: 50px) 85vw, 50px" />' (length=393)
  1 =>
   array (size=4)
     'postTitle' => string 'Duplicate Test' (length=14)
     'postUrl' => string 'http://lph/2017/01/02/duplicate-test/' (length=37)
     'postExcerpt' => string '' (length=0)
     'postThumbnail' => string '<img width="50" height="50" src="http://lph/wp-content/uploads/2017/01/stormtrooper-150x150.jpg" class="attachment-50x50 size-50x50 wp-post-image" alt="" srcset="http://lph/wp-content/uploads/2017/01/stormtrooper-150x150.jpg 150w, http://lph/wp-content/uploads/2017/01/stormtrooper-356x364.jpg 356w, http://lph/wp-content/uploads/2017/01/stormtrooper-16x16.jpg 16w" sizes="(max-width: 50px) 85vw, 50px" />' (length=405)
  2 =>
   array (size=4)
     'postTitle' => string 'Fallback Forum and Default Forum' (length=32)
     'postUrl' => string 'http://lph/2017/01/02/fallback-forum-and-default-forum/' (length=55)
     'postExcerpt' => string '' (length=0)
     'postThumbnail' => string '' (length=0)
  3 =>
   array (size=4)
     'postTitle' => string 'Schedule #4' (length=11)
     'postUrl' => string 'http://lph/2016/11/28/schedule-4/' (length=33)
     'postExcerpt' => string 'What happens with the var_dump? Now an edit.' (length=44)
     'postThumbnail' => string '' (length=0)
  4 =>
   array (size=4)
     'postTitle' => string 'Schedule #3' (length=11)
     'postUrl' => string 'http://lph/2016/11/28/schedule-3/' (length=33)
     'postExcerpt' => string 'Let's try this again. OK? Edit due to no forum Id assigned. Should write now. Change to empty.' (length=94)
     'postThumbnail' => string '<img width="50" height="50" src="http://lph/wp-content/uploads/2017/01/stormtrooper-150x150.jpg" class="attachment-50x50 size-50x50 wp-post-image" alt="" srcset="http://lph/wp-content/uploads/2017/01/stormtrooper-150x150.jpg 150w, http://lph/wp-content/uploads/2017/01/stormtrooper-356x364.jpg 356w, http://lph/wp-content/uploads/2017/01/stormtrooper-16x16.jpg 16w" sizes="(max-width: 50px) 85vw, 50px" />' (length=405)
I have the following in the template which is working to show the title and URL however the HTML img tag and src etc are all being returned instead of showing the thumbail. I see in the string is the <img ... but haven't worked enough with template syntax to know how to fix this. I didn't make another thread since this is the same project.

Rich (BB code):
<xen:foreach loop="$latestposts" value="$latestpost">

    <div class="recent_post">
        <h4><a href="{$latestpost.postUrl}">{$latestpost.postTitle}</a></h4>
        <div class="excerpt">{$latestpost.postExcerpt}</div>
    </div>

</xen:foreach>
What is the proper syntax to get the thumbnail to show when the array holds the img tag information and not just the src ? I obviously cannot use {$latestpost.postThumbnail}

XenLate Without Thumbnail showing.png

UPDATE: OH DUH. Never mind. This is what I get for posting too soon. It's obvious to pull the src then pass it rather than all the information. Done -- and working ! It's now adding in conditionals etc. Waaaahoooooo !
 
Last edited:

LPH

Well-known member
Done. I'll release the 1.1 version on Tux Reports first and add it here after there has been enough testing.

Image shows
  • thumbnail, title, and excerpt
  • thumbnail & title
  • title only
  • title & excerpt
XenLate 1.1 iPhone Image.png
 
Top