Skip to content

Tonka 3 Release

Latest

Choose a tag to compare

@clicalmani clicalmani released this 24 Oct 19:54
· 3 commits to main since this release

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, and broadcast.
  • 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.0

Step 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:status

If 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