Skip to content

Commit b476286

Browse files
committed
wip
1 parent ea5958e commit b476286

File tree

4 files changed

+157
-13
lines changed

4 files changed

+157
-13
lines changed

app/Front/Blog/BlogController.php

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Tempest\Cache\Cache;
77
use Tempest\Router\Get;
88
use Tempest\Router\Response;
9+
use Tempest\Router\Responses\NotFound;
910
use Tempest\Router\Responses\Ok;
1011
use Tempest\Router\StaticPage;
1112
use Tempest\Support\ArrayHelper;
@@ -26,22 +27,27 @@ public function index(BlogRepository $repository): View
2627

2728
#[Get('/blog/{slug}')]
2829
#[StaticPage(BlogDataProvider::class)]
29-
public function show(string $slug, BlogRepository $repository): View
30+
public function show(string $slug, BlogRepository $repository): Response|View
3031
{
3132
$post = $repository->find($slug);
3233

34+
if (! $post || ! $post->published) {
35+
return new NotFound();
36+
}
37+
3338
return view(__DIR__ . '/blog_show.view.php', post: $post);
3439
}
3540

3641
#[Get('/rss')]
3742
public function rss(
3843
Cache $cache,
39-
BlogRepository $repository
40-
): Response {
44+
BlogRepository $repository,
45+
): Response
46+
{
4147
$xml = $cache->resolve(
4248
key: 'rss',
4349
cache: fn () => $this->renderRssFeed($repository->all(loadContent: true)),
44-
expiresAt: new DateTimeImmutable('+1 hour')
50+
expiresAt: new DateTimeImmutable('+1 hour'),
4551
);
4652

4753
return (new Ok($xml))

app/Front/Blog/BlogPost.php

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,13 @@
77

88
final class BlogPost
99
{
10-
public function __construct(
11-
public string $slug,
12-
public string $title,
13-
public string $author,
14-
public string $content,
15-
public DateTimeImmutable $createdAt,
16-
public ?string $description = null,
17-
) {}
10+
public string $slug;
11+
public string $title;
12+
public string $author;
13+
public string $content;
14+
public DateTimeImmutable $createdAt;
15+
public ?string $description = null;
16+
public bool $published = true;
1817

1918
public function getUri(): string
2019
{

app/Front/Blog/BlogRepository.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ public function all(bool $loadContent = false): ArrayHelper
4545

4646
return $data;
4747
})
48-
->mapTo(BlogPost::class);
48+
->mapTo(BlogPost::class)
49+
->filter(fn (BlogPost $post) => $post->published);
4950
}
5051

5152
public function find(string $slug): ?BlogPost
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
---
2+
title: Static Websites with Tempest
3+
description: Tempest makes it super convenient to convert any controller action in statically generated pages
4+
author: Brent
5+
---
6+
7+
Let's say you have a controller that shows blog posts — kind of like the page you're reading now:
8+
9+
```php
10+
final readonly class BlogController
11+
{
12+
#[Get('/blog')]
13+
public function index(BlogRepository $repository): View
14+
{
15+
$posts = $repository->all();
16+
17+
return view(__DIR__ . '/blog_index.view.php', posts: $posts);
18+
}
19+
20+
#[Get('/blog/{slug}')]
21+
public function show(string $slug, BlogRepository $repository): Response|View
22+
{
23+
$post = $repository->find($slug);
24+
25+
return view(__DIR__ . '/blog_show.view.php', post: $post);
26+
}
27+
}
28+
```
29+
30+
These type of web pages are abundant: they show content that doesn't change based on the user viewing it — static content. Come to think of it, it's kind of inefficient having to boot a whole PHP framework to render exactly the same HTML over and over again with every request.
31+
32+
However, instead of messing around with complex caches in front of dynamic websites, what if you could mark a controller action as a "static page", and be done? That's exactly what Tempest allows you to do:
33+
34+
35+
```php
36+
use Tempest\Router\StaticPage;
37+
38+
final readonly class BlogController
39+
{
40+
#[StaticPage]
41+
#[Get('/blog')]
42+
public function index(BlogRepository $repository): View
43+
{
44+
$posts = $repository->all();
45+
46+
return view(__DIR__ . '/blog_index.view.php', posts: $posts);
47+
}
48+
49+
// …
50+
}
51+
```
52+
53+
And… that's it! Now you only need to run `{console}tempest static:generate`, and Tempest will convert all controller actions marked with `#[StaticPage]` to static HTML pages:
54+
55+
```console
56+
~ tempest static:generate
57+
58+
- <u>/blog</u> > <u>/web/tempestphp.com/public/blog/index.html</u>
59+
60+
<success>Done</success>
61+
```
62+
63+
Hold on though… that's all fine for a page like `/blog`, but what about `/blog/{slug}` where you have multiple variants of the same static page based on the blog post's slug?
64+
65+
Well for static pages that rely on data, you'll have to take one more step: use a data provider to let Tempest know what variants of that page are available:
66+
67+
```php
68+
use Tempest\Router\StaticPage;
69+
70+
final readonly class BlogController
71+
{
72+
// …
73+
74+
#[StaticPage(BlogDataProvider::class)]
75+
#[Get('/blog/{slug}')]
76+
public function show(string $slug, BlogRepository $repository): Response|View
77+
{
78+
// …
79+
}
80+
}
81+
```
82+
83+
The task of such a data provider is to supply Tempest with an array of strings for every variable required on this page. Here's what it looks like:
84+
85+
```php
86+
use Tempest\Router\DataProvider;
87+
88+
final readonly class BlogDataProvider implements DataProvider
89+
{
90+
public function __construct(
91+
private BlogRepository $repository,
92+
) {}
93+
94+
public function provide(): Generator
95+
{
96+
foreach ($this->repository->all() as $post) {
97+
yield ['slug' => $post->slug];
98+
}
99+
}
100+
}
101+
```
102+
103+
With that in place, let's rerun `{console}tempest static:generate`:
104+
105+
```console
106+
~ tempest static:generate
107+
108+
- <u>/blog</u> > <u>/web/tempestphp.com/public/blog/index.html</u>
109+
- <u>/blog/exit-codes-fallacy</u> > <u>/web/tempestphp.com/public/blog/exit-codes-fallacy/index.html</u>
110+
- <u>/blog/unfair-advantage</u> > <u>/web/tempestphp.com/public/blog/unfair-advantage/index.html</u>
111+
- <u>/blog/alpha-2</u> > <u>/web/tempestphp.com/public/blog/alpha-2/index.html</u>
112+
<comment>…</comment>
113+
- <u>/blog/alpha-5</u> > <u>/web/tempestphp.com/public/blog/alpha-5/index.html</u>
114+
- <u>/blog/static-websites-with-tempest</u> > <u>/web/tempestphp.com/public/blog/static-websites-with-tempest/index.html</u>
115+
116+
<success>Done</success>
117+
```
118+
119+
And we're done! All static pages are now available as static HTML pages that will be served by your webserver directly instead of having to boot Tempest. Also note that tempest generates `index.html` files within directories, so most webservers can serve these static pages directly without any additional server configuration required.
120+
121+
On a final note, you can always clean up the generated HTML files by running `{console}tempest static:clean`:
122+
123+
124+
```console
125+
~ tempest static:clean
126+
127+
- <u>/web/tempestphp.com/public/blog</u> directory removed
128+
- <u>/web/tempestphp.com/public/blog/exit-codes-fallacy</u> directory removed
129+
- <u>/web/tempestphp.com/public/blog/unfair-advantage</u> directory removed
130+
- <u>/web/tempestphp.com/public/blog/alpha-2</u> directory removed
131+
<comment>…</comment>
132+
- <u>/web/tempestphp.com/public/blog/alpha-5</u> directory removed
133+
- <u>/web/tempestphp.com/public/blog/static-websites-with-tempest</u> directory removed
134+
135+
<success>Done</success>
136+
```
137+
138+
It's a pretty cool feature that requires minimal effort, but will have a huge impact on your website's performance. If you want more insights into Tempest's static pages, you can head over to [the docs](/docs/framework/static-pages) to learn more.

0 commit comments

Comments
 (0)