Skip to content

Commit 5111e8b

Browse files
committed
Merge branch 'beta-1'
2 parents 45ca709 + da20331 commit 5111e8b

File tree

8 files changed

+248
-15
lines changed

8 files changed

+248
-15
lines changed

composer.lock

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
---
2+
title: Tempest is Beta
3+
description: |
4+
Today we release the first beta version of Tempest, the PHP framework for web and console apps that gets out of your way. It's one of the final steps towards a stable 1.0 release. We'll use this beta phase to fix bugs, and we're committed to not making any breaking changes anymore, apart from experimental features.
5+
author: brent
6+
tag: Release
7+
---
8+
9+
Two years ago, Tempest started out as an educational project during one of my livestreams. Since then, we've had 56 people contribute to the framework, merged 591 pull requests, resolved 455 issues, and have written around 50k lines of code. Two contributors joined the core team and dedicated a lot of their time to help make Tempest into something real. And today, we're tagging Tempest as beta.
10+
11+
We have to be real though: we won't get it perfect from the start. Tempest is now in beta, which means we don't plan any breaking changes to stable components anymore, but it also means we expect there to be bugs. And this puts us in an underdog position: why would anyone want to use a framework that has fewer features and likely more bugs than other frameworks?
12+
13+
It turns out, people _do_ see value in Tempest. It's the only reason I decided to work on it in the first place: there is a group of people who _want_ to use it, even when they are aware of its current shortcomings. There is interest in a framework that embraces modern PHP without 10 to 20 years of legacy to carry with it. There is interest in a project that dares to rethink what we've gotten used to over the years. There already is a dedicated community. People already are building with Tempest. Several core members have real use cases for Tempest and are working hard to be able to use it in their own projects as soon as possible. So while Tempest is the underdog, there already seems enough reason for people to use it today.
14+
15+
And I don't want Tempest to remain the underdog. Getting closer to that goal requires getting more people involved. We need hackers to build websites and console applications with Tempest, we need them to run into bugs and edge cases that we haven't thought of. We need entrepreneurs to look into third-party packages, we need to learn what should be improved on our side from their experience. We need you to be involved. That's the next step for Tempest.
16+
17+
Our commitment to you is that we're doing all we can to make Tempest the best developer experience possible. Tempest is and must stay the framework that truly gets out of your way. You need to focus on your code, not on hand-holding and guiding the framework. We're still uncertain about a handful of features and have clearly marked them as [experimental](/main/extra-topics/roadmap), with tried and tested alternatives in place. We're committed to a period of bug fixing to make sure Tempest can be trusted when we release the 1.0 version.
18+
19+
We're committed, and I hope you're intrigued to [give Tempest a go](/main/getting-started/introduction).
20+
21+
```
22+
{:hl-keyword:composer:} create-project:} tempest/app:1.0-beta.1 <name>
23+
```
24+
25+
All of that being said, let's look at what's new in this first beta release!
26+
27+
## A truly decoupled ORM
28+
29+
A long-standing issue within Tempest was our ORM: the goal of our model classes was to be truly disconnected from the database, but they weren't really. That's changed in beta.1, where we removed the `DatabaseModel` interface. Any object with typed public properties can now be considered "a model class" by the ORM:
30+
31+
```php
32+
use Tempest\Validation\Rules\Length;
33+
use App\Author;
34+
35+
final class Book
36+
{
37+
#[Length(min: 1, max: 120)]
38+
public string $title;
39+
40+
public ?Author $author = null;
41+
42+
/** @var \App\Chapter[] */
43+
public array $chapters = [];
44+
}
45+
```
46+
47+
Now that these model objects aren't tied to the database, they can receive and persistent their data from anywhere, not just a database:
48+
49+
```php
50+
use function Tempest\map;
51+
52+
$books = map($json)->collection()->to(Book::class);
53+
54+
$json = map($books)->toJson();
55+
```
56+
57+
We did decide to keep the `IsDatabaseModel` trait still, because we reckon database persistence is a very common use case:
58+
59+
```php
60+
use Tempest\Database\IsDatabaseModel;
61+
62+
final class Book
63+
{
64+
use IsDatabaseModel;
65+
66+
// …
67+
}
68+
69+
$book = Book::create(
70+
title: 'Timeline Taxi',
71+
author: $author,
72+
chapters: [
73+
new Chapter(index: 1, contents: '…'),
74+
new Chapter(index: 2, contents: '…'),
75+
new Chapter(index: 3, contents: '…'),
76+
],
77+
);
78+
79+
$books = Book::select()
80+
->where('publishedAt > ?', new DateTimeImmutable())
81+
->orderBy('title DESC')
82+
->limit(10)
83+
->with('author')
84+
->all();
85+
86+
$books[0]->chapters[2]->delete();
87+
```
88+
89+
However, we also added a new `query()` helper function that can be used instead of the `IsDatabaseModel` trait.
90+
91+
```php
92+
$data = query(Book::class)
93+
->select('title', 'index')
94+
->where('title = ?', 'Timeline Taxi')
95+
->andWhere('index <> ?', '1')
96+
->orderBy('index ASC')
97+
->all();
98+
```
99+
100+
We've managed to truly decouple model classes from the persistence layer, while still making them really convenient to use. This is a great example of how Tempest gets out of your way.
101+
102+
An important note to make here is that our ORM is one of the few experimental components within Tempest. We acknowledge that there's more work to be done to make it even better, and there might be some future breaking changes still. It's one of the prime examples where we need the community to help us learn what should be improved, and how.
103+
104+
## tempest/view changes
105+
106+
We've added support for [dynamic view components](/main/essentials/views#dynamic-view-components), which allows you to render view components based on runtime data:
107+
108+
```html
109+
<!-- $name = 'x-post' -->
110+
111+
<x-component :is="$name" :title="$title" />
112+
```
113+
114+
We've improved [boolean attributes](/main/essentials/views#boolean-attributes), they now also work for truthy and falsy values, as well as for custom expression attributes:
115+
116+
```html
117+
<div :data-active="{$isActive}"></div>
118+
119+
<!-- <div></div> when $isActive is falsy -->
120+
<!-- <div data-active></div> when $isActive is truthy -->
121+
```
122+
123+
Finally, we switched from PHP's built-in DOM parser to our custom implementation. We realized that trying to parse tempest/view syntax according to the official HTML spec added more problems than it solved. After all, tempest/view syntax is a superset of HTML: it compiles to spec-compliant HTML, but in itself it is not spec-compliant.
124+
125+
Moving to a custom parser written in PHP comes with a small performance price to pay, but our implementation is slightly more performant than [masterminds/html5](https://github.com/Masterminds/html5-php), the most popular PHP-based DOM parser, and everything our parser does is cached as well. You can [check out the implementation here](https://github.com/tempestphp/tempest-framework/tree/main/packages/view/src/Parser).
126+
127+
## Container features
128+
129+
We've added a new interface called `HasTag`, which allows any object to manually specify its container tag. This feature is especially useful combined with config files, and allows you to define multiple config files for multiple occasions. For example, to define multiple database connections:
130+
131+
132+
```php
133+
return new PostgresConfig(
134+
tag: 'backup',
135+
136+
// …
137+
);
138+
```
139+
140+
```php
141+
use Tempest\Database\Database;
142+
use Tempest\Container\Tag;
143+
144+
final readonly class BackupService
145+
{
146+
public function __construct(
147+
#[Tag('backup')]
148+
private Database $database,
149+
) {}
150+
151+
// …
152+
}
153+
```
154+
155+
We also added support for proxy dependencies, using PHP 8.4's new object proxies. Any dependency that might be expensive to construct, but not often used, can be injected as a proxy. As a proxy, the dependency will only get resolved when actually needed:
156+
157+
```php
158+
use Tempest\Container\Proxy;
159+
160+
final readonly class BookController
161+
{
162+
public function __construct(
163+
#[Proxy]
164+
private VerySlowClass $verySlowClass
165+
) { /* … */ }
166+
}
167+
```
168+
169+
## Middleware discovery
170+
171+
One thing that has felt icky for a long time was that middleware classes could not be discovered (this was the case for all HTTP, console, event bus and command bus middleware). The reason for this restriction was that in some cases, it's important to ensure middleware order: some middleware must come before other, and discovery doesn't guarantee that order. This restriction doesn't match our Tempest mindset, though: we forced all middleware to be manually configured, even though only a small number of middleware classes actually needed that flexibility.
172+
173+
So, as of beta.1, we've added middleware discovery to make the most common case very developer-friendly, and we added the tools necessary to make sure the edge cases are covered as well.
174+
175+
First, you can skip discovery for middleware classes entirely when needed:
176+
177+
```php
178+
use Tempest\Discovery\SkipDiscovery;
179+
use Tempest\Router\HttpMiddleware;
180+
181+
#[SkipDiscovery]
182+
final readonly class ValidateWebhook implements HttpMiddleware
183+
{
184+
public function __invoke(Request $request, HttpMiddlewareCallable $next): Response
185+
{
186+
// …
187+
}
188+
}
189+
```
190+
191+
And, second, you can define middleware priority for specific classes to ensure the right order when needed:
192+
193+
```php
194+
use Tempest\Core\Priority;
195+
196+
#[Priority(Priority::HIGHEST)]
197+
final readonly class OverviewMiddleware implements ConsoleMiddleware
198+
{
199+
public function __invoke(Invocation $invocation, ConsoleMiddlewareCallable $next): ExitCode|int
200+
{
201+
// …
202+
}
203+
}
204+
```
205+
206+
## Smaller features
207+
208+
Finishing with a couple of smaller changes, but it's these kinds of small details that make the difference in the long run. So thanks to everyone who contributed:
209+
210+
- We've added a couple of new commands: `make:migration` and `container:show`
211+
- We've added testing utilities for our event bus
212+
- There's a new `Back` response class to redirect to the previous page
213+
- We now allow controllers to also return strings and arrays directly
214+
- We've added a new storage component, which is a slim wrapper around [Flysystem](https://flysystem.thephpleague.com/docs/)
215+
- And, [a lot more](https://github.com/tempestphp/tempest-framework/releases/tag/v1.0.0-beta.1)
216+
217+
## In closing
218+
219+
It's amazing to see what we've achieved in a little less than two years. Tempest has grown from being a dummy project used during livestreams, to a real framework.
220+
221+
There's a long way to go still, but I'm confident when I see how many people are contributing to and excited about Tempest. You can follow along the beta progress on [GitHub](https://github.com/tempestphp/tempest-framework/milestone/16); and you can be part of the journey as well: [give Tempest a try](/main/getting-started/getting-started) and [join our Discord server](https://tempestphp.com/discord).
222+
223+
See you soon!
224+
225+
<img class="w-[1.66em] shadow-md rounded-full" src="/tempest-logo.png" alt="Tempest" />

src/Web/Documentation/ChapterController.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public function docsRedirect(string $path): Redirect
2626

2727
#[Get('/docs')]
2828
#[Get('/documentation')]
29+
#[Get('/main/framework/getting-started')]
2930
public function index(): Redirect
3031
{
3132
$version = Version::default();

src/Web/Documentation/content/main/0-getting-started/01-getting-started.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Once the prerequisites are installed, you can chose your installation method. Te
1616
To get started with a new Tempest project, you may use {`tempest/app`} as the starting point. The `composer create-project` command will scaffold it for you:
1717

1818
```sh
19-
{:hl-keyword:composer:} create-project tempest/app {:hl-type:my-app:} --stability alpha
19+
{:hl-keyword:composer:} create-project tempest/app {:hl-type:my-app:} --stability beta
2020
{:hl-keyword:cd:} {:hl-type:my-app:}
2121
```
2222

@@ -48,7 +48,7 @@ You may then [run the front-end development server](../1-essentials/04-asset-bun
4848
If you already have a project, you can opt to install {`tempest/framework`} as a standalone package. You could do this in any project; it could already contain code, or it could be an empty project.
4949

5050
```sh
51-
{:hl-keyword:composer:} require tempest/framework:1.0-alpha.6
51+
{:hl-keyword:composer:} require tempest/framework:1.0-beta.1
5252
```
5353

5454
Installing Tempest this way will give you access to the Tempest console, `./vendor/bin/tempest`. Optionally, you can choose to install Tempest's entry points in your project. To do so, you may run the framework installer:

src/Web/Documentation/content/main/1-essentials/01-container.md

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -393,16 +393,14 @@ will allow the container to instead inject a lazy proxy.
393393
Since lazy proxies are transparent to the consumer you do not need to change anything else in your code.
394394
The primary use case for this are heavy dependencies that may or may not be used.
395395

396-
```php
397-
// app/BookController.php
398-
399-
use Tempest\Container\Tag;
396+
```php app/BookController.php
400397
use Tempest\Container\Proxy;
401398

402399
final readonly class BookController
403400
{
404-
public function __constructor(
405-
#[Proxy] VerySlowClass $verySlowClass
401+
public function __construct(
402+
#[Proxy]
403+
private VerySlowClass $verySlowClass
406404
) { /* … */ }
407405
}
408406
```

src/Web/Documentation/content/main/1-essentials/03-views.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,15 @@ Using an expression attribute that return a boolean variable will follow the HTM
168168

169169
Depending on whether `$selected` evaluates to `true` or `false`, the above example may or may not render the `selected` attribute.
170170

171+
Apart from HTMLs boolean attributes, the same syntax can be used with any expression attribute as well:
172+
173+
```html
174+
<div :data-active="{$isActive}"></div>
175+
176+
<!-- <div></div> when $isActive is falsy -->
177+
<!-- <div data-active></div> when $isActive is truthy -->
178+
```
179+
171180
### Control flow directives
172181

173182
#### `:if`, `:elseif` and `:else`

src/Web/Documentation/content/main/3-console/07-standalone.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ title: Standalone
99
You can install `tempest/console` like so:
1010

1111
```
12-
composer require tempest/console:1.0-alpha.6
12+
composer require tempest/console:1.0-beta.1
1313
```
1414

1515
And run it like so:

src/Web/Homepage/home.view.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
</div>
4343
<button data-copy="#install-tempest-snippet" class="hidden md:flex group mt-6 md:mt-8 xl:mt-10 items-center justify-start gap-x-2 text-base font-mono relative cursor-pointer">
4444
<x-icon name="tabler:terminal" class="size-5 text-(--ui-primary)" />
45-
<span id="install-tempest-snippet" class="text-(--ui-text-muted)">composer create-project tempest/app --stability alpha</span>
45+
<span id="install-tempest-snippet" class="text-(--ui-text-muted)">composer create-project tempest/app --stability beta</span>
4646
<span class="ml-4 flex items-center justify-center opacity-0 group-hover:opacity-100 transition text-(--ui-text-dimmed) bg-(--ui-bg-muted) rounded border border-(--ui-border)">
4747
<x-icon name="tabler:copy" class="size-5 absolute" />
4848
<x-icon name="tabler:copy-check-filled" class="size-5 absolute opacity-0 group-[[data-copied]]:opacity-100 transition text-(--ui-success)" />

0 commit comments

Comments
 (0)