SEO
Introduction
The seo package allows for the management and injection of dynamic data into HTML tags for the purpose of Search Engine Optimization.
Create a SEO builder
Create a builder and register global seo by default.
db
is an instance of gorm.DBbuilder := seo.NewBuilder(db)
The default global seo name is
Global SEO
, if you want to customize the name, passing the name throughWithGlobalSEOName
option to theNewBuilder
function.builder := seo.NewBuilder(db, seo.WithGlobalSEOName("My Global SEO"))
Turn off the default inherit the upper level SEO data when the current SEO data is missing
builder := seo.NewBuilder(db, seo.WithInherited(false))
The seo package supports localization. you can pass the localization code you want to support through the
WithLocales
option to theNewBuilder
function. if no localization is set, it defaults to empty.builder := seo.NewBuilder(db, seo.WithLocales('zh', 'en', 'jp'))
You can pass multiple options at once
builder := seo.NewBuilder(db, seo.WithLocales('zh'), seo.WithInherited(false), seo.WithGlobalSEOName("My Global SEO"))
Register and remove SEO
All registered SEO names are unique, If you have already registered a SEO named Test
, attempting to register SEO with the same name Test
will cause the program to panic.
The second parameter named model
in the RegisterSEO(name string, model ...interface{})
method is an
instance of a type that has a field of type Setting
. If you pass a model
whose type
does not have such a field, the program will panic.
For Example:
builder.RegisterSEO("Test")
type Test struct {
...
// doesn't have a field of type Setting
}
builder.RegisterSEO("Test", &Test{}) // will panic
There are two types of SEOs, one is SEO with model, the other is SEO without model. if you want to register a no model SEO, you can call RegisterSEO method like this:
seoBuilder.RegisterSEO("About Us")
if you want to register a SEO with model, you can call RegisterSEO method like this:
seoBuilder.RegisterSEO("Product", &Product{})
- Remove a SEO
NOTE: The global seo cannot be removed
builder.RemoveSEO(&Product{}).RemoveSEO("Not Found")
When you remove an SEO, the new parent of its child SEO becomes the parent of the seo.
- The seo supports hierarchy, and you can use the
SetParent
orAppendChild
methods ofSEO
to configure the hierarchy.
With AppendChildren
method
builder.RegisterSEO("A").AppendChildren(
builder.RegisterSEO("B"),
builder.RegisterSEO("C"),
builder.RegisterSEO("D"),
)
With SetParent
Method
seoA := builder.RegisterSEO("A")
builder.RegisterSEO("B").SetParent(seoA)
builder.RegisterSEO("C").SetParent(seoA)
builder.RegisterSEO("D").SetParent(seoA)
The final seo structure is as follows:
Global SEO
├── A
├── B
└── C
└── D
Configure SEO
Register setting variables
builder := NewBuilder(dbForTest) builder.RegisterSEO(&Product{}). RegisterSettingVariables("Type", "Place of Origin")
Register context variables
seoBuilder = seo.NewBuilder(db) seoBuilder.RegisterSEO(&models.Post{}). RegisterContextVariable( "Title", func(object interface{}, _ *seo.Setting, _ *http.Request) string { if article, ok := object.(models.Post); ok { return article.Title } return "" }, )
Register Meta Property
seoBuilder = seo.NewBuilder(db) seoBuilder.RegisterSEO(&models.Post{}). RegisterMetaProperty( "og:audio", func(object interface{}, _ *seo.Setting, _ *http.Request) string { if article, ok := object.(models.Post); ok { return article.audio } return "" }, )
Render SEO html data
Render a single seo
Render a single seo with model
To call Render(obj interface{}, req *http.Request)
for rendering a single seo.
NOTE: obj
must be of type *NameObj
or a pointer to a struct that has a field of type Setting
.
type Post struct {
Title string
Author string
Content string
SEO Setting
}
post := &Post{
Title: "TestRender",
Author: "iBakuman",
Content: "Hello, Qor5 SEO",
SEO: Setting{
Title: "{.{Title}}",
Description: "post for testing",
EnabledCustomize: true,
},
}
builder := NewBuilder(dbForTest)
builder.RegisterSEO(&Post{}).RegisterContextVariable(
"Title",
func(post interface{}, _ *Setting, _ *http.Request) string {
if p, ok := post.(*Post); ok {
return p.Title
}
return "No title"
},
).RegisterMetaProperty("og:title",
func(post interface{}, _ *Setting, _ *http.Request) string {
return "Title for og:title"
},
)
defaultReq, _ := http.NewRequest("POST", "http://www.demo.qor5.com", nil)
res, err := builder.Render(post, defaultReq).MarshalHTML(context.TODO())
if err != nil {
panic(err)
}
fmt.Println(string(res))
The output of the above code is as follows:
<title>TestRender</title>
<meta name='description' content='post for testing'>
<meta name='keywords'>
<meta property='og:description' name='og:description'>
<meta property='og:url' name='og:url'>
<meta property='og:type' name='og:type' content='website'>
<meta property='og:image' name='og:image'>
<meta property='og:title' name='og:title' content='Title for og:title'>
Render a single seo without model
seoBuilder.Render(&NameObj{"About US", Locale: "en"})
Render multiple SEOs at once
Render multiple SEOs with model
To call BatchRender(objs []interface, req *http.Request)
for batch rendering.
NOTE: You need to ensure that all elements in objs
are of the same type.
BatchRender
does not check if all elements in objs
are of the same type, as this can be performance-intensive. Therefore,
it is the responsibility of the caller to ensure that all elements in objs
are of the same type.
If you pass objs
with various types of SEO, it will only take the type of the first element as the standard to obtain the SEO configuration used in the rendering process.
type Post struct {
Title string
Author string
Content string
SEO Setting
l10n.Locale
}
posts := []interface{}{
&Post{
Title: "TestRenderA",
Author: "iBakuman",
Content: "Hello, Qor5 SEO",
SEO: Setting{
Title: "{.{Title}}",
Description: "postA for testing",
EnabledCustomize: true,
},
},
&Post{
Title: "TestB",
Author: "iBakuman",
Content: "Hello, Qor5 SEO",
SEO: Setting{
Title: "{.{Title}}",
Description: "postB for testing",
EnabledCustomize: true,
},
},
}
builder := NewBuilder(dbForTest, WithLocales("en"))
builder.RegisterSEO(&Post{}).RegisterContextVariable(
"Title",
func(post interface{}, _ *Setting, _ *http.Request) string {
if p, ok := post.(*Post); ok {
return p.Title
}
return "No title"
},
).RegisterMetaProperty("og:title",
func(post interface{}, _ *Setting, _ *http.Request) string {
return "Title for og:title"
},
)
defaultReq, _ := http.NewRequest("POST", "http://www.demo.qor5.com", nil)
SEOs := builder.BatchRender(posts, defaultReq)
for _, seo := range SEOs {
html, _ := seo.MarshalHTML(context.TODO())
fmt.Println(string(html))
fmt.Println("----------------------------")
}
The output of the above code is as follows:
<title>TestRenderA</title>
<meta name='description' content='postA for testing'>
<meta name='keywords'>
<meta property='og:description' name='og:description'>
<meta property='og:url' name='og:url'>
<meta property='og:type' name='og:type' content='website'>
<meta property='og:image' name='og:image'>
<meta property='og:title' name='og:title' content='Title for og:title'>
----------------------------
<title>TestB</title>
<meta name='description' content='postB for testing'>
<meta name='keywords'>
<meta property='og:url' name='og:url'>
<meta property='og:type' name='og:type' content='website'>
<meta property='og:image' name='og:image'>
<meta property='og:title' name='og:title' content='Title for og:title'>
<meta property='og:description' name='og:description'>
----------------------------
Render multiple SEOs without model
seoBuilder.BatchRender(NewNonModelSEOSlice("Product", "en", "zh"))