Skip to content

Commit cc55042

Browse files
authored
Merge pull request #48 from Werkstattl/copilot/make-slug-editable
Make blog entry slug editable for SEO optimization
2 parents 99b1451 + 6cecd6d commit cc55042

File tree

18 files changed

+112
-25
lines changed

18 files changed

+112
-25
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "werkstattl/openblogware",
33
"description": "OpenBlogware: A Blog Module for Shopware 6.",
4-
"version": "5.0.2",
4+
"version": "5.1.0",
55
"type": "shopware-platform-plugin",
66
"keywords": ["blog", "news"],
77
"license":"MIT",

src/Content/Blog/BlogSeoUrlRoute.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
class BlogSeoUrlRoute implements SeoUrlRouteInterface
1414
{
1515
public const ROUTE_NAME = 'werkl.frontend.blog.detail';
16-
public const DEFAULT_TEMPLATE = 'blog/{{ entry.blogCategories.first.translated.name|lower }}/{{ entry.translated.title|lower }}';
16+
public const DEFAULT_TEMPLATE = 'blog/{{ entry.blogCategories.first.translated.name|lower }}/{{ entry.translated.slug|lower }}';
1717

1818
public function __construct(private readonly BlogEntryDefinition $blogEntryDefinition)
1919
{
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Werkl\OpenBlogware\Migration;
5+
6+
use Doctrine\DBAL\Connection;
7+
use Shopware\Core\Framework\Migration\MigrationStep;
8+
9+
class Migration1767122033UpdateSeoUrlTemplateToUseSlug extends MigrationStep
10+
{
11+
public function getCreationTimestamp(): int
12+
{
13+
return 1767122033;
14+
}
15+
16+
public function update(Connection $connection): void
17+
{
18+
// Update the SEO URL template to use slug instead of title
19+
// This handles both old and new template formats
20+
// This will cause all blog entry SEO URLs to be regenerated
21+
22+
// Update the newer template format (with categories)
23+
$connection->executeStatement(
24+
<<<SQL
25+
UPDATE `seo_url_template`
26+
SET `template` = 'blog/{{ entry.blogCategories.first.translated.name|lower }}/{{ entry.translated.slug|lower }}'
27+
WHERE `entity_name` = 'werkl_blog_entry'
28+
AND `template` = 'blog/{{ entry.blogCategories.first.translated.name|lower }}/{{ entry.translated.title|lower }}'
29+
SQL
30+
);
31+
32+
// Update the original template format (without categories)
33+
$connection->executeStatement(
34+
<<<SQL
35+
UPDATE `seo_url_template`
36+
SET `template` = 'blog/{{ entry.translated.slug|lower }}'
37+
WHERE `entity_name` = 'werkl_blog_entry'
38+
AND `template` = 'blog/{{ entry.translated.title|lower }}'
39+
SQL
40+
);
41+
}
42+
43+
public function updateDestructive(Connection $connection): void
44+
{
45+
// No destructive changes needed
46+
}
47+
}

src/Resources/app/administration/src/module/blog-module/extension/component/cms/werkl-cms-sidebar/werkl-cms-sidebar.html.twig

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
<mt-text-field
3333
v-model="blog.slug"
3434
required
35-
disabled
3635
:label="$tc('werkl-blog.detail.slugLabel')"
3736
:placeholder="$tc('werkl-blog.detail.slugPlaceholder')"
3837
:error="blogSlugError"

src/Resources/app/administration/src/module/blog-module/page/werkl-blog-detail/index.js

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ export default {
2121
localeLanguage: null,
2222
showSectionModal: false,
2323
sectionDontRemind: false,
24+
slugManuallyEdited: false,
25+
autoGeneratedSlug: null,
26+
isAutoGenerating: false,
2427
};
2528
},
2629

@@ -89,6 +92,22 @@ export default {
8992
'blog.title': function (blogTitle) {
9093
this.onBlogTitleChanged(blogTitle);
9194
},
95+
'blog.slug': function (newSlug) {
96+
// Only mark as manually edited if we're not currently auto-generating
97+
// For new blogs (autoGeneratedSlug is null), any slug value means manual edit
98+
// For existing blogs or after first auto-generation, check if different from auto-generated
99+
if (!this.isAutoGenerating) {
100+
if (this.autoGeneratedSlug === null || this.autoGeneratedSlug === undefined) {
101+
// New blog: if user types a slug before auto-generation, mark as manual
102+
if (newSlug) {
103+
this.slugManuallyEdited = true;
104+
}
105+
} else if (newSlug !== this.autoGeneratedSlug) {
106+
// Existing blog or after auto-generation: check if different
107+
this.slugManuallyEdited = true;
108+
}
109+
}
110+
},
92111
},
93112

94113
methods: {
@@ -151,6 +170,11 @@ export default {
151170
return this.blogRepository.get(blogId, Context.api, this.loadBlogCriteria).then((entity) => {
152171
this.blog = entity;
153172
this.originalSlug = entity.slug;
173+
// For existing blogs, mark slug as manually edited to prevent auto-updates
174+
// This ensures URL stability for published content
175+
this.slugManuallyEdited = true;
176+
// Initialize auto-generated slug with the current slug
177+
this.autoGeneratedSlug = entity.slug;
154178

155179
if (this.blog.translated.mediaId) {
156180
this.mediaRepository.get(this.blog.translated.mediaId).then((media) => {
@@ -389,7 +413,13 @@ export default {
389413

390414
this.page.name = blogTitle;
391415
this.getLocaleLanguage();
392-
this.generateSlug(blogTitle);
416+
417+
// Only auto-generate slug if:
418+
// 1. The slug is empty (new blog entry)
419+
// 2. OR the slug hasn't been manually edited (still matches auto-generated format)
420+
if (!this.blog.slug || !this.slugManuallyEdited) {
421+
this.generateSlug(blogTitle);
422+
}
393423
}, debounceTimeout),
394424

395425
addBlogError({
@@ -428,8 +458,13 @@ export default {
428458
lower: true,
429459
});
430460

461+
// Mark that we're auto-generating to avoid triggering manual edit detection
462+
this.isAutoGenerating = true;
463+
431464
if (!this.localeLanguage) {
432465
this.blog.slug = slug;
466+
this.autoGeneratedSlug = slug;
467+
this.isAutoGenerating = false;
433468
return;
434469
}
435470

@@ -445,8 +480,14 @@ export default {
445480
} else {
446481
this.blog.slug = slug;
447482
}
483+
484+
// After auto-generating, store this as the expected auto-generated slug
485+
this.autoGeneratedSlug = this.blog.slug;
486+
this.isAutoGenerating = false;
448487
}).catch(() => {
449488
this.blog.slug = slug;
489+
this.autoGeneratedSlug = this.blog.slug;
490+
this.isAutoGenerating = false;
450491
});
451492
},
452493

src/Resources/public/administration/.vite/entrypoints.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,19 +34,19 @@
3434
"/bundles/werklopenblogware/administration/assets/index-Nv1kM84q.js",
3535
"/bundles/werklopenblogware/administration/assets/index-djFVmhQq.js",
3636
"/bundles/werklopenblogware/administration/assets/index-CB3s9RZ7.js",
37-
"/bundles/werklopenblogware/administration/assets/index-RInXDG-m.js",
37+
"/bundles/werklopenblogware/administration/assets/index-DQF0lEx3.js",
3838
"/bundles/werklopenblogware/administration/assets/index-B1VMVOxB.js",
39-
"/bundles/werklopenblogware/administration/assets/index-CnzDf3uJ.js",
39+
"/bundles/werklopenblogware/administration/assets/index-DiCmnIhU.js",
4040
"/bundles/werklopenblogware/administration/assets/index-DLbe5nbw.js",
4141
"/bundles/werklopenblogware/administration/assets/index-DE9rQuyz.js",
4242
"/bundles/werklopenblogware/administration/assets/index-OQ9pEuOh.js",
4343
"/bundles/werklopenblogware/administration/assets/index-Dck_g9tK.js",
4444
"/bundles/werklopenblogware/administration/assets/index-C1ThOcnB.js",
45-
"/bundles/werklopenblogware/administration/assets/index-CI7M06HE.js",
45+
"/bundles/werklopenblogware/administration/assets/index-BmZACn-g.js",
4646
"/bundles/werklopenblogware/administration/assets/index-C293HHHL.js"
4747
],
4848
"js": [
49-
"/bundles/werklopenblogware/administration/assets/werkl-open-blogware-DEy1Vkif.js"
49+
"/bundles/werklopenblogware/administration/assets/werkl-open-blogware-DwBt8tES.js"
5050
],
5151
"legacy": false,
5252
"preload": []

src/Resources/public/administration/.vite/manifest.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"main.js": {
3-
"file": "assets/werkl-open-blogware-DEy1Vkif.js",
3+
"file": "assets/werkl-open-blogware-DwBt8tES.js",
44
"name": "werkl-open-blogware",
55
"src": "main.js",
66
"isEntry": true,
@@ -279,7 +279,7 @@
279279
]
280280
},
281281
"module/blog-module/extension/component/cms/werkl-cms-sidebar/index.js": {
282-
"file": "assets/index-RInXDG-m.js",
282+
"file": "assets/index-DQF0lEx3.js",
283283
"name": "index",
284284
"src": "module/blog-module/extension/component/cms/werkl-cms-sidebar/index.js",
285285
"isDynamicEntry": true,
@@ -297,7 +297,7 @@
297297
]
298298
},
299299
"module/blog-module/extension/sw-cms/component/sw-cms-sidebar/index.js": {
300-
"file": "assets/index-CnzDf3uJ.js",
300+
"file": "assets/index-DiCmnIhU.js",
301301
"name": "index",
302302
"src": "module/blog-module/extension/sw-cms/component/sw-cms-sidebar/index.js",
303303
"isDynamicEntry": true,
@@ -342,7 +342,7 @@
342342
"isDynamicEntry": true
343343
},
344344
"module/blog-module/page/werkl-blog-detail/index.js": {
345-
"file": "assets/index-CI7M06HE.js",
345+
"file": "assets/index-BmZACn-g.js",
346346
"name": "index",
347347
"src": "module/blog-module/page/werkl-blog-detail/index.js",
348348
"isDynamicEntry": true,

src/Resources/public/administration/assets/index-CI7M06HE.js renamed to src/Resources/public/administration/assets/index-BmZACn-g.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Resources/public/administration/assets/index-BmZACn-g.js.map

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Resources/public/administration/assets/index-CI7M06HE.js.map

Lines changed: 0 additions & 1 deletion
This file was deleted.

0 commit comments

Comments
 (0)