In Flarum SEO version 2, you can alter an objects SeoMeta
object.
What is a SeoMeta object?
A SeoMeta object is an object that can be used to transcribe objects in Flarum for SEO purposes, it can be a discussion, a post, a page, a blog-page, you name it.
The model has many properties, from title, descriptions, to even descriptions per OG/Twitter-tags. Usually, a model is managed
and updated automatically, but it can be customized as well.
Overriding properties
The following rule is applied: if a property is null
, it will automatically default to the original property.
For example, if a twitter_title
is defined, it will override the description
for that meta-tag. But if twitter_title
is null
, it will use the original description
value for the meta tag.
If none is set, it will revert automatically to the forums default meta-tags.
Example if only description
is defined:
<meta name="description" content="This is a demo description">
<meta name="twitter:description" content="This is a demo description">
<meta property="og:description" content="This is a demo description">
Example if the twitter_description
is defined:
<meta name="description" content="This is a demo description">
<meta name="twitter:description" content="This is a custom description for Twitter/X">
<meta property="og:description" content="This is a demo description">
This way you can customize your targets and meta-tags further.
Auto updated
MetaObjects are automatically updated, we it managed
. Updates are triggered in the background, for example when you update a discussions title, a discussion post or a tag. This information is pushed to the database so it doesn't need to recalculate your tags every time when you refresh a page.
Keywords is currently the only field that can be edited when in managed
mode. When you turn off Auto update meta tags
, you are able to customize all fields.
In case you manually updated the objects data, it will no longer be auto updated. If you want to re-enable auto-updating again, enable Auto update meta tags
. It will revert your changes and update them for you in the background from now on.
Images
An image is usually defined per content of the property, for example, the body of the first type. If Flarum SEO finds an image, the twitter_image
and open_graph_image
will be set. It can be customized per OG-tags and Twitter/X.
In case a different extension has set an image, the SEO extension does not override it. For this extension it is required to define the image_source
, then the SEO extension knows who is updating it. For example, the blog extension sets the image source
to v17development-flarum-blog
, this is visible to the user in the interface.
Important: The open_graph_image
is leading for both Twitter/X as Open Graph-tags. This means that if no Twitter/X value is set, it will show the Open Graph image. In case Twitter
is manually set, it will be used. If ONLY a twitter image is set, it will not create image tags (source).
Frontend
In your extensions webpack.config.js
, add v17development-seo
to the useExtensions
. It may look similar to:
const config = require('flarum-webpack-config');
module.exports = config({
useExtensions: ['fof-upload', 'v17development-seo'], // Example list
});
Now you are able to trigger the SeoMeta dialog.
Imporant: If you are writing an extension that can be used without Flarum SEO, be aware that you are required to wrap the following code arround an if
that prevents running in case the SEO extension is not enabled.
Important: It's important to verify if the user has the canConfigureSeo
permission, otherwise a request will be denied.
Querying by object Type and ID
One way to trigger the dialog, is by loading it's content by the objects type
and id
. The type can be anything,
// The 'discussion' variable is a Discussion model from the store
if ('v17development-seo' in flarum.extensions && app.forum.attribute('canConfigureSeo')) {
const {
components: { MetaSeoModal },
} = require('@v17development-seo');
// Example
app.modal.show(MetaSeoModal, {
objectType: 'discussions',
objectId: discussion.id(),
});
}
Query meta dialog by model
Another way is to load it by the objects 'seoMeta' relationship. For this to work, make sure to register the SeoMeta relationship in the frontend to the object you want.
// The 'discussion' variable is a Discussion model from the store
if ('v17development-seo' in flarum.extensions && app.forum.attribute('canConfigureSeo')) {
const {
components: { MetaSeoModal },
} = require('@v17development-seo');
// Example
app.modal.show(MetaSeoModal, {
object: discussion // Does automatically query for the .seoMeta relationship
});
}
⚠️ Important: Requires the seoMeta
relationship on the Model and loaded
Backend
The backend is built with listeners and trigger updates for the SeoMeta
object.
If you have your own model, you can
Registering to extend.php
Use the following code to optionally initialize the Flarum SEO extension.
Important: Do not use use V17Development\FlarumSeo\Extend\SEO
on top if your extend.php
! In case the user doesn't have the SEO extension installed, your extension will fail to load.
$extend = [
(new Extend\Frontend('forum'))
->js(__DIR__ . '/js/dist/forum.js')
->css(__DIR__ . '/less/Forum.less')
...the rest of your extension as an array...
];
$events = (new Extend\Event)
->listen(...other extension logic...);
// Extend Flarum SEO
if (class_exists("V17Development\FlarumSeo\Extend\SEO")) {
$extend[] = (new \V17Development\FlarumSeo\Extend\SEO())
->addExtender("blog_article", SeoBlogMeta::class); // Example for a blog article
// Add Blog subscriber event
$events->subscribe(SeoBlogSubscriber::class);
}
// Add events
$extend[] = $events;
return $extend;
Custom Subscriber
First it's time to set up your subscribers, do you want to update SEO tags on .
Example source to auto-update discussions: https://github.com/v17development/flarum-seo/blob/master/src/Subscribers/DiscussionSubscriber.php
Example source for the Flarum Blog extension: https://github.com/v17development/flarum-blog/blob/master/src/Subscribers/SeoBlogSubscriber.php
⚠️ Important: You must respect the auto_update_data
property. If it is set to false
, do NOT update the data.
⚠️ Important: You must respect the image_source
column and not override it if the content is not yours.
Routes
The SEO extension tries to load your tags on the correct page. However, you can add new classes that will manage meta tags and JSON for specific routes. You can use ->addExtender('your_extensions_purpose', SomeClassName::class)
.
The propper way is to create a SeoPage
folder and add the necesery pages which you want to manually manage, for example, a custom detail page. It's required to use the implements PageDriverInterface
(source)
The extension has defaults where tags are set. The name coresponds which is set in the addExtender
. Please also carefully review these examples:
Inside the PageDriverInterface
, you need to enter the route names where this class should trigger. These are the same routes as the Flarum frontend has.
// Example to trigger on 'discussion' and 'blog' route
public function handleRoutes(): array
{
return ['discussion', 'blog'];
}
Good example: Check the Flarum Blog extension, this is using the propper way.
Using $properties
in your PageDriverInterface
class
The page is populated with a SeoProperties $properties
which connects to the container using the tags on the page. We use it to actually update the page and meta-tags.
Read more about this here: https://community.v17.dev/knowledgebase/46
Extension dependencies
Some times you use logic that depends on a specific extension. The extensionDependencies
handles that for you. A great example is for the best-answer page, there we check if the tags extension is enabled (source).
public function extensionDependencies(): array
{
return ["flarum-tags"];
}
Find by model or create SeoMeta
You can use the following code to get or create an SeoMeta.
use V17Development\FlarumSeo\SeoMeta\SeoMeta;
// Get seo-meta
$seoMeta = SeoMeta::findByModelOrCreate(
$discussion
);
// Run events in case the model was created
$this->dispatchEventsFor($seoMeta); // Requires `use DispatchEventsTrait` and settings `Dispatcher $events`