qcodr/restate-sdk-laravel 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

qcodr/restate-sdk-laravel

Composer 安装命令:

composer require qcodr/restate-sdk-laravel

包简介

Laravel integration for the Restate PHP SDK — durable execution (workflows, virtual objects, sagas, durable timers) wired into Laravel's container, routing, and Artisan.

README 文档

README

CI codecov PHPStan level max Mutation testing badge Latest Stable Version Total Downloads PHP Version License

Laravel integration for the Restate PHP SDKdurable execution (workflows, virtual objects, sagas, durable timers, exactly-once side effects) wired into Laravel's container, routing, and Artisan.

Write Restate services as ordinary Laravel classes (with constructor DI), list them in config, and serve them either from inside your app (a request/response route) or over true bidirectional HTTP/2 (php artisan restate:serve).

Why

Restate adds guarantees Laravel's native tools don't give you for multi-step, long-running, or per-entity-stateful work:

Laravel area What Restate adds
Jobs / multi-step processes Durable sagas — order → payment → inventory → shipping with automatic retries and compensation, exactly-once
Per-entity state (counters, balances, inventory, rate limits) Virtual Objects — single-writer state per key, no lockForUpdate / row-lock races
Long-running / human-in-the-loop (approvals, email verify, reminders) Durable promises + timers — wait days for an external event, surviving restarts/deploys
Webhooks / side effects Exactly-once processing + the outbox pattern via ctx->run()
Scheduler Self-rescheduling durable timers that survive restarts (vs stateless cron)
API orchestration / fan-out Durable combinators (select/awaitAll) with retries

Requirements

  • PHP 8.2+
  • Laravel 12 or 13
  • qcodr/restate-sdk-php (pulled in automatically)
  • amphp/http-server only if you use php artisan restate:serve (bidi)

Installation

composer require qcodr/restate-sdk-laravel
php artisan vendor:publish --tag=restate-config   # publishes config/restate.php

The service provider is auto-discovered.

Quick start

Define a service as a normal Laravel class — constructor dependencies are injected from the container:

namespace App\Restate;

use Qcodr\Restate\Sdk\Context\Context;
use Qcodr\Restate\Sdk\Service\Attribute\{Service, Handler};

#[Service]
final class GreeterService
{
    public function __construct(private readonly \App\Services\Mailer $mailer) {}

    #[Handler]
    public function greet(Context $ctx, string $name): string
    {
        // Non-deterministic work (DB, HTTP, mail) goes inside ctx->run() so it runs
        // exactly once and replays from the journal on retries.
        $ctx->run('notify', fn () => $this->mailer->ping($name));

        return "Hello {$name}";
    }
}

Register it in config/restate.php:

'services' => [
    App\Restate\GreeterService::class,
],

Point a running Restate runtime at your app and invoke through the ingress:

restate deployments register http://localhost:8000/restate --use-http1.1
curl localhost:8080/GreeterService/greet -H 'content-type: application/json' -d '"world"'
# "Hello world"

One rule for handlers: they must be deterministic and stateless. Keep every DB / HTTP / mail / random call inside ctx->run() (a durable side effect); per-invocation data lives in local variables or Restate state, never in instance properties.

Serving

In-app route (default). The provider mounts a catch-all route at the configured prefix (restate by default), served request/response by your normal Laravel stack (FPM, Octane). Register the deployment at <app-url>/restate. Zero extra infrastructure.

Bidirectional streaming. For cancellation, signals, and fewer re-invokes on suspension-heavy handlers, run the amphp host instead:

composer require amphp/http-server
php artisan restate:serve --port=9080 --workers=8

Set config path to null to disable the in-app route when serving this way.

Calling Restate from Laravel

The two sections above expose your handlers to the runtime. The reverse direction — starting a Restate invocation from ordinary Laravel code (a controller, job, or listener) — goes through the Restate ingress with the RestateClient:

use Qcodr\Restate\Laravel\Client\RestateClient;

public function __construct(private readonly RestateClient $restate) {}

// Call and await the result (request/response):
$greeting = $this->restate->call('GreeterService', 'greet', 'Ada');

// Fire-and-forget — returns the invocation id immediately:
$id = $this->restate->send('OrderWorkflow', 'run', ['orderId' => $orderId], key: $orderId);

// Keyed object/workflow, idempotency, and a durable delayed send:
$this->restate->send('OnboardingService', 'nudge', ['userId' => $id], key: $id, delayMs: 3_600_000);

Configure the ingress in config/restate.php (ingress.url, RESTATE_INGRESS_URL; optional ingress.token, RESTATE_INGRESS_TOKENAuthorization: Bearer). Full recipe — controllers, jobs, listeners, idempotency, delayed send, error handling — in docs/usecases/dispatch.md.

Configuration

config/restate.php:

Key Description
services Service / virtual-object / workflow class names exposed by the deployment
path Route prefix the runtime calls (null disables the in-app route)
middleware Middleware group for the route (default ['api'])
identity_key publickeyv1_... to verify request signatures (needs ext-sodium)
ingress.{url,token} Restate ingress base URL + optional Bearer token for the RestateClient (the caller side)
server.{host,port,workers} restate:serve bind settings (workers: 0 = one per CPU)

Artisan

php artisan restate:discover          # list the bound services
php artisan restate:discover --json   # print the raw discovery manifest
php artisan restate:serve             # serve over bidi HTTP/2 (amphp)

Use cases

Worked, tested examples live under tests/Examples/ (proven offline against the SDK — discovery, business logic, and the durable flow over a fake context) with copy-pasteable recipes:

  • Saga — an order-processing workflow (reserve inventory → charge → ship) with automatic compensation on failure: a thin #[Workflow] over injected, idempotent services. The proof drives a failing payment and asserts inventory is released, shipping never runs, and a TerminalException surfaces.
  • Rate limiter — a per-key token bucket as a #[VirtualObject]: single-writer state per key, no lockForUpdate / Redis race. The proof drains one key while another stays full, showing per-key isolation.
  • Dispatch from Laravel — the caller side: start invocations (call-and-await, fire-and-forget send, keyed objects/workflows, idempotency, durable delayed send) from a controller, job, or listener via the RestateClient over the Restate ingress.
  • Queue connection — dispatch existing ShouldQueue jobs on the restate connection (->onConnection('restate')) to run them durably on Restate (exactly-once, durable retries) with no queue:work worker.
  • Validation — validate a handler's decoded-array input with Laravel's Validator at the boundary via the ValidatesInput trait, throwing a terminal 400 on bad input (the idiomatic answer to the array gotcha below).
  • TestingRestate::fake() + Restate::assertCalled(...) / assertSent(...), the Bus::fake() equivalent for Restate dispatches.
  • Generators & discoveryphp artisan make:restate-service (-object, -workflow) plus directory auto-discovery (config discover).
  • Typed clientsphp artisan restate:codegen generates an IDE-autocompletable client per service (GreeterClient::fromContext($ctx)->greet(...)).
  • SchedulerSchedule::restate('Svc','handler', $payload) ->dailyAt('03:00') fires durable Restate invocations from Laravel's scheduler.
  • Observability — handler logs flow into Laravel's logging stack (replay-aware), and an optional Telescope watcher tags ingress dispatches.
  • Auth & tenant — re-establish the authenticated user / tenant inside a handler from the headers the runtime forwards (withAuth), and forward them on outbound dispatches.

Several surface a real SDK boundary: JsonSerde hands handlers the decoded array, not a hydrated object, so a handler's input parameter is array/scalar and the value object is built (and validated — see the Validation trait) inside the handler.

Code quality

Mirrors the SDK's strict gate, all offline:

make check     # php-cs-fixer + PHPStan (max, with Larastan) + Psalm taint + PHPUnit

License

Apache-2.0

统计信息

  • 总下载量: 0
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 5
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: Apache-2.0
  • 更新时间: 2026-06-27

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固