CursorPool
← 返回首页

symfony-api

Generic rules, skills, and agents for Symfony and PHP 8+ REST API backends: PSR-12, strict types, thin controllers, service/DTO/response layering, Doctrine ORM and validation best practices

cursor.directory·41
规则

REST API layer — thin controllers, services for logic, DTOs and response objects

REST API layer — thin controllers, services for logic, DTOs and response objects

## Controllers

- Keep controllers **thin**: validate input, call a **service** (or handler), then build and return the HTTP response. No business logic or direct persistence in controllers.
- Extend your framework’s base controller (e.g. Symfony’s `AbstractController`) for helpers (e.g. JSON response, 404/403 helpers). Use **attributes** for routing, caching, and security.
- Use **dependency injection** to get services (constructor or action method arguments). Do not pull services from the container by name.
- Return consistent error responses (e.g. JSON with message keys and appropriate status codes). Use the same helpers for 404, 403, and validation errors across the API.

## Services (business logic)

- Put all **business logic** in **service classes** (or “handlers”/“application services”). One service per aggregate or feature area. Services should be **final** and **readonly** where possible.
- Services receive **DTOs** or value objects as input and return **entities** or **void**. They use the entity manager, repositories, and event dispatcher via constructor injection. Dispatch **domain or audit events** after persist/flush, not from controllers.
- Use `assert()` only for **invariants** (e.g. “entity loaded from DB is non-null after find”). Never use assert for **user input**; validate input with the validator component.

## Input (DTOs) and validation

- Use **DTOs** (or Form/Request objects) for request input. Prefer **constructor-promoted readonly properties** and typed fields (e.g. `?string` for optional UUIDs).
- Attach **validation constraints** to the DTO (or underlying object) so validation is reusable (API, forms, CLI). Use the Validator component; return validation errors as structured API responses with 422 or 400.
- Validate **before** calling services. Use entity-existence or custom constraints for IDs and references; return 404 when a referenced entity is missing.

## Output (responses and representations)

- Use **response objects** or **API resources** that implement `JsonSerializable` and expose a **HTTP status code**. For lists, use a consistent structure (e.g. `items`, `total`, pagination fields).
- Build **representation objects** (views/resources) from **entities** via a dedicated method (e.g. `createFromEntity(Entity $e): self`) so serialization stays in one place. Keep representation classes free of business logic.
- Document the API (e.g. OpenAPI) from the same DTOs and response types where possible so docs stay in sync with code.

## Lists and pagination

- For list endpoints, use a **single DTO** for filters, sort, and pagination. Prefer a **repository interface** that returns a paginator adapter (e.g. Pagerfanta) and filter options. Use **DBAL** or query builder for complex list queries to avoid N+1 and keep controllers thin.
规则

Doctrine entities and repositories — ORM attributes, UUIDs, timestamps, ORM vs DBAL

Doctrine entities and repositories — ORM attributes, UUIDs, timestamps, ORM vs DBAL

## Entities

- Use **Doctrine ORM PHP attributes** for mapping: `#[ORM\Entity]`, `#[ORM\Table(name: '...')]`, `#[ORM\Column]`, `#[ORM\ManyToOne]`, `#[ORM\OneToMany]`, `#[ORM\JoinColumn]`. Set `repositoryClass` on the entity.
- Organize entities under a clear **namespace** (e.g. `Entity\` or `Domain\*`). Prefer **immutable** design where the domain allows: readonly constructor arguments, minimal setters. Use setters only when the domain requires mutable state.
- Use **PHP backed enums** for status and type fields; map to string or integer in the database via Doctrine types or attributes as per your setup.

## Identifiers and timestamps

- Prefer **UUIDs** (e.g. `Symfony\Component\Uid\Uuid`) for primary keys when you need stable, non-sequential IDs. Expose string IDs in the API (e.g. `->toRfc4122()`). For **composite keys**, use `#[ORM\Id]` on each part and appropriate `JoinColumn`.
- For **timestamps** (e.g. created/updated), use an **injectable clock** in domain code (or a shared service) so tests can control time. Use `datetime_immutable` column type. Do not call `new \DateTimeImmutable()` or `time()` directly in entities.

## Repositories

- **ORM repositories**: Extend Doctrine’s `ServiceEntityRepository`. Use for loading **single entities** and **simple queries**. Keep query methods focused and named by intent (e.g. `findOneByEmail`, `findActive`).
- **DBAL / query builder**: Use for **complex list queries**, **reporting**, and when you need raw SQL or query builder without full entity hydration. Implement a **list interface** (e.g. `findAll(filters, sort, pagination)`, `getPaginatorAdapter`, `getFilterValues`) so controllers and services stay decoupled from persistence details.
- Do not put business logic in repositories; repositories are for data access only.

## Migrations

- Generate **migrations** via Doctrine Migrations (or your project’s migration tool) after entity changes. Do not hand-edit the database schema without a corresponding migration. Keep migrations reversible where practical.
规则

backend-api-reviewer

Reviews PHP/Symfony API code for layer compliance, security, and consistency with thin controllers, service/DTO/response patterns, and Doctrine conventions

# Backend API Reviewer

You are a reviewer for PHP/Symfony REST API backends. When reviewing code, check the following.

## Layer compliance

- **Controllers**  
  Must be thin: validate input, call a service, build and return the response. No business logic, no direct repository calls for writes. Use consistent helpers for JSON, 404, 403, and validation errors.

- **Services**  
  All write logic and event dispatch live in services (or handlers). Services are final and readonly where possible; they take DTOs and return entities or void. Use assertions only for invariants (e.g. loaded entity type); use the validator for user input.

- **DTOs**  
  Input is validated via constraints (on the DTO or underlying object). Entity existence and cross-field rules use custom or built-in constraints. No raw request data used without validation.

- **Representations and responses**  
  Output is built from entities via representation objects (e.g. `fromEntity`); responses expose a status code and JSON consistently. No business logic in representation classes.

- **Lists**  
  List endpoints use a single list DTO and a repository or service that returns a paginator and filter options. Prefer DBAL or query builder for complex lists to avoid N+1.

## Security and validation

- All user input is validated before use. No raw input in queries or business logic. IDs and references are validated (e.g. entity existence); return 404 when a referenced resource is missing.
- Access control is applied (firewall, access_control, or voters). No privilege escalation or missing authorization checks.

## Conventions

- `declare(strict_types=1);` in every PHP file. PSR-12 and project code style. Constructor injection only; final/readonly where the project uses them.
- Repositories: ORM for single-entity loads; DBAL or list interface for complex lists. Timestamps in entities use an injectable clock, not raw `new \DateTimeImmutable()`.
- Events: domain or audit events are dispatched from services after persist/flush, not from controllers.

Report issues with file/line and a short fix suggestion. Refer to the plugin’s PHP/Symfony, API layer, and Doctrine rules where relevant.
规则

symfony-backend-helper

General-purpose assistant for Symfony and PHP 8 backends with Doctrine, DTOs, services, and REST API layer (thin controllers, validation, representation objects)

# Symfony Backend Helper

You assist with Symfony and PHP 8+ backends that use:

- **Thin controllers** that validate input, call services, and return JSON responses.
- **Services** (or handlers) that contain business logic, use DTOs as input, and return entities or void. They use the entity manager, repositories, and event dispatcher via dependency injection.
- **DTOs** for request input, with validation constraints so validation is reusable across API, forms, and CLI.
- **Representation objects** (views/resources) that map entities to JSON; **response objects** that carry status codes and implement JSON serialization.
- **Doctrine ORM** for entities and simple queries; **DBAL** or query builder for complex list/report queries. **Strict types**, **final/readonly** where appropriate, and **constructor injection** throughout.

When the user asks to add a feature, fix a bug, or refactor:

1. **Identify the layer**: Controller, service, DTO, representation, response, entity, or repository. Follow existing naming and namespace conventions in the project.
2. **Preserve conventions**: `declare(strict_types=1);`, typed properties and return types, no business logic in controllers, validation on DTOs, events dispatched from services.
3. **Use framework and project patterns**: Symfony attributes for routing and security; validator for input; injectable clock for time in domain code; consistent error and list response shape.
4. **Testing**: Suggest or align with existing test patterns (e.g. endpoint tests, service unit tests) when relevant.

If a convention is unclear (e.g. custom validator or event naming), infer from the codebase or ask. Prefer applying the plugin’s generic PHP/Symfony, API layer, and Doctrine rules when making changes.
Skill

custom-validator-constraint

Add a custom Symfony Validator constraint and validator class. Use when validating DTOs or objects with rules not covered by built-in or existing constraints (e.g. cross-field or entity-existence checks).

# Custom Validator Constraint

## 1. Constraint class

- **Namespace**  
  Place in your validator constraints namespace (e.g. `App\Validator\Constraints\` or a shared module).

- **Class**  
  `final class` extending `Symfony\Component\Validator\Constraint`.

- **Constructor**  
  Accept options (e.g. `entity`, `fieldId`). Call `parent::__construct($options)` or pass options. Throw `MissingOptionsException` when a required option is missing.

- **Message**  
  Define a `public const string MESSAGE = 'translation_key';` (or a literal message) for the violation.

- **Validator**  
  Implement `validatedBy(): string` and return the validator class name (e.g. `MyConstraintValidator::class`).

## 2. Validator class

- **Class**  
  `final class MyConstraintValidator extends ConstraintValidator`. Name: constraint name + `Validator`.

- **Method**  
  `validate($value, Constraint $constraint): void`. Use `$this->context->buildViolation($constraint::MESSAGE)` (and `setParameter(...)` if needed), then `addViolation()` on failure. For **class-level** constraints, `$value` is the whole object; read other fields from it. For **property** constraints, `$value` is the property value; read the parent object from the context if needed.

- **Options**  
  Read constraint options (e.g. `$constraint->entity`, `$constraint->fieldId`) to perform repository or cross-field checks. Use the validator’s dependencies (injected via constructor) to load entities or run queries.

## 3. Using the constraint

- Attach the constraint to the DTO (or object) via **annotation/attribute** on the property or class, or in your project’s **constraint collection** (e.g. in a `getConstraints()` method that returns a constraint list per field). For class-level constraints, add them to the root.

## Example (entity existence)

- Constraint: e.g. `EntityExists(entity: User::class)`.
- Validator: resolve the repository for the given entity class, load by ID (from the value or another field); if not found, add a violation. Reuse a generic entity-exists validator if your project has one.
Skill

doctrine-entity-repository

Create or modify Doctrine ORM entities and repositories (ORM and DBAL). Use when adding new entities, tables, relations, or list/report repositories in a Symfony + Doctrine backend.

# Doctrine Entity & Repository

## New entity

1. **Class and namespace**  
   Place the entity in your domain/entity namespace (e.g. `App\Entity\*`). Use Doctrine ORM **attributes**: `#[ORM\Entity(repositoryClass: ...)]`, `#[ORM\Table(name: '...')]`, `#[ORM\Column(...)]`, `#[ORM\ManyToOne]` / `#[ORM\OneToMany]` / `#[ORM\JoinColumn]`. Prefer **readonly** and constructor promotion for required fields where the domain allows.

2. **Identifiers**  
   Use `Symfony\Component\Uid\Uuid` for single-column primary keys when you need stable IDs; expose in the API as `->toRfc4122()`. For **composite keys**, put `#[ORM\Id]` on each part and configure `JoinColumn` appropriately.

3. **Timestamps**  
   In the constructor, set created/updated via an **injectable clock** or shared time service, not `new \DateTimeImmutable()`. Use column type `datetime_immutable`.

4. **Repository**  
   Create a repository class extending Doctrine’s `ServiceEntityRepository` and set it in the entity’s `repositoryClass`. Use it for loading single entities and simple, intent-named queries.

5. **Migration**  
   Generate a migration after entity changes; do not skip migrations or edit the schema by hand without a migration.

## New list or report repository (DBAL)

1. **Class**  
   Create a repository (e.g. in `Repository\Dbal\*` or your project’s equivalent) that uses the DBAL connection and query builder (or raw SQL) for complex filters, sorting, and pagination.

2. **List interface**  
   If this list backs an API endpoint, implement your project’s list contract (e.g. `findAll(filters, sort)`, `getPaginatorAdapter`, `getFilterValues`) and return a paginator adapter (e.g. Pagerfanta) and filter options. This keeps controllers and services independent of persistence details.

3. **Reuse**  
   Reuse existing traits or base classes for common filter logic and naming so queries stay consistent and maintainable.

## Relations

- Use `targetEntity: Other::class` and `JoinColumn(nullable: true|false)`. For one-to-many collections, use `OneToMany` with `mappedBy` and the inverse `ManyToOne` to avoid orphans.
- For enum-like fields, use **PHP backed enums** and map them to string/int in the database via your Doctrine enum or scalar type setup.
Skill

symfony-api-endpoint

Add a new REST endpoint in a Symfony API using controller, service, DTO, and response objects. Use when adding a new API route, CRUD action, or resource endpoint in a Symfony backend that uses thin controllers and service/DTO/response layering.

# Add a Symfony REST API Endpoint

Use when adding a new REST endpoint that follows: **Controller → validate DTO → Service → build response → return JSON**.

## Checklist

- [ ] **DTO (input)**  
  Create or reuse a DTO with constructor-promoted readonly properties. Attach **validation constraints** (Symfony Validator) so the same rules apply for API and other entry points. Optionally implement an API-docs interface (e.g. OpenAPI schema) on the DTO.

- [ ] **Service (business logic)**  
  Create or extend a service class (e.g. in `Service\` or `Handler\`). Use a **final readonly** class; inject EntityManager, repositories, and EventDispatcher. Methods accept the DTO and return the entity or void. After persist/flush, dispatch domain or audit events. No business logic in the controller.

- [ ] **Representation (output)**  
  Create or reuse a representation object (View/Resource) that maps entity → array/JSON. Use a static factory (e.g. `fromEntity(Entity $e): self`) and `JsonSerializable`. Optionally add OpenAPI schema for documentation.

- [ ] **Response**  
  Use your project’s response base class (e.g. implements `getStatusCode()` and `JsonSerializable`). For lists, use a consistent list response type with items and pagination metadata.

- [ ] **Controller**  
  New action (or new controller). Use `#[Route(..., methods: ['GET'|'POST'|...])]`. Validate the request (body/query) into the DTO with the Validator; on failure return validation errors (e.g. 422). Call the service with the DTO; build the representation and response; return JSON. On not-found or forbidden, return 404/403 using your standard helpers.

- [ ] **Security**  
  Ensure the route is covered by your security configuration (firewall, access control or voters). Use attributes (e.g. `#[IsGranted]`) where applicable.

## Conventions

- Use **UUIDs** for resource IDs in the API when your domain uses them; expose as strings (e.g. `->toRfc4122()`).
- For **list endpoints**, use a single list DTO (filters, sort, page size) and a repository or service that returns a paginator adapter and filter options. Keep the controller thin.
- Do not put business logic or direct repository writes in the controller.
规则

run-static-analysis

Run PHPStan (and optionally ECS or PHP-CS-Fixer) in the PHP project; use Ddev or Docker when the project uses them

# Run static analysis

When the user wants to validate PHP code or fix static analysis issues:

1. **Ddev / Docker**  
   If the project uses **Ddev** or **Docker** for local PHP, run all PHP tools **inside the container** so the same PHP version and `vendor/` are used:
   - **Ddev**: From the repo root use `ddev phpstan` (if the project defines it), or `ddev exec "php backend/bin/phpstan analyse"` when the Composer root is `backend/`. For PHP-CS-Fixer or ECS: `ddev exec "php backend/bin/php-cs-fixer fix"` or `ddev exec "php backend/vendor/bin/ecs"` (adjust paths to the project’s Composer root).
   - **Docker Compose**: Use `docker compose exec <service> php bin/phpstan analyse` (or the project’s equivalent) from the directory that contains the Compose file. Use the service and paths that match the project.
   If unsure, check for `.ddev/` or `docker-compose.*` and any custom commands (e.g. `.ddev/commands/web/phpstan`).

2. **PHPStan**  
   From the Composer root (or repo root if same), run:
   - A project command such as `ddev phpstan` or `composer phpstan` if defined, or  
   - `vendor/bin/phpstan analyse` (or `php bin/phpstan analyse` with the correct path when Composer root is a subdir).  
   Fix reported errors by level; do not change intended behavior.

3. **Code style (ECS or PHP-CS-Fixer)**  
   If the project uses **Easy Coding Standard (ECS)** or **PHP-CS-Fixer**, run the project’s style command inside the container when applicable (e.g. `ddev exec "php backend/bin/php-cs-fixer fix"` or `ddev exec "php backend/vendor/bin/ecs check --fix"`). Change only style, not behavior.

4. **Scope**  
   Run from the repository root; Composer root may be a subdirectory (e.g. `backend/`). Use the paths and commands that match the project. Report any config or path issues to the user.

Interpret PHPStan output (e.g. missing types, wrong generics, Symfony/Doctrine extension messages) and suggest minimal code changes to satisfy the tool without altering behavior.
规则

hooks

Event hooks configuration

{
  "hooks": {
    "afterFileEdit": [
      {
        "command": "./scripts/cs-fix.sh",
        "matcher": ".*\\.php$"
      }
    ]
  }
}
规则

Run PHP, Composer, PHPStan, and code style tools via Ddev or Docker when the project uses them

Run PHP, Composer, PHPStan, and code style tools via Ddev or Docker when the project uses them

# PHP in Ddev or Docker

When the project uses **Ddev** or **Docker** for local PHP:

- **Run PHP tools inside the container** so the same PHP version, extensions, and `vendor/` are used. Do not run `php`, `composer`, `vendor/bin/phpstan`, or `vendor/bin/php-cs-fixer` (or ECS) directly on the host unless the project is set up for that.
- **Ddev**: Use `ddev composer ...`, `ddev php ...`, or custom Ddev commands (e.g. `ddev phpstan`) from the **repository root**. If the Composer root is a subdirectory (e.g. `backend/`), Ddev runs Composer there; run PHP binaries with that path, e.g. `ddev exec "php backend/bin/phpstan analyse"` or the project’s `ddev phpstan` command.
- **Docker Compose**: Use `docker compose exec <service> ...` (e.g. `docker compose exec php php bin/phpstan analyse`) from the directory that contains `compose.yaml` / `docker-compose.yml`. Use the service name and working directory that match the project (often the app root or Composer root).
- **Hooks and scripts**: Any script that runs PHPStan, PHP-CS-Fixer, or ECS should detect Ddev (e.g. `ddev describe` or a project script) and, when available, run the tool via `ddev exec ...` or a Ddev custom command so execution happens inside the container.
- **Finding the Composer root**: If the repo has a subdirectory that contains `composer.json` (e.g. `backend/`), that is usually the Composer root; run `composer` and PHP binaries from that directory inside the container, or pass the correct path (e.g. `backend/bin/phpstan`).

This keeps analysis and formatting consistent with CI and other environments that use the same container or PHP setup.
规则

PHP and Symfony backend coding standards — strict types, PSR-12, dependency injection, naming

PHP and Symfony backend coding standards — strict types, PSR-12, dependency injection, naming

You are an expert in PHP and Symfony and related web development technologies.

## Core principles

- Write concise, technical PHP with accurate examples. Prioritize SOLID principles and clean architecture.
- Follow PHP and Symfony best practices for consistency, readability, and maintainability.
- Prefer iteration and modularization over duplication. Use consistent, descriptive names for variables, methods, and classes.

## PHP standards

- Use **strict typing**: `declare(strict_types=1);` at the top of every PHP file.
- Adhere to **PSR-12** (and PSR-1/PSR-4) for code style. Use PHP CS Fixer or your project’s code-style tool to enforce it.
- Leverage modern PHP features where appropriate: **typed properties**, **constructor property promotion**, **readonly** classes and properties, **union and nullable types**, **match expressions**, **backed enums** for status/type fields.
- Prefer **final** classes for services, handlers, DTOs, and response/representation objects to prevent unintended inheritance.
- Use **explicit return types** and **parameter type hints** for all methods. Use `void` for procedures; use `return null;` when a function explicitly returns null and `return;` for void.
- Use **constructor injection only** for dependencies. No property injection. Use Symfony’s `#[Autowire]` (or equivalent) for configuration parameters.
- **Imports**: One `use` per class; order logically (framework/vendor, then project). Use fully qualified names for global exceptions (e.g. `\InvalidArgumentException`).
- **Exceptions**: Use custom exception classes where they add value; suffix with `Exception`. Exception messages should start with a capital letter and end with a period; use `sprintf()` for interpolation. Use the framework’s exception handling and logging; use try-catch only for expected, recoverable cases.
- **Time**: Prefer an injectable clock (or test double) for “now” in domain code rather than `new \DateTimeImmutable()` or `time()` so tests can control time.

## Symfony standards

- Follow **Symfony best practices**: thin controllers, business logic in services, use attributes for routing and security, dependency injection via constructor or method arguments.
- Use **environment variables** for infrastructure configuration (e.g. database URL, API keys); use **parameters** for application behavior (e.g. feature flags, sender email). Use **secrets** for sensitive configuration.
- Use **autowiring** and **autoconfiguration** for services. Prefer **private** services; access via DI, not `$container->get()`.
- Use **Doctrine PHP attributes** for entity mapping. Use **short, prefixed parameter names** (e.g. `app.`) to avoid collisions.
- **Naming**: PascalCase for class/file names; camelCase for methods, functions, and variables; SCREAMING_SNAKE_CASE for constants; snake_case for config keys, route names, and Twig variables. Suffix interfaces with `Interface`, traits with `Trait`, abstract classes with `Abstract`.

## Documentation and structure

- One class per file. Declare class properties before methods; public, then protected, then private. Keep controllers and services small and focused.
- Add PHPDoc where it improves clarity or static analysis (e.g. `@param`, `@return`, generics). Do not remove existing PHPDoc or comments from code unless refactoring.

来源:https://github.com/MaartenDeRammelaere1/symfony-cursor-plugin