Skip to content

Commit d2af226

Browse files
committed
[BUGFIX] Improve example data creation and deleting
Releases: main
1 parent 42bef0c commit d2af226

File tree

4 files changed

+173
-31
lines changed

4 files changed

+173
-31
lines changed

Classes/Controller/BackendController.php

Lines changed: 81 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,13 @@
1919

2020
use Psr\Http\Message\ResponseInterface;
2121
use Psr\Http\Message\ServerRequestInterface;
22+
use T3docs\BlogExample\Domain\Model\Administrator;
2223
use T3docs\BlogExample\Domain\Model\Blog;
2324
use T3docs\BlogExample\Domain\Model\Post;
25+
use T3docs\BlogExample\Domain\Repository\AdministratorRepository;
2426
use T3docs\BlogExample\Domain\Repository\BlogRepository;
2527
use T3docs\BlogExample\Domain\Repository\CommentRepository;
28+
use T3docs\BlogExample\Domain\Repository\PersonRepository;
2629
use T3docs\BlogExample\Domain\Repository\PostRepository;
2730
use T3docs\BlogExample\Service\BlogFactory;
2831
use TYPO3\CMS\Backend\Template\Components\ButtonBar;
@@ -31,6 +34,7 @@
3134
use TYPO3\CMS\Backend\Template\ModuleTemplate;
3235
use TYPO3\CMS\Backend\Template\ModuleTemplateFactory;
3336
use TYPO3\CMS\Backend\Utility\BackendUtility;
37+
use TYPO3\CMS\Core\Domain\Repository\PageRepository;
3438
use TYPO3\CMS\Core\Imaging\IconFactory;
3539
use TYPO3\CMS\Core\Imaging\IconSize;
3640
use TYPO3\CMS\Core\Localization\LanguageService;
@@ -56,12 +60,22 @@ public function __construct(
5660
protected readonly BlogFactory $blogFactory,
5761
protected readonly PostRepository $postRepository,
5862
protected readonly CommentRepository $commentRepository,
63+
protected readonly PersonRepository $personRepository,
64+
protected readonly AdministratorRepository $administratorRepository,
5965
protected readonly ModuleTemplateFactory $moduleTemplateFactory,
6066
private readonly IconFactory $iconFactory,
6167
private readonly LanguageServiceFactory $languageServiceFactory,
6268
protected readonly ComponentFactory $componentFactory,
6369
) {}
6470

71+
/**
72+
* Function will be called before every other action
73+
*/
74+
protected function initializeAction(): void
75+
{
76+
$this->pageUid = (int)($this->request->getQueryParams()['id'] ?? 0);
77+
}
78+
6579
public function addPopulateButton(ButtonBar $buttonBar): void
6680
{
6781
$populateButton = $this->componentFactory->createLinkButton()
@@ -91,20 +105,19 @@ public function getMetaInformation(): array|false
91105
);
92106
}
93107

94-
/**
95-
* Function will be called before every other action
96-
*/
97-
protected function initializeAction(): void
98-
{
99-
$this->pageUid = (int)($this->request->getQueryParams()['id'] ?? 0);
100-
}
101-
102108
/**
103109
* Index action for this controller. Displays a list of blogs.
104110
*/
105111
public function indexAction(int $currentPage = 1): ResponseInterface
106112
{
107113
$view = $this->initializeModuleTemplate($this->request);
114+
if (!$this->isCurrentPageSysfolder()) {
115+
116+
$view->assignMultiple([
117+
'error' => true,
118+
]);
119+
return $view->renderResponse('Index');
120+
}
108121
$allAvailableBlogs = $this->blogRepository->findAll();
109122
$paginator = new QueryResultPaginator(
110123
$allAvailableBlogs,
@@ -124,18 +137,29 @@ public function indexAction(int $currentPage = 1): ResponseInterface
124137
}
125138

126139
/**
127-
* Deletes all blogs
140+
* Deletes all blog data from the current backend page
128141
*/
129142
public function deleteAllAction(): ResponseInterface
130143
{
144+
if (!$this->isCurrentPageSysfolder()) {
145+
return $this->redirect('index');
146+
}
147+
// Deleting a blog deletes the posts and comments by cascade delete
131148
$this->blogRepository->removeAll();
149+
// Persons and administrators must be deleted explicitly
150+
$this->personRepository->removeAll();
151+
$this->administratorRepository->removeAll();
132152
return $this->redirect('index');
133153
}
154+
134155
/**
135156
* Deletes a blog
136157
*/
137158
public function deleteBlogAction(Blog $blog): ResponseInterface
138159
{
160+
if (!$this->isCurrentPageSysfolder()) {
161+
return $this->redirect('index');
162+
}
139163
$this->blogRepository->remove($blog);
140164
$this->addFlashMessage(sprintf('Blog "%s" was deleted. ', $blog->title), 'Blog was deleted');
141165
return $this->redirect('index');
@@ -146,9 +170,22 @@ public function deleteBlogAction(Blog $blog): ResponseInterface
146170
*/
147171
public function populateAction(): ResponseInterface
148172
{
173+
if (!$this->isCurrentPageSysfolder()) {
174+
return $this->redirect('index');
175+
}
149176
$numberOfExistingBlogs = $this->blogRepository->countAll();
177+
178+
// fetch administrator if they exist
179+
$administrator = $this->administratorRepository->findOneBy(['email' => 'john.doe@example.com']);
180+
if ($administrator === null) {
181+
// create administrator
182+
$administrator = new Administrator();
183+
$administrator->name = 'John Doe';
184+
$administrator->email = 'john.doe@example.com';
185+
$this->administratorRepository->add($administrator);
186+
}
150187
for ($blogNumber = $numberOfExistingBlogs + 1; $blogNumber < ($numberOfExistingBlogs + 5); $blogNumber++) {
151-
$blog = $this->blogFactory->createBlog($blogNumber);
188+
$blog = $this->blogFactory->createBlog($administrator, $blogNumber, new \DateTimeImmutable());
152189
$this->blogRepository->add($blog);
153190
}
154191
$this->addFlashMessage('populated');
@@ -163,6 +200,9 @@ public function showBlogAction(
163200
string $tag = '',
164201
int $currentPage = 1,
165202
): ResponseInterface {
203+
if (!$this->isCurrentPageSysfolder()) {
204+
return $this->redirect('index');
205+
}
166206
$view = $this->initializeModuleTemplate($this->request);
167207
if ($blog === null) {
168208
$this->addFlashMessage('Blog was not found', 'Warning', ContextualFeedbackSeverity::WARNING);
@@ -193,13 +233,19 @@ public function showBlogAction(
193233
*/
194234
public function showPostAction(Post $post): ResponseInterface
195235
{
236+
if (!$this->isCurrentPageSysfolder()) {
237+
return $this->redirect('index');
238+
}
196239
$view = $this->initializeModuleTemplate($this->request);
197240
$view->assign('post', $post);
198241
return $view->renderResponse('ShowPost');
199242
}
200243

201244
public function showAllCommentsAction(): ResponseInterface
202245
{
246+
if (!$this->isCurrentPageSysfolder()) {
247+
return $this->redirect('index');
248+
}
203249
$view = $this->initializeModuleTemplate($this->request);
204250
$comments = $this->commentRepository->findAll();
205251
$view->assign('comments', $comments);
@@ -300,4 +346,29 @@ private function buildMenu(ModuleTemplate $view, string &$context): Menu
300346
}
301347
return $menu;
302348
}
349+
350+
protected function isCurrentPageSysfolder(): bool
351+
{
352+
if ($this->pageUid <= 0) {
353+
$this->addFlashMessage(
354+
'Please open the module on a storage folder (sysfolder).',
355+
'Missing page context',
356+
ContextualFeedbackSeverity::ERROR,
357+
);
358+
return false;
359+
}
360+
361+
$page = BackendUtility::readPageAccess($this->pageUid, $GLOBALS['BE_USER']->getPagePermsClause(1));
362+
363+
// sysfolder check
364+
if (!is_array($page) || (int)$page['doktype'] !== PageRepository::DOKTYPE_SYSFOLDER) {
365+
$this->addFlashMessage(
366+
'This module must be used on a storage folder (sysfolder). Please select a folder in the page tree.',
367+
'Wrong page type',
368+
ContextualFeedbackSeverity::ERROR,
369+
);
370+
return false;
371+
}
372+
return true;
373+
}
303374
}

Classes/Controller/BlogController.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ public function populateAction(): ResponseInterface
174174
$this->checkBlogAdminAccess();
175175
$numberOfExistingBlogs = $this->blogRepository->countAll();
176176
for ($blogNumber = $numberOfExistingBlogs + 1; $blogNumber < ($numberOfExistingBlogs + 5); $blogNumber++) {
177-
$blog = $this->blogFactory->createBlog($blogNumber);
177+
$blog = $this->blogFactory->createBlog(null, $blogNumber, new \DateTimeImmutable());
178178
$this->blogRepository->add($blog);
179179
}
180180
$this->addFlashMessage('populated');

Classes/Service/BlogFactory.php

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@
77
use T3docs\BlogExample\Domain\Model\Administrator;
88
use T3docs\BlogExample\Domain\Model\Blog;
99
use T3docs\BlogExample\Domain\Model\Comment;
10-
use T3docs\BlogExample\Domain\Model\Person;
1110
use T3docs\BlogExample\Domain\Model\Post;
12-
use TYPO3\CMS\Core\SingletonInterface;
1311

1412
/*
1513
* This file is part of the TYPO3 CMS project.
@@ -27,51 +25,50 @@
2725
/**
2826
* A simple blog factory to create sample data
2927
*/
30-
class BlogFactory implements SingletonInterface
28+
readonly class BlogFactory
3129
{
3230
/**
3331
* Returns a sample blog populated with generic data
3432
* It is also an example how to handle objects and repositories in general
35-
*
36-
*
37-
* @return Blog
3833
*/
39-
public function createBlog(int $blogNumber = 1): Blog
34+
public function createBlog(?Administrator $administrator = null, int $blogNumber = 1, ?\DateTimeImmutable $baseDate = null): Blog
4035
{
36+
$baseDate ??= new \DateTimeImmutable('2026-01-01 12:00:00');
4137
// initialize blog
4238
$blog = new Blog();
4339
$blog->setTitle('Blog #' . $blogNumber);
4440
$blog->description = 'A blog about TYPO3 extension development.';
4541

46-
// create author
47-
$author = new Person('Stephen', 'Smith', 'foo.bar@example.com');
48-
49-
// create administrator
50-
$administrator = new Administrator();
51-
$administrator->name = 'John Doe';
52-
$administrator->email = 'john.doe@example.com';
53-
$blog->administrator = $administrator;
42+
if ($administrator !== null) {
43+
$blog->administrator = $administrator;
44+
}
5445

5546
// create sample posts
5647
for ($postNumber = 1; $postNumber < 6; $postNumber++) {
5748
// create post
5849
$post = new Post();
5950
$post->setTitle('The ' . $postNumber . '. post of blog #' . $blogNumber);
60-
$post->setAuthor($author);
6151
$post->setContent('Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.');
6252

6353
// create comments
6454
$comment = new Comment();
65-
$comment->setDate(new \DateTime());
55+
56+
$comment->setDate(\DateTime::createFromImmutable(
57+
$baseDate->modify(sprintf('+%d days', $postNumber)),
58+
));
6659
$comment->setAuthor('Peter Pan');
67-
$comment->setEmail('peter.pan@example.com');
60+
$comment->setEmail(sprintf('peter.pan%d@example.com', $blogNumber));
6861
$comment->setContent('Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.');
62+
$comment->setHidden(false);
6963
$post->addComment($comment);
7064

7165
$comment = new Comment();
72-
$comment->setDate(new \DateTime('2009-03-19 23:44'));
66+
$comment->setDate(\DateTime::createFromImmutable(
67+
$baseDate->modify(sprintf('+%d days', $postNumber + 1)),
68+
));
7369
$comment->setAuthor('John Smith');
74-
$comment->setEmail('john@matrix.org');
70+
$comment->setEmail(sprintf('john.smith%d@example.com', $blogNumber));
71+
$comment->setHidden(false);
7572
$comment->setContent('Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.');
7673
$post->addComment($comment);
7774

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace T3docs\BlogExample\Tests\Service;
6+
7+
/*
8+
* This file is part of the TYPO3 CMS project.
9+
*
10+
* It is free software; you can redistribute it and/or modify it under
11+
* the terms of the GNU General Public License, either version 2
12+
* of the License, or any later version.
13+
*
14+
* For the full copyright and license information, please read the
15+
* LICENSE.txt file that was distributed with this source code.
16+
*
17+
* The TYPO3 project - inspiring people to share!
18+
*/
19+
20+
use PHPUnit\Framework\Attributes\Test;
21+
use T3docs\BlogExample\Domain\Model\Administrator;
22+
use T3docs\BlogExample\Service\BlogFactory;
23+
use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
24+
25+
final class BlogFactoryTest extends FunctionalTestCase
26+
{
27+
protected array $testExtensionsToLoad = [
28+
't3docs/blog-example',
29+
];
30+
31+
#[Test]
32+
public function createBlogIsDeterministicAndUniquePerBlogNumber(): void
33+
{
34+
$factory = new BlogFactory();
35+
36+
$administrator = new Administrator();
37+
$administrator->name = 'John Doe';
38+
$administrator->email = 'john.doe@example.com';
39+
40+
$baseDate = new \DateTimeImmutable('2026-01-01 12:00:00');
41+
42+
$blog1 = $factory->createBlog($administrator, 1, $baseDate);
43+
$blog2 = $factory->createBlog($administrator, 2, $baseDate);
44+
45+
self::assertSame('Blog #1', $blog1->getTitle());
46+
self::assertSame('Blog #2', $blog2->getTitle());
47+
48+
// Administrator is the same object on both blogs (by design)
49+
self::assertSame('john.doe@example.com', $blog1->administrator?->email);
50+
self::assertSame('john.doe@example.com', $blog2->administrator?->email);
51+
52+
// Safer: rewind storages before current()
53+
$posts1 = $blog1->getPosts();
54+
$posts1->rewind();
55+
$post1Blog1 = $posts1->current();
56+
57+
$posts2 = $blog2->getPosts();
58+
$posts2->rewind();
59+
$post1Blog2 = $posts2->current();
60+
61+
$comments1 = $post1Blog1->getComments();
62+
$comments1->rewind();
63+
$firstComment1 = $comments1->current();
64+
65+
$comments2 = $post1Blog2->getComments();
66+
$comments2->rewind();
67+
$firstComment2 = $comments2->current();
68+
69+
self::assertNotSame($firstComment1->getEmail(), $firstComment2->getEmail());
70+
71+
$firstCommentDate = $firstComment1->getDate();
72+
self::assertSame('2026-01-02 12:00:00', $firstCommentDate->format('Y-m-d H:i:s'));
73+
}
74+
}

0 commit comments

Comments
 (0)