Juni 2026
Uppdatering
v1.2.8
ORM: förbättrad relationsladdning och query-stöd
Den här releasen innehåller en större förbättring av ORM-lagrets relationer, eager loading
och constrained loading. Fokus har varit att göra relationerna mer konsekventa, mer
testbara och enklare att använda tillsammans med QueryBuilder.
Alla ändringar är verifierade med:
PHPUnit: grönt
PHPStan: grönt
GitHub Actions: grönt
Infection: 5360 / 5360 mutanter dödade
Mutation Code Coverage: 100%
Covered Code MSI: 100%
Nytt och förbättrat
Förbättrad relationsstatus på modeller
Modeller har nu bättre stöd för att hantera laddade relationer.
Nya/uppdaterade metoder:
$model->relationLoaded('relation');
$model->unsetRelation('relation');
$model->withoutRelations();
relationLoaded() använder intern relations-cache och kan skilja mellan:
relation saknas
och:
relation är laddad men värdet är null
Det är särskilt viktigt för one-relationer som hasOne, belongsTo och hasOneThrough.
Bättre dynamisk access till laddade relationer
Om en relation redan är laddad returnerar modellens dynamiska property-access nu den
laddade relationsdatan före relationsobjektet.
Exempel:
$user->load('posts');
$posts = $user->posts;
Relationens metod fungerar fortfarande som tidigare:
$relation = $user->posts();
Det innebär:
$user->posts() => relationsobjekt
$user->posts => laddad relationsdata, om relationen är laddad
Säkrare relation-validering
relationExists() har förbättrats så att interna modellmetoder inte längre räknas som relationer.
Exempel på metoder som inte ska accepteras som relationer:
save
delete
load
toArray
Det gör både load() och eager loading säkrare och mer konsekvent.
loadMissing() respekterar laddade null-relationer
loadMissing() laddar nu bara relationer som faktiskt saknas.
En relation som redan är laddad med värdet null betraktas som laddad och laddas inte om.
Query-stöd på relationer
Flera relationstyper har fått eller förbättrat stöd för query().
Relationer med query-stöd:
HasMany
HasOne
BelongsTo
BelongsToMany
HasManyThrough
HasOneThrough
Det gör att relationer kan användas mer konsekvent tillsammans med QueryBuilder.
Exempel:
$user->posts()
->query()
->where('published', '=', 1)
->orderBy('created_at', 'DESC')
->get();
Constrained relation loading
Model::load() hanterar nu QueryBuilder-typade closures mer konsekvent.
Exempel:
$user->load([
'posts' => function (QueryBuilder $query): void {
$query->where('published', '=', 1);
},
]);
För many-relationer sparas resultatet som en Collection.
$posts = $user->getRelation('posts');
För one-relationer sparas resultatet som en modell eller null.
$profile = $user->getRelation('profile');
Relation cardinality
Relationer kan nu ange om de representerar en eller flera modeller.
Nya metoder på relationer:
$relation->isOne();
$relation->isMany();
Exempel:
$user->profile()->isOne(); // true
$user->posts()->isMany(); // true
Det används internt för att avgöra om constrained loading ska använda:
$query->first();
eller:
$query->get();
Bättre eager loading
Eager loading använder nu samma relation-validering som modellens load().
Det betyder att relationer kontrolleras via modellens relationslogik i stället för enkel method_exists()-kontroll.
Exempel:
$users = User::with(['profile', 'posts'])->get();
Med constraints:
$users = User::with([
'posts' => function (QueryBuilder $query): void {
$query->where('published', '=', 1);
},
])->get();
Mindre reflection i Model::load()
Model::load() använder nu publika relation-getters där det går, och faller bara tillbaka till
reflection när det behövs för bakåtkompatibilitet.
Exempel på metadata som nu kan läsas via publikt API:
getForeignKey()
getLocalKeyName()
getRelatedModelClass()
getRelatedTable()
Det gör relationerna lättare att introspektera och minskar beroendet av privata properties.
Förbättrade relationer
HasMany
HasMany har fått:
query()
getLocalKeyName()
isMany()
isOne()
Exempel:
$posts = $user->posts()
->query()
->where('published', '=', 1)
->get();
Om parent saknar local key används en tom-resultat-query:
WHERE (1 = 0)
HasOne
HasOne har fått:
query()
getLocalKeyName()
isMany()
isOne()
Exempel:
$profile = $user->profile()
->query()
->where('active', '=', 1)
->first();
BelongsTo
BelongsTo har fått:
query()
isMany()
isOne()
Exempel:
$author = $post->user()
->query()
->where('active', '=', 1)
->first();
BelongsToMany
BelongsToMany::query() har förbättrats.
Förbättringar:
stabilare pivot-join via raw join
bättre hantering när parent saknar id
stöd för whereRaw('1 = 0') vid tomt resultat
fortsatt stöd för withPivot()
befintlig builder återanvänds när query() redan har byggts
Exempel:
$roles = $user->roles()
->query()
->where('roles.active', '=', 1)
->get();
Pivot-kolumner:
$roles = $user->roles()
->withPivot('created_by', 'created_at')
->query()
->get();
HasManyThrough
HasManyThrough har fått query-stöd.
Exempel:
$votes = $category->votes()
->query()
->where('points', '>', 0)
->get();
HasOneThrough
HasOneThrough har fått query-stöd.
Exempel:
$topVote = $category->topVote()
->query()
->where('points', '>', 0)
->first();
Bättre testtäckning
Den här releasen lägger till och förbättrar tester för:
relation state
relationLoaded()
unsetRelation()
withoutRelations()
relationExists()
Model::load()
loadMissing()
QueryBuilder-constraints
HasMany::query()
HasOne::query()
BelongsTo::query()
BelongsToMany::query()
HasManyThrough::query()
HasOneThrough::query()
relation cardinality
eager loading
pivot select
fallback via reflection
getQuery/query relation resolution
Mutation testing visar:
5360 mutations were generated
5360 mutants were killed by Test Framework
Mutation Code Coverage: 100%
Covered Code MSI: 100%
Bakåtkompatibilitet
Ändringarna är gjorda med bakåtkompatibilitet i åtanke.
Befintlig användning fungerar fortfarande:
$user->posts()->get();
$user->profile()->first();
$user->load('posts');
$user->loadMissing('profile');
Reflection-fallbacks finns kvar där äldre/custom relationer kan sakna nya publika getters.
Det finns dock ett mer korrekt beteende vid QueryBuilder-typade closures:
$user->load([
'profile' => function (QueryBuilder $query): void {
$query->where('active', '=', 1);
},
]);
One-relationer lagras nu som:
Model|null
Many-relationer lagras som:
Collection
Det är mer konsekvent med relationens cardinality.
Rekommenderad uppgradering
Efter uppdatering rekommenderas:
composer update
composer dump-autoload
Kör sedan projektets tester:
composer test
composer stan
Om appen har egna relationer bör du särskilt verifiera:
load()
loadMissing()
with()
withCount()
withSum()
belongsToMany()
withPivot()
Sammanfattning
Den här releasen gör ORM-relationer mer konsekventa, säkrare och bättre integrerade med
QueryBuilder.
Största förbättringarna är:
query-stöd på alla centrala relationstyper
bättre constrained loading
korrekt hantering av one/many-relationer
säkrare relation-validering
mindre reflection
starkare test- och mutationstäckning
Maj 2026
Uppdatering
v1.2.6
Säkerhet: stöd för Cross-Origin-Resource-Policy
Denna release lägger till stöd för att konfigurera HTTP-headern Cross-Origin-Resource-Policy
via Radix SecurityHeaders middleware.
Headern kan användas för att styra hur resurser från applikationen får användas av andra
origins, och ger ett extra skyddslager mot oönskad cross-origin-användning av resurser.
Ny valfri miljövariabel: SECURITY_CORP=same-origin
Tillåtna värden:
same-origin
same-site
cross-origin
off
Rekommenderad standard för vanliga installationer är same-origin.
Det passar när webb, API och statiska resurser ligger på samma origin, exempelvis:
https://example.com
https://example.com/api/v1/...
https://example.com/assets/...
Använd same-site om applikationen är uppdelad på flera subdomäner inom samma site,
exempelvis:
https://app.example.com
https://api.example.com
https://static.example.com
Använd cross-origin endast om resurser från applikationen behöver kunna användas av
externa domäner.
Använd off om headern inte ska sättas av frameworkets SecurityHeaders middleware.
Ändringen är bakåtkompatibel. Befintliga applikationer som saknar SECURITY_CORP
påverkas inte automatiskt. Headern sätts endast när värdet är konfigurerat.
EnvValidator har uppdaterats för att validera SECURITY_CORP när variabeln är satt.
Ogiltiga värden stoppas tidigt med ett tydligt konfigurationsfel.
Testsviten har utökats för att säkerställa att SECURITY_CORP=same-origin accepteras, att
ogiltiga värden nekas och att befintlig bakåtkompatibilitet bibehålls.
För de flesta projekt rekommenderas:
SECURITY_CORP=same-origin
Om Apache eller annan webbserver används för att servera statiska filer direkt kan
motsvarande header även sättas på webbservernivå för att även omfatta exempelvis
JavaScript, CSS, bilder och fonter.