From 90b1395566a21d93b64ef54d56629f0c586e4f6d Mon Sep 17 00:00:00 2001 From: bidi Date: Wed, 12 Aug 2026 00:05:03 +0300 Subject: [PATCH] added summary, FAQ Signed-off-by: bidi --- docs/book/v7/architecture-at-a-glance.md | 66 +++++++++++++++++ docs/book/v7/commands/create-admin-account.md | 33 +++++++++ .../commands/display-available-endpoints.md | 36 ++++++++++ .../commands/generate-database-migrations.md | 38 ++++++++++ docs/book/v7/commands/generate-tokens.md | 37 ++++++++++ docs/book/v7/core-features/authentication.md | 67 +++++++++++++++++ docs/book/v7/core-features/authorization.md | 46 ++++++++++++ .../v7/core-features/content-validation.md | 49 +++++++++++++ .../v7/core-features/dependency-injection.md | 40 +++++++++++ docs/book/v7/core-features/error-reporting.md | 55 ++++++++++++++ docs/book/v7/core-features/exceptions.md | 46 ++++++++++++ .../rendering-and-sending-emails.md | 40 +++++++++++ .../book/v7/extended-features/core-and-app.md | 39 ++++++++++ .../v7/extended-features/handler-structure.md | 37 ++++++++++ .../injectable-input-filters.md | 39 ++++++++++ .../v7/extended-features/problem-details.md | 38 ++++++++++ .../v7/extended-features/route-grouping.md | 37 ++++++++++ docs/book/v7/flow/default-library-flow.md | 28 ++++++++ docs/book/v7/flow/library-flow-for-email.md | 28 ++++++++ docs/book/v7/flow/middleware-flow.md | 32 +++++++++ docs/book/v7/installation/composer.md | 52 ++++++++++++++ .../v7/installation/configuration-files.md | 39 ++++++++++ docs/book/v7/installation/doctrine-orm.md | 65 +++++++++++++++++ docs/book/v7/installation/faq.md | 32 +++++++++ docs/book/v7/installation/getting-started.md | 35 +++++++++ .../v7/installation/test-the-installation.md | 45 ++++++++++++ docs/book/v7/introduction/file-structure.md | 56 +++++++++++++++ docs/book/v7/introduction/introduction.md | 55 ++++++++++++++ docs/book/v7/introduction/packages.md | 43 +++++++++++ docs/book/v7/introduction/psr.md | 50 +++++++++++++ .../v7/introduction/server-requirements.md | 51 +++++++++++++ .../book/v7/openapi/generate-documentation.md | 42 +++++++++++ docs/book/v7/openapi/getting-help.md | 27 +++++++ .../book/v7/openapi/initialized-components.md | 49 +++++++++++++ docs/book/v7/openapi/introduction.md | 28 ++++++++ docs/book/v7/openapi/render-documentation.md | 45 ++++++++++++ docs/book/v7/openapi/use-documentation.md | 59 +++++++++++++++ docs/book/v7/openapi/write-documentation.md | 50 +++++++++++++ .../v7/reference/account-anonymization.md | 38 ++++++++++ docs/book/v7/security/basic-security.md | 49 +++++++++++++ docs/book/v7/security/oauth2-security.md | 39 ++++++++++ .../api-tools-vs-dotkernel-api.md | 38 ++++++++++ .../discovery-phase.md | 40 +++++++++++ .../transition-approach.md | 34 +++++++++ docs/book/v7/tutorials/api-evolution.md | 45 ++++++++++++ docs/book/v7/tutorials/cors.md | 44 ++++++++++++ .../create-book-module-via-dot-maker.md | 71 +++++++++++++++++++ docs/book/v7/tutorials/create-book-module.md | 68 ++++++++++++++++++ .../v7/tutorials/find-user-by-identity.md | 51 +++++++++++++ .../book/v7/tutorials/token-authentication.md | 53 ++++++++++++++ docs/book/v7/upgrading/UPGRADE-6.0.md | 36 ++++++++++ docs/book/v7/upgrading/UPGRADE-7.0.md | 31 ++++++++ docs/book/v7/upgrading/upgrading.md | 32 +++++++++ 53 files changed, 2323 insertions(+) diff --git a/docs/book/v7/architecture-at-a-glance.md b/docs/book/v7/architecture-at-a-glance.md index cf062625..8e3092b5 100644 --- a/docs/book/v7/architecture-at-a-glance.md +++ b/docs/book/v7/architecture-at-a-glance.md @@ -1,5 +1,11 @@ # Architecture at a Glance +## Summary + +A single-page map of how Dotkernel API is put together: the Core/App split introduced in 6.0, the Headless Platform and modular-monolith layout, the path a request takes through the middleware pipeline into a handler, the roles of handlers, services, repositories, input filters and entities, how configuration and dependency injection are organized, the PSRs behind it all, and the four security layers. + +## Details + Dotkernel API follows a modular, middleware-based architecture designed for scalability and maintainability. Understanding the core structure is essential before diving into development. @@ -226,3 +232,63 @@ They ensure that your code can integrate with other PSR-compliant libraries. | Handler | Request/response mapping | Extract user ID, call service | | Service | Business rules | Validate user data, calculate totals | | Repository | Data queries | Find users, save entity | + +## FAQ + +**Q: Where should I put a new feature?** + +A: In the App layer, under `src/App/src/` or your own module. +Core (`src/Core/src/`) is reserved for infrastructure such as authentication, database access and shared entities. +See [Core and App](extended-features/core-and-app.md). + +**Q: What is the execution order from request to database?** + +A: `Handler → Service → Repository → Database`. +The handler maps the request, the service holds business rules, and the repository is the only layer that touches the database. + +**Q: Which middlewares run before my handler?** + +A: CORS, authentication, authorization, content negotiation and routing, in that order, as defined in `config/pipeline.php`. +See [Middleware flow](flow/middleware-flow.md). + +**Q: What runs after the handler returns?** + +A: The response-side middlewares: problem details for exceptions, the deprecation headers, and any custom response headers. + +**Q: What are the built-in modules?** + +A: `Admin`, `User`, `Security`, `App` and `Core`. +Your own modules — `Book`, `Product`, `Article` and so on — follow the same pattern. + +**Q: Is this a monolith or microservices?** + +A: Out of the box it is a modular monolith, structured so that individual modules can later be split into separate services. + +**Q: Where is business logic supposed to live?** + +A: In services, not handlers or repositories. +Handlers extract and validate request data and delegate; repositories only query and persist. + +**Q: How do dependencies reach my classes?** + +A: Through constructor injection declared with the `#[Inject]` attribute and resolved by `AttributedServiceFactory`. +See [Dependency injection](core-features/dependency-injection.md). + +**Q: Which configuration file does what?** + +A: `config.php` is the entry point, `pipeline.php` the middleware stack, `container.php` the DI container, and `config/autoload/` holds per-concern files — with `local.php` for environment-specific values that stay out of version control. + +**Q: What are the four security layers?** + +A: Authentication with OAuth2 tokens, authorization with RBAC permissions, input validation with input filters, and content negotiation on `Accept` and `Content-Type`. +See [Basic security](security/basic-security.md). + +**Q: Which databases are supported?** + +A: MariaDB and PostgreSQL. +See [Server requirements](introduction/server-requirements.md). + +**Q: Which PSRs are core rather than supporting?** + +A: PSR-7, PSR-11 and PSR-15 are core to the architecture; the rest arrive through dependencies. +See [PSRs](introduction/psr.md). diff --git a/docs/book/v7/commands/create-admin-account.md b/docs/book/v7/commands/create-admin-account.md index 551dcdf4..6bd90cd1 100644 --- a/docs/book/v7/commands/create-admin-account.md +++ b/docs/book/v7/commands/create-admin-account.md @@ -1,5 +1,10 @@ # Creating admin accounts in Dotkernel API +## Summary + +The `admin:create-admin` CLI command creates an administrator account from the command line, taking an identity, a password and a first and last name. +Accounts created this way always receive the `admin` role. + ## Usage Run the following command in your application’s root directory: @@ -35,3 +40,31 @@ You can get more help with this command by running: ```shell php ./bin/cli.php help admin:create ``` + +## FAQ + +**Q: Can I choose the role of the created account?** + +A: No. The command always assigns the `admin` role; other roles must be set afterwards. +See [Authorization](../core-features/authorization.md). + +**Q: What can I use as the identity?** + +A: Either a username or an email address, as long as it is not already taken. + +**Q: My name or password contains special characters and the command fails. What do I do?** + +A: Surround the value in double quotes so the shell passes it through unchanged. + +**Q: Are the short and long option forms equivalent?** + +A: Yes. +`-i`, `-p`, `-f` and `-l` are shorthand for `--identity`, `--password`, `--firstName` and `--lastName`. + +**Q: How do I know the account was created?** + +A: The command prints `Admin account has been created.` and the account is immediately usable. + +**Q: Where do I see the full command help?** + +A: Run `php ./bin/cli.php help admin:create`. diff --git a/docs/book/v7/commands/display-available-endpoints.md b/docs/book/v7/commands/display-available-endpoints.md index 2c58b880..fed37a41 100644 --- a/docs/book/v7/commands/display-available-endpoints.md +++ b/docs/book/v7/commands/display-available-endpoints.md @@ -1,5 +1,10 @@ # Displaying Dotkernel API endpoints using dot-cli +## Summary + +The `route:list` CLI command inspects the application's routes at runtime and prints every endpoint's request method, route name and path. +Results can be filtered by name, path or method. + ## Usage Run the following command in your application’s root directory: @@ -71,3 +76,34 @@ Get more help by running this command: ```shell php ./bin/cli.php route:list --help ``` + +## FAQ + +**Q: Is the output generated from a static file?** + +A: No. The command walks the application's registered routes in realtime, so it always reflects the current configuration. + +**Q: Which filters are available?** + +A: `-i|--name`, `-p|--path` and `-m|--method`. +They are case-insensitive and can be combined. + +**Q: Why do route names matter beyond documentation?** + +A: Because a permission in Dotkernel API is a route name, so this listing is also the list of permissions you can grant. +See [Authorization](../core-features/authorization.md). + +**Q: My new route does not appear. What should I check?** + +A: That its module's `RoutesDelegator` is registered and the route is declared there. +See [Route grouping](../extended-features/route-grouping.md). + +**Q: How is this different from the OpenAPI documentation?** + +A: `route:list` reports what the application actually routes; the OpenAPI file describes the documented contract. +Comparing the two is a quick way to spot undocumented endpoints. +See [OpenAPI documentation](../openapi/introduction.md). + +**Q: Where do I see the full command help?** + +A: Run `php ./bin/cli.php route:list --help`. diff --git a/docs/book/v7/commands/generate-database-migrations.md b/docs/book/v7/commands/generate-database-migrations.md index d0df10ef..c7dadfa8 100644 --- a/docs/book/v7/commands/generate-database-migrations.md +++ b/docs/book/v7/commands/generate-database-migrations.md @@ -1,5 +1,10 @@ # Generate a database migration without dropping custom tables +## Summary + +`doctrine-migrations diff` generates migrations from your entity mappings, but it also emits `DROP TABLE` statements for unmapped tables such as `oauth_*`. +Passing a `filter-expression` excludes those prefixes so the generated migration leaves them alone. + ## Usage Run the following command in your application’s root directory: @@ -62,3 +67,36 @@ You can get more help with this command by running: ```shell vendor/bin/doctrine-migrations help diff ``` + +## FAQ + +**Q: Why does the generated migration try to drop my `oauth_*` tables?** + +A: Because no Doctrine entity describes them. +From the ORM's point of view they are not part of the schema, so `diff` proposes removing them. + +**Q: What should I do with a migration that already contains those DROP queries?** + +A: Delete that migration file and regenerate it with a `filter-expression`, rather than editing the queries out by hand. + +**Q: Why do the quotes differ between platforms?** + +A: Windows shells require double quotes around the expression, while Linux and macOS shells require single quotes to prevent the pattern from being interpreted. + +**Q: How do I exclude more than one prefix?** + +A: Concatenate the prefixes with a pipe inside the negative lookahead, for example `/^(?!foo_|bar_)/`. + +**Q: The filter is ignored in PowerShell. What is happening?** + +A: PowerShell treats `^` as a special character and strips it, so the expression arrives without the anchor. +Escaping does not help — run the command from your IDE, a Linux shell, or the Command Prompt instead. + +**Q: Where do generated migrations end up?** + +A: Under `data/doctrine/migrations/`. +See [Doctrine ORM](../installation/doctrine-orm.md). + +**Q: How do I see all options for the command?** + +A: Run `vendor/bin/doctrine-migrations help diff`. diff --git a/docs/book/v7/commands/generate-tokens.md b/docs/book/v7/commands/generate-tokens.md index a27cc52f..29bc88aa 100644 --- a/docs/book/v7/commands/generate-tokens.md +++ b/docs/book/v7/commands/generate-tokens.md @@ -1,5 +1,12 @@ # Generating tokens in Dotkernel API +## Summary + +`token:generate` is a multipurpose CLI command that issues the tokens different parts of the API require. +Currently it supports the `error-reporting` type, whose generated value is pasted into `config/autoload/error-handling.global.php`. + +## Details + This is a multipurpose command that allows creating tokens required by different parts of the API. ## Usage @@ -62,3 +69,33 @@ Save and close `config/autoload/error-handling.global.php`. ```shell php ./bin/clear-config-cache.php ``` + +## FAQ + +**Q: Which token types can the command generate?** + +A: `error-reporting`. +Run `php ./bin/cli.php token:generate --help` to see the current list. + +**Q: What is the error reporting token for?** + +A: It authorizes calls to the error reporting endpoint, so only clients holding the token can submit error reports. +See [Error reporting](../core-features/error-reporting.md). + +**Q: Where do I put the generated token?** + +A: In the `tokens` array under `ErrorReportServiceInterface::class` in `config/autoload/error-handling.global.php`. + +**Q: Can I configure more than one token?** + +A: Yes. +`tokens` is an array, so several valid tokens can coexist — useful when rotating a token without downtime. + +**Q: The token has no effect after I saved the config. Why?** + +A: Outside development mode the configuration is cached. +Clear it with `php ./bin/clear-config-cache.php`. + +**Q: Does the command store the token for me?** + +A: No. It only prints the value; copying it into the configuration file is a manual step. diff --git a/docs/book/v7/core-features/authentication.md b/docs/book/v7/core-features/authentication.md index 279bc9ca..d5e60bc6 100644 --- a/docs/book/v7/core-features/authentication.md +++ b/docs/book/v7/core-features/authentication.md @@ -1,5 +1,13 @@ # Authentication +## Summary + +Dotkernel API authenticates with the OAuth2 password grant through `mezzio/mezzio-authentication-oauth2`. +Clients exchange credentials at `POST /security/generate-token` for an access token and a refresh token, then send the access token in the `Authorization` header; `POST /security/refresh-token` renews it. +Requests without an `Authorization` header get a default `guest` identity, which can reach only public endpoints. + +## Details + Authentication is the process by which an identity is presented to the application. It ensures that the entity making the request has the proper credentials to access the API. @@ -185,3 +193,62 @@ Get new Access Token ─────────────────── - **Rotate credentials**: Change default OAuth client secrets in production. - **Token expiration**: Access tokens expire (default 1 day). Implement refresh logic in clients. - **Never expose refresh tokens**: Refresh tokens should only be stored client-side, never in logs or public code. + +## FAQ + +**Q: What happens if a request sends no `Authorization` header?** + +A: The application assigns a default `guest` identity, an instance of `Mezzio\Authentication\UserInterface`. +Guests can reach public endpoints but not protected ones. + +**Q: Which OAuth2 grant does the API use?** + +A: The password grant: credentials are exchanged once for tokens, and subsequent requests carry the access token instead of the credentials. + +**Q: What must I run before authenticating for the first time?** + +A: The migrations and fixtures — `php ./vendor/bin/doctrine-migrations migrate` followed by `php ./bin/doctrine fixtures:execute` — which create the OAuth tables and seed the initial credentials. +See [Doctrine ORM](../installation/doctrine-orm.md). + +**Q: What are the seeded credentials?** + +A: `admin` / `dotadmin` for the admin account and `test@dotkernel.com` / `dotkernel` for the user account. +Remove or change them before production. +See [Basic security](../security/basic-security.md). + +**Q: Why are admins and users in separate tables?** + +A: To keep application users away from data that only administrators should reach. +Authenticated identities therefore come from either the `admin` or the `user` table. + +**Q: Which parameters does the token request need?** + +A: `grant_type`, `client_id`, `client_secret`, `scope`, `username` and `password`. +The client values come from `oauth_clients` and the scope from `oauth_scopes`. + +**Q: How long do the tokens last?** + +A: Access tokens expire after one day and refresh tokens after one month, both configurable under the `authentication` key in `config/autoload/local.php`. + +**Q: How do I renew an expired access token?** + +A: Post the `refresh_token` to `/security/refresh-token` with `grant_type` set to `refresh_token`. +If the refresh token has expired too, authenticate again with credentials. + +**Q: I get an "Invalid scope" error. What is wrong?** + +A: `scope` must be `"api"` — it is the only configured scope. + +**Q: I get "Invalid credentials" even though the password is right. What else could it be?** + +A: A mismatched `client_id` or `client_secret` against the `oauth_clients` table, or an account that does not exist or is inactive. + +**Q: Which middleware performs authentication?** + +A: `Api\App\Middleware\AuthenticationMiddleware`, which runs before authorization in the pipeline. +See [Middleware flow](../flow/middleware-flow.md). + +**Q: Is authenticating enough to access an endpoint?** + +A: No. Authentication establishes the identity; the role still needs permission for the route. +See [Authorization](authorization.md). diff --git a/docs/book/v7/core-features/authorization.md b/docs/book/v7/core-features/authorization.md index c15865c0..cc1b3b51 100644 --- a/docs/book/v7/core-features/authorization.md +++ b/docs/book/v7/core-features/authorization.md @@ -1,5 +1,12 @@ # Authorization +## Summary + +Authorization decides whether an already-authenticated identity may reach a given resource. +Dotkernel API implements it with role-based access control through `Mezzio\Authorization\Rbac\LaminasRbac`, applied by `AuthorizationMiddleware` and configured in `config/autoload/authorization.global.php`, where each permission is a route name and roles inherit from their parents. + +## Details + Authorization is the process by which a system takes a validated identity and checks if that identity has access to a given resource. **Dotkernel API**'s implementation of authorization uses `Mezzio\Authorization\Rbac\LaminasRbac` as a model of Role-Based Access Control (RBAC). @@ -70,3 +77,42 @@ A permission in Dotkernel API is basically a route name. As you can see, the `superuser` does not have its own permissions, because it gains all the permissions from `admin`, no need to define explicit permissions. The `user` role, gains all the permission from `guest` so no need to define that `user` can access `home` route, but `guest` cannot access user-specific routes. + +## FAQ + +**Q: How does authorization differ from authentication?** + +A: Authentication establishes who the caller is; authorization checks what that established identity is allowed to do. +See [Authentication](authentication.md). + +**Q: What exactly is a permission in Dotkernel API?** + +A: A route name. +Granting a role a permission means granting it access to the route of that name. + +**Q: Where do I add permissions for a route I just created?** + +A: To the relevant role's array in `config/autoload/authorization.global.php`. +A route with no permission entry is unreachable for that role. + +**Q: Which access control model is used?** + +A: RBAC, via `mezzio-authorization-rbac` backed by `laminas-permissions-rbac`. + +**Q: How does role inheritance work here?** + +A: A role listed inside another role's entry is its parent's beneficiary: because `admin` lists `superuser`, `superuser` receives everything granted to `admin`. +That is why `superuser` needs no explicit permissions of its own. + +**Q: Where are roles stored?** + +A: Each authenticatable entity — admin or user — has its own `roles` table where its roles are defined. + +**Q: Which middleware enforces this?** + +A: `Api\App\Middleware\AuthorizationMiddleware`. +See [Middleware flow](../flow/middleware-flow.md). + +**Q: Can I use ACL instead of RBAC?** + +A: The ACL adapter ships with the project, but RBAC is what Dotkernel API is configured for; switching means replacing the authorization configuration. diff --git a/docs/book/v7/core-features/content-validation.md b/docs/book/v7/core-features/content-validation.md index 51b6f24a..6b827233 100644 --- a/docs/book/v7/core-features/content-validation.md +++ b/docs/book/v7/core-features/content-validation.md @@ -1,5 +1,12 @@ # Content Negotiation +## Summary + +Content negotiation matches what a client says it is sending and what it wants back against what the API can actually handle. +Dotkernel API validates the `Accept` and `Content-Type` headers in middleware, per route or from a mandatory `default` entry in `config/autoload/content-negotiation.global.php`, returning `406 Not Acceptable` or `415 Unsupported Media Type` when a format is not supported. + +## Details + > Introduced in Dotkernel API 5.0.0 An application performs **Content Negotiation** to: @@ -95,3 +102,45 @@ The server will check if the format in the `Accept` header for the request can b The way **Dotkernel API** returns a response in handler means a content type is always set. This cannot be the case in any custom response, but the server will always check the `Content-Type` for the response and will try to validate that against the `Accept` header of the request. If the validation fails, a status code `406 - Not Acceptable` will be returned. + +## FAQ + +**Q: What is the difference between a 406 and a 415 response?** + +A: `406 Not Acceptable` means the API cannot produce the format the client asked for in `Accept`. +`415 Unsupported Media Type` means the API cannot consume the format the client sent in `Content-Type`. + +**Q: Which configuration keys are required?** + +A: `default` is mandatory, and every entry — including `default` — must define both `Accept` and `Content-Type`. + +**Q: How do I configure negotiation for one specific route?** + +A: Add a key matching the route name, for example `admin.list`, alongside `default`. +Routes without their own key fall back to `default`. + +**Q: What happens when the client sends `Accept: */*`?** + +A: Any format the API can produce is acceptable, so the check passes. + +**Q: What if the request has no `Content-Type` header?** + +A: The server attempts to deserialize the body as best it can, rather than rejecting the request outright. + +**Q: Will `application/vnd.api+json` be rejected if I only configured `application/json`?** + +A: No. Validation resolves to the more generic media type, so the request is served as JSON. + +**Q: How do I configure a file upload endpoint?** + +A: Set that route's `Content-Type` to `multipart/form-data`. +Clients sending `application/json` to it will then receive a 415. + +**Q: What is the third validation pass for?** + +A: It confirms that the response's `Content-Type` satisfies the request's `Accept` header. +Handler responses always set a content type, but a custom response may not, and a mismatch results in a 406. + +**Q: Since which version is this available?** + +A: Dotkernel API 5.0.0. diff --git a/docs/book/v7/core-features/dependency-injection.md b/docs/book/v7/core-features/dependency-injection.md index f9a29dd5..3d638b35 100644 --- a/docs/book/v7/core-features/dependency-injection.md +++ b/docs/book/v7/core-features/dependency-injection.md @@ -1,5 +1,12 @@ # Dependency Injection +## Summary + +Dotkernel API resolves dependencies through the `dot-dependency-injection` package, which supports constructor injection only. +You declare the services a class needs with the `#[Inject]` attribute on its constructor, then register the class in a `ConfigProvider` using `AttributedServiceFactory`. + +## Details + Dependency injection is a design pattern used in software development to implement inversion of control. In simpler terms, it's the act of providing dependencies for an object during instantiation. @@ -55,3 +62,36 @@ When your object is instantiated from the container, it will automatically have > Dependency injection is available to any object within Dotkernel API. > For example, you can inject dependencies in a service, a handler and so on, simply by registering it in the `ConfigProvider`. + +## FAQ + +**Q: Which injection styles are supported?** + +A: Constructor injection only. +`dot-dependency-injection` deliberately leaves out setter and property injection. + +**Q: What are the two steps to make a class injectable?** + +A: Add the `#[Inject]` attribute to its constructor listing the dependencies in order, then register the class in the `ConfigProvider` under `factories` with `AttributedServiceFactory::class`. + +**Q: Must the `#[Inject]` arguments match the constructor parameters?** + +A: Yes, in the same order. +Each entry in the attribute maps to the parameter at that position. + +**Q: How do I inject configuration instead of a service?** + +A: Pass `"config"` to receive the whole configuration array, or use dot notation such as `config.example` to receive a single key's value. + +**Q: Can I inject an interface rather than a concrete class?** + +A: Yes, provided the interface is registered in the container and resolves to an implementation. +Typing the parameter against the interface keeps your class decoupled from the implementation. + +**Q: Which classes can use this?** + +A: Any object in Dotkernel API — handlers, services, filters and others — as long as it is registered in a `ConfigProvider`. + +**Q: Since which version is this available?** + +A: Dotkernel API 5.0.0. diff --git a/docs/book/v7/core-features/error-reporting.md b/docs/book/v7/core-features/error-reporting.md index 7bfe6d02..fa22e67e 100644 --- a/docs/book/v7/core-features/error-reporting.md +++ b/docs/book/v7/core-features/error-reporting.md @@ -1,5 +1,13 @@ # Error reporting endpoint +## Summary + +The `/error-report` endpoint lets frontend applications post their own errors back to your API. +Access is controlled by an `Error-Reporting-Token` header plus a domain or IP whitelist, all configured in `config/autoload/error-handling.global.php`. +Accepted reports are appended to a log file that records the token, so you can tell which application sent each message. + +## Details + The error reporting endpoint was designed to allow the **frontend developers** of your API to report any bugs they encounter securely that are fully under your control. To prevent unauthorized usage, the endpoint is protected by a token in the request's header. @@ -123,3 +131,50 @@ Whenever an error is found, the frontend will call `postError()` with a relevant ```javascript apiService.postError({message: 'ERROR MESSAGE'}) ``` + +## FAQ + +**Q: Who is this endpoint for?** + +A: Frontend and third-party applications that need to report their own errors back to an API you control, rather than to an external service. + +**Q: How do I generate a token?** + +A: Run `php ./bin/cli.php token:generate error-reporting` and copy the value into the `tokens` array under `ErrorReportServiceInterface::class`. +See [Generating tokens](../commands/generate-tokens.md). + +**Q: Which headers must the reporting application send?** + +A: `Error-Reporting-Token` with a configured token, and `Origin` set to the reporting application's URL. + +**Q: Which configuration keys are required?** + +A: All of `enabled`, `path`, `tokens`, `domain_whitelist` and `ip_whitelist` must exist under `ErrorReportServiceInterface::class`, with `enabled` set to `true`, `path` set to a value, at least one token, and at least one entry across the two whitelists. + +**Q: Why is my report rejected with a `403`?** + +A: Because `checkRequest()` in `ErrorReportService` matched neither the domain whitelist nor the IP whitelist, so a `ForbiddenException` was thrown and nothing was stored. + +**Q: Can I override the settings per environment?** + +A: Yes. +The keys live in `error-handling.global.php` but can be set or overridden in `config/autoload/local.php`, which is not committed. + +**Q: How do I tell which application sent a report?** + +A: The log entry includes the token used, and tokens can be defined as key-value pairs — an alias such as `frontend` mapped to the token — so each application gets its own identifiable token. + +**Q: Do these tokens expire?** + +A: No. Because they are indefinite, rotate them manually from time to time. +See [Basic security](../security/basic-security.md). + +**Q: Where do the reports end up?** + +A: In the file named by `ErrorReportServiceInterface::class`, `path`. +If it does not exist, it is created automatically. + +**Q: Does this endpoint need an auth token as well?** + +A: No. It is authorized solely by the error reporting token. +See [Using the documentation](../openapi/use-documentation.md). diff --git a/docs/book/v7/core-features/exceptions.md b/docs/book/v7/core-features/exceptions.md index 43d605d0..12e46b13 100644 --- a/docs/book/v7/core-features/exceptions.md +++ b/docs/book/v7/core-features/exceptions.md @@ -1,5 +1,10 @@ # Exceptions +## Summary + +Dotkernel API expresses error conditions through a small set of problem-specific exceptions — `BadRequestException`, `ConflictException`, `ExpiredException`, `ForbiddenException`, `MethodNotAllowedException`, `NotFoundException` and `UnauthorizedException` — each mapped to an HTTP status code. +The page lists when to throw each one and walks through adding a custom exception with its own status code. + ## What are exceptions? Exceptions are a powerful mechanism for handling errors and other exceptional conditions that may occur during the execution of a script. @@ -125,3 +130,44 @@ Save and close the file. Access your API's home page URL, which should return the same content. Notice that this time it returns `418 I'm a teapot` HTTP status code. + +## FAQ + +**Q: Which exception should I throw for invalid request data?** + +A: `BadRequestException`, which produces a `400 Bad Request`. +It is what input filter failures raise. +See [Injectable input filters](../extended-features/injectable-input-filters.md). + +**Q: What is the difference between `UnauthorizedException` and `ForbiddenException`?** + +A: `UnauthorizedException` (401) means the client is not authenticated at all; `ForbiddenException` (403) means it is authenticated but its role does not grant access. +See [Authorization](authorization.md). + +**Q: When do I use `ConflictException`?** + +A: When a resource cannot be created because one with the same identifier exists, or cannot change state because it is already in that state. +It returns `409 Conflict`. + +**Q: What does `ExpiredException` cover?** + +A: Resources that can no longer be used because they expired, such as an activation link, or because they were already consumed, such as a one-time password. +It returns `410 Gone`. + +**Q: What happens to an exception I do not handle?** + +A: Generic exceptions, along with `MailException` and `RuntimeException`, produce a `500 Internal Server Error`. + +**Q: How do I map a custom exception to a specific status code?** + +A: Create the exception class, then add a `catch` block for it in the `handle` method of `HandlerTrait.php` that returns `errorResponse()` with your chosen status code. + +**Q: Why does my custom exception return 500 before I touch `HandlerTrait`?** + +A: Because nothing catches it yet, so it falls through to the generic handler. +Adding the catch block is what changes the status code. + +**Q: How do exceptions relate to problem details responses?** + +A: The exceptions carry the title, type, status, detail and any additional fields that the problem details middleware renders. +See [Problem details](../extended-features/problem-details.md). diff --git a/docs/book/v7/core-features/rendering-and-sending-emails.md b/docs/book/v7/core-features/rendering-and-sending-emails.md index da20a7fc..f70d82bd 100644 --- a/docs/book/v7/core-features/rendering-and-sending-emails.md +++ b/docs/book/v7/core-features/rendering-and-sending-emails.md @@ -1,5 +1,12 @@ # Rendering and sending emails +## Summary + +Email bodies are no longer rendered inside the mail service with Twig. +A lightweight custom renderer (`Api\App\Template\Renderer`) renders `phtml` templates in the handler, and the resulting body is passed to the core `MailService`, which is responsible only for sending. + +## Details + In the previous versions of Dotkernel API we have been composing email bodies using **Twig** from the `mezzio/mezzio-twigrenderer` package. In the current version of Dotkernel API, we introduced the core mail service `Core/src/App/src/Service/MailService` which is responsible for sending all emails. @@ -42,3 +49,36 @@ $this->mailService->sendWelcomeMail($user, $body); ``` > Other Dotkernel applications implementing the Core architecture do the same in the handlers but keep using Twig as the template renderer. + +## FAQ + +**Q: Why was Twig replaced?** + +A: Dotkernel API mostly returns JSON rendered by a different renderer, so pulling in a full templating engine solely for email bodies was unnecessary weight. +A lightweight renderer for `phtml` files covers the need. + +**Q: What template format does the custom renderer use?** + +A: Files combining PHP and HTML with the `.phtml` extension. + +**Q: Does `MailService` still need a renderer injected?** + +A: No. Rendering happens in the handler, and the finished body is passed to the mail service as a parameter. + +**Q: How do I render a template and send it?** + +A: Inject `MailService` and `RendererInterface` into your handler, call `$this->renderer->render('user::welcome', [...])`, then pass the result to the relevant `MailService` method. + +**Q: What does the `user::welcome` syntax mean?** + +A: It is a namespaced template name: `user` is the registered template namespace and `welcome` the template within it. + +**Q: Can I keep using Twig?** + +A: Other Dotkernel applications on the Core architecture do exactly that — the pattern in the handler is the same, only the renderer differs. +Within Dotkernel API the custom renderer is the default. + +**Q: Where is the mail transport configured?** + +A: In `config/autoload/mail.local.php`, under the `mail` key. +See [Configuration files](../installation/configuration-files.md). diff --git a/docs/book/v7/extended-features/core-and-app.md b/docs/book/v7/extended-features/core-and-app.md index 6941e247..32b3bcc1 100644 --- a/docs/book/v7/extended-features/core-and-app.md +++ b/docs/book/v7/extended-features/core-and-app.md @@ -1,5 +1,12 @@ # Core and App code structure +## Summary + +From version 6.0 onward the project is split into **Core**, which holds the low-level business logic and infrastructure, and **App**, where you build your own features. +The split supports a Headless Platform architecture and keeps project-specific code separate from the framework's foundations. + +## Details + Since version 6.0, the project is split into two main parts: **App** and **Core**. When you start a new project, there are chances that the requirements are not defined well. @@ -36,3 +43,35 @@ The **App** is where you build your actual project — the "body" of your applic - Error reporting If you're building features for the project, you're mostly working here. + +## FAQ + +**Q: Which part should my new feature go into?** + +A: App. +Put routes, handlers and feature-specific logic there, and only touch Core when you need to change how the system works underneath. + +**Q: Why was the codebase split this way?** + +A: To keep project code decoupled from the platform's foundations, so requirements can change without rewriting infrastructure. +This is what makes a Headless Platform architecture practical. + +**Q: What does "Headless Platform" mean here?** + +A: A backend that exposes data and functionality purely through an API, with no bundled frontend. +Any number of frontends can consume it, and backend and frontend work can proceed in parallel. + +**Q: Can Core code depend on App code?** + +A: No — the dependency runs one way. +App builds on Core; Core must remain independent of any particular project's features. + +**Q: Where do entities and repositories live?** + +A: Shared, low-level persistence concerns belong in Core, while entities and repositories specific to your own modules belong in App. +See [File structure](../introduction/file-structure.md). + +**Q: Was this split present before version 6.0?** + +A: No. It was introduced in 6.0 when common logic was moved into the Core module. +See [Upgrading from 5.x to 6.0](../upgrading/UPGRADE-6.0.md). diff --git a/docs/book/v7/extended-features/handler-structure.md b/docs/book/v7/extended-features/handler-structure.md index 6a4b514a..4e684a6b 100644 --- a/docs/book/v7/extended-features/handler-structure.md +++ b/docs/book/v7/extended-features/handler-structure.md @@ -1,5 +1,12 @@ # The new handler structure +## Summary + +Since version 6.0 Dotkernel API uses PSR-15 handlers instead of multi-action controllers, with each handler responsible for a single request. +This page explains what a handler is, the naming pattern that encodes method, resource and action into the class name, and where to find the full handler mapping. + +## Details + Since version 6.0, Dotkernel API contains some new architectural changes compared to its older version that uses controllers. The goal of this update is to implement PSR-15 handlers into Dotkernel API. @@ -41,3 +48,33 @@ In this way, the developer can easily figure out the functionality of each handl The full mapping of the handlers and their current paths and actions can be found in the full [naming convention table](https://docs.dotkernel.org/img/api/v7/naming-convention.png). [![naming-convention-thumbnail](https://docs.dotkernel.org/img/api/v7/naming-convention-thumbnail.png)](https://docs.dotkernel.org/img/api/v7/naming-convention.png) + +## FAQ + +**Q: How is a handler different from a controller?** + +A: A controller groups several actions in one class; a handler deals with a single request and returns a response. +Splitting them keeps each class to one responsibility, following the single-responsibility principle. + +**Q: What should I name a handler that creates an admin via POST?** + +A: Follow the pattern of method, resource, action and the `Handler` suffix — for example `PostAdminResourceHandler`. +The name alone should tell a reader what the class does. + +**Q: When is `Form` part of the name?** + +A: Only when the handler returns a form that will trigger another action on submission. + +**Q: Do handlers have to extend a base class?** + +A: Dotkernel's handlers extend `AbstractHandler`, which supplies shared response helpers. +Implementing `RequestHandlerInterface` directly also works. + +**Q: Where do I see all existing handlers and their routes?** + +A: In the [naming convention table](https://docs.dotkernel.org/img/api/v7/naming-convention.png). + +**Q: How do handlers receive their dependencies?** + +A: Through constructor injection, declared with the `#[Inject]` attribute. +See [Injectable input filters](injectable-input-filters.md) and [Dependency injection](../core-features/dependency-injection.md). diff --git a/docs/book/v7/extended-features/injectable-input-filters.md b/docs/book/v7/extended-features/injectable-input-filters.md index 790fb568..416695d3 100644 --- a/docs/book/v7/extended-features/injectable-input-filters.md +++ b/docs/book/v7/extended-features/injectable-input-filters.md @@ -1,5 +1,12 @@ # Injectable input filters +## Summary + +Input filters are injected into handler constructors rather than instantiated inside `handle()`. +The page contrasts the old inline approach with the current one and shows how injection makes handlers easier to test, since the filter can be replaced with a mock. + +## Details + In the current version of Dotkernel API has an Injectable Input Filter system into the constructors of our handlers. When building APIs or backend applications in PHP, especially within frameworks that support dependency injection, input validation is a critical concern. @@ -70,3 +77,35 @@ $response = $handler->handle($request); ``` You're no longer tied to the real filter logic in your handler tests. + +## FAQ + +**Q: Why is injecting an input filter better than creating one in the handler?** + +A: Inline instantiation couples the handler to a concrete filter class, which makes the logic harder to reuse and impossible to substitute in tests. +Injection moves that decision to the container. + +**Q: How does the container know which filter to inject?** + +A: From the `#[Inject]` attribute on the constructor, which lists the services to resolve in order. +See [Dependency injection](../core-features/dependency-injection.md). + +**Q: Is an injected filter shared between requests?** + +A: Each request gets a handler instance with its own filter, and `setData()` is called per request. +Do not rely on data set during an earlier call. + +**Q: What happens when validation fails?** + +A: The handler throws a `BadRequestException` carrying the filter's messages, which is rendered as a Problem Details response. +See [Problem details](problem-details.md). + +**Q: Do I still call `setData()` and `isValid()` myself?** + +A: Yes. +Injection only supplies the filter; populating it from the request body and checking validity remain the handler's job. + +**Q: Can one handler use more than one input filter?** + +A: Yes. +List each of them in the `#[Inject]` attribute and accept them as separate constructor parameters. diff --git a/docs/book/v7/extended-features/problem-details.md b/docs/book/v7/extended-features/problem-details.md index b41c42af..19398e12 100644 --- a/docs/book/v7/extended-features/problem-details.md +++ b/docs/book/v7/extended-features/problem-details.md @@ -1,5 +1,12 @@ # Problem details +## Summary + +Dotkernel API returns errors as RFC 9457 Problem Details documents via `mezzio/mezzio-problem-details`, so a failed request carries a title, type, status and detail instead of an opaque message. +This page shows the response shape, the middleware that produces it, the slimmed-down exceptions behind it, and where to map status codes to documentation links. + +## Details + With the usage of `mezzio/mezzio-problem-details` we have implemented a way to help the developers understand better the errors that they are getting from their APIs based on the [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457.html) standards. Example of a response with details: @@ -76,3 +83,34 @@ return [ ], ]; ``` + +## FAQ + +**Q: Which standard do the error responses follow?** + +A: [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457.html), the Problem Details format for HTTP APIs. + +**Q: What fields does a problem details response contain?** + +A: At minimum a title, a type, the HTTP status and a detail message. +You can add further fields when a specific error needs more context. + +**Q: Where are the middlewares registered?** + +A: `ProblemDetailsMiddleware` and `ProblemDetailsNotFoundHandler` are wired into `config/pipeline.php`. +See [Middleware flow](../flow/middleware-flow.md). + +**Q: How do I change the `type` link for a status code?** + +A: Edit `default_types_map` in `config/autoload/problem-details.global.php` and point the status code at your own URL. + +**Q: How do I raise a problem details error from my own code?** + +A: Throw one of the project exceptions, for example `BadRequestException::create($detail)`. +The middleware converts it into the response. +See [Exceptions](../core-features/exceptions.md). + +**Q: Can I attach extra data to an error response?** + +A: Yes. +Pass the `additional` array to the exception's `create()` method and those fields appear alongside the standard ones. diff --git a/docs/book/v7/extended-features/route-grouping.md b/docs/book/v7/extended-features/route-grouping.md index 48eba079..151e6559 100644 --- a/docs/book/v7/extended-features/route-grouping.md +++ b/docs/book/v7/extended-features/route-grouping.md @@ -1,5 +1,12 @@ # Route grouping +## Summary + +The `dot-router` package lets you declare routes that share a common path prefix as a single group instead of repeating the prefix on every line. +The result is less duplication, easier refactoring, and routes that belong together staying together in the code. + +## Details + In Dotkernel API with the help of the new [dot-router](https://docs.dotkernel.org/dot-router/v1/overview/) package, we have managed to implement a nicer way of creating routes. A lot of the times developers need to create sets of routes that have a similar format. As an example: @@ -27,3 +34,33 @@ The advantages of this new implementation: - **encapsulation**: similar routes are grouped in a single block of code (versus each route a separate statement) - **easy path refactoring**: modify all routes at once by changing only the prefix - **easy copying/moving**: copying/moving an entire group makes sure that you don't accidentally omit a route + +## FAQ + +**Q: Which package provides route grouping?** + +A: [dot-router](https://docs.dotkernel.org/dot-router/v1/overview/), which builds on `mezzio/mezzio-fastroute`. + +**Q: Where do I declare my routes?** + +A: In your module's `RoutesDelegator.php`, for example `src/User/src/RoutesDelegator.php`. + +**Q: Do I still name each route individually?** + +A: Yes. +Grouping shares the path prefix, not the route name, so every route keeps its own name such as `user::view-user`. + +**Q: Can I nest groups?** + +A: Groups are built around a shared base path, so a nested group extends its parent's prefix. +Keep nesting shallow, otherwise the effective path of a route becomes hard to read. + +**Q: Is the older per-route style still supported?** + +A: Yes. +Calls like `$app->get(...)` continue to work; grouping is an additional option, not a replacement. + +**Q: What happens to the path when the route part is an empty string?** + +A: The group prefix becomes the full path. +That is why `->get('', ...)` inside `group('/user/' . $id)` maps to `/user/{id}`. diff --git a/docs/book/v7/flow/default-library-flow.md b/docs/book/v7/flow/default-library-flow.md index c91750e4..a3995e80 100644 --- a/docs/book/v7/flow/default-library-flow.md +++ b/docs/book/v7/flow/default-library-flow.md @@ -1,5 +1,33 @@ # Default Library Flow +## Summary + +An overview diagram of how Dotkernel's libraries interact with each other during a typical request, showing which packages depend on which and where your application code plugs in. + +## Details + The graph below demonstrates a default flow between Dotkernel's libraries. ![Dotkernel API Default Library Flow!](https://docs.dotkernel.org/img/api/v7/dotkernel-library-flow.png) + +## FAQ + +**Q: What is this diagram useful for?** + +A: It gives you a mental map of the stack. +When you need to extend or debug behaviour, the diagram shows which library owns that responsibility so you know where to look first. + +**Q: Does the flow include third-party packages?** + +A: The diagram focuses on Dotkernel's own `dot-*` libraries and the Mezzio/Laminas components they build on. +See [Packages](../introduction/packages.md) for the full dependency list. + +**Q: Where does my own module fit in?** + +A: Your modules sit on top of this flow: they consume the services the libraries provide through the DI container. +See [Dependency Injection](../core-features/dependency-injection.md). + +**Q: Is there a separate diagram for the request lifecycle?** + +A: Yes. +The middleware pipeline is documented separately in [Middleware flow](middleware-flow.md). diff --git a/docs/book/v7/flow/library-flow-for-email.md b/docs/book/v7/flow/library-flow-for-email.md index ba2a3570..764e4b7b 100644 --- a/docs/book/v7/flow/library-flow-for-email.md +++ b/docs/book/v7/flow/library-flow-for-email.md @@ -1,5 +1,33 @@ # Library Flow for Email +## Summary + +A simplified diagram of the libraries involved when Dotkernel API sends an email, from the service that triggers the message through template rendering to the mail transport. + +## Details + The graph below demonstrates the simplified flow between Dotkernel's libraries for sending an email. ![Dotkernel API Default Library Flow!](https://docs.dotkernel.org/img/api/v7/dotkernel-library-flow-email.png) + +## FAQ + +**Q: Which libraries handle email in Dotkernel API?** + +A: `dot-mail` handles composing and transporting the message, while the templating layer renders the message body. +See [Rendering and sending emails](../core-features/rendering-and-sending-emails.md). + +**Q: Where do I configure the mail transport?** + +A: In the mail configuration under `config/autoload/`. +See [Configuration files](../installation/configuration-files.md). + +**Q: Can I send email without using the template renderer?** + +A: Yes. +The renderer is only needed when the message body comes from a template; you can also set the body directly on the message. + +**Q: Why is the email flow shown separately from the default library flow?** + +A: Because sending email is a side flow triggered from a service rather than part of the request pipeline. +Keeping it separate makes both diagrams easier to read. diff --git a/docs/book/v7/flow/middleware-flow.md b/docs/book/v7/flow/middleware-flow.md index 83396c07..c5b050b8 100644 --- a/docs/book/v7/flow/middleware-flow.md +++ b/docs/book/v7/flow/middleware-flow.md @@ -1,5 +1,37 @@ # Middleware flow +## Summary + +A diagram of the default middleware pipeline in Dotkernel API, showing the order in which middlewares process an incoming request and the response on the way back out. + +## Details + The graph below demonstrates a default flow between Dotkernel's middlewares. ![Dotkernel API Middleware Flow!](https://docs.dotkernel.org/img/api/v7/dotkernel-middleware-flow.png) + +## FAQ + +**Q: Why does middleware order matter?** + +A: Each middleware may short-circuit the request or add attributes the next one relies on. +For example, authentication must run before authorization, because authorization needs the resolved identity. + +**Q: Where is the pipeline defined?** + +A: In `config/pipeline.php`. +That file is the authoritative order; the diagram is a visual summary of it. + +**Q: How do I add my own middleware?** + +A: Register it in the container, then insert it into `config/pipeline.php` at the position where it needs to run, or attach it to a specific route. + +**Q: Which middlewares are responsible for authentication and authorization?** + +A: `AuthenticationMiddleware` and `AuthorizationMiddleware`. +See [Authentication](../core-features/authentication.md) and [Authorization](../core-features/authorization.md). + +**Q: What happens when a middleware throws?** + +A: The error handling middleware at the top of the pipeline converts it into a Problem Details response. +See [Problem Details](../extended-features/problem-details.md). diff --git a/docs/book/v7/installation/composer.md b/docs/book/v7/installation/composer.md index 05f19d1a..d01b6af6 100644 --- a/docs/book/v7/installation/composer.md +++ b/docs/book/v7/installation/composer.md @@ -1,5 +1,12 @@ # Composer Installation of Packages +## Summary + +Running `composer install` pulls in the dependencies and also runs the project's setup scripts, which generate the OAuth2 keys and the initial `config/autoload` files. +This page covers the prompts you will be asked during installation and how to enable, disable and check development mode afterwards. + +## Details + In this step you will: - [Install dependencies](#install-dependencies-using-composer). @@ -103,3 +110,48 @@ composer development-status ``` You should see the message `Development mode is ENABLED` or `Development mode is DISABLED`. + +## FAQ + +**Q: Why should I run `composer install` from the CLI rather than an IDE?** + +A: The setup script asks interactive questions, and some IDEs cannot display those prompts. +Answering them incorrectly — or not at all — leaves the project misconfigured. + +**Q: What does the installation do besides downloading packages?** + +A: It writes `composer.lock`, configures PHP CodeSniffer, generates the OAuth2 keys into `data/oauth`, and creates the initial `config/autoload` files. + +**Q: Will re-running `composer install` overwrite my configuration?** + +A: No. The post-install scripts run on every `composer install` and `composer update`, but they check whether each file already exists before writing it. + +**Q: Composer asks where to inject `Laminas\Diactoros\ConfigProvider`. What do I answer?** + +A: `0` — do not inject. +Dotkernel already registers its own ConfigProvider, and a duplicate registration can break packages you add later. + +**Q: Should I answer `y` to remembering that choice?** + +A: Yes. +It applies the same answer to other packages of the same type, so the rest of the installation runs without further prompts. + +**Q: Why does the package count differ from the documentation?** + +A: The number changes as dependencies are updated. +The example figure is only indicative. + +**Q: What does development mode actually change?** + +A: It stops certain files from being cached in `data/cache` and activates the development error handlers, so code and configuration changes take effect immediately. + +**Q: How do I check or change development mode later?** + +A: `composer development-status` reports the state, `composer development-enable` turns it on and `composer development-disable` turns it off. +Never leave it enabled in production. +See [Basic security](../security/basic-security.md). + +**Q: What is the next installation step?** + +A: Reviewing the generated configuration files. +See [Configuration files](configuration-files.md). diff --git a/docs/book/v7/installation/configuration-files.md b/docs/book/v7/installation/configuration-files.md index c31492d2..b82c2ff4 100644 --- a/docs/book/v7/installation/configuration-files.md +++ b/docs/book/v7/installation/configuration-files.md @@ -1,5 +1,12 @@ # Configuration Files +## Summary + +Composer's `post-update-cmd` scripts create the local configuration files your installation needs: `cors.local.php`, `local.php`, `mail.local.php` and `local.test.php`. +Each one must be reviewed before the application will run correctly, in development and again in production. + +## Details + The post-update scripts from `composer.json` (under the key `post-update-cmd`) should have already created the files mentioned on this page. > We mention these files explicitly because you will need to visit them to fully configure your development environment. @@ -29,3 +36,35 @@ The installation script will duplicate the following files: * `config/autoload/local.test.php.dist` as `config/autoload/local.test.php` to run and create tests. > This creates a new in-memory database that your tests will run on. + +## FAQ + +**Q: The configuration files are missing. What went wrong?** + +A: The `post-update-cmd` scripts did not run. +Run `composer update` (or `composer install` without a lock file) from the project root to trigger them. + +**Q: Which file holds the database connection?** + +A: `config/autoload/local.php`, which is also where the API key and other environment-specific settings live. + +**Q: Should I commit these files to version control?** + +A: No. The `*.local.php` files hold environment-specific values and are excluded from the repository; only the `.dist` templates are tracked. + +**Q: When do I need to edit `cors.local.php`?** + +A: Whenever another application consumes your API — set `allowed_origins` to the consuming origins. +The remaining options can normally stay as shipped. +See the [CORS tutorial](https://docs.dotkernel.org/api-documentation/v7/tutorials/cors/). + +**Q: Where does `mail.local.php` come from?** + +A: It is copied from the `dot-mail` package in `vendor`. +Configure the `mail` key, and the `smtp_options` sub-key if you send through SMTP. +See [Rendering and sending emails](../core-features/rendering-and-sending-emails.md). + +**Q: Do tests use my development database?** + +A: No. `local.test.php` points the test suite at a separate in-memory database. +See [Test the installation](test-the-installation.md). diff --git a/docs/book/v7/installation/doctrine-orm.md b/docs/book/v7/installation/doctrine-orm.md index c35c8a36..4efd97b4 100644 --- a/docs/book/v7/installation/doctrine-orm.md +++ b/docs/book/v7/installation/doctrine-orm.md @@ -1,5 +1,11 @@ # Doctrine ORM +## Summary + +The database step of the installation: enter your MariaDB or PostgreSQL credentials in `config/autoload/local.php`, decide whether to prefix table names, then generate a migration from the entities, run it to build the schema, and execute the fixtures to seed the initial roles, OAuth clients and accounts. + +## Details + In this step you will: - [Save the database connection credentials in the API configuration file](#setup-database). @@ -233,3 +239,62 @@ Fixtures have been loaded. ``` More details on how fixtures work can be found on [dot-data-fixtures documentation](https://github.com/dotkernel/dot-data-fixtures#usage) + +## FAQ + +**Q: Where do I put the database credentials?** + +A: In `config/autoload/local.php`, under `$databases['mariadb']` or `$databases['postgresql']`. + +**Q: How do I switch from MariaDB to PostgreSQL?** + +A: Comment out `'params' => $databases['mariadb']` and uncomment the `postgresql` line under `doctrine.connection.orm_default`. +Only one active connection is allowed at a time, even if the array defines several. + +**Q: Do I have to name the database `dotkernel`?** + +A: No. That is only an example — use any name, as long as the configuration and the database you create agree. + +**Q: What is `table_prefix` for?** + +A: It prepends a string to every table name from the entities, which keeps table sets apart when several applications share one database. +The prefix is applied verbatim, with no separator added, and `doctrine_migration_versions` is left untouched because Doctrine Migrations owns it. + +**Q: Why use migrations instead of editing the schema by hand?** + +A: Migrations make schema changes repeatable, trackable and safe to apply across environments. + +**Q: Which command creates a migration, and which applies it?** + +A: `php ./vendor/bin/doctrine-migrations diff` generates one from the current entities; `php ./vendor/bin/doctrine-migrations migrate` applies all pending migrations in chronological order. + +**Q: Migrating warns about previously executed migrations that are not registered. Should I continue?** + +A: Check what the listed migrations would do first. +The warning means the database records migrations the codebase does not know about, which is worth understanding before proceeding. + +**Q: Will migrating drop tables I created outside Doctrine?** + +A: `diff` proposes dropping unmapped tables such as the `oauth_*` ones. +Use a filter expression to exclude them. +See [Generate a database migration without dropping custom tables](../commands/generate-database-migrations.md). + +**Q: What do the fixtures create?** + +A: The admin and user roles, the OAuth clients and scopes, and the initial admin and user accounts. + +**Q: Must fixtures run after migrations?** + +A: Yes. +The tables have to exist before they can be populated. + +**Q: How do I change the seeded credentials?** + +A: Edit `setIdentity`, `usePassword` and optionally `setFirstName` and `setLastName` in `src/Core/src/App/Fixture/UserLoader.php` and `AdminLoader.php` before running the fixtures. +Leaving the defaults in place lets anyone log in. +See [Basic security](../security/basic-security.md). + +**Q: Can I still change the seeded records afterwards?** + +A: Yes. +Roles and initial users can be edited after the fixtures have run. diff --git a/docs/book/v7/installation/faq.md b/docs/book/v7/installation/faq.md index 71a57722..d1b2bec1 100644 --- a/docs/book/v7/installation/faq.md +++ b/docs/book/v7/installation/faq.md @@ -1,5 +1,10 @@ # Frequently Asked Questions +## Summary + +Solutions to the permission errors most often seen right after installation. +Each one comes from a directory the application needs to write to — `data`, `data/cache`, `public/uploads` or `log` — and is fixed by granting write access to that directory. + ## How do I fix common permission issues? If running your project, you encounter some permission issues, follow the below steps. @@ -37,3 +42,30 @@ chmod -R 777 public/uploads ```shell chmod -R 777 log ``` + +## FAQ + +**Q: Why do these errors appear right after installation?** + +A: The application writes caches to `data`, uploaded files to `public/uploads` and log files to `log`. +If those directories are not writable by the web server user, the first request that needs them fails. + +**Q: Which directories need write access?** + +A: `data` (including `data/cache` and `data/cache/doctrine`), `public/uploads` and `log`. +Applying the permissions recursively with `-R` covers the subdirectories. + +**Q: Is granting `777` safe?** + +A: Access to application files is governed by the `.htaccess` rules: only `public` is served directly and everything else is routed through `index.php`, so these writable folders are not reachable from the web. +See [Clone the project](getting-started.md). + +**Q: I get a blank `500` response with no message. Where do I look?** + +A: Check the web server error log first, since a log directory that is not writable prevents the application from recording the error itself. +Fixing the `log` permissions usually makes the real message appear. + +**Q: Are there other common installation problems besides permissions?** + +A: Yes — missing configuration files and an unmigrated database. +See [Configuration files](configuration-files.md) and [Doctrine ORM](doctrine-orm.md). diff --git a/docs/book/v7/installation/getting-started.md b/docs/book/v7/installation/getting-started.md index f00c082b..01da932c 100644 --- a/docs/book/v7/installation/getting-started.md +++ b/docs/book/v7/installation/getting-started.md @@ -1,5 +1,11 @@ # Clone the project +## Summary + +The first installation step: clone the Dotkernel API repository into an empty directory, then grant write permissions on the `data`, `public/uploads` and `log` folders so the application can write caches, uploads and logs. + +## Details + In this step you will: - [Clone the Dotkernel API project](#clone-the-project). @@ -38,3 +44,32 @@ chmod -R 777 log ``` > The `-R` parameter is used to recursively apply the permissions to all subdirectories and files. + +## FAQ + +**Q: Why must the target directory be empty?** + +A: `git clone .` clones into the current directory and refuses to run if that directory already contains files. + +**Q: Is setting `777` on those folders safe?** + +A: Access to application files is controlled by the `.htaccess` rules: only the `public` folder is served directly and everything else is routed through `index.php`. +The three writable folders sit outside the served path. + +**Q: Which folders need write access, and why?** + +A: `data` for caches and generated files, `public/uploads` for uploaded content, and `log` for the error log. + +**Q: I hit permission errors after installing. What now?** + +A: Re-run the `chmod` commands, and see the [FAQ](faq.md) page, which lists the specific error messages and their fixes. + +**Q: Can I develop on Windows?** + +A: Yes, using WSL2 as the development environment. +See the [WSL2 setup guide](https://www.dotkernel.com/how-to/installing-almalinux-10-in-wsl2-php-mariadb-composer-phpmyadmin/). + +**Q: What comes after cloning?** + +A: Install dependencies with Composer, then configure the local files. +See [Composer](composer.md) and [Configuration files](configuration-files.md). diff --git a/docs/book/v7/installation/test-the-installation.md b/docs/book/v7/installation/test-the-installation.md index f46c99f1..3d56ed48 100644 --- a/docs/book/v7/installation/test-the-installation.md +++ b/docs/book/v7/installation/test-the-installation.md @@ -1,5 +1,11 @@ # Test the installation +## Summary + +The final installation step: confirm the API answers on your virtual host, optionally serve it with PHP's built-in server instead, import the Bruno collection to explore the endpoints, and run the unit and functional test suites. + +## Details + In this final step you will: - [Test the installation of your virtual host](#running-the-application). @@ -89,3 +95,42 @@ vendor/bin/phpunit --testsuite=UnitTests --testdox --colors=always ```shell vendor/bin/phpunit --testsuite=FunctionalTests --testdox --colors=always ``` + +## FAQ + +**Q: How do I know the installation succeeded?** + +A: A GET request to the home page returns `{"message": "Dotkernel API version 7"}`. + +**Q: I get a 500 error instead. What should I check first?** + +A: Folder permissions on `data`, `public/uploads` and `log`. +The [FAQ page](faq.md) lists the exact error messages and their fixes. + +**Q: Do I need a virtual host?** + +A: No. `php -S 0.0.0.0:8080 -t public` serves the application without one, which is convenient for a quick check. + +**Q: What is Bruno and why use it?** + +A: Bruno is a Git-native API client. +The repository ships a ready-made collection of every endpoint in `documentation/Dotkernel_API_Bruno.zip`, so you can exercise the API without writing requests by hand. + +**Q: Can I import the collection from the Postman files instead?** + +A: Yes — Bruno also reads the included Postman collection and environment files. +If you already imported the `.zip`, there is no need. + +**Q: How do I share the collection with my team?** + +A: From the collection's `...` menu choose `Share`, then either initialize a Git repository (recommended) or export to `.zip` or `.yaml`. + +**Q: How do I run only one kind of test?** + +A: Pass the suite name: `--testsuite=UnitTests` or `--testsuite=FunctionalTests`. +Running `php vendor/bin/phpunit` with no arguments runs both. + +**Q: Which database do the tests use?** + +A: The in-memory database configured in `config/autoload/local.test.php`, not your development database. +See [Configuration files](configuration-files.md). diff --git a/docs/book/v7/introduction/file-structure.md b/docs/book/v7/introduction/file-structure.md index 41d406ce..4bd59026 100644 --- a/docs/book/v7/introduction/file-structure.md +++ b/docs/book/v7/introduction/file-structure.md @@ -1,5 +1,11 @@ # File structure +## Summary + +A tour of the directories a default Dotkernel API installation ships with — `bin` for CLI entry points, `config` and `config/autoload` for application and service configuration, `data` for caches, migrations and OAuth keys, `log` for daily logs, `public` as the web entry point, and `src` for the modules — plus the folders and files each module is expected to contain. + +## Details + The Dotkernel API file structure follows the [PSR-4](https://www.php-fig.org/psr/psr-4/) standards. Standardizing the file structure of your project is considered good practice because it makes it easier to find and navigate the code. @@ -113,3 +119,53 @@ This folder contains the template files, used, for example, to help render e-mai > `twig` is used as Templating Engine. > All template files have the extension `.html.twig` + +## FAQ + +**Q: Which standard does the structure follow?** + +A: [PSR-4](https://www.php-fig.org/psr/psr-4/) autoloading, so a class's namespace maps directly onto its path. + +**Q: Where does my own code go?** + +A: In a new folder under `src`, alongside the default `Admin`, `App`, `Core`, `Security` and `User` modules. +See [Core and App](../extended-features/core-and-app.md). + +**Q: Which files must every module have?** + +A: `ConfigProvider.php` for its configuration, `RoutesDelegator.php` for its routes and `OpenAPI.php` for its endpoint documentation. + +**Q: What folders does a module typically contain?** + +A: `Handler`, `Entity`, `Service` and `Repository`, omitting any that would be empty. +Modules commonly also add `InputFilter`, `EventListener`, `Helper`, `Command` and `Factory`. + +**Q: Which directories need to be writable?** + +A: `data`, `log` and `public/uploads`. +See [Clone the project](../installation/getting-started.md). + +**Q: What is the application's entry point?** + +A: `public/index.php`. +Only the `public` folder is served directly; everything else is routed through it by the `.htaccess` rewrite rules. + +**Q: Where is the middleware pipeline defined?** + +A: `config/pipeline.php`, which lists the middlewares in execution order. +See [Middleware flow](../flow/middleware-flow.md). + +**Q: What is the difference between `config` and `config/autoload`?** + +A: `config` holds application-level wiring — the container, the pipeline, the config aggregator. +`config/autoload` holds per-service configuration, split into `*.global.php` files that are committed and `*.local.php` files that are not. + +**Q: Where are the OAuth2 keys kept?** + +A: In `data/oauth`. +They are generated during installation and must never be committed. +See [OAuth2 security](../security/oauth2-security.md). + +**Q: Why is `robots.txt` shipped as `robots.txt.dist`?** + +A: So you can activate it deliberately: copy it to `robots.txt` and comment out the lines that do not match your environment. diff --git a/docs/book/v7/introduction/introduction.md b/docs/book/v7/introduction/introduction.md index 45204c8b..0ff19ef1 100644 --- a/docs/book/v7/introduction/introduction.md +++ b/docs/book/v7/introduction/introduction.md @@ -1,5 +1,9 @@ # Introduction +## Summary + +A tour of Dotkernel API: a PSR-15 middleware REST framework on PHP 8.2+ acting as a Headless Platform, with OAuth2 authentication, RBAC authorization, content negotiation, Doctrine ORM persistence, HAL payloads, CORS handling, email through `dot-mail`, OpenAPI and Bruno documentation, per-module routing and configuration, CLI commands with a file locker, and unit and functional test suites — closing with the pitfalls to avoid and where to go next. + ## What is Dotkernel API? Dotkernel API is a modern, PSR-15 middleware-based REST API framework built on PHP 8.2+. @@ -136,3 +140,54 @@ Ready to get started? - Check out the [Architecture at a Glance](https://docs.dotkernel.org/api-documentation/v7/architecture-at-a-glance/). - Review the [Core Features](https://docs.dotkernel.org/api-documentation/v7/core-features/authentication/). - Run through the [Tutorials](https://docs.dotkernel.org/api-documentation/v7/tutorials/cors/) for step-by-step instructions on how to use Dotkernel API. + +## FAQ + +**Q: What kind of projects is Dotkernel API a good fit for?** + +A: REST APIs in a microservices setup, headless CMS backends, e-commerce and SaaS backends, reporting APIs, and any project that needs strict RBAC, standardized errors and OpenAPI documentation. + +**Q: Where do I configure each feature?** + +A: OAuth2 in `config/autoload/local.php`, RBAC in `config/autoload/authorization.global.php`, content negotiation in `config/autoload/content-negotiation.global.php`; the OpenAPI documentation is generated rather than configured. + +**Q: How do I register a new module?** + +A: Add its `ConfigProvider.php` to `config.php`. +Routes then live in the module's own `RoutesDelegator.php`. + +**Q: Where do I add a middleware?** + +A: To `config/pipeline.php`, which also determines the order middlewares run in. +See [Middleware flow](../flow/middleware-flow.md). + +**Q: How do I register a CLI command?** + +A: Extend `Symfony\Component\Console\Command\Command` and register the class in `config/autoload/cli.global.php`. + +**Q: What does the file locker do?** + +A: It writes a `command-{command-default-name}.lock` file so a second instance of the same command cannot start until the first finishes. +It is enabled by default. + +**Q: Why Doctrine ORM?** + +A: So you can work with objects and business logic and treat persistence as a secondary concern. +See [Doctrine ORM](../installation/doctrine-orm.md). + +**Q: What is HAL used for?** + +A: `mezzio/mezzio-hal` shapes API payloads, describing each resource together with its relational links and any embedded child resources. + +**Q: Why keep Bruno files in their own Git repository?** + +A: So the endpoint collections can be shared with the team and kept current independently of the API's own history. +See [Test the installation](../installation/test-the-installation.md). + +**Q: How do I run the tests?** + +A: `php vendor/bin/phpunit` runs both suites; add `--testsuite=UnitTests` or `--testsuite=FunctionalTests` to run one. + +**Q: What are the most common mistakes to avoid?** + +A: Leaving the default OAuth2 client credentials in production, enabling development mode outside local work, deploying without configuring CORS origins, and accessing the API before running migrations and fixtures. diff --git a/docs/book/v7/introduction/packages.md b/docs/book/v7/introduction/packages.md index 30227103..652f62cd 100644 --- a/docs/book/v7/introduction/packages.md +++ b/docs/book/v7/introduction/packages.md @@ -1,5 +1,12 @@ # Packages +## Summary + +The third-party and Dotkernel packages Dotkernel API depends on, with the version constraint and purpose of each. +They fall into a few groups: Doctrine for persistence, Laminas components for configuration, hydration, validation and the service container, Mezzio for the PSR-15 pipeline with OAuth2, RBAC, CORS, HAL and problem details, the `dotkernel/dot-*` libraries, and `zircote/swagger-php` for API documentation. + +## Details + * `doctrine/dbal`:`^4.4` - Powerful PHP database abstraction layer (DBAL) with many features for database schema introspection and management. * `doctrine/orm`:`^3.6` - Object-Relational-Mapper for PHP * `dotkernel/dot-cache`:`^4.4` - Cache component extending symfony-cache @@ -31,3 +38,39 @@ * `roave/psr-container-doctrine`:`^5.2` || `^6.1` - Doctrine Factories for PSR-11 Containers * `symfony/filesystem`:`^7.4` - Provides basic utilities for the filesystem * `zircote/swagger-php`:`^5.8` - Generate interactive documentation for your RESTful API using PHP attributes (preferred) or PHPDoc annotations + +## FAQ + +**Q: Is this list authoritative?** + +A: The authoritative source is the `require` section of the project's `composer.json`. +This page mirrors it with a short explanation of each entry. + +**Q: Which package provides the framework itself?** + +A: `mezzio/mezzio`, a PSR-15 middleware microframework. +See [PSRs](psr.md). + +**Q: Which packages handle authentication and authorization?** + +A: `mezzio/mezzio-authentication-oauth2` for OAuth2 authentication, and `mezzio/mezzio-authorization-rbac` for role-based authorization. +See [Authentication](../core-features/authentication.md) and [Authorization](../core-features/authorization.md). + +**Q: Why are both `doctrine/dbal` and `doctrine/orm` listed?** + +A: DBAL is the database abstraction and schema layer; ORM maps entities on top of it. +The ORM requires DBAL underneath. +See [Doctrine ORM](../installation/doctrine-orm.md). + +**Q: What are the `dotkernel/dot-*` packages for?** + +A: They are Dotkernel's own components — CLI, cache, mail, router, error handler, dependency injection and data fixtures — reused across Dotkernel projects rather than duplicated in each one. + +**Q: Can I remove a package I don't use?** + +A: Some can be removed, but many are wired into the default configuration and pipeline. +Remove the corresponding configuration and pipeline entries first, then verify the application and tests still run. + +**Q: Why are both the ACL and RBAC authorization adapters present?** + +A: RBAC is what Dotkernel API uses by default; the ACL adapter is available if your project needs access-control-list semantics instead. diff --git a/docs/book/v7/introduction/psr.md b/docs/book/v7/introduction/psr.md index 94608d6a..872da8a1 100644 --- a/docs/book/v7/introduction/psr.md +++ b/docs/book/v7/introduction/psr.md @@ -1,5 +1,10 @@ # PSRs +## Summary + +Dotkernel API is built on the PHP-FIG standards, which keep your code portable and let PSR-compliant libraries drop in without adapters. +PSR-7 (HTTP messages), PSR-15 (handlers and middleware) and PSR-11 (container) are architectural foundations; PSR-3, PSR-4, PSR-6, PSR-13, PSR-14, PSR-17, PSR-18 and PSR-20 arrive through dependencies. + ## Why PSRs Matter for Dotkernel API - **Vendor Lock-In Prevention**: By following PSRs, you're not locked into Dotkernel API. Your code can be reused in other PSR-compliant frameworks. @@ -154,3 +159,48 @@ Provides a standard interface for reading the system clock. │ (Loads services automatically) │ └───────────────────────────────────────────┘ ``` + +## FAQ + +**Q: Which PSRs are essential to the architecture?** + +A: PSR-7, PSR-15 and PSR-11. +Everything else is supporting, arriving through dependencies rather than shaping the design. + +**Q: What does building on PSRs buy me in practice?** + +A: Portability. +Because your handlers and services depend on standard interfaces rather than framework classes, they can be reused in any other PSR-compliant framework, and third-party PSR libraries integrate without custom adapters. + +**Q: What implements PSR-7 here?** + +A: `Laminas\Diactoros`, which also provides the PSR-17 HTTP factories. + +**Q: How does PSR-15 show up in the code I write?** + +A: Every handler implements `RequestHandlerInterface` and serves a single action, and requests travel through a middleware pipeline. +See [The new handler structure](../extended-features/handler-structure.md). + +**Q: Which container is used?** + +A: `Laminas\ServiceManager`, behind the PSR-11 interface. +See [Dependency injection](../core-features/dependency-injection.md). + +**Q: Which PSR governs the file layout?** + +A: PSR-4 autoloading, which maps namespaces to paths. +See [File structure](file-structure.md). + +**Q: Do I need to install anything to use PSR-3 logging or PSR-6 caching?** + +A: No. They come with `dotkernel/dot-errorhandler` and `dotkernel/dot-cache` respectively. +See [Packages](packages.md). + +**Q: How do I call an external API from Dotkernel API?** + +A: Through a PSR-18 HTTP client such as `symfony/http-client`, so your calling code depends on the interface rather than a specific client. + +**Q: Are PSR-14 events and PSR-20 clock available out of the box?** + +A: Not by default. +Both are supplied by third-party packages when a project needs them. diff --git a/docs/book/v7/introduction/server-requirements.md b/docs/book/v7/introduction/server-requirements.md index ecef76c8..cf70b1ec 100644 --- a/docs/book/v7/introduction/server-requirements.md +++ b/docs/book/v7/introduction/server-requirements.md @@ -1,5 +1,12 @@ # Server Requirements +## Summary + +What Dotkernel API v7 needs to run: a Linux host in production (Windows via WSL2 for development), Apache with `mod_rewrite` or an Nginx equivalent, PHP 8.2 or newer with `mbstring` and CLI SAPI, Composer 2.0 or newer, and MariaDB or PostgreSQL — MySQL is not supported because it lacks native UUID types. +Recommended extensions and baseline server hardening are listed as well. + +## Details + For production environments, we highly recommend a Linux-based system. Windows is supported for development via WSL2. @@ -112,3 +119,47 @@ Dotkernel API requires Composer >= 2.0 for managing PHP dependencies. - **Database**: Use strong passwords, restrict user permissions - **Files**: Set proper permissions (644 for files, 755 for directories) - **Updates**: Keep PHP, web server, and database updated with security patches + +## FAQ + +**Q: Why is MySQL not supported?** + +A: Dotkernel API v7 stores identifiers as native UUIDs, and MySQL has no native UUID type or functions. +MariaDB and PostgreSQL both do. + +**Q: Which database versions are tested?** + +A: MariaDB 10.7, 10.11 LTS, 11.4 LTS and 11.8 LTS, and PostgreSQL 13 and above. +LTS releases are recommended for stability and security updates. + +**Q: What collation should I create the database with?** + +A: `utf8mb4_general_ci` or `utf8mb4_unicode_ci` on MariaDB, and `C.UTF-8` or `en_US.UTF-8` on PostgreSQL. + +**Q: What is the minimum PHP version?** + +A: PHP 8.2. +Earlier versions are not supported, because the project relies on named arguments, match expressions, attributes, union types, the nullsafe operator and constructor property promotion. + +**Q: Do I need the CLI SAPI as well as FPM?** + +A: Yes. +FPM (or FastCGI) serves web requests, while the CLI SAPI is required for cron jobs, migrations and fixtures. + +**Q: Can I run this on Nginx?** + +A: Yes, but the shipped `.htaccess` in `public` is Apache-specific — you must translate its rewrite rules into Nginx configuration. + +**Q: Can I develop on Windows?** + +A: Yes, through WSL2. macOS and Linux are also supported for development; production should be Linux. + +**Q: Which PHP extensions are actually required?** + +A: `mbstring` plus the PDO driver for your database. +The rest — `opcache`, `gd`, `curl`, `zip`, `dom`, `simplexml`, `sqlite3` and others — depend on what your application does, with `sqlite3` needed to run the tests. + +**Q: What are the baseline hardening steps?** + +A: Expose only ports 80 and 443, disable `exec`, `shell_exec`, `passthru` and `system` in PHP, use strong database passwords with restricted permissions, set 644 on files and 755 on directories, and keep the stack patched. +See [Basic security](../security/basic-security.md). diff --git a/docs/book/v7/openapi/generate-documentation.md b/docs/book/v7/openapi/generate-documentation.md index c261df56..e20c3cd1 100644 --- a/docs/book/v7/openapi/generate-documentation.md +++ b/docs/book/v7/openapi/generate-documentation.md @@ -1,5 +1,11 @@ # Generating the documentation file +## Summary + +How to run `zircote/swagger-php` against the `src` directory to produce your OpenAPI file: printing it to the terminal, writing it to a chosen location, and selecting the OpenAPI version (`3.0.0` or `3.1.0`) and output format (`yaml` or `json`). + +## Details + > Make sure that in `src/App/src/OpenAPI.php`, on the line with `#[OA\Server` the value of `url` is set to the of URL of your instance of **Dotkernel API**. Using your terminal, move to the root directory of your project. @@ -51,3 +57,39 @@ Or be specific about the format by appending the `--format` argument: ``` These will place the generated file `openapi.json` in the `public` directory. + +## FAQ + +**Q: What must I configure before generating?** + +A: The `url` value on the `#[OA\Server` line in `src/App/src/OpenAPI.php`, which has to point at your own instance. +Otherwise the generated file advertises the wrong server. + +**Q: Why is `./src` the path passed to the command?** + +A: Because Dotkernel API keeps its OpenAPI attributes in the `src` directory. +The generator scans that path for annotated classes. + +**Q: Which OpenAPI versions can I generate?** + +A: `3.0.0` (the default) and `3.1.0`, selected with `--version`. + +**Q: YAML or JSON?** + +A: YAML is the default. +Use a `.json` output filename and the generator picks JSON, or state it explicitly with `--format json`. + +**Q: Where should the generated file live?** + +A: Anywhere your renderer can read it; `public/openapi.yaml` is the common choice, since it can then be served directly. +See [Render documentation](render-documentation.md). + +**Q: Do I have to regenerate after changing annotations?** + +A: Yes. +The file is a static snapshot, so re-run the command whenever the annotations change. + +**Q: The command runs but my endpoint is missing. Why?** + +A: Its class is most likely outside the scanned path, or its attributes are incomplete. +See [Write documentation](write-documentation.md) and [Getting help](getting-help.md). diff --git a/docs/book/v7/openapi/getting-help.md b/docs/book/v7/openapi/getting-help.md index 27c1ae37..6a4bb1c6 100644 --- a/docs/book/v7/openapi/getting-help.md +++ b/docs/book/v7/openapi/getting-help.md @@ -1,5 +1,11 @@ # Getting help +## Summary + +Where to look when an OpenAPI annotation does not behave as expected: the specification itself, `zircote/swagger-php`'s examples and documentation, and the library's built-in help output. + +## Details + - consult the OpenAPI [specs](https://spec.openapis.org/oas/latest.html) for a complete reference of the presented objects - see more examples of OpenAPI object representations in `zircote/swagger-php`'s [GitHub repository](https://zircote.github.io/swagger-php/guide/examples.html) - consult `zircote/swagger-php`'s [online documentation](http://zircote.github.io/swagger-php/guide/generating-openapi-documents.html) or run the following command to see their help page: @@ -7,3 +13,24 @@ ```shell ./vendor/bin/openapi --help ``` + +## FAQ + +**Q: My annotation is ignored in the generated output. What should I check first?** + +A: Check that the annotated class is inside a path scanned by the generator, and that the attribute is spelled and nested correctly. +Running the generator usually reports the offending file. + +**Q: Where do I find the meaning of a specific OpenAPI object?** + +A: In the [OpenAPI specification](https://spec.openapis.org/oas/latest.html). +It is the authoritative reference for every object and field name. + +**Q: How do I see the available generator options?** + +A: Run `./vendor/bin/openapi --help`. + +**Q: Should I report annotation problems to Dotkernel or to swagger-php?** + +A: If the problem is in how Dotkernel API is annotated or configured, report it to Dotkernel. +If the generator itself misbehaves, report it upstream to `zircote/swagger-php`. diff --git a/docs/book/v7/openapi/initialized-components.md b/docs/book/v7/openapi/initialized-components.md index 2b4fde9f..b83787d0 100644 --- a/docs/book/v7/openapi/initialized-components.md +++ b/docs/book/v7/openapi/initialized-components.md @@ -1,5 +1,12 @@ # Initialized OpenAPI components +## Summary + +The OpenAPI components Dotkernel API already defines in `src/App/src/OpenAPI.php`: `OA\Info` for API metadata, `OA\Server` for instance URLs, `OA\SecurityScheme` for the `AuthToken` and `ErrorReportingToken` headers, `OA\ExternalDocumentation`, and reusable schemas. +It also shows how to turn an entity or a collection into an `OA\Schema` and reference it with `ref` instead of repeating the definition. + +## Details + Below you will find details on some prepopulated OpenAPI components we added to Dotkernel API. ## OA\Info @@ -232,3 +239,45 @@ We provided some schemas that are reusable across the entire project. They are d - `#/components/schemas/Collection`: provides the default **HAL** structure to all the collections extending it - `#/components/schemas/ErrorMessage`: describes an operation that resulted in an error—may contain multiple messages - `#/components/schemas/InfoMessage`: describes an operation that completed successfully—may contain multiple messages + +## FAQ + +**Q: Where are these components defined?** + +A: All of them in `src/App/src/OpenAPI.php`. + +**Q: What must I edit before generating documentation?** + +A: The `url` on the `#[OA\Server` line, which has to point at your own instance and must not have a trailing slash. + +**Q: Can I document more than one environment?** + +A: Yes. +Duplicate the `OA\Server` entry — one per instance — and use `description` to label each as `Dev`, `Staging`, `Production` or similar. + +**Q: Which security schemes are predefined?** + +A: `AuthToken` for the OAuth2 bearer token and `ErrorReportingToken` for the `/error-report` header. +Naming one in an endpoint's `security` parameter marks that endpoint as protected. + +**Q: What is the difference between an entity and a schema?** + +A: The entity is the PHP class Doctrine maps to a table; the schema is the OpenAPI description of how that object appears in requests and responses. +You write the schema separately, referencing the entity with a `@see` annotation. + +**Q: How do I avoid describing the same object twice?** + +A: Reference the existing schema with `ref: '#/components/schemas/UserRole'`. +Later changes then happen in one place only. + +**Q: How do I describe a collection?** + +A: Define a schema whose `_embedded` property holds an array of `OA\Items` referencing the item schema, and combine it with `#/components/schemas/Collection` through `allOf`. + +**Q: What do the shipped common schemas provide?** + +A: `Collection` gives collections their default HAL structure, while `ErrorMessage` and `InfoMessage` describe failed and successful operations, each able to carry several messages. + +**Q: Where do I look up the fields of an OpenAPI object?** + +A: In the [OpenAPI specification](https://spec.openapis.org/oas/latest.html), linked per object throughout this page. diff --git a/docs/book/v7/openapi/introduction.md b/docs/book/v7/openapi/introduction.md index 38a3a8e2..1aaf8a13 100644 --- a/docs/book/v7/openapi/introduction.md +++ b/docs/book/v7/openapi/introduction.md @@ -1,5 +1,33 @@ # OpenAPI documentation +## Summary + +Dotkernel API ships with `zircote/swagger-php`, letting you describe endpoints with PHP attributes and auto-generate an interactive OpenAPI specification from them. + +## Details + To provide an interactive documentation, Dotkernel API implemented [zircote/swagger-php](https://github.com/zircote/swagger-php). Developers can use this library to auto-generate documentation that outlines available endpoints, their request details, and their response formats. + +## FAQ + +**Q: What is OpenAPI?** + +A: OpenAPI is a standard, machine-readable format for describing HTTP APIs. +Tools can consume the specification to render documentation, generate clients, or drive tests. + +**Q: Do I write the specification by hand?** + +A: No. You annotate your handlers and models with `zircote/swagger-php` attributes, then generate the specification file from them. +See [Write documentation](write-documentation.md). + +**Q: Which OpenAPI version is used?** + +A: The version supported by the installed release of `zircote/swagger-php`. +Consult the [OpenAPI specification](https://spec.openapis.org/oas/latest.html) for the object reference. + +**Q: How do I view the generated documentation?** + +A: Generate the specification, then serve it through a renderer. +See [Generate documentation](generate-documentation.md) and [Render documentation](render-documentation.md). diff --git a/docs/book/v7/openapi/render-documentation.md b/docs/book/v7/openapi/render-documentation.md index c038bc3f..35e938ca 100644 --- a/docs/book/v7/openapi/render-documentation.md +++ b/docs/book/v7/openapi/render-documentation.md @@ -1,5 +1,12 @@ # Rendering the documentation file +## Summary + +A generated OpenAPI file is only data; to browse and try your endpoints you need a renderer. +This page shows how to serve the file from the `public` directory through either Swagger UI or Redoc, using a small HTML page that points at your `openapi.yaml` or `openapi.json`. + +## Details + At this step, you only have a static documentation file. You will need an interface that can render it so that you will be able to interact with your Dotkernel API. @@ -79,3 +86,41 @@ Redoc.init('./openapi.yaml', {}, document.getElementById('redoc-container')); Using your browser, open a new tab and type in the URL of your instance of Dotkernel API and append `/redoc.html` to it. You should see the Redoc interface with your documentation file loaded in it. From here, you can inspect each endpoint, see its URL, check if it needs authentication, the request payload (if any) and the possible response(s). + +## FAQ + +**Q: Should I choose Swagger UI or Redoc?** + +A: Swagger UI offers an interactive console for sending requests, while Redoc renders a cleaner read-only reference. +Both consume the same OpenAPI file, so you can set up either or both. + +**Q: Why must the HTML file live in `public`?** + +A: Only `public` is served directly by the web server; files elsewhere are routed through `index.php`. +The page and the OpenAPI file both need to be reachable by the browser. + +**Q: Does the filename matter?** + +A: No. `swagger.html` and `redoc.html` are suggestions — the URL you open just has to match whatever you named the file. + +**Q: My page loads but shows no endpoints. What is wrong?** + +A: `PATH_TO_YOUR_OPENAPI_FILE` was most likely not replaced, or the relative path does not resolve. +Set it to something like `./openapi.yaml`, matching where you generated the file. +See [Generate documentation](generate-documentation.md). + +**Q: Can the renderer read a JSON file instead of YAML?** + +A: Yes. +Both renderers accept either format; point the URL at `openapi.json` if that is what you generated. + +**Q: Should I expose this in production?** + +A: It is not recommended. +Enabling the documentation publicly widens your attack surface. +See [Basic security](../security/basic-security.md). + +**Q: Do these pages load resources from the internet?** + +A: Yes — the examples pull Swagger UI and Redoc from a CDN. +Host the assets yourself if your environment has no outbound access. diff --git a/docs/book/v7/openapi/use-documentation.md b/docs/book/v7/openapi/use-documentation.md index 55780635..e93f4830 100644 --- a/docs/book/v7/openapi/use-documentation.md +++ b/docs/book/v7/openapi/use-documentation.md @@ -1,5 +1,11 @@ # Using the documentation +## Summary + +How to drive your API from Swagger UI: recognising which endpoints are protected, generating an access token from `/security/generate-token` and pasting it into the `Authorize` modal, refreshing an expired token via `/security/refresh-token`, supplying an error reporting token for `/error-report`, and executing requests against your own instance. + +## Details + Since Redoc is readonly, in the following section we will focus only on using Swagger UI. ## Protected endpoints @@ -114,3 +120,56 @@ Once finished, you will see the response as the first item under `Responses`, in You can repeat the request by clicking again on the `Execute` button. This will first clear the previous output and display the new response in the same place. Additionally, between two executions, you can manually clear any previous output using the `Clear` button next to the `Execute` button. + +## FAQ + +**Q: Why does this page cover only Swagger UI?** + +A: Redoc is read-only, so it cannot send requests. +Only Swagger UI lets you authenticate and execute calls. +See [Render documentation](render-documentation.md). + +**Q: What does the lock symbol next to an endpoint mean?** + +A: The endpoint is protected: it requires authentication with an account that holds the right permissions. + +**Q: How do I tell whether an endpoint needs admin or user privileges?** + +A: From its description. +"Admin lists user accounts" needs a `(super)admin` token, while "User fetches their own account" needs a `user` token. + +**Q: How do I authenticate in the UI?** + +A: Generate a token with `/security/generate-token`, copy the `access_token` value without the surrounding quotes, then paste it as `AuthToken` in the `Authorize` modal and click **Authorize**. + +**Q: How long does the UI keep me logged in?** + +A: Until you close or refresh the browser tab. +After that you have to authorize again. + +**Q: My token expired. Do I have to log in again?** + +A: Not necessarily. +Call `/security/refresh-token` with the `refresh_token` you saved when generating the token, then authorize with the new `access_token`. +Access tokens last one day by default. + +**Q: How do I switch to an account with different privileges?** + +A: Open the `Authorize` modal, click **Logout** for `AuthToken`, paste the new token and authorize again. + +**Q: What is the `ErrorReportingToken` for?** + +A: It authorizes the `/error-report` endpoint only, which third-party applications and frontends use to report errors back to the API. +That endpoint does not need an `AuthToken`. +See [Generating tokens](../commands/generate-tokens.md). + +**Q: Are requests sent from the UI real?** + +A: Yes — they hit your actual instance, and there is no confirmation prompt. +Check any destructive operation before clicking `Execute`. + +**Q: Where do I find the demo credentials?** + +A: In the [Token authentication](../tutorials/token-authentication.md) tutorial. +Remove or change these accounts before production. +See [Basic security](../security/basic-security.md). diff --git a/docs/book/v7/openapi/write-documentation.md b/docs/book/v7/openapi/write-documentation.md index b9e490e0..6561d09a 100644 --- a/docs/book/v7/openapi/write-documentation.md +++ b/docs/book/v7/openapi/write-documentation.md @@ -1,5 +1,12 @@ # Writing documentation +## Summary + +OpenAPI attributes live in a dedicated `OpenAPI.php` file per module rather than in the handlers themselves. +This page lists the request attributes (`OA\Get`, `OA\Post`, `OA\Patch`, `OA\Put`, `OA\Delete`), the component objects that describe payloads (`OA\Schema`, `OA\Parameter`, `OA\RequestBody`), and the parameters each request attribute should define. + +## Details + > To avoid polluting PHP files with maybe thousands of lines of OpenAPI attributes, we opted for storing them in separate files, called `OpenAPI.php`, one for each module. We already covered all the endpoints available in Dotkernel API, you can consult the existing documentation in each module's own `OpenAPI.php` file. @@ -97,3 +104,46 @@ To summarize, the typical scenario on working on your own instance of Dotkernel - add functionality to your new module (routes, entities, repositories, handlers, services, tests etc) - create file `OpenAPI.php` in the new module and describe each new endpoint - generate the latest version of a documentation file as described [in this tutorial](./generate-documentation.md) + +## FAQ + +**Q: Why are the attributes not placed on the handlers?** + +A: A fully documented endpoint can run to hundreds of lines of attributes. +Keeping them in a per-module `OpenAPI.php` leaves the handlers readable. + +**Q: Where do I start when documenting a new module?** + +A: Copy the shape of an existing module's `OpenAPI.php`. +All the endpoints shipped with Dotkernel API are documented there and serve as working examples. + +**Q: Which parameters should every request attribute define?** + +A: `path`, `description`, `summary`, `tags`, `parameters` and `responses`, plus `requestBody` for the methods that accept a body, and `security` when the endpoint is protected. + +**Q: How do I document an unprotected endpoint?** + +A: Omit the `security` parameter. + +**Q: What is the difference between `description` and `summary`?** + +A: `summary` is a short one-line label; `description` is the verbose explanation. +Renderers display them in different places. + +**Q: What are `tags` used for?** + +A: Grouping related endpoints in the rendered documentation — for example tagging every user endpoint with `User`. + +**Q: When do I use `OA\Post` versus `OA\Put`?** + +A: `OA\Post` creates a new resource, while `OA\Put` creates it and overwrites an existing one. + +**Q: Where do I find attributes not covered here?** + +A: In the [OpenAPI specification](https://spec.openapis.org/oas/latest.html) and `zircote/swagger-php`'s examples. +See [Getting help](getting-help.md). + +**Q: What is the order of work when adding a documented feature?** + +A: Create the module, build its functionality, describe the endpoints in its `OpenAPI.php`, then regenerate the documentation file. +See [Generate documentation](generate-documentation.md). diff --git a/docs/book/v7/reference/account-anonymization.md b/docs/book/v7/reference/account-anonymization.md index 5d6a71a0..bbc3c088 100644 --- a/docs/book/v7/reference/account-anonymization.md +++ b/docs/book/v7/reference/account-anonymization.md @@ -1,5 +1,10 @@ # Account anonymization +## Summary + +Anonymization is the GDPR-compliant alternative to deleting a user's personal data. +Dotkernel API replaces the stored first name, last name and email with timestamp-based placeholders and deletes the avatar, optionally appending a domain configured through `userAnonymizeAppend`. + ## Premise According to the GDPR, companies that record personal data from EU citizens must delete said data if its owner requests its deletion. @@ -38,3 +43,36 @@ The anonymization process makes these replacements: The `userAnonymizeAppend` key can be set in `config/autoload/local.php` or left empty. > Using an email domain for `userAnonymizeAppend` would work as a catch-all email, if your email service provider has this option enabled. + +## FAQ + +**Q: Why anonymize instead of delete?** + +A: The GDPR requires that personal data stop identifying the individual; anonymizing satisfies that while leaving the surrounding records intact, so related data does not have to be removed as well. + +**Q: Which personal data does Dotkernel API store by default?** + +A: The user's first name, last name and email (used as the identity). +These are needed for password reset and account activation emails. + +**Q: What do the anonymized values look like?** + +A: The name fields become `anonymous` plus the current UNIX timestamp, for example `anonymous1725980747`, and the email becomes that value plus the configured append value, for example `anonymous1725980747@example.com`. + +**Q: Where do I configure `userAnonymizeAppend`?** + +A: In `config/autoload/local.php`. +It may also be left empty. +See [Configuration files](../installation/configuration-files.md). + +**Q: What should I set `userAnonymizeAppend` to?** + +A: A domain you control works well, because anonymized addresses then land in a catch-all mailbox if your provider supports it. + +**Q: What happens to the user's avatar?** + +A: Both the image file and its database record are deleted. + +**Q: Is anonymization reversible?** + +A: No. The original values are overwritten, so keep your own backup policy in mind before running it. diff --git a/docs/book/v7/security/basic-security.md b/docs/book/v7/security/basic-security.md index 2adbfa55..86cb9f3a 100644 --- a/docs/book/v7/security/basic-security.md +++ b/docs/book/v7/security/basic-security.md @@ -1,5 +1,11 @@ # Basic Security +## Summary + +A checklist of the security tools Dotkernel API ships with and the steps you must take yourself: validating input with input filters, configuring content negotiation and CORS, extending RBAC to your own routes, removing the demo accounts, managing error reporting tokens and whitelists, keeping OpenAPI documentation out of production, tracking dependency vulnerabilities, and keeping secrets out of version control and development mode off. + +## Details + Dotkernel API provides all necessary tools to implement safe applications; however, you will need to manually make use of some of them. This section will go over the provided tools and any steps you need to follow to use them successfully, as well as a few general considerations. @@ -83,3 +89,46 @@ composer development-status - Dotkernel API ships with a [Laminas Continuous Integration](https://github.com/laminas/laminas-continuous-integration-action) GitHub Action, if you are using a public repository, consider keeping it in your custom applications to ensure code quality. > Read more about using [Laminas Continuous Integration](https://getlaminas.org/blog/2024-08-05-using-laminas-continuous-integration.html). + +## FAQ + +**Q: What must I change before going to production?** + +A: At minimum: update or remove the demo `admin` and `test@dotkernel.com` accounts, restrict `allowed_origins` in the CORS configuration, review the error reporting tokens and whitelists, disable development mode, and keep the OpenAPI documentation private. + +**Q: Where do secrets belong?** + +A: In `*.local.php` files, which are ignored by version control. +Never place them in `*.global.php` or `*.php.dist` files, which are committed. + +**Q: How do I check whether development mode is enabled?** + +A: Run `composer development-status`. + +**Q: Do the error reporting tokens expire?** + +A: No. Because they never expire, rotate them manually on a schedule of your choosing, and set `ip_whitelist` or `domain_whitelist` in `config/autoload/error-handling.global.php` to limit who can use the endpoint. + +**Q: Why is committing error reporting tokens risky?** + +A: `error-handling.global.php` is tracked by version control, so a token added locally can reach production — or a public repository — through an ordinary commit. + +**Q: The default CORS configuration allows any origin. Is that a problem?** + +A: In production, yes. +Replace `ANY_ORIGIN` with the specific origins that need access. +See the [CORS](../tutorials/cors.md) tutorial. + +**Q: Does adding a route make it protected automatically?** + +A: No. Every new route and role needs an entry in `config/autoload/authorization.global.php`. +See [Authorization](../core-features/authorization.md). + +**Q: Why should OpenAPI documentation stay out of production?** + +A: It publishes your full endpoint surface, and any examples in it are published too — which is also why sensitive values must never be used as examples. +See [OpenAPI documentation](../openapi/introduction.md). + +**Q: How do I keep dependencies safe over time?** + +A: Review published vulnerabilities for the packages you depend on, follow the Dotkernel API changelog, and keep the shipped Laminas Continuous Integration action in place on public repositories. diff --git a/docs/book/v7/security/oauth2-security.md b/docs/book/v7/security/oauth2-security.md index 0e869154..92b2e4a3 100644 --- a/docs/book/v7/security/oauth2-security.md +++ b/docs/book/v7/security/oauth2-security.md @@ -1,5 +1,11 @@ # OAuth2 Security +## Summary + +The security steps to take before an OAuth2-protected Dotkernel API reaches production: remove or re-password the default `admin` and `frontend` OAuth clients, tune access and refresh token lifetimes, and understand how the JWT signing key pair is regenerated and where it must be kept. + +## Details + Dotkernel API uses the [mezzio/mezzio-authentication-oauth2](https://github.com/mezzio/mezzio-authentication-oauth2) component to provide the OAuth2 authentication service. As a security stating point, when developing an application using this project, make sure you go over the following steps. @@ -31,3 +37,36 @@ While hidden to the VCS by default, keep in mind not to commit any local keys. > Autogeneration of keys can be disabled by simply removing the `php ./vendor/bin/generate-oauth2-keys` command from the mentioned key. > > While not related to Dotkernel API itself, do ensure that the directory containing the keys is properly secured. + +## FAQ + +**Q: What is the single most important step before going live?** + +A: Deleting or re-passwording the default `admin` and `frontend` OAuth clients. +They ship with passwords equal to their names, so leaving them in place hands anyone a valid client. + +**Q: Where do I change token lifetimes?** + +A: Under the `authentication` key in `config/autoload/local.php`. +Defaults are one day for access tokens and one month for refresh tokens; shorter values are generally safer. + +**Q: Can I invalidate a user's tokens before they expire?** + +A: Yes, via the `revokeTokens` method of `UserService`. + +**Q: When are the OAuth2 keys regenerated?** + +A: After every `composer update`, and after `composer install` when there is no lock file, through the `php ./vendor/bin/generate-oauth2-keys` script in `composer.json`. + +**Q: How do I stop the keys from being regenerated?** + +A: Remove `php ./vendor/bin/generate-oauth2-keys` from the `scripts.post-update-cmd` key in `composer.json`. +This matters on servers where regenerating keys would invalidate tokens already issued. + +**Q: Should the key pair be committed?** + +A: No. The keys are excluded from version control by default, and the directory holding them must be secured at the filesystem level. + +**Q: Where are the OAuth2 flows themselves documented?** + +A: In [Authentication](../core-features/authentication.md) and the [Token authentication](../tutorials/token-authentication.md) tutorial. diff --git a/docs/book/v7/transition-from-api-tools/api-tools-vs-dotkernel-api.md b/docs/book/v7/transition-from-api-tools/api-tools-vs-dotkernel-api.md index 3d53e077..39d52049 100644 --- a/docs/book/v7/transition-from-api-tools/api-tools-vs-dotkernel-api.md +++ b/docs/book/v7/transition-from-api-tools/api-tools-vs-dotkernel-api.md @@ -1,5 +1,12 @@ # Laminas API Tools compared to Dotkernel API +## Summary + +A side-by-side comparison of Laminas API Tools (formerly Apigility) and Dotkernel API across architecture, PHP support, database layer, authentication, authorization, documentation and tooling. +API Tools is archived and MVC/event-driven; Dotkernel API is actively maintained and middleware-based. + +## Details + | | API Tools (formerly Apigility) | Dotkernel API | |---------------------|------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------| | URL | [api-tools](https://api-tools.getlaminas.org/) | [Dotkernel API](https://www.dotkernel.org) | @@ -19,3 +26,34 @@ | Unit Tests | Yes | Yes | | Code Generator | Yes | [dotkernel/dot-maker](https://www.dotkernel.com/headless-platform/dotmaker-generate-common-code-in-dotkernel/) | | PSR | PSR-7 | PSR-7, PSR-15 | + +## FAQ + +**Q: Is Dotkernel API a drop-in replacement for API Tools?** + +A: No. The two projects differ in architecture, components and functionality, so a transition is a rewrite rather than a swap. +See [Transition approach](transition-approach.md). + +**Q: What is the biggest architectural difference?** + +A: API Tools is MVC and event-driven; Dotkernel API is a PSR-15 middleware pipeline. +Request handling, routing and extension points all work differently as a result. + +**Q: Does Dotkernel API support RPC-style endpoints?** + +A: No. Dotkernel API is REST only, while API Tools supported both REST and RPC. + +**Q: How is versioning handled without API Tools' version support?** + +A: Dotkernel API uses deprecation rather than parallel versioned namespaces. +See [API evolution](../tutorials/api-evolution.md). + +**Q: Is there a code generator like API Tools' Admin UI?** + +A: There is no web UI, but [dotkernel/dot-maker](https://www.dotkernel.com/headless-platform/dotmaker-generate-common-code-in-dotkernel/) generates common code from the command line. +See [Creating a book module using DotMaker](../tutorials/create-book-module-via-dot-maker.md). + +**Q: Why should I move off API Tools at all?** + +A: API Tools is archived, so it receives no further development. +Dotkernel API targets current PHP versions and is actively maintained. diff --git a/docs/book/v7/transition-from-api-tools/discovery-phase.md b/docs/book/v7/transition-from-api-tools/discovery-phase.md index 802f0ada..159664de 100644 --- a/docs/book/v7/transition-from-api-tools/discovery-phase.md +++ b/docs/book/v7/transition-from-api-tools/discovery-phase.md @@ -1,5 +1,11 @@ # Discovery phase for a current system built using API Tools [WIP] +## Summary + +A checklist of what to inventory in an existing api-tools system before migrating it: the database and its access layer, the authentication and authorization schemes, each module's configuration, routes, response formats and validation rules, and any custom code that was written by hand rather than generated. + +## Details + To transition a system built using api-tools to Dotkernel API, we need to analyze the core components of it. ## Database @@ -35,3 +41,37 @@ For instance: - jobs and queues - third-parties - tests + +## FAQ + +**Q: Why is a discovery phase necessary?** + +A: Because api-tools generated much of its behaviour from configuration, the working system contains decisions that are not obvious from the code alone. +Documenting them first prevents discovering missing requirements mid-migration. + +**Q: What if the current API uses a database library other than Doctrine?** + +A: Plan on mapping the schema to Doctrine entities. +Dotkernel API's default data layer is Doctrine ORM, so laminas-db or Eloquent code does not carry over. +See [Doctrine ORM](../installation/doctrine-orm.md). + +**Q: Which database versions can I target?** + +A: Dotkernel API version 7 is tested with MariaDB 10.7, 10.11 LTS, 11.4 LTS and 11.8 LTS, and with PostgreSQL 13 and above. +See [Server requirements](../introduction/server-requirements.md). + +**Q: My api-tools API uses HTTP Basic authentication. What is the equivalent?** + +A: Dotkernel API authenticates with OAuth2. +Basic and Digest schemes have no direct equivalent, so consumers need to be updated. +See [Authentication](../core-features/authentication.md). + +**Q: How do I document existing response formats?** + +A: Record which endpoints return JSON, HAL or other representations, then map them onto Dotkernel API's content negotiation. +See [Content validation](../core-features/content-validation.md). + +**Q: What counts as "custom functionality"?** + +A: Anything that could not be produced by the api-tools Admin UI — caching, event listeners, services, queues, third-party integrations and tests. +These always need manual reimplementation. diff --git a/docs/book/v7/transition-from-api-tools/transition-approach.md b/docs/book/v7/transition-from-api-tools/transition-approach.md index f93842a0..83fdcf90 100644 --- a/docs/book/v7/transition-from-api-tools/transition-approach.md +++ b/docs/book/v7/transition-from-api-tools/transition-approach.md @@ -1,5 +1,12 @@ # Transition approach [WIP] +## Summary + +How to think about moving an api-tools project onto Dotkernel API. +Because the two are not equivalent, the transition means recreating functionality — either by rebuilding all endpoints and entities one-to-one, or by running a new Dotkernel API platform alongside the old one until api-tools is retired. + +## Details + Dotkernel API is not a one-to-one replacement of api-tools (former Apigility), but is only a potential solution to migrate to. Functionalities, components and architecture are different. @@ -12,3 +19,30 @@ There are at least two approaches for this transition: - Clone 1:1 and recreate all endpoints and entities - Build a new version of the current API using Dotkernel API and keep it running as separate platforms until the sunset of the current version of api-tools + +## FAQ + +**Q: Which of the two approaches should I choose?** + +A: A one-to-one clone suits small APIs with a stable contract and few consumers. +Running both platforms in parallel suits larger APIs, because it lets you migrate consumers gradually instead of coordinating a single cutover. + +**Q: Can I reuse my api-tools code in Dotkernel API?** + +A: Business logic and validation rules usually transfer with adjustment, but controllers, routing and module configuration do not, since Dotkernel API uses a middleware architecture. +See [Laminas API Tools compared to Dotkernel API](api-tools-vs-dotkernel-api.md). + +**Q: What should I do before starting the transition?** + +A: Inventory the existing system first — database, authentication, modules, routes and custom code. +The [Discovery phase](discovery-phase.md) page lists the questions to answer. + +**Q: Can I keep my existing database?** + +A: Often yes, but Dotkernel API uses Doctrine ORM rather than laminas-db, so you will need to map your existing schema to entities. +See [Doctrine ORM](../installation/doctrine-orm.md). + +**Q: Can the two platforms share authentication while running in parallel?** + +A: Only if both issue and accept the same tokens. +Dotkernel API uses OAuth2, so an api-tools installation using HTTP Basic or Digest cannot share sessions with it directly. diff --git a/docs/book/v7/tutorials/api-evolution.md b/docs/book/v7/tutorials/api-evolution.md index 556dd92b..23318e84 100644 --- a/docs/book/v7/tutorials/api-evolution.md +++ b/docs/book/v7/tutorials/api-evolution.md @@ -1,5 +1,12 @@ # API Evolution pattern +## Summary + +Dotkernel API lets you evolve endpoints without breaking existing consumers by marking a handler with the `ResourceDeprecation` attribute. +The `DeprecationMiddleware` then adds `Sunset` and `Link` response headers, telling consumers when the resource may stop responding and where the change is documented. + +## Details + API evolution: Updating an API while keeping it compatible for existing consumers by adding new features, fixing bugs, planning and removing outdated features. ## How it works @@ -63,3 +70,41 @@ Vary: Origin > Deprecations can only be attached to handler classes that implement `RequestHandlerInterface`. > The `rel` and `type` arguments are optional, they default to `sunset` and `text/html` if no value is provided and are `Link` related parts. + +## FAQ + +**Q: What do the `Sunset` and `Link` headers mean?** + +A: `Sunset` is the date on which the deprecated resource may stop responding; `Link` points to documentation describing the change. +They are independent, so either can be used alone. + +**Q: What do I need in place before deprecations work?** + +A: `DeprecationMiddleware::class` must be present in your pipeline — in the default project, `config/pipeline.php`. +See [Middleware flow](../flow/middleware-flow.md). + +**Q: Can I deprecate a single method rather than a whole resource?** + +A: The `ResourceDeprecation` attribute is applied to the handler class, so the unit of deprecation is the handler. +Since each handler serves one method and route, deprecating the handler deprecates that method. + +**Q: What happens if I leave `Sunset` or `Link` empty?** + +A: The corresponding header is simply omitted from the response. + +**Q: What if the `Sunset` date is invalid?** + +A: It throws an error. +The value has to be a valid date. + +**Q: Are `rel` and `type` required?** + +A: No. They default to `sunset` and `text/html`, and both relate to the `Link` header. + +**Q: Which classes can carry a deprecation?** + +A: Only handler classes implementing `RequestHandlerInterface`. + +**Q: How do I check that the headers are being sent?** + +A: Request the endpoint with `curl --head` and inspect the response headers. diff --git a/docs/book/v7/tutorials/cors.md b/docs/book/v7/tutorials/cors.md index 185815a7..1c146f09 100644 --- a/docs/book/v7/tutorials/cors.md +++ b/docs/book/v7/tutorials/cors.md @@ -1,5 +1,10 @@ # CORS +## Summary + +Browsers block cross-origin requests unless the API says otherwise. +This tutorial explains the mechanism, then walks through enabling it in Dotkernel API with `mezzio/mezzio-cors`: installing the package, registering its `ConfigProvider`, piping its middleware before routing, and configuring allowed origins, headers, cache duration and credentials in `config/autoload/cors.local.php`. + ## What is CORS? **Cross-Origin Resource Sharing** or _CORS_ is an HTTP header-based mechanism that allows a server to indicate any other @@ -88,3 +93,42 @@ Save and close the file. > On the **production** environment, make sure you allow only specific origins by adding them to the `allowed_origins` array and removing the current value of `ConfigurationInterface::ANY_ORIGIN`. For more info, see [mezzio/mezzio-cors documentation](https://docs.mezzio.dev/mezzio-cors/v1/middleware/#configuration). + +## FAQ + +**Q: Why do I get a "No 'Access-Control-Allow-Origin' header" error?** + +A: The API is not configured to accept requests from the calling origin. +Add that origin to `allowed_origins`. + +**Q: Where must the CORS middleware sit in the pipeline?** + +A: Before `RouteMiddleware::class`, so preflight requests are answered without needing to match a route. + +**Q: Which origins should production allow?** + +A: Only the ones that genuinely consume the API. +Replace `ConfigurationInterface::ANY_ORIGIN` with an explicit list before deploying. + +**Q: What does `credentials_allowed` control?** + +A: Whether the browser may send cookies with cross-origin requests. +Enable it only if your clients rely on cookie-based state. + +**Q: What is `allowed_max_age` for?** + +A: It sets how long a client may cache the preflight response, in seconds. +A higher value means fewer preflight round trips. + +**Q: When do I need to change `allowed_headers`?** + +A: Whenever clients send a header not already listed — the defaults cover `Accept`, `Content-Type` and `Authorization`. + +**Q: What is `exposed_headers` for?** + +A: It lists response headers the browser should make readable to client-side code; by default only a small set of standard headers is exposed. + +**Q: Do I need to install the package on a fresh Dotkernel API?** + +A: No. `mezzio/mezzio-cors` ships with the project and `cors.local.php` is created during installation — you only need to review its values. +See [Configuration files](../installation/configuration-files.md). diff --git a/docs/book/v7/tutorials/create-book-module-via-dot-maker.md b/docs/book/v7/tutorials/create-book-module-via-dot-maker.md index 5dfbc2ae..9a4a322f 100644 --- a/docs/book/v7/tutorials/create-book-module-via-dot-maker.md +++ b/docs/book/v7/tutorials/create-book-module-via-dot-maker.md @@ -1,5 +1,12 @@ # Implementing a book module in Dotkernel API using DotMaker +## Summary + +Builds the same `Book` module as the manual tutorial, but generates the scaffolding with `dotkernel/dot-maker`. +It covers the resulting file layout across the `Api` and `Core` namespaces, the prompts `dot-maker` asks, the registration steps it leaves to you, the entity, service and input filter code you then fill in, and finally the migration, authorization entries and curl calls that prove the endpoints work. + +## Details + The `dotkernel/dot-maker` library can be used to programmatically generate project files and directories. It can be added to your API installation by following the [official documentation](https://docs.dotkernel.org/dot-maker/). @@ -452,3 +459,67 @@ The link should have the following format: ```shell curl http://0.0.0.0:8080/book/{id} ``` + +## FAQ + +**Q: What does `dot-maker` do that I would otherwise do by hand?** + +A: It generates the module skeleton — entity, repository, service and interface, handlers, collection, input filter, both `ConfigProvider` classes and the `OpenAPI.php` documentation — and splits the files between the `Api` and `Core` namespaces without being told to. + +**Q: How do I invoke it?** + +A: `./vendor/bin/dot-maker module`, or `composer make` if you added the optional script. +Enter `book` when prompted for the module name. + +**Q: What do I answer to the component prompts?** + +A: Yes to entity and repository, service and service interface, and handler; no to command and middleware. +Under the handler prompts, yes to listing, viewing and creating, and no to deleting, editing and replacing. + +**Q: What is left for me to do after generation?** + +A: Register `Api\Book\ConfigProvider::class` and `Core\Book\ConfigProvider::class` in `config/config.php`, add both namespaces to `autoload.psr-4` in `composer.json`, run `composer dump`, then fill in the custom logic. + +**Q: Why defer the migration that `dot-maker` offers to generate?** + +A: Because the generated entity has no `name`, `author` or `releaseDate` yet. +Generating the migration after editing the entity means the table matches the finished mapping. + +**Q: Which generated files need editing for this tutorial?** + +A: `Book.php` to add the three properties with their accessors and constructor, `BookService.php` to handle those properties in `getBooks()` and `saveBook()`, and `CreateBookInputFilter.php` to register the inputs. + +**Q: Does `dot-maker` create the `Input` classes as part of the module?** + +A: No. Generate them separately with `./vendor/bin/dot-maker input`, entering `Author`, `Name` and `ReleaseDate`. +As generated they need no further changes. + +**Q: Can I define the inputs inline instead?** + +A: Yes — the page shows the equivalent inline definitions. +`dot-maker` will not write them into the constructor for you, so that approach is manual. + +**Q: How do I verify the mapping before migrating?** + +A: Run `php ./bin/doctrine orm:validate-schema`, then `php ./vendor/bin/doctrine-migrations diff` and `migrate`. +See [Doctrine ORM](../installation/doctrine-orm.md). + +**Q: Why do the route names differ from the manual tutorial?** + +A: `dot-maker` uses its own naming, producing `book::list-books`, `book::view-book` and `book::create-book`. +Those are the names to grant permissions to. + +**Q: The endpoints return 403. What did I miss?** + +A: The authorization entries. +Add the three route names under `UserRoleEnum::Guest->value` in `config/autoload/authorization.global.php`. +See [Authorization](../core-features/authorization.md). + +**Q: Where do I get the URL for a single book?** + +A: From the list response, under `_embedded`, `books`, then each item's `_links`, `self`, `href`. + +**Q: Should I use this tutorial or the manual one?** + +A: Use this one to build modules quickly. +Work through [Creating a book module](create-book-module.md) first if you want to understand what each generated file is for. diff --git a/docs/book/v7/tutorials/create-book-module.md b/docs/book/v7/tutorials/create-book-module.md index a017027e..c9f84b0c 100644 --- a/docs/book/v7/tutorials/create-book-module.md +++ b/docs/book/v7/tutorials/create-book-module.md @@ -1,5 +1,10 @@ # Implementing a book module in Dotkernel API +## Summary + +A complete, hand-written walkthrough of adding a `Book` module: the file layout split between the `Api\Book` and `Core\Book` namespaces, then the code for the entity, repository, collection, service and interface, the reusable inputs and their input filter, the list, view and create handlers, both `ConfigProvider` classes and the `RoutesDelegator`. +It closes with registering the module and its namespaces, granting route permissions, generating and running the migration, and exercising the endpoints with curl. + ## Folder and files structure The below files structure is what we will have at the end of this tutorial and is just an example; you can have multiple components such as event listeners, wrappers, etc. @@ -894,3 +899,66 @@ The link should have the following format: ```shell curl http://0.0.0.0:8080/book/{id} ``` + +## FAQ + +**Q: Why is the module split across two namespaces?** + +A: `Api\Book` holds what serves requests — handlers, service, input filters, routes — while `Core\Book` holds the persistence layer: the entity, its repository and the Doctrine configuration. +See [Core and App](../extended-features/core-and-app.md). + +**Q: What are the three steps to register the module?** + +A: Add `Api\Book\ConfigProvider::class` and `Core\Book\ConfigProvider::class` to `config/config.php`, map both namespaces under `autoload.psr-4` in `composer.json`, and run `composer dump-autoload`. + +**Q: What does each `ConfigProvider` do?** + +A: The `Api` one registers the handlers, service and input filter as factories and declares the HAL resource and collection metadata. +The `Core` one registers the repository and points Doctrine's attribute driver at the module's `Entity` folder. + +**Q: Why does `GetBookResourceHandler` need no constructor?** + +A: The `#[Resource(entity: Book::class)]` attribute makes the middleware load the entity and place it on the request, so the handler only reads the request attribute and renders it. + +**Q: How does the create handler validate input?** + +A: It fills the injected `CreateBookInputFilter` from the parsed body and, if validation fails, throws `BadRequestException` with the filter's messages in the `errors` field. +See [Injectable input filters](../extended-features/injectable-input-filters.md). + +**Q: Why are the inputs separate classes?** + +A: To keep `CreateBookInputFilter` readable and let the same input be reused by other filters. +Defining them inline works too — the page shows that version. + +**Q: What is `BookCollection` for?** + +A: It wraps the query builder returned by the service so the list endpoint renders as a HAL collection with embedded items and links. + +**Q: Why does `BookService` restrict the sortable columns?** + +A: Because the sort field comes from the query string. +Only `book.name`, `book.author`, `book.releaseDate` and `book.created` are accepted, and anything else falls back to `book.created`. + +**Q: Where do the route names come from and why do they matter?** + +A: They are the third argument in `RoutesDelegator` — `book::create-book`, `book::view-book` and `book::list-books`. +A permission in Dotkernel API *is* a route name, so these are the values you grant. +See [Authorization](../core-features/authorization.md). + +**Q: My endpoints return 403 even though the code is in place. What is missing?** + +A: The three route names under `UserRoleEnum::Guest->value` in `config/autoload/authorization.global.php`. + +**Q: Why does `RoutesDelegator` use a UUID regexp for the id?** + +A: Because entities identify themselves with UUIDs via `UuidIdentifierTrait`, so `/book/{id}` should only match a UUID-shaped segment. + +**Q: When do I create the database table?** + +A: After the entity is finished: validate the mapping with `php ./bin/doctrine orm:validate-schema`, then run `doctrine-migrations diff` and `migrate`. +See [Doctrine ORM](../installation/doctrine-orm.md). + +**Q: Is there a faster way to produce all these files?** + +A: Yes — `dotkernel/dot-maker` generates the same skeleton. +See [Creating a book module using DotMaker](create-book-module-via-dot-maker.md). diff --git a/docs/book/v7/tutorials/find-user-by-identity.md b/docs/book/v7/tutorials/find-user-by-identity.md index 05edebb8..88ccabe8 100644 --- a/docs/book/v7/tutorials/find-user-by-identity.md +++ b/docs/book/v7/tutorials/find-user-by-identity.md @@ -1,5 +1,10 @@ # A practical example: Find a user by identity +## Summary + +A worked example of adding an endpoint by following an existing one. +Starting from `user.view`, which fetches a user by UUID, it builds an `IdentityHandler` that looks a user up by identity, registers it in the module's `ConfigProvider` and `RoutesDelegator`, grants the route a permission, and covers it with functional tests. + ## Our goal Create a new endpoint that fetches a user record by its identity column. @@ -210,3 +215,49 @@ class IdentityTest extends AbstractFunctionalTest ``` Planning and coding a new feature can be challenging at times, but reviewing our existing code or tutorials can serve as a source of inspiration. + +## FAQ + +**Q: How do I find the code behind an existing endpoint?** + +A: List the routes with `php ./bin/cli.php route:list`, then search for the route name in the module's `RoutesDelegator.php` to find the handler it points to. +See [Displaying Dotkernel API endpoints](../commands/display-available-endpoints.md). + +**Q: What are the steps to add an endpoint?** + +A: Create the handler, register it in the module's `ConfigProvider` under `factories`, declare the route in `RoutesDelegator.php`, and grant the route name a permission in `config/autoload/authorization.global.php`. + +**Q: Why must the new route be registered last?** + +A: Because `/user/{identity}` and `/user/{id}` match the same shape. +Registering the new route last stops it from shadowing the existing ones. + +**Q: Which factory do I register the handler with?** + +A: `AttributedServiceFactory::class`, which resolves the dependencies declared by the handler's `#[Inject]` attribute. +See [Dependency injection](../core-features/dependency-injection.md). + +**Q: Why does the handler throw two different exceptions?** + +A: `BadRequestException` covers a missing identity in the request (a client error in the input), while `NotFoundException` covers a valid identity with no matching record. +They map to 400 and 404 respectively. +See [Exceptions](../core-features/exceptions.md). + +**Q: Why is the route added under `UserRole::ROLE_GUEST`?** + +A: Only to keep the example simple — it lets everyone, including guests, view accounts. +Real deployments should grant it to the narrowest role that needs it. +See [Authorization](../core-features/authorization.md). + +**Q: Will the endpoint work without an authorization entry?** + +A: No. A route with no permission granted to the caller's role is refused, even though the handler and route exist. + +**Q: What should the tests cover?** + +A: The three outcomes: an empty identity, an identity with no matching user, and a valid identity returning the expected record. + +**Q: Where do functional tests live?** + +A: In the `test/Functional` folder, extending `AbstractFunctionalTest`. +See [Test the installation](../installation/test-the-installation.md). diff --git a/docs/book/v7/tutorials/token-authentication.md b/docs/book/v7/tutorials/token-authentication.md index 1af11065..362b220a 100644 --- a/docs/book/v7/tutorials/token-authentication.md +++ b/docs/book/v7/tutorials/token-authentication.md @@ -1,5 +1,10 @@ # Token authentication +## Summary + +A hands-on walkthrough of token authentication in Dotkernel API. +It explains how the `Authorization` header determines whether a caller is a guest, a user or an admin, lists the shipped credentials and OAuth clients, then shows the curl requests for generating and refreshing both admin and user access tokens, the success and failure responses for each, and a start-to-finish test of both flows. + ## What is token authentication? Token authentication means making a request to an API endpoint while also sending a special header that contains an access token. @@ -359,3 +364,51 @@ curl --location 'https://api.dotkernel.net/user/my-account' \ Replace `` with the previously stored access token. You should get a `200 OK` JSON response with the requested resource in the body. + +## FAQ + +**Q: Which header carries the access token?** + +A: `Authorization`, using the token type from the response — for example `Authorization: Bearer eyJ0e...`. + +**Q: What happens if I send no token at all?** + +A: The caller is treated as a `guest`. +Public endpoints still respond; protected ones return `403 Forbidden`. + +**Q: Why do admin and user tokens use different clients?** + +A: Each account type has its own OAuth client: `admin` for accounts in the `admin` table and `frontend` for accounts in the `user` table. +The `client_id` and `client_secret` must match the account you are authenticating. + +**Q: Do I have to generate a token for every request?** + +A: No. Generate it once, store it, and reuse it until it expires — then refresh rather than re-authenticate. + +**Q: What is the difference between the generate and refresh requests?** + +A: Generating uses `grant_type: password` with a username and password; refreshing uses `grant_type: refresh_token` with the refresh token and no credentials. + +**Q: How long is an access token valid?** + +A: 86400 seconds — one day — configurable via `authentication`, `access_token_expire` in `config/autoload/local.php`. + +**Q: I get `400 Bad Request` with "Invalid credentials". What should I check?** + +A: The username and password, and that `client_id` and `client_secret` match a row in `oauth_clients`. +Both credential fields come from the `admin` or `user` table depending on the account type. + +**Q: I get `401 Unauthorized` with "The refresh token is invalid". Why?** + +A: The refresh token could not be decrypted — it is malformed, has expired, or was issued for a different client than the one in the request. + +**Q: Are the shipped credentials safe to keep?** + +A: No. The `admin` / `dotadmin` and `test@dotkernel.com` / `dotkernel` accounts, and the OAuth clients whose secrets equal their names, must be changed or removed before production. +See [OAuth2 security](../security/oauth2-security.md). + +**Q: Where should the tokens be stored on the client?** + +A: Somewhere private to the client. +Never commit them or write them to logs, and send them only over HTTPS. +See [Authentication](../core-features/authentication.md). diff --git a/docs/book/v7/upgrading/UPGRADE-6.0.md b/docs/book/v7/upgrading/UPGRADE-6.0.md index 344eed42..f6f0f453 100644 --- a/docs/book/v7/upgrading/UPGRADE-6.0.md +++ b/docs/book/v7/upgrading/UPGRADE-6.0.md @@ -1,5 +1,12 @@ # Upgrading from 5.x to 6.0 +## Summary + +The changes you need to port into your project when moving from Dotkernel API 5.x to 6.0, each linked to the pull request that introduced it. +This release reorganised shared logic into the Core module, refactored handlers and services, introduced route grouping, and replaced Twig with a custom templating solution. + +## Details + > You can find a complete list in [Changelog](https://github.com/dotkernel/api/blob/7.0/CHANGELOG.md) * Move common logic to Core module [https://github.com/dotkernel/api/pull/358](https://github.com/dotkernel/api/pull/358) @@ -22,3 +29,32 @@ * Replaced `Twig` with custom templating solution [https://github.com/dotkernel/api/pull/419](https://github.com/dotkernel/api/pull/419) * Increased `PHPStan` level to 8 [https://github.com/dotkernel/api/pull/421](https://github.com/dotkernel/api/pull/421) * Split the `/security/token` endpoint into two separate endpoints [https://github.com/dotkernel/api/pull/423](https://github.com/dotkernel/api/pull/423) + +## FAQ + +**Q: Which change in 6.0 is most likely to break my code?** + +A: Moving common logic into the Core module, together with the handler and service refactoring. +Namespaces and constructor signatures changed, so your own handlers and services need updating. +See [Core and App](../extended-features/core-and-app.md). + +**Q: What replaced Twig for templating?** + +A: A custom templating solution shipped with Dotkernel. +Templates that relied on Twig syntax need to be rewritten. +See [Rendering and sending emails](../core-features/rendering-and-sending-emails.md). + +**Q: Why was the `/security/token` endpoint split in two?** + +A: To separate issuing a token from refreshing one, which makes each endpoint's request and response contract explicit. +See [Token authentication](../tutorials/token-authentication.md). + +**Q: What does "inject `InputFilters` in handlers" change for me?** + +A: Input filters are now resolved from the container and injected rather than constructed inside the handler. +See [Injectable input filters](../extended-features/injectable-input-filters.md). + +**Q: Do I need to regenerate my OAuth2 keys?** + +A: Keys are generated automatically when cloning the project from 6.0 onward. +Existing installations can keep their current keys. diff --git a/docs/book/v7/upgrading/UPGRADE-7.0.md b/docs/book/v7/upgrading/UPGRADE-7.0.md index 4c5f0580..656d1577 100644 --- a/docs/book/v7/upgrading/UPGRADE-7.0.md +++ b/docs/book/v7/upgrading/UPGRADE-7.0.md @@ -1,5 +1,12 @@ # Upgrading from 6.x to 7.0 +## Summary + +The changes you need to port into your project when moving from Dotkernel API 6.x to 7.0, each linked to the pull request that introduced it. +The headline items are native UUIDs in the database, PostgreSQL support, and the removal of the `MethodDeprecation` implementation. + +## Details + > You can find a complete list in [Changelog](https://github.com/dotkernel/api/blob/7.0/CHANGELOG.md) * Use native UUIDs in database via `ramsey/uuid` [https://github.com/dotkernel/api/pull/456](https://github.com/dotkernel/api/pull/456) @@ -7,3 +14,27 @@ * PostgreSQL implementation [https://github.com/dotkernel/api/pull/462](https://github.com/dotkernel/api/pull/462) * Remove `MethodDeprecation` implementation [https://github.com/dotkernel/api/pull/470](https://github.com/dotkernel/api/pull/470) * Clarify instructions regarding multiple connections in `config/autoload/local.php.dist` [https://github.com/dotkernel/api/pull/472](https://github.com/dotkernel/api/pull/472) + +## FAQ + +**Q: Is there an automated upgrade from 6.x to 7.0?** + +A: No. You implement each listed change manually in your own project. +See [Upgrades](upgrading.md) for the recommended procedure. + +**Q: What does the switch to native UUIDs mean for my database?** + +A: Identifiers are stored using the database's own UUID handling via `ramsey/uuid` rather than a generic column type, so existing tables need a migration. +Review pull request 456 before touching production data. + +**Q: Do I have to move to PostgreSQL in 7.0?** + +A: No. PostgreSQL is now supported in addition to MariaDB; either is a valid choice. + +**Q: `MethodDeprecation` was removed — how do I deprecate an endpoint now?** + +A: Use the deprecation approach described in [API evolution](../tutorials/api-evolution.md). + +**Q: Where do I find the complete list of changes?** + +A: In the project [CHANGELOG.md](https://github.com/dotkernel/api/blob/7.0/CHANGELOG.md). diff --git a/docs/book/v7/upgrading/upgrading.md b/docs/book/v7/upgrading/upgrading.md index ba17e9a1..d1143b67 100644 --- a/docs/book/v7/upgrading/upgrading.md +++ b/docs/book/v7/upgrading/upgrading.md @@ -1,5 +1,12 @@ # Upgrades +## Summary + +Dotkernel API has no automatic upgrade path. +You upgrade by applying the changes from each release to your own project by hand, using the project `CHANGELOG.md` to track which version your copy corresponds to. + +## Details + Dotkernel API does not provide an automatic upgrade path. Instead, the recommended procedure is to manually implement each modification listed in [releases](https://github.com/dotkernel/api/releases). Additionally, release info can also be accessed as an [RSS](https://github.com/dotkernel/api/releases.atom) feed. @@ -17,3 +24,28 @@ This allows you to track your API's version and keep your project up to date wit ## Version to version upgrading Starting from [version 5.3](UPGRADE-6.0.md) the upgrading procedure is detailed version to version. + +## FAQ + +**Q: Why is there no automatic upgrade path?** + +A: Dotkernel API is a project skeleton you own and modify, not a dependency you bump. +Because your code lives alongside the skeleton's, only you can decide how each upstream change applies to it. + +**Q: How do I tell which version my project is based on?** + +A: Compare the entries in your project's `CHANGELOG.md` with the upstream one. +The last upstream entry you have applied is your effective version. + +**Q: Do I have to apply every release in order?** + +A: Applying them in order is strongly recommended, since later changes often build on earlier ones. +Skipping releases means reconciling several sets of changes at once. + +**Q: How do I stay informed about new releases?** + +A: Watch the [releases page](https://github.com/dotkernel/api/releases) or subscribe to the [RSS feed](https://github.com/dotkernel/api/releases.atom). + +**Q: Where do I find instructions for a specific major version jump?** + +A: Version-to-version pages exist from 5.3 onward, for example [Upgrading from 5.x to 6.0](UPGRADE-6.0.md) and [Upgrading from 6.x to 7.0](UPGRADE-7.0.md).