{"id":2236,"date":"2020-01-19T11:06:28","date_gmt":"2020-01-19T17:06:28","guid":{"rendered":"https:\/\/thepizzy.net\/blog\/?p=2236"},"modified":"2020-01-19T11:06:36","modified_gmt":"2020-01-19T17:06:36","slug":"phpbb-extension-template-partials","status":"publish","type":"post","link":"https:\/\/thepizzy.net\/blog\/2020\/01\/phpbb-extension-template-partials\/","title":{"rendered":"phpBB extension Template Partials"},"content":{"rendered":"\n<p>I&#8217;ve been working on replacing the front-end for <a href=\"https:\/\/the-spot.net\">the-spot.net<\/a> and thus, the front-end of <a href=\"https:\/\/www.phpbb.com\/community\/viewforum.php?f=461\">phpBB forums<\/a>, using only the phpBB extension framework, trying to avoid having to put custom code into the core files, as was required pre-3.2 for some of my features.<\/p>\n\n\n\n<p>It turns out that the existing topics on the phpBB forums are not entirely helpful for doing something as advanced as this project has turned out to be, and most topics are trivially solved with clearing cache or reading the <a href=\"https:\/\/area51.phpbb.com\/docs\/dev\/3.2.x\/extensions\/tutorial_basics.html\">documentation<\/a>.<\/p>\n\n\n\n<p>Occasionally, I need to do something that requires multiple help forum threads and documentation pages, so as I come across those solutions, I&#8217;ll combine them into a post here, because that&#8217;s the purpose of this blog, after all.<\/p>\n\n\n\n<!--more-->\n\n\n\n<h2 class=\"wp-block-heading\">Rendering Template Partials<\/h2>\n\n\n\n<p>The first super complicated issue to solve was rendering what amounts to &#8220;part&#8221; of a template (hereafter referred to as &#8220;Partials&#8221;), without the <code>html<\/code> shell. So, let&#8217;s get right to it then&#8230;you didn&#8217;t come here for my life&#8217;s story.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Goal: AJAX HTML Response<\/h3>\n\n\n\n<p>To start, I have an infinite scroll that delivers a set of Topics, instead of using pagination. To avoid duplicating twig templating engine in JS, I&#8217;ll just deliver HTML, and use JS to update any existing elements, or append new elements.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Step 1: Create <code>template<\/code> class<\/h3>\n\n\n\n<p>First, create a template.php file to add a method for rendering Partials. I&#8217;ve placed mine for the extension &#8220;tsn\/tsn&#8221; at <code>tsn\/tsn\/framework\/logic\/template.php<\/code>. Here&#8217;s a look at the basics of the file.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;?php\n\nnamespace tsn\\tsn\\framework\\logic;\n\nuse phpbb\\template\\twig\\twig;\nuse Twig\\Error\\LoaderError;\nuse Twig\\Error\\RuntimeError;\nuse Twig\\Error\\SyntaxError;\n\n\/**\n * Class template\n * Adds functionality for rendering template partials\n * @package tsn\\tsn\\framework\\logic\n *\/\nclass template extends twig\n{\n\n    \/\/ Directories\n    const TEMPLATE_DIR = '@tsn_tsn\/';\n    const PARTIALS_DIR = self::TEMPLATE_DIR . 'partials\/';\n\n    \/\/ Partials\n    const P_MYSPOT_FEED = self::PARTIALS_DIR . 'tsn_myspot_feed.html';\n    const P_TOPIC_CARD = self::PARTIALS_DIR . 'tsn_topic_card.html';\n\n    \/**\n     * @param string $templateConstant A template path name or constant\n     *\n     * @return false|string|null\n     *\/\n    public function renderPartial($templateConstant)\n    {\n        try {\n\n            $output = $this->twig->render($templateConstant, $this->get_template_vars());\n            \/\/ Usually this is via AJAX, so compress the whitespace\n            $output = preg_replace('\/\\s+\/', ' ', $output);\n\n        } catch (LoaderError $e) {\n            $output = false;\n            error_log('Template Loader error: ' . $e->getMessage() . ' :: ' . $e->getCode());\n        } catch (RuntimeError $e) {\n            $output = false;\n            error_log('Template Runtime error: ' . $e->getMessage() . ' :: ' . $e->getCode());\n        } catch (SyntaxError $e) {\n            $output = false;\n            error_log('Template Syntax error: ' . $e->getMessage() . ' :: ' . $e->getCode());\n        }\n\n        return $output;\n    }\n}<\/code><\/pre>\n\n\n\n<p>It inherits <code>phpbb\\template\\twig\\twig<\/code>, and thus its constructor. It has constants to help combine the various directories and file names should any of them need to be changed along the way. And it adds a single method to allow the render of a partial template.<\/p>\n\n\n\n<p>This new method is a wrapper for the call to the inherited <code>twig<\/code> property and its <code>render()<\/code> method. This new method receives a single parameter for the Partial template name to render. The template name (or constant) and template vars are then passed into the twig&#8217;s render method, and the HTML is returned. If the render method fails, it throws exceptions, so catch them and handle them as appropriate for your needs.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p>Optional: In my case, I am using this for AJAX right now, so I am reducing all sequential whitespace to a single space character.<\/p><\/blockquote>\n\n\n\n<h3 class=\"wp-block-heading\">Step 2: Service Decoration<\/h3>\n\n\n\n<p>Now that we have the class, let&#8217;s put it in play. This is done with <a href=\"https:\/\/area51.phpbb.com\/docs\/dev\/3.2.x\/extensions\/tutorial_advanced.html#using-service-decoration\">Service Decoration<\/a> in phpBB 3.2.x, which tells the phpBB code to use this class instead of the original class. In this case, anywhere we would have used <code>$this->template<\/code>, it will invoke our class instead of the original class.<\/p>\n\n\n\n<p>This block goes into your <code>services.yml<\/code> file<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><strong>tsn.tsn.framework.logic.template<\/strong>:<br \/>    <strong>class<\/strong>: tsn\\tsn\\framework\\logic\\template<br \/>    <strong>decorates<\/strong>: 'template'<br \/>    <strong>arguments<\/strong>:<br \/>        - '@path_helper'<br \/>        - '@config'<br \/>        - '@template_context'<br \/>        - '@template.twig.environment'<br \/>        - '%core.template.cache_path%'<br \/>        - '@user'<br \/>        - '@template.twig.extensions.collection'<br \/>        - '@ext.manager'<\/pre>\n\n\n\n<p>The key line is the <code>decorates: 'template'<\/code> to tell the extension manager which service to replace.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Step 3: Method Usage<\/h3>\n\n\n\n<p>As mentioned above, I am using this implementation in an AJAX controller to return HTML strings for further processing. When standing up my AJAX controller, I hint that this template variable is of my new template class.<\/p>\n\n\n\n<p>For context, my AJAX controller route looks like this in <code>services.yml<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><strong>tsn.tsn.controller.ajax<\/strong>:<br \/>    <strong>class<\/strong>: tsn\\tsn\\controller\\ajax_controller<br \/>    <strong>arguments<\/strong>:<br \/>        - '@auth'<br \/>        - '@auth.provider_collection'<br \/>        - '@captcha.factory'<br \/>        - '@config'<br \/>        - '@content.visibility'<br \/>        - '@dbal.conn'<br \/>        - '@controller.helper'<br \/>        - '@language'<br \/>        - '@path_helper'<br \/>        - '@request'<br \/>        - '@template'<br \/>        - '@user'<br \/><\/pre>\n\n\n\n<p>And for now, it looks like this in <code>routing.yml<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><strong>tsn_tsn_ajax<\/strong>:<br \/>    <strong>pattern<\/strong>: \/tsn\/ajax\/{route}<br \/>    <strong>defaults<\/strong>: { <strong>_controller<\/strong>: tsn.tsn.controller.ajax:doIndex, <strong>route<\/strong>: \"index\" }<\/pre>\n\n\n\n<p>So the actual controller looks like this (with the extra stuff commented away)&#8230;<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">&lt;?php\n\n\/\/ ... use statements\n\n<em>\/**\n * Class ajax_controller\n * <\/em><strong><em>@package <\/em><\/strong><em>tsn\\tsn\\controller\n *\/\n<\/em><strong>class <\/strong>ajax_controller <strong>extends <\/strong>AbstractBase\n{\n    <em>\/** <\/em><strong><em>@var <\/em><\/strong><em>stdClass the JSON response *\/\n<\/em>    <strong>private <\/strong>$response = <strong>null<\/strong>;\n\n    <em>\/**\n<\/em>    <em> * ajax_controller constructor.\n<\/em>    <em> *\n<\/em>    <em> * \/\/ Other Params\n<\/em>    <em> * <\/em><strong><em>@param <\/em><\/strong><em>\\tsn\\tsn\\framework\\logic\\template $template<\/em>\n     * \/\/ Other params<em>\n<\/em>    <em> *\/\n<\/em>    <strong>public function <\/strong>__construct(\/* other params... , *\/ template $template \/* , ... other params *\/)\n    {\n        <strong>parent<\/strong>::<em>__construct<\/em>(\/* other params... , *\/ $template \/* , ... other params *\/);\n\n        $this->response = <strong>new <\/strong>stdClass();\n        $this->response->status = 0; \/\/ 0: error; 1: success, 2: info\/warning\n        $this->response->data = []; \/\/ whatever is necessary\n        $this->response->message = <strong>null<\/strong>; \/\/ message for 0\/2 status\n    }\n\n    <em>\/**\n     * <\/em><strong><em>@param <\/em><\/strong><em>$route\n     *\n     * <\/em><strong><em>@return <\/em><\/strong><em>\\Symfony\\Component\\HttpFoundation\\Response\n     * <\/em><strong><em>@uses <\/em><\/strong><em>url::AJAX_MYSPOT_FEED_PAGE\n     *\/\n    <\/em><strong>public function <\/strong>doIndex($route)\n    {\n        \/\/ ... Setup User Access\n\n        $statusCode = Response::<em>HTTP_OK<\/em>;\n\n        <strong>switch <\/strong>($route) {\n            <strong>case <\/strong>url::<em>AJAX_MYSPOT_FEED_PAGE<\/em>:\n\n                \/\/ ...do work to set template variables\n\n                $this->response->status = 1;\n                $this->response->data['html'] = $this->template->renderPartial(template::<em>P_MYSPOT_FEED<\/em>);\n                <strong>break<\/strong>;\n            <strong>default<\/strong>:\n                $statusCode = Response::<em>HTTP_NOT_FOUND<\/em>;\n                <strong>break<\/strong>;\n        }\n\n        $headers = ['Content-Type' => 'application\/json; charset=UTF-8'];\n\n        <strong>if <\/strong>(!<strong>empty<\/strong>($this->user->data['is_bot'])) {\n            $headers['X-PHPBB-IS-BOT'] = 'yes';\n        }\n\n        <strong>return new <\/strong>Response(json_encode($this->response), $statusCode, $headers);\n    }\n}<\/pre>\n\n\n\n<p>The key line for usage is <code>$this->template->renderPartial(template::<em>P_MYSPOT_FEED<\/em>);<\/code> with the constant for the template to be rendered as the only parameter.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Step 4: The Partial<\/h3>\n\n\n\n<p>And finally, the partial for this example looks like any other Twig partial:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">{#\n    This template spools up a page of Topic Cards for the MySpot Feed\n    This template requires a `topics` blockvar to iterate over\n#}\n{% <strong>if <\/strong>topics %}\n    {% <strong>for <\/strong>topic <strong>in <\/strong>topics %}\n        {% <strong>include <\/strong>'@tsn_tsn\/partials\/tsn_topic_card.html' %}\n    {% <strong>endfor <\/strong>%}\n{% <strong>endif <\/strong>%}<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Key Takeaway<\/h2>\n\n\n\n<p><strong>Avoid duplicating code where necessary.<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Use the existing template engine, and avoid recreating it in either PHP or JS.<\/li><li>Use Partials to render the same Template everywhere, in as generic a way as possible. Using wrapping elements with modifier classes to affect minor visual changes to the inner elements.<\/li><li>Add your own PHP classes &amp; traits to handle shared logic between Controllers.<\/li><li>Use Service Decoration to replace\/extend existing phpBB Services with your own classes.<\/li><\/ul>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>I&#8217;ve been working on replacing the front-end for the-spot.net and thus, the front-end of phpBB forums, using only the phpBB extension framework, trying to avoid having to put custom code into the core files, as was required pre-3.2 for some of my features. It turns out that the existing topics on the phpBB forums are [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"How to render Template Partials with a phpBB extension","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[506,25,910,3,9,347],"tags":[974,152,47,973,951,359,970,972],"class_list":["post-2236","post","type-post","status-publish","format-standard","hentry","category-how-to","category-programming","category-tech-fixes","category-tech","category-the-spot-net","category-the-spot-network","tag-how-to-render-phpbb-template-partials-extension","tag-php","tag-phpbb","tag-phpbb-ext-partial-rendering","tag-the-spot-net","tag-tsn","tag-tsn9","tag-twig"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_likes_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/prOO4-A4","jetpack-related-posts":[{"id":2213,"url":"https:\/\/thepizzy.net\/blog\/2020\/01\/new-decade-new-projects\/","url_meta":{"origin":2236,"position":0},"title":"New Decade, New Projects","author":"[[Neo]]","date":"January 2, 2020","format":false,"excerpt":"A new decade rolls around, and with it some ideas for new technology projects to get started on.","rel":"","context":"In &quot;Programming&quot;","block_context":{"text":"Programming","link":"https:\/\/thepizzy.net\/blog\/category\/tech\/programming\/"},"img":{"alt_text":"tsn9 View Topic Mockup","src":"https:\/\/i0.wp.com\/thepizzy.net\/blog\/wp-content\/uploads\/2020\/01\/ENPfnSJXUAE0pKv.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/thepizzy.net\/blog\/wp-content\/uploads\/2020\/01\/ENPfnSJXUAE0pKv.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/thepizzy.net\/blog\/wp-content\/uploads\/2020\/01\/ENPfnSJXUAE0pKv.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/thepizzy.net\/blog\/wp-content\/uploads\/2020\/01\/ENPfnSJXUAE0pKv.png?resize=700%2C400&ssl=1 2x"},"classes":[]},{"id":96,"url":"https:\/\/thepizzy.net\/blog\/2008\/04\/tsn7-style-in-progress\/","url_meta":{"origin":2236,"position":1},"title":"tsn.7 Style in progress","author":"[[Neo]]","date":"April 30, 2008","format":false,"excerpt":"I've started work on the tsn.7 style and theme for the new forums, and I must say, it's lookin' pretty sweet. Once I get a working set going, I'll redo the splash page to give a bit of a preview and then load it up to the forum index. Let's\u2026","rel":"","context":"In &quot;Programming&quot;","block_context":{"text":"Programming","link":"https:\/\/thepizzy.net\/blog\/category\/tech\/programming\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/thepizzy.net\/blog\/wp-content\/uploads\/2008\/04\/Untitled-2.png?fit=400%2C400&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]},{"id":25,"url":"https:\/\/thepizzy.net\/blog\/2006\/04\/update-tsnx-sub-forums-80-complete\/","url_meta":{"origin":2236,"position":2},"title":"Update: tsnX &#8211; Sub-forums, 80% complete","author":"[[Neo]]","date":"April 5, 2006","format":false,"excerpt":"This is the longest mod to do by hand, because it's sooooo long. It was one I was hoping to do with the EasyMod script. But it's about 80% done. After that comes the Profile Control Panel mod, and hopefully I can still use the EasyMod script on it, but\u2026","rel":"","context":"In &quot;Technology&quot;","block_context":{"text":"Technology","link":"https:\/\/thepizzy.net\/blog\/category\/tech\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":97,"url":"https:\/\/thepizzy.net\/blog\/2008\/01\/tsn7-style-in-progress-2\/","url_meta":{"origin":2236,"position":3},"title":"tsn.7 Style in progress","author":"[[Neo]]","date":"January 2, 2008","format":false,"excerpt":"I've started work on the tsn.7 style and theme for the new forums, and I must say, it's lookin' pretty sweet. Once I get a working set going, I'll redo the splash page to give a bit of a preview and then load it up to the forum index. Let's\u2026","rel":"","context":"In &quot;Programming&quot;","block_context":{"text":"Programming","link":"https:\/\/thepizzy.net\/blog\/category\/tech\/programming\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/thepizzy.net\/blog\/wp-content\/uploads\/2008\/04\/Untitled-2.png?fit=400%2C400&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]},{"id":55,"url":"https:\/\/thepizzy.net\/blog\/2006\/05\/update-tsnx3-registrationlogin-fixed\/","url_meta":{"origin":2236,"position":4},"title":"Update: tsnX.3 &#8211; Registration\/Login Fixed","author":"[[Neo]]","date":"May 15, 2006","format":false,"excerpt":"The issues that we had with the cache and logging in after you registered have been fixed. There is nothing more frustrating than going through a mod that has worked in the past, but doesn't work this time, because everything else that it had interacted with before has changed. The\u2026","rel":"","context":"In &quot;Technology&quot;","block_context":{"text":"Technology","link":"https:\/\/thepizzy.net\/blog\/category\/tech\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":91,"url":"https:\/\/thepizzy.net\/blog\/2007\/05\/update-new-rss-code-in-the-works\/","url_meta":{"origin":2236,"position":5},"title":"Update: New RSS Code in the works.","author":"[[Neo]]","date":"May 28, 2007","format":false,"excerpt":"Earlier I posted on the RSS Code that I'm working on. I've done quite a bit of work on it so far - learning about the various functions that PHP offers. Most of what I thought I was learning while setting up the forums was not actually PHP's functions, they\u2026","rel":"","context":"In &quot;Programming&quot;","block_context":{"text":"Programming","link":"https:\/\/thepizzy.net\/blog\/category\/tech\/programming\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/thepizzy.net\/blog\/wp-content\/uploads\/2008\/04\/Untitled-2.png?fit=400%2C400&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]}],"jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/thepizzy.net\/blog\/wp-json\/wp\/v2\/posts\/2236","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/thepizzy.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/thepizzy.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/thepizzy.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/thepizzy.net\/blog\/wp-json\/wp\/v2\/comments?post=2236"}],"version-history":[{"count":6,"href":"https:\/\/thepizzy.net\/blog\/wp-json\/wp\/v2\/posts\/2236\/revisions"}],"predecessor-version":[{"id":2442,"href":"https:\/\/thepizzy.net\/blog\/wp-json\/wp\/v2\/posts\/2236\/revisions\/2442"}],"wp:attachment":[{"href":"https:\/\/thepizzy.net\/blog\/wp-json\/wp\/v2\/media?parent=2236"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/thepizzy.net\/blog\/wp-json\/wp\/v2\/categories?post=2236"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/thepizzy.net\/blog\/wp-json\/wp\/v2\/tags?post=2236"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}