Justoverclock here's a working example.
Unfortunately the $document->content
property which would be the best place to add the HTML cannot actually be used in this way. The problem is not really in the way it works, it's a priority issue. Because a route's dedicated content class runs last, and sets ->content
to a string, we can't append to the string since our code runs before.
Maybe we should refactor $document->content
into an array so that it works similarly to ->head
, ->foot
, etc. But the priority issue is also a problem because it prevents extensions from overriding the content entirely or changing other properties like ->canonicalUrl
.
This example adds the content into a separate <noscript>
tag at the very bottom of the page. I think ->foot
should work just as well as ->content
from an SEO standpoint, both are at the same level of the DOM anyway.
For larger bits of HTML, it would be a good idea to resolve Illuminate\Contracts\View\Factory
and then use $factory->make('view.prefix::view.name', ['variable' => 'value'])
to generate the HTML from a blade view.
The biggest downside of the content extender right now is that there's no way to access the model object for the page without making a new database request, which incurs a performance impact. It might be possible to skip the request in some circumstances by looking at the serialized data in $document->getForumApiDocument()
but due to priority the necessary data probably won't be in there at the time it's needed, and proper access control must still be taken into account to make sure you're not retrieving objects from the database the user was not supposed to see.
extend.php
:
(new Extend\Frontend('forum'))
// maybe some existing ->js() and ->css() calls here
->content(Content\RelatedDiscussion::class),
src/Content/RelatedDiscussion.php
:
<?php
namespace MyDeveloper\MyExtension\Content;
use Flarum\Discussion\Discussion;
use Flarum\Frontend\Document;
use Flarum\Http\RequestUtil;
use Flarum\Http\SlugManager;
use Flarum\Http\UrlGenerator;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Support\Arr;
use Psr\Http\Message\ServerRequestInterface;
class RelatedDiscussion
{
protected $slugManager;
protected $url;
public function __construct(SlugManager $slugManager, UrlGenerator $url)
{
$this->slugManager = $slugManager;
$this->url = $url;
}
public function __invoke(Document $document, ServerRequestInterface $request): void
{
// If this is not the "show discussion" route, skip
if ($request->getAttribute('routeName') !== 'discussion') {
return;
}
$actor = RequestUtil::getActor($request);
try {
// Retrieve discussion from the ID in the URL
$discussion = $this->slugManager->forResource(Discussion::class)->fromSlug(
Arr::get($request->getQueryParams(), 'id'),
$actor
);
} catch (ModelNotFoundException $exception) {
// If the discussion is a 404 error, skip
return;
}
// For this example, the related discussion will be the last discussion posted before the current one that the actor can see
$relatedDiscussion = Discussion::whereVisibleTo($actor)
->where('created_at', '<', $discussion->created_at)
->orderBy('created_at', 'desc')
->first();
// If we couldn't find any "related" discussion, skip
if (!$relatedDiscussion) {
return;
}
// Use e() to escape the HTML! When using a blade template, it's done automatically by {{ }}
$href = e($this->url->to('forum')->route('discussion', [
'id' => $this->slugManager->forResource(Discussion::class)->toSlug($relatedDiscussion),
]));
$label = e($relatedDiscussion->title);
$document->foot[] = <<<HTML
<noscript>
<p>We think you'll also like <a href="$href">$label</a>!</p>
</noscript>
HTML;
}
}