Repository navigation
Tonka v3.0.0 Release Notes
Release Date: October 24, 2026
Tonka v3.0.0 is a major release that modernizes the framework's data layer, expands the broadcasting and dependency injection capabilities, and introduces queueable metrics and notifications. Several changes affect existing APIs, so please review the Breaking Changes and Upgrade Guide sections before updating.
Highlights
- Modernized entity attributes — PHP 8 native types replace custom attributes for defaults, nullability, and custom properties.
- Custom broadcasting channels — Broadcasting now supports channels beyond
mail,database, andbroadcast. - Method-level service injection — Custom services can now be injected into controller methods via
InjectionResolverInterface. - Queueable metrics and notifications — Metrics and notifications can be dispatched to the queue for background processing.
Breaking Changes
The following changes are not backward compatible. Applications upgrading from v2.x must update their entity definitions and custom property implementations.
1. Default values are now assigned directly to properties
The Property(default: ...) attribute has been removed. Default values must now be assigned directly to the class property using native PHP syntax.
Before (v2.x):
use Clicalmani\Database\DataTypes\Enum;
use Clicalmani\Database\DataTypes\Integer;
use Clicalmani\Database\Factory\Entity;
use Clicalmani\Database\Factory\Property;
class UserEntity extends Entity
{
#[Property(values: ['active', 'on-leave', 'resigned'], default: 'active')]
public Enum $status;
#[Property(length: 10, unsigned: true, default: 0)]
public Integer $login_count;
}After (v3.x):
class UserEntity extends Entity
{
#[Property(values: ['active', 'on-leave', 'resigned'])]
public Enum|string $status = 'active'
#[Property(length: 10, unsigned: true)]
public Integer|int $login_count = 0;
}2. Nullable properties use native PHP 8 nullable types
The Property(nullable: true) attribute has been removed. Nullable columns must now be declared using PHP 8's nullable type syntax (?type).
Before (v2.x):
#[Property(nullable: true)]
protected Text $bio;After (v3.x):
protected ?Text $bio;3. Custom properties moved from models to entities
Custom properties previously defined on models using Laravel-style accessors (getXXXAttribute) are now defined on entities using PHP 8 property hooks or dedicated accessor methods. This aligns custom property resolution with the entity layer rather than the model layer.
Before (v2.x):
// app/Models/User.php
class User extends Elegant
{
public function getFullNameAttribute(): string
{
return "{$this->first_name} {$this->last_name}";
}
}After (v3.x):
// app/Entities/User.php
class UserEntity extends Entity
{
public string $full_name {
get => "{$this->first_name} {$this->last_name}";
}
}This separation clarifies the responsibility boundary: models handle persistence and query building, while entities handle value representation and presentation logic.
What's New
1. Database & Entities
Native PHP 8 attributes for schema definition
Entity definitions are now fully expressed through native PHP 8 syntax — typed properties, default values, and nullable types. This makes entity classes self-documenting and eliminates the need for custom attributes.
class Product extends Entity
{
public Integer $id;
public VarChar $name;
public ?Text $description = null;
public Double|float $price = 0.0;
public Boolean|tru $is_active = true;
}Improved migration versioning through manifests
Migration versioning is now more discoverable and traceable through manifests. Each migration run produces a manifest entry that records the applied migrations and changes. This improves:
- Auditability — you can trace exactly which migrations were applied and when.
- Rollback safety — manifests detect modified migrations that were already applied.
- Team collaboration — merge conflicts in migration files are flagged before they cause schema drift.
php tonka migrate:status+----------------+---------------------+---------+------------------+
| Migration | Applied At | Batch | Checksum |
+----------------+---------------------+---------+------------------+
| 2026_01_users | 2026-10-01 09:14:22 | 1 | a3f9c8d2... |
| 2026_02_orders | 2026-10-01 09:14:23 | 1 | 7b2e1f4a... |
+----------------+---------------------+---------+------------------+
2. Custom Broadcasting Channels
Broadcasting previously supported three built-in channels: mail, database, and broadcast. In v3.0, you can define custom channels to deliver broadcast events through any transport — SMS, push notifications, webhooks, or proprietary protocols.
A custom channel is a class that implements the channel contract and is registered in your application:
<?php
namespace App\Broadcasting\Channels;
use Clicalmani\Broadcasting\Channel;
use App\Services\SmsService;
class SmsChannel implements Channel
{
public function __construct(
protected SmsService $sms
) {}
public function send($notifiable, $event): void
{
$this->sms->send(
$notifiable->phone_number,
$event->toSms($notifiable)
);
}
}Register the channel in your broadcasting configuration and reference it by name in your events or notifications:
public function via($notifiable): array
{
return ['mail', 'database', 'sms'];
}This makes Tonka's broadcasting system fully extensible — the built-in channels are no longer a closed set.
3. Method-Level Service Injection
Custom services can now be injected into controller methods (in addition to controller constructors) by implementing InjectionResolverInterface. This reduces constructor bloat for controllers that use a service in only one or two methods.
<?php
namespace App\Services;
class PaymentService implements ServiceProvider, InjectionResolverInterface
{
// ...
}The resolver inspects the method signature, resolves the type-hinted services from the container, and injects them at call time. This is particularly useful for:
- Feature-focused services that are used in a single action.
- Testing — injecting fakes via the container is simpler than reconstructing the controller.
- Avoiding circular dependencies in large controllers.
4. Queueable Metrics and Notifications
Notifications can now be queued for background processing, following the same patterns used for queueable mail and broadcast events. This is useful when:
- A metric write involves an external service (e.g., StatsD, Prometheus push gateway).
- A notification's rendering is expensive and should not block the HTTP response.
use Clicalmani\Core\Metrics\Metric;
// Dispatch the metric to the queue
Notification::queue(new UserRegistered, 300);Upgrade Guide
Follow these steps to upgrade from v2.x to v3.0:
Step 1 — Update the framework
composer require clicalmani/tonka:^3.0Step 2 — Migrate entity attributes
Search your codebase for Property( usages and replace them with native PHP syntax:
grep -rn "Property(" app/Entities/For each occurrence:
| v2.x | v3.x |
|---|---|
#[Property(default: 'active')] |
protected string $status = 'active'; |
#[Property(nullable: true)] |
protected ?string $bio = null; |
Step 3 — Migrate custom properties
Move custom property definitions from models (getXXXAttribute) to entities using PHP 8 property hooks or explicit accessor methods.
Step 4 — Review migration manifests
Run the migration status command to verify that all previous migrations are correctly recorded:
php tonka migrate:statusIf a migration is reported as modified, restore the original file from version control before continuing.
Step 5 — Review custom broadcasting channels
If you relied on the closed set of built-in channels, no action is required — they are still supported. If you previously implemented ad-hoc delivery logic, consider refactoring it into a custom channel for clarity and reuse.
Step 6 — Review controllers for method-level injection
No action is required. Method-level injection via InjectionResolverInterface is opt-in. You can adopt it incrementally where it improves clarity.
Step 7 — Test in staging
Run your full test suite and exercise the application in a staging environment before deploying to production. Pay particular attention to:
- Entity hydration and persistence.
- Custom property access in views and serializers.
- Migration status and rollbacks.
Deprecations
The following APIs are deprecated in v3.0 and will be removed in v4.0:
| Deprecated | Replacement | Removal |
|---|---|---|
Property(default: ...) attribute |
Native PHP default values | v4.0 |
Property(nullable: true) attribute |
Native PHP ?type |
v4.0 |
Model-level getXXXAttribute() accessors |
Entity-level custom properties | v4.0 |
Deprecated APIs continue to function in v3.0 with a deprecation notice logged once per request. Plan your migration accordingly.
Bug Fixes
- Fixed an issue where nullable entity properties with no default value caused schema generation to infer a non-nullable column.
- Fixed a race condition in migration manifests when multiple migrations were applied in parallel.
- Fixed custom broadcasting channel resolution when multiple channels of the same type were registered.
- Fixed method-level injection failing for services registered under an interface binding.
Contributors
Thank you to everyone who contributed to this release. Your feedback, bug reports, and pull requests made this release possible.
Looking Ahead
Tonka v4.0 will remove the deprecated attributes and model-level accessors introduced in v2.x. We encourage all applications to migrate before then. In the meantime, we are continuing to invest in:
- Richer observability (structured logging, tracing).
- Deeper first-party integrations with the Mercure hub.
- Expanded testing tooling for DriftQL and HTTP feature tests.
Stay tuned.
Upgrade issues? Open an issue on GitHub or reach out on the community forum.
Full Changelog: v2.5...v3.0