Creating Pages & Controllers - HL Vanilla Community
<main> <article class="userContent"> <div class="embedExternal embedImage display-large float-none"> <div class="embedExternal-content"> <a class="embedImage-link" href="https://us.v-cdn.net/6030677/uploads/D8Q0J933BAO6/microsoftteams-image-288-29.png" rel="nofollow noreferrer noopener ugc" target="_blank"> <img class="embedImage-img" src="https://us.v-cdn.net/6030677/uploads/D8Q0J933BAO6/microsoftteams-image-288-29.png" alt="MicrosoftTeams-image (8).png" height="108" width="1356" loading="lazy" data-display-size="large" data-float="none"></img></a> </div> </div> <p>Looking to create routes containing some UI and content? This is the article for you!</p><h2 data-id="differences-from-gdn_controller">Differences from Gdn_Controller</h2><p>A <code class="code codeInline" spellcheck="false" tabindex="0">PageDispatchController</code> serves a different purpose to a <code class="code codeInline" spellcheck="false" tabindex="0">Gdn_Controller</code>.</p><p><strong>PageDispatchController</strong></p><ul><li>Core static master view only.</li><li>Primary views expected to be rendered in React.</li><li>Server side rendering only of SEO-critical content using Twig.</li><li>Offers utilities for preloading data into a frontend store.</li><li>Uses the new dispatching rules.</li><li>Hands off to the frontend for future routing.</li><li>Return based result content.</li></ul><p><strong>Gdn_Controller</strong></p><ul><li>Uses a master view from any enabled theme.</li><li>Views rendered primarily server side with Twig, Smarty, and PHP.</li><li>Controllers holds Gdn_Assets & Gdn_Modules.</li><li>Additional page contents are outputted using <code class="code codeInline" spellcheck="false" tabindex="0">echo</code> in 100s of events.</li><li>Exclusively server-side routing.</li><li>Data often pulled from models and not the API. Using <code class="code codeInline" spellcheck="false" tabindex="0">Gdn_Form</code> instead of API submission and must handle post-backs directly.</li></ul><h2 data-id="how-does-the-routing-work">How does the routing work?</h2><p>Pages are routed using <code class="code codeInline" spellcheck="false" tabindex="0">ResourceRoute</code> which means all of the rules in our <a href="https://success.vanillaforums.com/kb/articles/161-dispatching-urls#gardenwebdispatcher-routes" rel="nofollow noreferrer ugc">Dispatcher documentation apply</a>. In practice the only HTTP verbs used for pages like this are going to be <code class="code codeInline" spellcheck="false" tabindex="0">GET</code> and <code class="code codeInline" spellcheck="false" tabindex="0">INDEX</code> though.</p><p><em>Using POST, PATCH, PUT, and DELETE verbs </em><strong><em>is strongly discouraged. </em></strong><em>Prefer to use ApiControllers for those types of actions.</em></p><h2 data-id="registering-a-route-with-the-dispatcher">Registering a Route With the Dispatcher</h2><p>The first step to creating a Page for your addon is register some rule with the dispatcher.</p><p>This registration should be done through <code class="code codeInline" spellcheck="false" tabindex="0">\Garden\Container</code>.</p><p><strong>youraddon/settings/bootstrap.php</strong></p><pre class="code codeBlock" spellcheck="false" tabindex="0">$container = \Gdn::getContainer(); $container ->rule(\Garden\Web\Dispatcher::class) ->addCall('addRoute', ['route' => new \Garden\Container\Callback(function () { $route = new \Garden\Web\ResourceRoute('/my-prefix', '*\\%sMyPrefixController'); $route->setMeta('CONTENT_TYPE', 'text/html; charset=utf-8'); })]); </pre><p>This example assumes we want page to be served from the prefix <code class="code codeInline" spellcheck="false" tabindex="0">/my-prefix</code> and our controllers should be in a pattern of <code class="code codeInline" spellcheck="false" tabindex="0">Some\Namespace\ResourceMyPrefixController</code>. In this case that controller would serve <code class="code codeInline" spellcheck="false" tabindex="0">/my-prefix/resource</code>.</p><h2 data-id="creating-the-controller">Creating the Controller</h2><p>We're going to start with a very simple controller setup and gradually build it out.</p><p><strong>youraddon/Controllers/ResourceMyPrefixController.php</strong></p><pre class="code codeBlock" spellcheck="false" tabindex="0">namespace MyAddon\Controllers; use Vanilla\Web\PageDispatchController; class ResourceMyPrefixController extends PageDispatchController { /** * Render out the /my-prefix/resource/{id}/editor page. * * @param int $id */ public function get_editor(int $id) { return $this ->useSimplePage(\Gdn::translate('Resource Editor')) ->blockRobots() ->requiresSession("/my-prefix/resource/$id/editor") ->render() ; } /** * Render out the /my-prefix/resource/add page. */ public function get_add() { return $this ->useSimplePage(\Gdn::translate('New Resource')) ->blockRobots() ->requiresSession("/my-prefix/resource/add") ->render() ; } } </pre><p><strong>What did we do?</strong></p><p>Here we've made 2 simple page containers for client rendered views. It would be expected that the primary page content will be rendered using React, as all that these pages will render are a search engine information and an empty body.</p><ul><li>2 pages, 1 for add and 1 for edit of the resource.</li><li>These pages require the user to be signed in. Signed out users will be sent to the sign in page and then redirected back after a successful login.</li><li>Search engines are blocked from crawling these pages.</li></ul><h2 data-id="simplepage">SimplePage</h2><p>Simple pages are ideal for when we want the server to deal with search engines (Either by providing some SEO info, like title and description, or blocking them), then handing things off to ReactJS.</p><h3 data-id="when-should-you-use-a-simple-page-instead-of-a-custom-one">When should you use a simple page instead of a custom one?</h3><p>Simple pages generally best for when the server doesn't have any data to preload, and just wants to render a shell, before the frontend router takes over. The following metadata can be configured after creating the simple page.</p><ul><li>Set the page title - Part of the constructor.</li><li>Blocking Search engines - <code class="code codeInline" spellcheck="false" tabindex="0">blockRobots()</code>.</li><li>Require users to be signed in <code class="code codeInline" spellcheck="false" tabindex="0">requiresSession()</code>.</li><li>Set any information from <code class="code codeInline" spellcheck="false" tabindex="0">PageHeadInterface</code>.<ul><li>Meta tags</li><li>Title</li><li>Description</li></ul></li></ul><h3 data-id="custom-simple-pages">Custom simple pages</h3><p>By default <code class="code codeInline" spellcheck="false" tabindex="0">PageDispatchController</code> uses <code class="code codeInline" spellcheck="false" tabindex="0">SimpleTitlePage</code> for creating simple pages, but it is encouraged to create your own and configure it for your <code class="code codeInline" spellcheck="false" tabindex="0">PageDispatchController</code>. This way you can configure common metadata or add custom configuration methods.</p><p>To set a custom simple page, create a subclass of <code class="code codeInline" spellcheck="false" tabindex="0">Vanilla\Web\Page</code> and assign it in your controller.</p><pre class="code codeBlock" spellcheck="false" tabindex="0">// If targetting forum or KB use ThemedPage instead. class MyCustomPageClass extends Page { /** * @return string */ public function getAssetSection(): string { return "admin"; // forum, admin, or knowledge } /** * @inheritdoc */ public function initialize(string $title = "") { $this->setSeoRequired(false); $this->setSeoTitle($title); } } class ResourceMyPrefixController extends PageDispatchController { protected $simplePageClass = MyCustomPageClass::class; } </pre><h2 data-id="defining-an-asset-section">Defining an Asset Section</h2><p>In our previous example we implemented <code class="code codeInline" spellcheck="false" tabindex="0">getAssetSection()</code>. This is a method of <code class="code codeInline" spellcheck="false" tabindex="0">Page</code> that must be implemented in order to tell the controller what scripts to add to the page.</p><p>Custom assets can be created, but generally you want to define either "forum", "admin" or "knowledge".</p><p>See the build documentation on asset sections.</p><div class="js-embed embedResponsive" data-embedjson="{"body":"Vanilla Forums, create an online community that your customers will love","url":"https:\/\/docs.vanillaforums.com\/developer\/tools\/building-frontend\/#site-sections","embedType":"link","name":"Vanilla Forums Documentation | Building Javascript"}"> <a href="https://docs.vanillaforums.com/developer/tools/building-frontend/#site-sections" rel="nofollow noreferrer ugc"> https://docs.vanillaforums.com/developer/tools/building-frontend/#site-sections </a> </div><h2 data-id="custom-pages">Custom Pages</h2><p><em>This section is under construction.</em></p> </article> </main>