一键导入
saloon
Saloon-based service layer pattern for all external API integrations. Every new external API integration must use Saloon — no raw HTTP calls.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Saloon-based service layer pattern for all external API integrations. Every new external API integration must use Saloon — no raw HTTP calls.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Readonly data containers with typed factory methods (`fromArray`, `fromModel`, `fromCollection`, `fromRequest`) used to pass structured data between application layers — especially for external API responses, Eloquent models, and service boundaries. Use this skill whenever creating, reviewing, or refactoring DTOs, Data Transfer Objects, value objects for inter-layer communication, or mapping payloads from APIs, models, or collections into typed PHP objects. Also trigger when the user mentions spatie/laravel-data alternatives, data mapping, or payload normalization in a Laravel context.
Eloquent model conventions for mass assignment, casts, relationship naming, activity logging, and mandatory model tests (CRUD + relations).
Single-purpose business logic classes that encapsulate one well-defined business operation. Actions are the primary location for business logic in Laravel applications, invoked from controllers, commands, or jobs.
Albatros accounting API integration via Saloon. Use when working with app/Services/Albatros/, AlbatrosConnector, or Albatros DTOs.
Laravel Blade template conventions covering components, output escaping, security, structure, and formatting.
Artisan console command classes that serve as the CLI entry point for operations. Commands validate input and delegate all business logic to Actions or Services.
| name | saloon |
| description | Saloon-based service layer pattern for all external API integrations. Every new external API integration must use Saloon — no raw HTTP calls. |
| compatible_agents | ["architect","implement","refactor","review"] |
app/Services/{ServiceName}/.Http::get() is acceptable.saloonphp/saloon is installed and configured.config/services.php.Http::get(...), file_get_contents, curl)app/Services/{ServiceName}/Saloon\Http\Connector and handles authentication and base URLSaloon\Http\RequestdefaultQuery() for query parametersHasBody + use HasJsonBody trait with defaultBody()Http::get() is acceptable for simple binary file downloads (not API integrations)Services/SKILL.md for naming and class boundariesDirectory Structure:
app/Services/Stripe/
StripeConnector.php
StripeService.php
Requests/
Charges/
CreateChargeRequest.php
ListChargesRequest.php
DataObjects/
ChargeData.php
// Connector
namespace App\Services\Stripe;
use Saloon\Http\Connector;
class StripeConnector extends Connector
{
public function resolveBaseUrl(): string
{
return 'https://api.stripe.com/v1';
}
protected function defaultHeaders(): array
{
return [
'Authorization' => 'Bearer ' . config('services.stripe.secret'),
'Content-Type' => 'application/json',
];
}
}
// GET Request
use Saloon\Enums\Method;
use Saloon\Http\Request;
class ListChargesRequest extends Request
{
protected Method $method = Method::GET;
public function __construct(
protected int $limit = 10,
protected ?string $customer = null,
) {}
public function resolveEndpoint(): string
{
return '/charges';
}
protected function defaultQuery(): array
{
return array_filter([
'limit' => $this->limit,
'customer' => $this->customer,
]);
}
}
// POST Request
use Saloon\Contracts\Body\HasBody;
use Saloon\Enums\Method;
use Saloon\Http\Request;
use Saloon\Traits\Body\HasJsonBody;
class CreateChargeRequest extends Request implements HasBody
{
use HasJsonBody;
protected Method $method = Method::POST;
public function __construct(
protected int $amount,
protected string $currency,
protected string $customerId,
) {}
public function resolveEndpoint(): string
{
return '/charges';
}
protected function defaultBody(): array
{
return [
'amount' => $this->amount,
'currency' => $this->currency,
'customer' => $this->customerId,
];
}
}
// Service class
use Saloon\Exceptions\Request\FatalRequestException;
use Saloon\Exceptions\Request\RequestException;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
class StripeService
{
public function __construct(?StripeConnector $connector = null)
{
$this->connector = $connector ?? new StripeConnector();
}
/** @return Collection<int, ChargeData> */
public function listCharges(string $customerId): Collection
{
try {
$response = $this->connector->send(
new ListChargesRequest(customer: $customerId)
);
} catch (RequestException|FatalRequestException $exception) {
Log::warning('Stripe request failed.', [
'customer_id' => $customerId,
'message' => $exception->getMessage(),
]);
throw $exception;
}
return collect($response->json('data'))
->map(fn (array $item) => ChargeData::fromArray($item));
}
}
resolveEndpoint(), defaultQuery(), defaultBody()) independently.Http::get(...) or file_get_contents() for API integrationscurl directlyconfig()DTO/SKILL.md — service classes return typed DTOsServices/SKILL.md — general service class conventions