BuildNot yet confirmed elsewhere1 publisher2 min readPublished
Green Framework 3.0 lets projects adopt relation attributes one table at a time
Green Framework 3.0 supports three relation definition styles at once, so existing projects can adopt attributes one table at a time. Configuration works differently: it is now assembled once at bootstrap and read-only afterward.
The Engineer · Build desk
Old relation declarations keep working while tables move to attributes one at a time. An attribute replaces a same-named older relation. Code setting config after bootstrap breaks the read-only contract. A failing seeder rolls back only itself; earlier seeders stay committed.
- capability Existing relation code Old declarations keep working while new code adopts attributes one table at a time, claim 5
- exposure Same-named relation tables An attribute added beside a same-named relations() or $relations entry replaces that older definition, claim 18
- constraint Code setting config at runtime Configuration is assembled once at bootstrap and read-only afterward, so later writes break the contract, claim 2
- exposure Multi-seeder runs A failing seeder rolls back only its own work; seeders that ran before it stay committed, claim 17
| Who | How | Kind | Claim |
|---|---|---|---|
| Existing relation code | Old declarations keep working while new code adopts attributes one table at a time | capability | 5 |
| Same-named relation tables | An attribute added beside a same-named relations() or $relations entry replaces that older definition | exposure | 18 |
| Code setting config at runtime | Configuration is assembled once at bootstrap and read-only afterward, so later writes break the contract | constraint | 2 |
| Multi-seeder runs | A failing seeder rolls back only its own work; seeders that ran before it stay committed | exposure | 17 |
What happened
- The release also adds first-class polymorphic relations, transactional database seeders and a lightweight queue system alongside the new attribute syntax.
- Duplicate relation names resolve in a fixed order: #[RelatesTo] attributes first, then entries from relations(), then the legacy $relations property.
- Each seeder runs inside its own transaction, and a failed seeder is rolled back and stops the run.
Why it matters
- decision Each table is its own conversion decision, and an attribute added beside a same-named old definition replaces it, so the two need comparing first.
- constraint Any code that sets configuration after bootstrap now breaks the new contract, and the relation precedence rules do nothing to soften that.
- exposure A seeding run that fails partway can leave the database half seeded, because the earlier seeders have already committed.
Relations are the one area with a documented gradual path. The post sets a precedence for duplicate names: `#[RelatesTo]` attributes, then `relations()`, then the legacy `$relations` property, with the higher item winning [4]. Existing declarations keep working while new code adopts attributes [5]. The new style wins, so a team adds an attribute, runs its tests and deletes the old entry afterward.
Configuration has no documented migration path. According to the post, it completes a redesign that was already under way [3], and a setting written after bootstrap now meets read-only state [2]. The post does not say whether that write throws, is ignored, or does something else. In our view the place to start looking is request handlers and test setup, since a value is most likely to change mid-run there.
Seeders are the other place to plan. The generator adds numeric prefixes such as 001_, and files run alphabetically [9]. Numeric prefixes make alphabetical order into a hand-kept dependency list. After a mid-run failure there are two ways back. `db:seed --class` targets one seeder [16]. `--fresh` clears all tables declared by the selected seeders before execution [11]. A plain rerun of everything starts at the first seeder again [16].
The queue changes where `handle()` runs. Dispatch is the same call under either driver, `$queue->dispatch(...)` through `QueueManager` [15]. Under the sync driver the job is processed immediately. Under the database driver it is persisted for a worker started with `php green queue:work` [14]. A test suite that runs on sync never exercises the persisted path.
The post's example job takes an integer user id and resolves the user inside `handle()` [13]. Only the id is stored with the job, so the user's data is current when `handle()` runs.
Polymorphic relations are new in this release [6]. Models declare an alias with `#[MorphAlias]`, and `MorphMap` discovers aliases from those attributes, with manual registration for explicit control [7]. In the post's example, the comment's `MorphTo` lists Post and Video as its models [6]. If `commentable_type` holds the alias, as the post's wording suggests, a renamed class should keep matching its rows. Eager loading is batched per relationship type, which avoids an N+1 query for every parent model [8].
What to watch
- A changelog or upgrade guide that says whether Green 3.0 throws on, or ignores, configuration writes after bootstrap.
- A later release that removes the legacy $relations property, which would end the three-style overlap.
- Documentation of the remaining queue:work options and of how the database driver stores jobs.
Clarity's read
What the record supports and how the coverage leans. The claims behind it follow.
Reality
- Evidence40
- Adoption
- Insufficient
- Hype gap+10
- Incentives30
- Confidence60
Claim ledger
Ranked by verification strength, evidence, and original report placement.
- [1]
Green Framework 3.0 adds PHP attribute-based relations, first-class polymorphic relations, transactional database seeders and a lightweight queue system.
- [2]
Configuration is now assembled once during bootstrap and remains read-only at runtime.
- [3]
The post describes the read-only configuration as completing an important configuration redesign.
- [4]
Green 3.0 supports three relation definition styles and resolves duplicate names in this order: #[RelatesTo] attributes, entries returned by relations(), then the legacy $relations property. The higher item in that list wins.
- [5]
Existing relation declarations continue to work while new code can adopt attributes one table at a time.
- [6]
Green 3.0 introduces MorphTo, MorphMany and MorphOne. In the example a comment can belong to either a post or a video through the same commentable_type and commentable_id columns, and the comment's MorphTo lists Post and Video as its models.
- [7]
Models declare stable database aliases with #[MorphAlias]. MorphMap discovers aliases from the model attributes, with manual registration available when an application needs explicit control.
- [8]
Dedicated eager-loading strategies batch each relationship type, avoiding an N+1 query for every parent model.
- [9]
Green writes seeders to database/seeders/ with an auto-incrementing numeric prefix such as 001_, 002_ and 003_, and files run alphabetically.
- [10]
Each seeder runs inside its own transaction. If it fails, Green rolls it back and stops the run.
- [11]
Declared table names are validated before truncation, and --fresh clears all tables declared by the selected seeders before execution.
- [12]
The v3.0.0 tag ships Green's first queue subsystem, with synchronous and database-backed drivers.
- [13]
A queued job implements JobInterface with handle(), maxAttempts() and retryDelay(). In the example job the constructor takes an integer user id, handle() resolves the user and sends the message, maxAttempts() returns 3 and retryDelay() returns 10.
- [14]
The sync queue driver processes work immediately. The database driver persists jobs for a worker, run with php green queue:work.
- [15]
A job is dispatched through QueueManager with $queue->dispatch(new SendWelcomeEmail($user->id)).
- [16]
php green db:seed runs every seeder, --class=UserSeeder targets one class, and --fresh requests a fresh pass.
- [17]
Because each seeder has its own transaction and a failure stops the run, a failing seeder rolls back only its own work; seeders that ran before it stay committed.
- [18]
Because the higher item wins on duplicate names, an attribute added to a table that already defines the same relation name in relations() or $relations replaces that older definition.
Sources
1 independent publisher whose own reporting we read for this story.
- dev.toGreen Framework 3.0 Released: Attribute-Based Relations, Queues, Seeders, and Polymorphic Relations
1 article · October 11, 2026
Topics and entities
Follow any of these and your For You feed starts watching them — no settings page required.