engeni/api-client-2
Composer 安装命令:
composer require engeni/api-client-2
包简介
Engeni - API client V2
关键字:
README 文档
README
This refactor delivers a brand-new, SOLID-oriented Engeni API client that mirrors Laravel Eloquent's read/query and write behavior while remaining purely HTTP-driven. The legacy implementation now lives under old/, letting the package evolve without backward-compatibility constraints.
Highlights
- Complete CRUD support: Fluent, Eloquent-style operations for reading (
first,firstOrFail,find,findOrFail,get,paginate,simplePaginate,pluck,value,count,exists,doesntExist,cursor,chunk) and writing (create,save,update,delete,destroy,upsert). - Mass assignment protection: Laravel-style
$fillableand$guardedproperties for secure attribute handling. - Change tracking: Full support for
isDirty(),wasChanged(),getChanges(),getOriginal(), andgetPrevious()methods. - Dependency inversion throughout: a swappable
HttpClientInterfacekeeps the runtime and tests fully mockable. - Lightweight resource models that hydrate from Engeni's REST responses while offering familiar Eloquent ergonomics.
- Laravel-first integration with automatic service-provider discovery; once installed, the client is resolved from the container without manual wiring.
- Requires PHP 8.3+, letting us lean on modern language features such as typed class constants for safer configuration.
- Comprehensive PHPUnit + Orchestra Testbench suite, plus Bitbucket Pipelines that run
pint --testandphpuniton pull requests and master merges. - Observability helpers such as
Engeni\ApiClient\Http\TracingHttpClientlet you capture the exact URL, method, and query parameters generated by the client without enabling verbose debug output.
Requirements
- PHP 8.3 or newer
- Laravel support components 12.x or 13.x (pulled in automatically)
Installation
composer require engeni/api-client
Quick Start
use Engeni\ApiClient\Resources\LaGuiaOnline\Business;
// Configure base URI and auth headers in config/engeni.php (see Configuration).
// Reading data
$popularBusinesses = Business::query()
->where('country_id', 54)
->orderBy('name')
->paginate(25);
// Creating records
$business = Business::create([
'name' => 'Acme Corp',
'country_id' => 54,
'category_id' => 123,
]);
// Updating records
$business->name = 'Updated Corp Name';
$business->save();
// Deleting records
$business->delete();
Configuration
- The package ships with
config/engeni.php(publish viaphp artisan vendor:publish --tag=engeni-api-client-config). - By default it reads the following environment variables (with sensible fallbacks):
ENGENI_API_CLIENT_BASE_URI(falls back toENGENI_SERVICE_BASE_URI)ENGENI_API_CLIENT_TIMEOUTENGENI_API_CLIENT_DEBUGENGENI_API_CLIENT_HTTP_DEBUGENGENI_API_CLIENT_VERIFY_SSLENGENI_API_CLIENT_USER_AGENT(falls back toENGENI_SERVICE_IDfor the defaultUser-Agent)
ENGENI_INTERNAL_SECRET— when set, the client adds signedX-Internal-*headers on each outbound request (seeClient/ service provider).- The service provider binds both
Client::classandHttpClientInterface::classas singletons and shares the instance with everyResourceModel, so you can call any resource class without manual bootstrapping. - Credentials are not inferred from a legacy service token env key. Put whatever upstream auth your app needs (for example
Authorization: Bearer …orX-Api-Token: …) underengeni.api_client.headersin your published config, or bind a customClient/HttpClientInterfaceif you need dynamic headers. - Need custom transport? Bind your own implementation of
Engeni\ApiClient\Contracts\Http\HttpClientInterfaceor tweak the published config.
Write Semantics
ResourceModel::save() keeps REST-style create/update semantics:
- new models are created with
POST - existing models are updated with
PATCH - existing models with no dirty attributes skip the HTTP request entirely
- existing model updates only send dirty attributes
- non-2xx create/update responses throw
HttpExceptioninstead of returningfalse
This keeps create operations explicit while making updates line up with the package's change-tracking API and the documented API convention that PATCH represents partial updates.
Writable Resource Contract
create(), updateOrCreate(), fill(), and save() all respect Laravel-style mass assignment rules. The base model defaults to protected static array $guarded = ['*'];, so a writable resource must explicitly define either $fillable or $guarded = [].
use Engeni\ApiClient\Models\ResourceModel;
final class ProductEvent extends ResourceModel
{
protected static ?string $rootPath = 'cxm/products';
protected static string $resourceName = 'events';
protected static array $fillable = [
'product_id',
'product_type',
'event',
'order_id',
];
protected static array $guarded = [];
}
If a resource omits fillable/guarded configuration, create([...]) will discard the provided attributes before sending the request. Tests for every writable resource must assert the outbound JSON payload, not only the URL and status code.
Error handling
Query builder methods (find, first, get, paginate, …) treat a 404 response as an empty result and return null or an empty collection. findOrFail and firstOrFail throw ModelNotFoundException on 404. Any other non-2xx status throws Symfony\Component\HttpKernel\Exception\HttpException.
Model writes (create, save, updateOrCreate, query-builder update/upsert) throw HttpException on any non-2xx create/update response. The exception message is extracted from error.message, then message, then the raw response body, then a generic fallback. This is intentional: write failures must be visible to queue workers and production logs.
Resources that override save() may document narrower legacy behavior. For example, Toby account-scoped resources still return false on rejected nested writes for compatibility with existing callers.
Action resource methods — resources that extend ActionResource and return data (getActionData, postActionData, postCollectionActionData) — apply the following rules:
| Response | Result |
|---|---|
404 | null — not found is a valid state, not an error |
| Other non-2xx | Throws HttpException; status code preserved; message from error.message → message → generic fallback |
2xx with data key | Returns $payload['data'] |
2xx without data key | Returns full payload array |
2xx empty body / "null" | null |
| 2xx scalar or non-JSON body | Decoded scalar value or raw body string |
Bool-returning action helpers (purge, syncProducts, authorize, etc.) use isSuccessfulResponse() directly and return false — not null — on 404.
Request-scoped Authorization forwarding
Some Laravel apps use this package as an authenticated proxy in front of api.engeni.com. In that setup, the inbound request may already carry a user or session token that must be forwarded as Authorization so the upstream API sees the same caller.
If the client always sent a fixed service credential instead, the upstream gateway could evaluate the wrong identity (permissions, tenant, or non-JSON errors).
Shipped behaviour: when engeni.api_client.auth.mode is omitted or set to forward_request, the service provider registers an auth resolver that copies only the inbound Authorization header to the outbound request when it is non-empty. Inbound X-Api-Token is not read or forwarded by the package.
When Authorization is forwarded, Client strips any static Authorization and X-Api-Token defaults from that outbound request before applying the forwarded header, so you do not send two incompatible credentials at once.
When there is no inbound Laravel request (queue workers, Artisan, schedulers) or the inbound request has no Authorization header, the resolver does nothing: outbound auth is whatever you put in engeni.api_client.headers (or nothing).
// config/engeni.php — optional static upstream credential for workers / CLI
'api_client' => [
'base_uri' => env('ENGENI_API_CLIENT_BASE_URI', env('ENGENI_SERVICE_BASE_URI', 'https://api.engeni.com')),
'headers' => [
'X-Api-Token' => env('MY_SERVICE_X_API_TOKEN', ''),
// or: 'Authorization' => 'Bearer '.env('MY_MACHINE_TOKEN'),
],
'auth' => [
'mode' => env('ENGENI_API_CLIENT_AUTH_MODE', 'forward_request'),
],
],
Set auth.mode to any value other than forward_request to disable request-scoped forwarding entirely (for example if every call should use only the headers you define under api_client.headers).
Downstream Engeni services that use engeni/api-tools still validate Authorization: Bearer (user token) and X-Api-Token (service token) in their own order; this package simply does not set or forward X-Api-Token unless you add it to headers or per-request options.
Tracing Requests
Need to inspect the exact HTTP request being dispatched without enabling verbose dumps? Wrap any existing HTTP transport in the TracingHttpClient decorator. It records every call, exposing helper methods that you can assert against in tests or print in diagnostic scripts.
use Engeni\ApiClient\Client;
use Engeni\ApiClient\Http\TracingHttpClient;
use Engeni\ApiClient\Contracts\Http\HttpClientInterface;
use Engeni\ApiClient\Resources\LaGuiaOnline\Country;
// In Laravel, the package transport is backed by `Illuminate\Http\Client\Factory`,
// so it is compatible with `Http::fake()`. To trace requests, decorate the
// resolved transport:
$http = new TracingHttpClient(app(HttpClientInterface::class));
$client = new Client(
http: $http,
baseUri: config('services.engeni.base_uri'),
);
Country::setClient($client);
Country::query()->with('states')->limit(3)->get();
dump($http->lastRequest());
// [
// 'method' => 'GET',
// 'uri' => 'https://api.engeni.com/lgo/countries',
// 'options' => [
// 'query' => [
// 'embed' => 'states',
// 'limit' => 3,
// ],
// ],
// ]
$http->clear(); // reset the log once you have asserted what you need
Because TracingHttpClient implements the same contract as the production transport you can drop it into integration tests, CLI tools, or debugging sessions. The examples/countries.php playground ships with the tracer enabled so you can see every generated URL after each scenario.
Toby Resources
The package also ships Toby resources under Engeni\ApiClient\Resources\Toby.
use Engeni\ApiClient\Resources\Toby\Invoice;
use Engeni\ApiClient\Resources\Toby\PaymentMethod;
use Engeni\ApiClient\Resources\Toby\AccountPaymentMethod;
// Read global Toby catalogs
$paymentMethods = PaymentMethod::all();
// Read and write account-scoped Toby resources
$accountMethods = AccountPaymentMethod::forAccount(2503)->get();
$method = AccountPaymentMethod::createForAccount(2503, [
'name' => 'Stripe',
'payment_method_id' => 'STRIPE',
'account_invoice_method_id' => 3,
'enabled' => true,
'public' => false,
]);
// Trigger invoice actions exposed by Toby
$invoice = Invoice::findOrFail(99);
$invoice->charge(asyncMode: true);
Account-scoped Toby resources such as AccountPaymentMethod, AccountInvoiceMethod, and AccountPaymentType must be accessed through forAccount(...) or createForAccount(...) so the client can target the nested accounts/{account_id}/... endpoints correctly.
These resources override the standard ResourceModel::save() flow for legacy nested Toby endpoints. A successful nested write returns true; a rejected nested write returns false.
For CXM-style account payment type lists, PaymentTypeEnum includes getCaseByAccountTypes() to map the Toby types payloads into enum cases while ignoring TELECOM, matching the legacy behavior.
CXM Product Events
Use Engeni\ApiClient\Resources\CXM\Product\Event to notify CXM that an external product finished a lifecycle transition. The resource posts to /cxm/products/events and is explicitly fillable so payload fields are preserved.
use Engeni\ApiClient\Resources\CXM\Product\Event;
use Engeni\ApiClient\Resources\CXM\Product\EventStatusEnum;
use Symfony\Component\HttpKernel\Exception\HttpException;
try {
Event::create([
'product_id' => 1029,
'product_type' => 'FLOKEE',
'event' => EventStatusEnum::ACTIVE->value,
'order_id' => 108400,
]);
} catch (HttpException $exception) {
report($exception); // Contains CXM's response message when available.
throw $exception;
}
This endpoint is usually called from queued lifecycle listeners. Do not ignore the result: a missing or failed CXM callback leaves the CXM order/product stuck in an intermediate status such as ACTIVATING.
There is also a CLI example at examples/cxm-product-event.php for manual diagnostics/replays. It prints the exact request captured by TracingHttpClient and exits non-zero with CXM's error message when the callback is rejected.
Legacy Namespace Coverage
This package now includes the first compatibility layer for the main legacy resource families:
Engeni\ApiClient\Resources\CXM\*Engeni\ApiClient\Resources\CXM\Product\*Engeni\ApiClient\Resources\CXM\SystemEvent\*Engeni\ApiClient\Resources\Landkit\*Engeni\ApiClient\Resources\Cleepo\*Engeni\ApiClient\Resources\Flokee\*Engeni\ApiClient\Resources\MediaManager\*Engeni\ApiClient\Resources\LaGuiaOnline\*Engeni\ApiClient\Resources\Toby\*
This compatibility layer now covers the legacy helper methods for the main CXM, Cleepo, Flokee, Landkit, MediaManager, and Toby resource families, while keeping the new typed Api Client resource model. The focus is still on migration-safe endpoint coverage rather than reproducing every legacy internal pattern.
Eloquent Patches
The package also ships Laravel 12 Eloquent patch files under patches/eloquent/12.0 and advertises them in composer.json via netresearch/composer-patches-plugin.
These patches are global consumer-app changes. They extend Eloquent so Laravel models can define belongsTo() and hasMany() relations that point to ApiClient resources, and they add eager-loading support for those patched relation types.
Architecture at a Glance
- Client – Holds the base URI, default headers, and exposes
newQuery()for every resource definition. - HttpClientInterface – Abstraction over the transport layer (backed by Laravel's HTTP client).
- Query\Builder – Fluent query object that translates Eloquent-style calls into Engeni-friendly query strings.
- ResourceModel – Minimal model layer that hydrates responses, offers attribute access, collections, and paginator interop.
- Support utilities – Helpers for pagination, response parsing, and tests (see
tests/Support).
Documentation
See context/guide.md for an extensive reference covering:
- Contract diagram, dependency injection guidelines, and how to provide your own HTTP transport.
- Every query builder method with signatures, behaviour notes, and examples.
- Pagination semantics (mapping Engeni
meta/linksinto Laravel paginators). - Testing recipes (mocking
HttpClientInterface, using Orchestra, asserting request shapes). - Pipelines, coding standards, and the roadmap for introducing write operations.
See context/resources.md for the concrete resource catalog, including every shipped resource class, its path root, and each compatibility/helper method exposed by that resource.
Quality Gates
composer install
composer lint # runs Pint in --test mode
composer test # runs PHPUnit + Orchestra suite
composer test:coverage # runs tests with HTML and text coverage reports
composer test:coverage:clover # runs tests with Clover XML coverage report
After running composer test:coverage, open coverage/html/index.html in your browser to view the detailed coverage report.
bitbucket-pipelines.yml executes the same commands on pull requests and on merges to master, ensuring styling and tests stay green before code lands.
Contributing
- For writable resources, define the mass-assignment contract (
$fillableor$guarded = []) and add tests that assert the outbound JSON payload and error path. - Prefer new contracts over deep inheritance; the package leans on dependency injection to ease mocking.
- Update docs and tests whenever you add behaviour.
License
GNU GPLv3 © Engeni International LLC.
engeni/api-client-2 适用场景与选型建议
engeni/api-client-2 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 1.57k 次下载、GitHub Stars 达 0, 最近一次更新时间为 2025 年 11 月 10 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「engeni」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 engeni/api-client-2 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 engeni/api-client-2 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 engeni/api-client-2 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
统计信息
- 总下载量: 1.57k
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 2
- 依赖项目数: 0
- 推荐数: 1
其他信息
- 授权协议: GPL-3.0-or-later
- 更新时间: 2025-11-10