From 2ad63c2c4c0775a5cb4cf17270a958c9c6910b4b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 10 Feb 2024 08:58:26 +0000 Subject: [PATCH] deploy: 84957ddbc317c8d63eda3745c643ecbe1af80fcc --- 404.html | 4 ++-- ...06b8c.deea3b76.js => 13d06b8c.16ae6aff.js} | 2 +- assets/js/2e3626b2.1acba910.js | 1 + assets/js/2e3626b2.5a9e1a76.js | 1 - ...c09c4.c2453e03.js => 3ffc09c4.0e2414c3.js} | 2 +- assets/js/66d590de.93f0783d.js | 1 - assets/js/66d590de.eb8f2ff4.js | 1 + ...n.54e3bf55.js => runtime~main.5a8dd5a8.js} | 2 +- .../apiato-containers/debugger/index.html | 4 ++-- .../documentation/index.html | 4 ++-- .../apiato-containers/localization/index.html | 4 ++-- .../apiato-containers/overview/index.html | 4 ++-- .../apiato-containers/payments/index.html | 4 ++-- .../apiato-containers/settings/index.html | 4 ++-- .../social-authentication/index.html | 4 ++-- .../community-containers/overview/index.html | 4 ++-- docs/10.x/contribution-guide/index.html | 4 ++-- .../core-features/api-versioning/index.html | 4 ++-- .../core-features/authentication/index.html | 4 ++-- .../core-features/authorization/index.html | 4 ++-- .../core-features/code-generator/index.html | 4 ++-- .../core-features/data-caching/index.html | 4 ++-- .../default-endpoints/index.html | 4 ++-- docs/10.x/core-features/etag/index.html | 4 ++-- docs/10.x/core-features/hash-id/index.html | 4 ++-- docs/10.x/core-features/pagination/index.html | 4 ++-- docs/10.x/core-features/profiler/index.html | 4 ++-- .../core-features/query-parameters/index.html | 4 ++-- .../core-features/rate-limiting/index.html | 4 ++-- .../core-features/useful-commands/index.html | 4 ++-- .../user-registration/index.html | 4 ++-- docs/10.x/core-features/validation/index.html | 4 ++-- docs/10.x/faq/index.html | 4 ++-- .../container-installer/index.html | 4 ++-- .../conventions-and-principles/index.html | 4 ++-- .../getting-started/installation/index.html | 4 ++-- .../markdown-features/index.html | 4 ++-- docs/10.x/getting-started/requests/index.html | 4 ++-- .../10.x/getting-started/responses/index.html | 4 ++-- docs/10.x/getting-started/samples/index.html | 4 ++-- .../index.html | 4 ++-- docs/10.x/index.html | 4 ++-- docs/10.x/main-components/actions/index.html | 4 ++-- .../main-components/controllers/index.html | 4 ++-- .../main-components/exceptions/index.html | 4 ++-- docs/10.x/main-components/models/index.html | 4 ++-- docs/10.x/main-components/requests/index.html | 4 ++-- docs/10.x/main-components/routes/index.html | 4 ++-- .../main-components/subactions/index.html | 4 ++-- docs/10.x/main-components/tasks/index.html | 4 ++-- .../main-components/transformers/index.html | 4 ++-- docs/10.x/main-components/views/index.html | 4 ++-- .../miscellaneous/tasks-queuing/index.html | 4 ++-- .../miscellaneous/tasks-scheduling/index.html | 4 ++-- .../miscellaneous/tests-helpers/index.html | 4 ++-- .../optional-components/commands/index.html | 4 ++-- .../optional-components/configs/index.html | 4 ++-- .../optional-components/criterias/index.html | 4 ++-- .../optional-components/events/index.html | 4 ++-- .../optional-components/factories/index.html | 4 ++-- .../optional-components/helpers/index.html | 4 ++-- docs/10.x/optional-components/jobs/index.html | 4 ++-- .../optional-components/languages/index.html | 4 ++-- .../10.x/optional-components/mails/index.html | 4 ++-- .../middlewares/index.html | 4 ++-- .../optional-components/migrations/index.html | 4 ++-- .../notifications/index.html | 4 ++-- .../optional-components/providers/index.html | 4 ++-- .../repositories/index.html | 4 ++-- .../optional-components/seeders/index.html | 4 ++-- .../10.x/optional-components/tests/index.html | 4 ++-- .../optional-components/values/index.html | 4 ++-- docs/10.x/upgrade-guide/index.html | 4 ++-- .../documentation/index.html | 4 ++-- .../localization/index.html | 4 ++-- .../additional-features/overview/index.html | 4 ++-- .../social-authentication/index.html | 4 ++-- docs/11.x/contribution-guide/index.html | 4 ++-- .../core-features/api-versioning/index.html | 4 ++-- .../core-features/authentication/index.html | 4 ++-- .../core-features/authorization/index.html | 4 ++-- .../core-features/code-generator/index.html | 4 ++-- .../core-features/data-caching/index.html | 4 ++-- .../default-endpoints/index.html | 4 ++-- docs/11.x/core-features/etag/index.html | 4 ++-- docs/11.x/core-features/hash-id/index.html | 4 ++-- docs/11.x/core-features/pagination/index.html | 4 ++-- docs/11.x/core-features/profiler/index.html | 4 ++-- .../core-features/query-parameters/index.html | 4 ++-- .../core-features/rate-limiting/index.html | 4 ++-- .../core-features/useful-commands/index.html | 4 ++-- .../user-registration/index.html | 4 ++-- docs/11.x/core-features/validation/index.html | 4 ++-- docs/11.x/faq/index.html | 4 ++-- .../container-installer/index.html | 4 ++-- .../conventions-and-principles/index.html | 4 ++-- .../getting-started/installation/index.html | 4 ++-- .../markdown-features/index.html | 4 ++-- docs/11.x/getting-started/requests/index.html | 4 ++-- .../11.x/getting-started/responses/index.html | 4 ++-- docs/11.x/getting-started/samples/index.html | 4 ++-- .../index.html | 4 ++-- docs/11.x/index.html | 4 ++-- docs/11.x/main-components/actions/index.html | 4 ++-- .../main-components/controllers/index.html | 4 ++-- .../main-components/exceptions/index.html | 4 ++-- docs/11.x/main-components/models/index.html | 4 ++-- docs/11.x/main-components/requests/index.html | 4 ++-- docs/11.x/main-components/routes/index.html | 4 ++-- .../main-components/subactions/index.html | 4 ++-- docs/11.x/main-components/tasks/index.html | 4 ++-- .../main-components/transformers/index.html | 4 ++-- docs/11.x/main-components/views/index.html | 4 ++-- .../miscellaneous/tasks-queuing/index.html | 4 ++-- .../miscellaneous/tasks-scheduling/index.html | 4 ++-- .../miscellaneous/tests-helpers/index.html | 4 ++-- .../optional-components/commands/index.html | 4 ++-- .../optional-components/configs/index.html | 4 ++-- .../optional-components/criterias/index.html | 4 ++-- .../optional-components/events/index.html | 4 ++-- .../optional-components/factories/index.html | 4 ++-- .../optional-components/helpers/index.html | 4 ++-- docs/11.x/optional-components/jobs/index.html | 4 ++-- .../optional-components/languages/index.html | 4 ++-- .../11.x/optional-components/mails/index.html | 4 ++-- .../middlewares/index.html | 4 ++-- .../optional-components/migrations/index.html | 4 ++-- .../notifications/index.html | 4 ++-- .../optional-components/providers/index.html | 4 ++-- .../repositories/index.html | 4 ++-- .../optional-components/seeders/index.html | 4 ++-- .../11.x/optional-components/tests/index.html | 4 ++-- .../optional-components/values/index.html | 4 ++-- docs/11.x/upgrade-guide/index.html | 4 ++-- docs/9.x/contribution-guide/index.html | 4 ++-- .../core-features/admin-dashboard/index.html | 4 ++-- .../api-docs-generator/index.html | 4 ++-- .../core-features/api-versioning/index.html | 4 ++-- .../core-features/authentication/index.html | 4 ++-- .../core-features/authorization/index.html | 4 ++-- .../core-features/code-generator/index.html | 4 ++-- .../9.x/core-features/data-caching/index.html | 4 ++-- .../default-endpoints/index.html | 4 ++-- docs/9.x/core-features/etag/index.html | 4 ++-- docs/9.x/core-features/hash-id/index.html | 4 ++-- .../9.x/core-features/localization/index.html | 4 ++-- docs/9.x/core-features/pagination/index.html | 4 ++-- docs/9.x/core-features/payments/index.html | 4 ++-- docs/9.x/core-features/profiler/index.html | 4 ++-- .../core-features/query-parameters/index.html | 4 ++-- .../core-features/rate-limiting/index.html | 4 ++-- .../core-features/requests-monitor/index.html | 4 ++-- .../search-query-parameter/index.html | 4 ++-- .../social-authentication/index.html | 4 ++-- .../core-features/system-settings/index.html | 4 ++-- .../core-features/useful-commands/index.html | 4 ++-- .../user-registration/index.html | 4 ++-- docs/9.x/core-features/validation/index.html | 4 ++-- docs/9.x/faq/index.html | 4 ++-- .../conventions-and-principles/index.html | 4 ++-- .../getting-started/installation/index.html | 4 ++-- .../markdown-features/index.html | 4 ++-- docs/9.x/getting-started/overview/index.html | 4 ++-- docs/9.x/getting-started/requests/index.html | 4 ++-- docs/9.x/getting-started/responses/index.html | 4 ++-- .../index.html | 4 ++-- docs/9.x/index.html | 4 ++-- docs/9.x/main-components/actions/index.html | 4 ++-- .../main-components/controllers/index.html | 4 ++-- docs/9.x/main-components/models/index.html | 4 ++-- docs/9.x/main-components/requests/index.html | 4 ++-- docs/9.x/main-components/routes/index.html | 4 ++-- .../9.x/main-components/subactions/index.html | 4 ++-- docs/9.x/main-components/tasks/index.html | 4 ++-- .../main-components/transformers/index.html | 4 ++-- .../main-components/transporters/index.html | 4 ++-- docs/9.x/main-components/views/index.html | 4 ++-- .../container-installer/index.html | 4 ++-- .../9.x/miscellaneous/magical-call/index.html | 4 ++-- docs/9.x/miscellaneous/postman/index.html | 4 ++-- .../miscellaneous/tasks-queuing/index.html | 4 ++-- .../miscellaneous/tasks-scheduling/index.html | 4 ++-- .../miscellaneous/tests-helpers/index.html | 4 ++-- .../optional-components/commands/index.html | 4 ++-- .../optional-components/configs/index.html | 4 ++-- .../optional-components/criterias/index.html | 4 ++-- .../9.x/optional-components/events/index.html | 4 ++-- .../optional-components/exceptions/index.html | 4 ++-- .../optional-components/factories/index.html | 4 ++-- docs/9.x/optional-components/jobs/index.html | 4 ++-- .../optional-components/languages/index.html | 4 ++-- docs/9.x/optional-components/mails/index.html | 4 ++-- .../middlewares/index.html | 4 ++-- .../optional-components/migrations/index.html | 4 ++-- .../notifications/index.html | 4 ++-- .../optional-components/providers/index.html | 4 ++-- .../repositories/index.html | 4 ++-- .../optional-components/seeders/index.html | 4 ++-- docs/9.x/optional-components/tests/index.html | 4 ++-- .../9.x/optional-components/values/index.html | 4 ++-- docs/9.x/upgrade-guide/index.html | 4 ++-- .../components/index.html | 4 ++-- .../container/index.html | 4 ++-- docs/architecture-concepts/index.html | 4 ++-- docs/architecture-concepts/porto/index.html | 4 ++-- .../request-lifecycle/index.html | 4 ++-- docs/components/index.html | 4 ++-- .../main-components/actions/index.html | 4 ++-- .../main-components/controllers/index.html | 4 ++-- .../main-components/exceptions/index.html | 4 ++-- docs/components/main-components/index.html | 4 ++-- .../main-components/models/index.html | 4 ++-- .../main-components/requests/index.html | 21 ++++++------------- .../main-components/routes/index.html | 4 ++-- .../main-components/subactions/index.html | 4 ++-- .../main-components/tasks/index.html | 4 ++-- .../main-components/transformers/index.html | 4 ++-- .../main-components/views/index.html | 4 ++-- .../optional-components/commands/index.html | 4 ++-- .../optional-components/configs/index.html | 4 ++-- .../optional-components/events/index.html | 4 ++-- .../optional-components/factories/index.html | 4 ++-- .../optional-components/helpers/index.html | 4 ++-- .../components/optional-components/index.html | 4 ++-- .../optional-components/jobs/index.html | 4 ++-- .../optional-components/mail/index.html | 4 ++-- .../middlewares/index.html | 4 ++-- .../optional-components/migrations/index.html | 4 ++-- .../notifications/index.html | 4 ++-- .../optional-components/policies/index.html | 4 ++-- .../repository/criterias/index.html | 4 ++-- .../repository/repositories/index.html | 4 ++-- .../optional-components/seeders/index.html | 4 ++-- .../service-providers/index.html | 4 ++-- .../optional-components/tests/index.html | 4 ++-- .../optional-components/values/index.html | 4 ++-- docs/consulting/index.html | 4 ++-- .../api-versioning/index.html | 4 ++-- .../code-generator/index.html | 4 ++-- docs/framework-features/etag/index.html | 4 ++-- docs/framework-features/index.html | 4 ++-- docs/framework-features/profiler/index.html | 4 ++-- .../rate-limiting/index.html | 4 ++-- docs/framework-features/rbac/index.html | 4 ++-- .../getting-started/best-practices/index.html | 4 ++-- .../customized-laravel-components/index.html | 4 ++-- docs/getting-started/installation/index.html | 4 ++-- .../components/index.html | 4 ++-- .../container/index.html | 4 ++-- docs/next/architecture-concepts/index.html | 4 ++-- .../architecture-concepts/porto/index.html | 4 ++-- .../request-lifecycle/index.html | 4 ++-- docs/next/components/index.html | 4 ++-- .../main-components/actions/index.html | 4 ++-- .../main-components/controllers/index.html | 4 ++-- .../main-components/exceptions/index.html | 4 ++-- .../components/main-components/index.html | 4 ++-- .../main-components/models/index.html | 4 ++-- .../main-components/requests/index.html | 21 ++++++------------- .../main-components/routes/index.html | 4 ++-- .../main-components/subactions/index.html | 4 ++-- .../main-components/tasks/index.html | 4 ++-- .../main-components/transformers/index.html | 4 ++-- .../main-components/views/index.html | 4 ++-- .../optional-components/commands/index.html | 4 ++-- .../optional-components/configs/index.html | 4 ++-- .../optional-components/events/index.html | 4 ++-- .../optional-components/factories/index.html | 4 ++-- .../optional-components/helpers/index.html | 4 ++-- .../components/optional-components/index.html | 4 ++-- .../optional-components/jobs/index.html | 4 ++-- .../optional-components/mail/index.html | 4 ++-- .../middlewares/index.html | 4 ++-- .../optional-components/migrations/index.html | 4 ++-- .../notifications/index.html | 4 ++-- .../optional-components/policies/index.html | 4 ++-- .../repository/criterias/index.html | 4 ++-- .../repository/repositories/index.html | 4 ++-- .../optional-components/seeders/index.html | 4 ++-- .../service-providers/index.html | 4 ++-- .../optional-components/tests/index.html | 4 ++-- .../optional-components/values/index.html | 4 ++-- docs/next/consulting/index.html | 4 ++-- .../api-versioning/index.html | 4 ++-- .../code-generator/index.html | 4 ++-- docs/next/framework-features/etag/index.html | 4 ++-- docs/next/framework-features/index.html | 4 ++-- .../framework-features/profiler/index.html | 4 ++-- .../rate-limiting/index.html | 4 ++-- docs/next/framework-features/rbac/index.html | 4 ++-- .../getting-started/best-practices/index.html | 4 ++-- .../customized-laravel-components/index.html | 4 ++-- .../getting-started/installation/index.html | 4 ++-- docs/next/pacakges/documentation/index.html | 4 ++-- docs/next/pacakges/index.html | 4 ++-- docs/next/pacakges/localization/index.html | 4 ++-- .../pacakges/social-authentication/index.html | 4 ++-- .../prologue/contribution-guide/index.html | 4 ++-- docs/next/prologue/release-notes/index.html | 4 ++-- docs/next/prologue/upgrade-guide/index.html | 4 ++-- docs/next/security/authentication/index.html | 4 ++-- docs/next/security/authorization/index.html | 6 +++--- .../security/email-varification/index.html | 4 ++-- docs/next/security/hash-id/index.html | 4 ++-- docs/next/security/password-reset/index.html | 4 ++-- docs/next/security/registration/index.html | 4 ++-- docs/next/tags/action/index.html | 4 ++-- docs/next/tags/api-versioning/index.html | 4 ++-- docs/next/tags/architecture/index.html | 4 ++-- docs/next/tags/authorization/index.html | 4 ++-- docs/next/tags/code-generator/index.html | 4 ++-- docs/next/tags/command/index.html | 4 ++-- docs/next/tags/component/index.html | 4 ++-- docs/next/tags/config/index.html | 4 ++-- docs/next/tags/container/index.html | 4 ++-- docs/next/tags/controller/index.html | 4 ++-- docs/next/tags/criteria/index.html | 4 ++-- docs/next/tags/etag/index.html | 4 ++-- docs/next/tags/event/index.html | 4 ++-- docs/next/tags/exception/index.html | 4 ++-- docs/next/tags/factory/index.html | 4 ++-- docs/next/tags/framework-feature/index.html | 4 ++-- docs/next/tags/helper/index.html | 4 ++-- docs/next/tags/index.html | 4 ++-- docs/next/tags/job/index.html | 4 ++-- docs/next/tags/lifecycle/index.html | 4 ++-- docs/next/tags/listener/index.html | 4 ++-- docs/next/tags/mail/index.html | 4 ++-- docs/next/tags/main-component/index.html | 4 ++-- docs/next/tags/middleware/index.html | 4 ++-- docs/next/tags/migration/index.html | 4 ++-- docs/next/tags/model/index.html | 4 ++-- docs/next/tags/notification/index.html | 4 ++-- docs/next/tags/optional-component/index.html | 4 ++-- docs/next/tags/policy/index.html | 4 ++-- docs/next/tags/porto/index.html | 4 ++-- docs/next/tags/profiler/index.html | 4 ++-- docs/next/tags/queue/index.html | 4 ++-- docs/next/tags/rate-limiting/index.html | 4 ++-- docs/next/tags/repository/index.html | 4 ++-- docs/next/tags/request/index.html | 4 ++-- docs/next/tags/response/index.html | 4 ++-- .../tags/role-based-access-control/index.html | 4 ++-- docs/next/tags/route/index.html | 4 ++-- docs/next/tags/seeder/index.html | 4 ++-- docs/next/tags/service-provider/index.html | 4 ++-- docs/next/tags/sub-action/index.html | 4 ++-- docs/next/tags/task/index.html | 4 ++-- docs/next/tags/test/index.html | 4 ++-- docs/next/tags/testing/index.html | 4 ++-- docs/next/tags/transformer/index.html | 4 ++-- docs/next/tags/value/index.html | 4 ++-- docs/next/tags/view/index.html | 4 ++-- docs/pacakges/documentation/index.html | 4 ++-- docs/pacakges/index.html | 4 ++-- docs/pacakges/localization/index.html | 4 ++-- .../pacakges/social-authentication/index.html | 4 ++-- docs/prologue/contribution-guide/index.html | 4 ++-- docs/prologue/release-notes/index.html | 4 ++-- docs/prologue/upgrade-guide/index.html | 4 ++-- docs/security/authentication/index.html | 4 ++-- docs/security/authorization/index.html | 6 +++--- docs/security/email-varification/index.html | 4 ++-- docs/security/hash-id/index.html | 4 ++-- docs/security/password-reset/index.html | 4 ++-- docs/security/registration/index.html | 4 ++-- docs/tags/action/index.html | 4 ++-- docs/tags/api-versioning/index.html | 4 ++-- docs/tags/architecture/index.html | 4 ++-- docs/tags/authorization/index.html | 4 ++-- docs/tags/code-generator/index.html | 4 ++-- docs/tags/command/index.html | 4 ++-- docs/tags/component/index.html | 4 ++-- docs/tags/config/index.html | 4 ++-- docs/tags/container/index.html | 4 ++-- docs/tags/controller/index.html | 4 ++-- docs/tags/criteria/index.html | 4 ++-- docs/tags/etag/index.html | 4 ++-- docs/tags/event/index.html | 4 ++-- docs/tags/exception/index.html | 4 ++-- docs/tags/factory/index.html | 4 ++-- docs/tags/framework-feature/index.html | 4 ++-- docs/tags/helper/index.html | 4 ++-- docs/tags/index.html | 4 ++-- docs/tags/job/index.html | 4 ++-- docs/tags/lifecycle/index.html | 4 ++-- docs/tags/listener/index.html | 4 ++-- docs/tags/mail/index.html | 4 ++-- docs/tags/main-component/index.html | 4 ++-- docs/tags/middleware/index.html | 4 ++-- docs/tags/migration/index.html | 4 ++-- docs/tags/model/index.html | 4 ++-- docs/tags/notification/index.html | 4 ++-- docs/tags/optional-component/index.html | 4 ++-- docs/tags/policy/index.html | 4 ++-- docs/tags/porto/index.html | 4 ++-- docs/tags/profiler/index.html | 4 ++-- docs/tags/queue/index.html | 4 ++-- docs/tags/rate-limiting/index.html | 4 ++-- docs/tags/repository/index.html | 4 ++-- docs/tags/request/index.html | 4 ++-- docs/tags/response/index.html | 4 ++-- .../tags/role-based-access-control/index.html | 4 ++-- docs/tags/route/index.html | 4 ++-- docs/tags/seeder/index.html | 4 ++-- docs/tags/service-provider/index.html | 4 ++-- docs/tags/sub-action/index.html | 4 ++-- docs/tags/task/index.html | 4 ++-- docs/tags/test/index.html | 4 ++-- docs/tags/testing/index.html | 4 ++-- docs/tags/transformer/index.html | 4 ++-- docs/tags/value/index.html | 4 ++-- docs/tags/view/index.html | 4 ++-- index.html | 4 ++-- 414 files changed, 829 insertions(+), 847 deletions(-) rename assets/js/{13d06b8c.deea3b76.js => 13d06b8c.16ae6aff.js} (93%) create mode 100644 assets/js/2e3626b2.1acba910.js delete mode 100644 assets/js/2e3626b2.5a9e1a76.js rename assets/js/{3ffc09c4.c2453e03.js => 3ffc09c4.0e2414c3.js} (94%) delete mode 100644 assets/js/66d590de.93f0783d.js create mode 100644 assets/js/66d590de.eb8f2ff4.js rename assets/js/{runtime~main.54e3bf55.js => runtime~main.5a8dd5a8.js} (98%) diff --git a/404.html b/404.html index 72da7f7c7..9813ce6c8 100644 --- a/404.html +++ b/404.html @@ -4,13 +4,13 @@ Page Not Found | Apiato - +
Skip to main content

Page Not Found

We could not find what you were looking for.

Please contact the owner of the site that linked you to the original URL and let them know their link is broken.

- + \ No newline at end of file diff --git a/assets/js/13d06b8c.deea3b76.js b/assets/js/13d06b8c.16ae6aff.js similarity index 93% rename from assets/js/13d06b8c.deea3b76.js rename to assets/js/13d06b8c.16ae6aff.js index 306326cdb..af2394850 100644 --- a/assets/js/13d06b8c.deea3b76.js +++ b/assets/js/13d06b8c.16ae6aff.js @@ -1 +1 @@ -"use strict";(self.webpackChunkdocumentation=self.webpackChunkdocumentation||[]).push([[6011],{95788:(e,t,n)=>{n.d(t,{Iu:()=>c,yg:()=>y});var o=n(11504);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function i(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var l=o.createContext({}),p=function(e){var t=o.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},c=function(e){var t=p(e.components);return o.createElement(l.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},m=o.forwardRef((function(e,t){var n=e.components,r=e.mdxType,a=e.originalType,l=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),u=p(n),m=r,y=u["".concat(l,".").concat(m)]||u[m]||d[m]||a;return n?o.createElement(y,i(i({ref:t},c),{},{components:n})):o.createElement(y,i({ref:t},c))}));function y(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var a=n.length,i=new Array(a);i[0]=m;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[u]="string"==typeof e?e:r,i[1]=s;for(var p=2;p{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>i,default:()=>d,frontMatter:()=>a,metadata:()=>s,toc:()=>p});var o=n(45072),r=(n(11504),n(95788));const a={sidebar_position:3,title:"Authorization",tags:["component","optional-component","authorization","request","policy"]},i=void 0,s={unversionedId:"security/authorization",id:"version-12.x/security/authorization",title:"Authorization",description:"Apiato provides a Role-Based Access Control (RBAC) through its Authorization Container.",source:"@site/versioned_docs/version-12.x/security/authorization.mdx",sourceDirName:"security",slug:"/security/authorization",permalink:"/docs/security/authorization",draft:!1,editUrl:"https://github.com/apiato/documentation/tree/master/versioned_docs/version-12.x/security/authorization.mdx",tags:[{label:"component",permalink:"/docs/tags/component"},{label:"optional-component",permalink:"/docs/tags/optional-component"},{label:"authorization",permalink:"/docs/tags/authorization"},{label:"request",permalink:"/docs/tags/request"},{label:"policy",permalink:"/docs/tags/policy"}],version:"12.x",lastUpdatedBy:"Mohammad Alavi",lastUpdatedAt:1697511591,formattedLastUpdatedAt:"Oct 17, 2023",sidebarPosition:3,frontMatter:{sidebar_position:3,title:"Authorization",tags:["component","optional-component","authorization","request","policy"]},sidebar:"tutorialSidebar",previous:{title:"Authentication",permalink:"/docs/security/authentication"},next:{title:"Email Verification",permalink:"/docs/security/email-varification"}},l={},p=[{value:"How it works",id:"how-it-works",level:2},{value:"Code Example",id:"code-example",level:2}],c={toc:p},u="wrapper";function d(e){let{components:t,...n}=e;return(0,r.yg)(u,(0,o.c)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,r.yg)("p",null,"Apiato provides a Role-Based Access Control (RBAC) through its Authorization Container.\nBehind the scenes,\nApiato uses the ",(0,r.yg)("a",{parentName:"p",href:"https://github.com/spatie/laravel-permission"},"Laravel Permission")," package."),(0,r.yg)("h2",{id:"how-it-works"},"How it works"),(0,r.yg)("p",null,"Authorization in Apiato is indeed straightforward and easy.\nIt operates by linking permissions to roles and then assigning roles to users."),(0,r.yg)("p",null,"To implement the authorization process, follow these steps:"),(0,r.yg)("ol",null,(0,r.yg)("li",{parentName:"ol"},"Create Roles and Permissions"),(0,r.yg)("li",{parentName:"ol"},"Attach Permissions to Roles"),(0,r.yg)("li",{parentName:"ol"},"Attach Roles and/or Permissions to Users"),(0,r.yg)("li",{parentName:"ol"},"Protect Endpoints with Permissions and/or Roles")),(0,r.yg)("p",null,"To ",(0,r.yg)("a",{parentName:"p",href:"/docs/components/main-components/requests#request-properties"},"protect your endpoints"),",\nyou have to specify the required permissions and/or roles in the ",(0,r.yg)("inlineCode",{parentName:"p"},"Request")," class.\nIn doing so, you can check whether the current user has the necessary access rights to reach a particular endpoint.\nBy verifying permissions and roles at the request level,\nyou ensure that unauthorized users are denied access before any further processing takes place."),(0,r.yg)("admonition",{title:"Default Roles & Permissions",type:"info"},(0,r.yg)("p",{parentName:"admonition"},"Apiato comes with some default Roles and Permissions.\nYou can find them in ",(0,r.yg)("inlineCode",{parentName:"p"},"app/Containers/AppSection/Authorization/Data/Seeders"),".\nYou can use them as a starting point, or delete them and create your own.")),(0,r.yg)("h2",{id:"code-example"},"Code Example"),(0,r.yg)("p",null,"Protecting the ",(0,r.yg)("strong",{parentName:"p"},"delete user")," endpoint with ",(0,r.yg)("inlineCode",{parentName:"p"},"delete-users")," permission:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-php"},"use App\\Ship\\Parents\\Requests\\Request as ParentRequest;\n\nclass DeleteUserRequest extends ParentRequest\n{\n protected array $access = [\n 'permissions' => 'delete-users',\n 'roles' => '',\n ];\n\n public function authorize(): bool\n {\n return $this->check([\n 'hasAccess',\n ]);\n }\n}\n")),(0,r.yg)("p",null,"Authorization failed JSON response:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-json"},'{\n "message": "This action is unauthorized.",\n "errors": []\n}\n')))}d.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkdocumentation=self.webpackChunkdocumentation||[]).push([[6011],{95788:(e,t,n)=>{n.d(t,{Iu:()=>c,yg:()=>y});var o=n(11504);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function i(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var l=o.createContext({}),p=function(e){var t=o.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},c=function(e){var t=p(e.components);return o.createElement(l.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},m=o.forwardRef((function(e,t){var n=e.components,r=e.mdxType,a=e.originalType,l=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),u=p(n),m=r,y=u["".concat(l,".").concat(m)]||u[m]||d[m]||a;return n?o.createElement(y,i(i({ref:t},c),{},{components:n})):o.createElement(y,i({ref:t},c))}));function y(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var a=n.length,i=new Array(a);i[0]=m;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[u]="string"==typeof e?e:r,i[1]=s;for(var p=2;p{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>i,default:()=>d,frontMatter:()=>a,metadata:()=>s,toc:()=>p});var o=n(45072),r=(n(11504),n(95788));const a={sidebar_position:3,title:"Authorization",tags:["component","optional-component","authorization","request","policy"]},i=void 0,s={unversionedId:"security/authorization",id:"version-12.x/security/authorization",title:"Authorization",description:"Apiato provides a Role-Based Access Control (RBAC) through its Authorization Container.",source:"@site/versioned_docs/version-12.x/security/authorization.mdx",sourceDirName:"security",slug:"/security/authorization",permalink:"/docs/security/authorization",draft:!1,editUrl:"https://github.com/apiato/documentation/tree/master/versioned_docs/version-12.x/security/authorization.mdx",tags:[{label:"component",permalink:"/docs/tags/component"},{label:"optional-component",permalink:"/docs/tags/optional-component"},{label:"authorization",permalink:"/docs/tags/authorization"},{label:"request",permalink:"/docs/tags/request"},{label:"policy",permalink:"/docs/tags/policy"}],version:"12.x",lastUpdatedBy:"Mohammad Alavi",lastUpdatedAt:1707555342,formattedLastUpdatedAt:"Feb 10, 2024",sidebarPosition:3,frontMatter:{sidebar_position:3,title:"Authorization",tags:["component","optional-component","authorization","request","policy"]},sidebar:"tutorialSidebar",previous:{title:"Authentication",permalink:"/docs/security/authentication"},next:{title:"Email Verification",permalink:"/docs/security/email-varification"}},l={},p=[{value:"How it works",id:"how-it-works",level:2},{value:"Code Example",id:"code-example",level:2}],c={toc:p},u="wrapper";function d(e){let{components:t,...n}=e;return(0,r.yg)(u,(0,o.c)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,r.yg)("p",null,"Apiato provides a Role-Based Access Control (RBAC) through its Authorization Container.\nBehind the scenes,\nApiato uses the ",(0,r.yg)("a",{parentName:"p",href:"https://github.com/spatie/laravel-permission"},"Laravel Permission")," package."),(0,r.yg)("h2",{id:"how-it-works"},"How it works"),(0,r.yg)("p",null,"Authorization in Apiato is indeed straightforward and easy.\nIt operates by linking permissions to roles and then assigning roles to users."),(0,r.yg)("p",null,"To implement the authorization process, follow these steps:"),(0,r.yg)("ol",null,(0,r.yg)("li",{parentName:"ol"},"Create Roles and Permissions"),(0,r.yg)("li",{parentName:"ol"},"Attach Permissions to Roles"),(0,r.yg)("li",{parentName:"ol"},"Attach Roles and/or Permissions to Users"),(0,r.yg)("li",{parentName:"ol"},"Protect Endpoints with Permissions and/or Roles")),(0,r.yg)("p",null,"To ",(0,r.yg)("a",{parentName:"p",href:"/docs/components/main-components/requests#request-properties"},"protect your endpoints"),",\nyou have to specify the required permissions and/or roles in the ",(0,r.yg)("inlineCode",{parentName:"p"},"Request")," class.\nIn doing so, you can check whether the current user has the necessary access rights to reach a particular endpoint.\nBy verifying permissions and roles at the request level,\nyou ensure that unauthorized users are denied access before any further processing takes place."),(0,r.yg)("admonition",{title:"Default Roles & Permissions",type:"info"},(0,r.yg)("p",{parentName:"admonition"},"Apiato comes with some default Roles and Permissions.\nYou can find them in ",(0,r.yg)("inlineCode",{parentName:"p"},"app/Containers/AppSection/Authorization/Data/Seeders"),".\nYou can use them as a starting point, or delete them and create your own.")),(0,r.yg)("h2",{id:"code-example"},"Code Example"),(0,r.yg)("p",null,"Protecting the ",(0,r.yg)("strong",{parentName:"p"},"delete user")," endpoint with ",(0,r.yg)("inlineCode",{parentName:"p"},"delete-users")," permission:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-php"},"use App\\Ship\\Parents\\Requests\\Request as ParentRequest;\n\nclass DeleteUserRequest extends ParentRequest\n{\n protected array $access = [\n 'permissions' => 'delete-users',\n 'roles' => '',\n ];\n\n public function authorize(): bool\n {\n return $this->hasAccess();\n }\n}\n")),(0,r.yg)("p",null,"Authorization failed JSON response:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-json"},'{\n "message": "This action is unauthorized.",\n "errors": []\n}\n')))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/2e3626b2.1acba910.js b/assets/js/2e3626b2.1acba910.js new file mode 100644 index 000000000..654947856 --- /dev/null +++ b/assets/js/2e3626b2.1acba910.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkdocumentation=self.webpackChunkdocumentation||[]).push([[5328],{95788:(e,n,t)=>{t.d(n,{Iu:()=>u,yg:()=>g});var a=t(11504);function r(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function i(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);n&&(a=a.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,a)}return t}function o(e){for(var n=1;n=0||(r[t]=e[t]);return r}(e,n);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(r[t]=e[t])}return r}var l=a.createContext({}),p=function(e){var n=a.useContext(l),t=n;return e&&(t="function"==typeof e?e(n):o(o({},n),e)),t},u=function(e){var n=p(e.components);return a.createElement(l.Provider,{value:n},e.children)},d="mdxType",c={inlineCode:"code",wrapper:function(e){var n=e.children;return a.createElement(a.Fragment,{},n)}},m=a.forwardRef((function(e,n){var t=e.components,r=e.mdxType,i=e.originalType,l=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),d=p(t),m=r,g=d["".concat(l,".").concat(m)]||d[m]||c[m]||i;return t?a.createElement(g,o(o({ref:n},u),{},{components:t})):a.createElement(g,o({ref:n},u))}));function g(e,n){var t=arguments,r=n&&n.mdxType;if("string"==typeof e||r){var i=t.length,o=new Array(i);o[0]=m;var s={};for(var l in n)hasOwnProperty.call(n,l)&&(s[l]=n[l]);s.originalType=e,s[d]="string"==typeof e?e:r,o[1]=s;for(var p=2;p{t.r(n),t.d(n,{assets:()=>l,contentTitle:()=>o,default:()=>c,frontMatter:()=>i,metadata:()=>s,toc:()=>p});var a=t(45072),r=(t(11504),t(95788));const i={sidebar_position:3,title:"Requests",tags:["component","main-component","request","route","controller","action"]},o=void 0,s={unversionedId:"components/main-components/requests",id:"components/main-components/requests",title:"Requests",description:"Requests components are a way to interact with the current HTTP request",source:"@site/docs/components/main-components/requests.md",sourceDirName:"components/main-components",slug:"/components/main-components/requests",permalink:"/docs/next/components/main-components/requests",draft:!1,editUrl:"https://github.com/apiato/documentation/tree/master/docs/components/main-components/requests.md",tags:[{label:"component",permalink:"/docs/next/tags/component"},{label:"main-component",permalink:"/docs/next/tags/main-component"},{label:"request",permalink:"/docs/next/tags/request"},{label:"route",permalink:"/docs/next/tags/route"},{label:"controller",permalink:"/docs/next/tags/controller"},{label:"action",permalink:"/docs/next/tags/action"}],version:"current",lastUpdatedBy:"Mohammad Alavi",lastUpdatedAt:1707555342,formattedLastUpdatedAt:"Feb 10, 2024",sidebarPosition:3,frontMatter:{sidebar_position:3,title:"Requests",tags:["component","main-component","request","route","controller","action"]},sidebar:"tutorialSidebar",previous:{title:"Controllers",permalink:"/docs/next/components/main-components/controllers"},next:{title:"Actions",permalink:"/docs/next/components/main-components/actions"}},l={},p=[{value:"Definition & Principles",id:"definition--principles",level:2},{value:"Rules",id:"rules",level:2},{value:"Folder Structure",id:"folder-structure",level:2},{value:"Code Example",id:"code-example",level:2},{value:"Validation",id:"validation",level:2},{value:"Request Properties",id:"request-properties",level:2},{value:"access",id:"access",level:3},{value:"decode",id:"decode",level:3},{value:"urlParameters",id:"urlparameters",level:3},{value:"Helper Methods",id:"helper-methods",level:2},{value:"hasAccess",id:"hasaccess",level:3},{value:"sanitizeInput",id:"sanitizeinput",level:3},{value:"getInputByKey",id:"getinputbykey",level:3},{value:"mapInput",id:"mapinput",level:3},{value:"injectData",id:"injectdata",level:3},{value:"withUrlParameters",id:"withurlparameters",level:3},{value:"Bypassing Authorization",id:"bypassing-authorization",level:2},{value:"Force Accept Header",id:"force-accept-header",level:2},{value:"Etag",id:"etag",level:2}],u={toc:p},d="wrapper";function c(e){let{components:n,...t}=e;return(0,r.yg)(d,(0,a.c)({},u,t,{components:n,mdxType:"MDXLayout"}),(0,r.yg)("p",null,(0,r.yg)("a",{parentName:"p",href:"https://laravel.com/docs/requests"},"Requests")," components are a way to interact with the current HTTP request\nbeing handled by your application as well as retrieve the input,\ncookies, and files that were submitted with the request."),(0,r.yg)("p",null,"To generate new requests you may use the ",(0,r.yg)("inlineCode",{parentName:"p"},"apiato:generate:request")," interactive command:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre"},"php artisan apiato:generate:request\n")),(0,r.yg)("h2",{id:"definition--principles"},"Definition & Principles"),(0,r.yg)("p",null,"Read ",(0,r.yg)("a",{parentName:"p",href:"https://github.com/Mahmoudz/Porto#definitions--principles"},(0,r.yg)("strong",{parentName:"a"},"Porto SAP Documentation (#Requests)")),"."),(0,r.yg)("h2",{id:"rules"},"Rules"),(0,r.yg)("ul",null,(0,r.yg)("li",{parentName:"ul"},"Validation rules MUST be defined in the ",(0,r.yg)("inlineCode",{parentName:"li"},"rules")," method."),(0,r.yg)("li",{parentName:"ul"},"All API Requests MUST be placed in the ",(0,r.yg)("inlineCode",{parentName:"li"},"app/Containers/{Section}/{Container}/UI/API/Requests")," directory."),(0,r.yg)("li",{parentName:"ul"},"All Web Requests MUST be placed in the ",(0,r.yg)("inlineCode",{parentName:"li"},"app/Containers/{Section}/{Container}/UI/WEB/Requests")," directory."),(0,r.yg)("li",{parentName:"ul"},"All Requests:",(0,r.yg)("ul",{parentName:"li"},(0,r.yg)("li",{parentName:"ul"},"MUST extend the ",(0,r.yg)("inlineCode",{parentName:"li"},"App\\Ship\\Parents\\Requests\\Request")," class.",(0,r.yg)("ul",{parentName:"li"},(0,r.yg)("li",{parentName:"ul"},"The parent extension SHOULD be aliased as ",(0,r.yg)("inlineCode",{parentName:"li"},"ParentRequest"),"."))),(0,r.yg)("li",{parentName:"ul"},"MUST have a public ",(0,r.yg)("inlineCode",{parentName:"li"},"rules")," method, returning an array of validation rules."),(0,r.yg)("li",{parentName:"ul"},"MUST have a public ",(0,r.yg)("inlineCode",{parentName:"li"},"authorize")," method, returning a boolean value.")))),(0,r.yg)("h2",{id:"folder-structure"},"Folder Structure"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-markdown"},"app\n\u2514\u2500\u2500 Containers\n \u2514\u2500\u2500 Section\n \u2514\u2500\u2500 Container\n \u2514\u2500\u2500 UI\n \u251c\u2500\u2500 API\n \u2502 \u2514\u2500\u2500 Requests\n \u2502 \u251c\u2500\u2500 CreateUserRequest.php\n \u2502 \u251c\u2500\u2500 DeleteUserRequest.php\n \u2502 \u2514\u2500\u2500 ...\n \u2514\u2500\u2500 WEB\n \u2514\u2500\u2500 Requests\n \u251c\u2500\u2500 Login.php\n \u251c\u2500\u2500 Logout.php\n \u2514\u2500\u2500 ...\n")),(0,r.yg)("h2",{id:"code-example"},"Code Example"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-php"},"use App\\Ship\\Parents\\Requests\\Request as ParentRequest;\n\nclass DemoRequest extends ParentRequest\n{\n protected array $access = [];\n protected array $decode = [];\n protected array $urlParameters = [];\n\n public function rules(): array\n {\n return [\n 'field' => 'min:3|max:50',\n ];\n }\n\n public function authorize(): bool\n {\n return true;\n }\n}\n")),(0,r.yg)("h2",{id:"validation"},"Validation"),(0,r.yg)("p",null,"In Apiato,\nvalidation of incoming requests follows the\nLaravel ",(0,r.yg)("a",{parentName:"p",href:"https://laravel.com/docs/validation#form-request-validation"},"Form Request Validation")," approach."),(0,r.yg)("p",null,"Validation rules are defined within the respective Request class.\nThese rules are automatically enforced when a Request is injected into a Controller."),(0,r.yg)("p",null,"Here's an example of a Request class with validation rules:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-php"},"use App\\Ship\\Parents\\Requests\\Request as ParentRequest;\n\nclass RegisterUserRequest extends ParentRequest\n{\n public function rules(): array\n {\n return [\n 'email' => 'required|email|max:200|unique:users,email',\n 'password' => 'required|min:20|max:300',\n 'name' => ['required', 'min:2', 'max:400'],\n ];\n }\n\n}\n")),(0,r.yg)("p",null,"And here's how you would use this Request class within a Controller:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-php"},"public function __invoke(RegisterUserRequest $request)\n{\n $user = app(RegisterUserAction::class)->run($request);\n \n return $this->transform($user, UserTransformer::class);\n}\n")),(0,r.yg)("p",null,"In this example,\nthe validation rules defined in ",(0,r.yg)("inlineCode",{parentName:"p"},"RegisterUserRequest")," will be automatically applied\nbefore the ",(0,r.yg)("inlineCode",{parentName:"p"},"__invoke")," method is executed.\nIf the validation fails, an appropriate error response will be generated."),(0,r.yg)("h2",{id:"request-properties"},"Request Properties"),(0,r.yg)("p",null,"Apiato introduces new properties to the Request Class that enhance its functionality."),(0,r.yg)("h3",{id:"access"},"access"),(0,r.yg)("p",null,"The ",(0,r.yg)("inlineCode",{parentName:"p"},"$access")," property allows you to define Roles and Permissions that can access a specific endpoint.\nIt's used by the ",(0,r.yg)("inlineCode",{parentName:"p"},"hasAccess")," method to check if a user has the required Roles and Permissions to use that endpoint."),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-php"},"class DemoRequest extends ParentRequest\n{\n protected array $access = [\n 'permissions' => 'delete-users',\n 'roles' => 'manager'\n ];\n\n public function authorize(): bool\n {\n return $this->hasAccess();\n }\n}\n")),(0,r.yg)("p",null,"You can also use the ",(0,r.yg)("inlineCode",{parentName:"p"},"array notation")," or ",(0,r.yg)("inlineCode",{parentName:"p"},"pipe")," to define multiple Roles and Permissions."),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-php"},"class DemoRequest extends ParentRequest\n{\n protected $access = [\n 'permissions' => ['delete-users', 'another-permissions'],\n 'roles' => 'manager|admin',\n ];\n \n // ...\n}\n")),(0,r.yg)("admonition",{type:"tip"},(0,r.yg)("p",{parentName:"admonition"},"If there's no need to set any roles or permissions,\nyou can simply set the ",(0,r.yg)("inlineCode",{parentName:"p"},"$permissions")," or ",(0,r.yg)("inlineCode",{parentName:"p"},"$roles")," property to an empty string ",(0,r.yg)("inlineCode",{parentName:"p"},"''"),", an empty array ",(0,r.yg)("inlineCode",{parentName:"p"},"[]"),", or ",(0,r.yg)("inlineCode",{parentName:"p"},"null"),".")),(0,r.yg)("h3",{id:"decode"},"decode"),(0,r.yg)("p",null,"The ",(0,r.yg)("inlineCode",{parentName:"p"},"$decode")," property is used to handle the decoding of Hashed IDs from the incoming Request."),(0,r.yg)("p",null,"When you enable the ",(0,r.yg)("a",{parentName:"p",href:"/docs/next/security/hash-id"},"Hash ID")," feature, your application can receive Hashed IDs from users.\nThese Hashed IDs need to be decoded before they can be effectively validated.\nApiato facilitates this process\nby providing a property in its Requests class where you can specify which Hashed IDs need to be decoded.\nThis ensures that the validation procedure seamlessly integrates with Hashed IDs."),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-php"},"class DemoRequest extends ParentRequest\n{\n protected array $decode = [\n 'user_id',\n 'item_id',\n ];\n \n // ...\n}\n")),(0,r.yg)("admonition",{type:"note"},(0,r.yg)("p",{parentName:"admonition"},"Keep in mind that validation rules relying on your ID, such as ",(0,r.yg)("inlineCode",{parentName:"p"},"exists:users,id"),",\nwill not function correctly unless you decode the ID before passing it to the validation process.")),(0,r.yg)("h3",{id:"urlparameters"},"urlParameters"),(0,r.yg)("p",null,"The ",(0,r.yg)("inlineCode",{parentName:"p"},"$urlParameters")," property simplifies the process of applying validation rules to URL parameters."),(0,r.yg)("p",null,"By default, Laravel doesn't provide validation for URL parameters (",(0,r.yg)("inlineCode",{parentName:"p"},"/stores/999/items"),").\nHowever, by using the ",(0,r.yg)("inlineCode",{parentName:"p"},"$urlParameters")," property, you can enable validation for these parameters.\nBy specifying the desired URL parameters within this property,\nyou not only enable validation but also gain direct access to these parameters from the Request object."),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-php"},"// URL: /stores/{id}/items\n// GET /stores/999/items\nclass DemoRequest extends ParentRequest\n{\n protected array $urlParameters = [\n 'id',\n ];\n\n public function rules(): array\n {\n return [\n 'id' => 'integer', // url parameter\n ];\n }\n}\n")),(0,r.yg)("h2",{id:"helper-methods"},"Helper Methods"),(0,r.yg)("h3",{id:"hasaccess"},"hasAccess"),(0,r.yg)("p",null,"The ",(0,r.yg)("inlineCode",{parentName:"p"},"hasAccess")," method assesses a user's access rights based on the Request's ",(0,r.yg)("inlineCode",{parentName:"p"},"$access")," property.\nIf the user has any of the specified Roles or Permissions, the method will return ",(0,r.yg)("inlineCode",{parentName:"p"},"true")," otherwise it will\nreturn ",(0,r.yg)("inlineCode",{parentName:"p"},"false"),"."),(0,r.yg)("h3",{id:"sanitizeinput"},"sanitizeInput"),(0,r.yg)("p",null,"The ",(0,r.yg)("inlineCode",{parentName:"p"},"sanitizeInput")," method is employed to cleanse request data before its utilization within the application."),(0,r.yg)("p",null,"Particularly useful for ",(0,r.yg)("inlineCode",{parentName:"p"},"PATCH")," requests,\nwhere you may want\nto submit only the fields\nintended for modification to minimize traffic or perform partial updates to the corresponding database resource.\nTraditional checks for the presence or absence of specific keys in the request can lead to extensive ",(0,r.yg)("inlineCode",{parentName:"p"},"if")," blocks,\nsuch as:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-php"},"if ($request->has('data.name')) {\n $data['name'] = $request->input('data.name');\n}\n")),(0,r.yg)("p",null,"To circumvent these ",(0,r.yg)("inlineCode",{parentName:"p"},"if")," blocks, you might utilize ",(0,r.yg)("inlineCode",{parentName:"p"},"array_filter($data)")," to remove empty fields from the request.\nHowever, be aware that in PHP, both ",(0,r.yg)("inlineCode",{parentName:"p"},"false")," and an empty string ",(0,r.yg)("inlineCode",{parentName:"p"},"''")," are considered as ",(0,r.yg)("inlineCode",{parentName:"p"},"empty"),"."),(0,r.yg)("p",null,"For streamlining data sanitization when using ",(0,r.yg)("inlineCode",{parentName:"p"},"application/json")," instead of ",(0,r.yg)("inlineCode",{parentName:"p"},"x-www-form-urlencoded"),",\nApiato provides the convenient ",(0,r.yg)("inlineCode",{parentName:"p"},"sanitizeInput")," method."),(0,r.yg)("p",null,"Consider the following request:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-json"},'{\n "data": {\n "name": "Demo",\n "description": "Some description",\n "is_private": false,\n "address": "",\n "foo": {\n "number": 1,\n "bar": "bar"\n }\n },\n "meta": "some meta data"\n}\n')),(0,r.yg)("p",null,"The ",(0,r.yg)("inlineCode",{parentName:"p"},"sanitizeInput")," method enables you to specify a list of fields,\nemploying dot notation, to be accessed and extracted from the request."),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-php"},"$data = $request->sanitizeInput([\n 'data.description',\n 'data.is_private',\n 'data.address',\n 'data.foo.number',\n 'email', // will be ignored\n 'meta',\n]);\n")),(0,r.yg)("p",null,"The extracted data will appear as follows:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-php"},'[\n "data" => [\n "description" => "Some description"\n "is_private" => false,\n "address" => null, // empty string is converted to null by Laravel\n "foo" => [\n "number" => 1,\n ]\n ],\n "meta" => "some meta data",\n]\n')),(0,r.yg)("p",null,"Note that ",(0,r.yg)("inlineCode",{parentName:"p"},"email")," is excluded from the sanitized array, as it was absent in the request.\nAdditionally, any other fields not specified are omitted.\nIn essence, the method filters the request, retaining only the defined values."),(0,r.yg)("p",null,"You can also assign default values during the data sanitization process:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-php"},"$sanitizedData = $request->sanitizeInput([\n 'name' => 'John', // If name is not provided, the default value will be set\n 'product.company.address' => 'Somewhere in the world', // dot notation is supported\n 'email',\n 'password'\n]);\n")),(0,r.yg)("h3",{id:"getinputbykey"},"getInputByKey"),(0,r.yg)("p",null,"The ",(0,r.yg)("inlineCode",{parentName:"p"},"getInputByKey")," method retrieves data from the ",(0,r.yg)("inlineCode",{parentName:"p"},"request")," by specifying the field name.\nSimilar to ",(0,r.yg)("inlineCode",{parentName:"p"},"$request->input('key.here')"),", this method operates on the ",(0,r.yg)("inlineCode",{parentName:"p"},"decoded")," values instead of the original data."),(0,r.yg)("p",null,"Consider the following request:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-json"},'{\n "id": "XbPW7awNkzl83LD6"\n}\n')),(0,r.yg)("p",null,"While ",(0,r.yg)("inlineCode",{parentName:"p"},"$request->input('id')")," would return ",(0,r.yg)("inlineCode",{parentName:"p"},'"XbPW7awNkzl83LD6"'),",\n",(0,r.yg)("inlineCode",{parentName:"p"},"$request->getInputByKey('id')")," would return the decoded value\n(e.g., ",(0,r.yg)("inlineCode",{parentName:"p"},"4"),")."),(0,r.yg)("p",null,"Moreover, you can set a ",(0,r.yg)("inlineCode",{parentName:"p"},"default")," value to be returned if the key is absent or unset, like this:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-php"},"$request->getInputByKey('data.name', 'Undefined')\n")),(0,r.yg)("h3",{id:"mapinput"},"mapInput"),(0,r.yg)("p",null,"In certain cases, you might need to remap input from the request to different fields.\nWhile manual field mapping is possible, you can also leverage the ",(0,r.yg)("inlineCode",{parentName:"p"},"mapInput"),' method for this purpose.\nThis helper method allows you to "redefine" keys within the request, making subsequent processing easier.'),(0,r.yg)("p",null,"Consider the following request:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-json"},'{\n "data": {\n "name": "John Doe"\n }\n}\n')),(0,r.yg)("p",null,"However, for processing purposes, you require the ",(0,r.yg)("inlineCode",{parentName:"p"},"username")," field instead of ",(0,r.yg)("inlineCode",{parentName:"p"},"data.name"),"."),(0,r.yg)("p",null,"You can use the helper as follows:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-php"},"$request->mapInput([\n 'data.name' => 'username',\n]);\n")),(0,r.yg)("p",null,"The resulting structure would be:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-json"},'{\n "username": "John Doe"\n}\n')),(0,r.yg)("p",null,"And you can access the value as follows:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-php"},"$request->input('username');\n")),(0,r.yg)("h3",{id:"injectdata"},"injectData"),(0,r.yg)("p",null,"The ",(0,r.yg)("inlineCode",{parentName:"p"},"injectData")," method allows you to inject data into the request.\nThis can be particularly helpful during testing\nwhen you wish to provide data directly to the request instead of sending it through the request body."),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-php"},"$request = RegisterUserRequest::injectData($data);\n")),(0,r.yg)("h3",{id:"withurlparameters"},"withUrlParameters"),(0,r.yg)("p",null,"The ",(0,r.yg)("inlineCode",{parentName:"p"},"withUrlParameters")," method enables you to inject URL parameters into the request.\nThis is especially useful when you need to include properties in the request that are not part of the request body\nbut are required for the request to be processed.\nThis method is often used in conjunction with the ",(0,r.yg)("inlineCode",{parentName:"p"},"injectData")," method."),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-php"},"$request = RegisterUserRequest::injectData($data)\n ->withUrlParameters(['id' => 123]);\n")),(0,r.yg)("h2",{id:"bypassing-authorization"},"Bypassing Authorization"),(0,r.yg)("p",null,"To grant certain Roles access to all endpoints within the system without the need\nto define the role in each Request object,\nyou can follow this approach.\nThis is particularly beneficial when you want to provide unrestricted access to users with the ",(0,r.yg)("inlineCode",{parentName:"p"},"admin")," role.\nTo implement this, define the relevant roles in ",(0,r.yg)("inlineCode",{parentName:"p"},"app/Ship/Configs/apiato.php")," as shown below:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-php"},"'requests' => [\n 'allow-roles-to-access-all-routes' => ['admin'],\n],\n")),(0,r.yg)("h2",{id:"force-accept-header"},"Force Accept Header"),(0,r.yg)("p",null,"Typically, when making calls to a JSON API, you should include the ",(0,r.yg)("inlineCode",{parentName:"p"},"accept: application/json")," HTTP header.\nIn Apiato, you have the option to either enforce users to send this header or allow them to skip it."),(0,r.yg)("p",null,"To enforce the ",(0,r.yg)("inlineCode",{parentName:"p"},"accept: application/json")," header,\nnavigate to the ",(0,r.yg)("inlineCode",{parentName:"p"},"app/Ship/Configs/apiato.php")," configuration file and set the ",(0,r.yg)("inlineCode",{parentName:"p"},"force-accept-header")," to ",(0,r.yg)("inlineCode",{parentName:"p"},"true"),"."),(0,r.yg)("p",null,"Conversely, if you wish to allow users to skip this header, set ",(0,r.yg)("inlineCode",{parentName:"p"},"force-accept-header")," to ",(0,r.yg)("inlineCode",{parentName:"p"},"false"),"."),(0,r.yg)("admonition",{type:"info"},(0,r.yg)("p",{parentName:"admonition"},"Forcing the accept header is disabled by default.")),(0,r.yg)("h2",{id:"etag"},"Etag"),(0,r.yg)("p",null,"The ",(0,r.yg)("strong",{parentName:"p"},"ETag")," or ",(0,r.yg)("strong",{parentName:"p"},"entity tag")," is part of HTTP, the protocol for the World Wide Web.\nIt is one of several mechanisms that HTTP provides for Web cache validation,\nwhich allows a client to make conditional requests.\nThis mechanism allows caches to be more efficient and saves bandwidth,\nas a Web server does not need to send a full response if the content has not changed.\n(",(0,r.yg)("a",{parentName:"p",href:"https://en.wikipedia.org/wiki/HTTP_ETag"},"Wikipedia"),")"),(0,r.yg)("p",null,"Apiato offers support for Etag through the ",(0,r.yg)("inlineCode",{parentName:"p"},"Apiato\\Core\\Middlewares\\HttpProcessETagHeadersMiddleware")," middleware,\nwhich employs the Shallow technique.\nThis middleware can be particularly valuable in reducing bandwidth usage for clients, especially on mobile devices."),(0,r.yg)("p",null,"Please note that this feature is ",(0,r.yg)("strong",{parentName:"p"},"disabled by default"),". To enable it, follow these steps:"),(0,r.yg)("ol",null,(0,r.yg)("li",{parentName:"ol"},"Navigate to the ",(0,r.yg)("inlineCode",{parentName:"li"},"app/Ship/Configs/apiato.php")," configuration file."),(0,r.yg)("li",{parentName:"ol"},"Inside the configuration file, locate the ",(0,r.yg)("inlineCode",{parentName:"li"},"use-etag")," configuration parameter."),(0,r.yg)("li",{parentName:"ol"},"Set the ",(0,r.yg)("inlineCode",{parentName:"li"},"use-etag")," parameter to ",(0,r.yg)("inlineCode",{parentName:"li"},"true"),".")),(0,r.yg)("p",null,"Keep in mind that for this feature to function correctly, the client must include the ",(0,r.yg)("inlineCode",{parentName:"p"},"If-None-Match")," HTTP header,\nwhich corresponds to the ETag value, in their request."))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/2e3626b2.5a9e1a76.js b/assets/js/2e3626b2.5a9e1a76.js deleted file mode 100644 index 4e651ec05..000000000 --- a/assets/js/2e3626b2.5a9e1a76.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkdocumentation=self.webpackChunkdocumentation||[]).push([[5328],{95788:(e,n,t)=>{t.d(n,{Iu:()=>u,yg:()=>h});var a=t(11504);function i(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function r(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);n&&(a=a.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,a)}return t}function o(e){for(var n=1;n=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var l=a.createContext({}),p=function(e){var n=a.useContext(l),t=n;return e&&(t="function"==typeof e?e(n):o(o({},n),e)),t},u=function(e){var n=p(e.components);return a.createElement(l.Provider,{value:n},e.children)},d="mdxType",c={inlineCode:"code",wrapper:function(e){var n=e.children;return a.createElement(a.Fragment,{},n)}},m=a.forwardRef((function(e,n){var t=e.components,i=e.mdxType,r=e.originalType,l=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),d=p(t),m=i,h=d["".concat(l,".").concat(m)]||d[m]||c[m]||r;return t?a.createElement(h,o(o({ref:n},u),{},{components:t})):a.createElement(h,o({ref:n},u))}));function h(e,n){var t=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var r=t.length,o=new Array(r);o[0]=m;var s={};for(var l in n)hasOwnProperty.call(n,l)&&(s[l]=n[l]);s.originalType=e,s[d]="string"==typeof e?e:i,o[1]=s;for(var p=2;p{t.r(n),t.d(n,{assets:()=>l,contentTitle:()=>o,default:()=>c,frontMatter:()=>r,metadata:()=>s,toc:()=>p});var a=t(45072),i=(t(11504),t(95788));const r={sidebar_position:3,title:"Requests",tags:["component","main-component","request","route","controller","action"]},o=void 0,s={unversionedId:"components/main-components/requests",id:"components/main-components/requests",title:"Requests",description:"Requests components are a way to interact with the current HTTP request",source:"@site/docs/components/main-components/requests.md",sourceDirName:"components/main-components",slug:"/components/main-components/requests",permalink:"/docs/next/components/main-components/requests",draft:!1,editUrl:"https://github.com/apiato/documentation/tree/master/docs/components/main-components/requests.md",tags:[{label:"component",permalink:"/docs/next/tags/component"},{label:"main-component",permalink:"/docs/next/tags/main-component"},{label:"request",permalink:"/docs/next/tags/request"},{label:"route",permalink:"/docs/next/tags/route"},{label:"controller",permalink:"/docs/next/tags/controller"},{label:"action",permalink:"/docs/next/tags/action"}],version:"current",lastUpdatedBy:"Mohammad Alavi",lastUpdatedAt:1704287783,formattedLastUpdatedAt:"Jan 3, 2024",sidebarPosition:3,frontMatter:{sidebar_position:3,title:"Requests",tags:["component","main-component","request","route","controller","action"]},sidebar:"tutorialSidebar",previous:{title:"Controllers",permalink:"/docs/next/components/main-components/controllers"},next:{title:"Actions",permalink:"/docs/next/components/main-components/actions"}},l={},p=[{value:"Definition & Principles",id:"definition--principles",level:2},{value:"Rules",id:"rules",level:2},{value:"Folder Structure",id:"folder-structure",level:2},{value:"Code Example",id:"code-example",level:2},{value:"Validation",id:"validation",level:2},{value:"Request Properties",id:"request-properties",level:2},{value:"access",id:"access",level:3},{value:"decode",id:"decode",level:3},{value:"urlParameters",id:"urlparameters",level:3},{value:"Helper Methods",id:"helper-methods",level:2},{value:"check",id:"check",level:3},{value:"hasAccess",id:"hasaccess",level:3},{value:"sanitizeInput",id:"sanitizeinput",level:3},{value:"getInputByKey",id:"getinputbykey",level:3},{value:"mapInput",id:"mapinput",level:3},{value:"injectData",id:"injectdata",level:3},{value:"withUrlParameters",id:"withurlparameters",level:3},{value:"Custom Authorize Methods",id:"custom-authorize-methods",level:2},{value:"Bypass Authorization",id:"bypass-authorization",level:2},{value:"Force Accept Header",id:"force-accept-header",level:2},{value:"Etag",id:"etag",level:2}],u={toc:p},d="wrapper";function c(e){let{components:n,...t}=e;return(0,i.yg)(d,(0,a.c)({},u,t,{components:n,mdxType:"MDXLayout"}),(0,i.yg)("p",null,(0,i.yg)("a",{parentName:"p",href:"https://laravel.com/docs/requests"},"Requests")," components are a way to interact with the current HTTP request\nbeing handled by your application as well as retrieve the input,\ncookies, and files that were submitted with the request."),(0,i.yg)("p",null,"To generate new requests you may use the ",(0,i.yg)("inlineCode",{parentName:"p"},"apiato:generate:request")," interactive command:"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre"},"php artisan apiato:generate:request\n")),(0,i.yg)("h2",{id:"definition--principles"},"Definition & Principles"),(0,i.yg)("p",null,"Read ",(0,i.yg)("a",{parentName:"p",href:"https://github.com/Mahmoudz/Porto#definitions--principles"},(0,i.yg)("strong",{parentName:"a"},"Porto SAP Documentation (#Requests)")),"."),(0,i.yg)("h2",{id:"rules"},"Rules"),(0,i.yg)("ul",null,(0,i.yg)("li",{parentName:"ul"},"Validation rules MUST be defined in the ",(0,i.yg)("inlineCode",{parentName:"li"},"rules")," method."),(0,i.yg)("li",{parentName:"ul"},"All API Requests MUST be placed in the ",(0,i.yg)("inlineCode",{parentName:"li"},"app/Containers/{Section}/{Container}/UI/API/Requests")," directory."),(0,i.yg)("li",{parentName:"ul"},"All Web Requests MUST be placed in the ",(0,i.yg)("inlineCode",{parentName:"li"},"app/Containers/{Section}/{Container}/UI/WEB/Requests")," directory."),(0,i.yg)("li",{parentName:"ul"},"All Requests:",(0,i.yg)("ul",{parentName:"li"},(0,i.yg)("li",{parentName:"ul"},"MUST extend the ",(0,i.yg)("inlineCode",{parentName:"li"},"App\\Ship\\Parents\\Requests\\Request")," class.",(0,i.yg)("ul",{parentName:"li"},(0,i.yg)("li",{parentName:"ul"},"The parent extension SHOULD be aliased as ",(0,i.yg)("inlineCode",{parentName:"li"},"ParentRequest"),"."))),(0,i.yg)("li",{parentName:"ul"},"MUST have a public ",(0,i.yg)("inlineCode",{parentName:"li"},"rules")," method, returning an array of validation rules."),(0,i.yg)("li",{parentName:"ul"},"MUST have a public ",(0,i.yg)("inlineCode",{parentName:"li"},"authorize")," method, returning a boolean value.")))),(0,i.yg)("h2",{id:"folder-structure"},"Folder Structure"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-markdown"},"app\n\u2514\u2500\u2500 Containers\n \u2514\u2500\u2500 Section\n \u2514\u2500\u2500 Container\n \u2514\u2500\u2500 UI\n \u251c\u2500\u2500 API\n \u2502 \u2514\u2500\u2500 Requests\n \u2502 \u251c\u2500\u2500 CreateUserRequest.php\n \u2502 \u251c\u2500\u2500 DeleteUserRequest.php\n \u2502 \u2514\u2500\u2500 ...\n \u2514\u2500\u2500 WEB\n \u2514\u2500\u2500 Requests\n \u251c\u2500\u2500 Login.php\n \u251c\u2500\u2500 Logout.php\n \u2514\u2500\u2500 ...\n")),(0,i.yg)("h2",{id:"code-example"},"Code Example"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},"use App\\Ship\\Parents\\Requests\\Request as ParentRequest;\n\nclass DemoRequest extends ParentRequest\n{\n protected array $access = [];\n protected array $decode = [];\n protected array $urlParameters = [];\n\n public function rules(): array\n {\n return [\n 'field' => 'min:3|max:50',\n ];\n }\n\n public function authorize(): bool\n {\n return true;\n }\n}\n")),(0,i.yg)("h2",{id:"validation"},"Validation"),(0,i.yg)("p",null,"In Apiato,\nvalidation of incoming requests follows the\nLaravel ",(0,i.yg)("a",{parentName:"p",href:"https://laravel.com/docs/validation#form-request-validation"},"Form Request Validation")," approach."),(0,i.yg)("p",null,"Validation rules are defined within the respective Request class.\nThese rules are automatically enforced when a Request is injected into a Controller."),(0,i.yg)("p",null,"Here's an example of a Request class with validation rules:"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},"use App\\Ship\\Parents\\Requests\\Request as ParentRequest;\n\nclass RegisterUserRequest extends ParentRequest\n{\n public function rules(): array\n {\n return [\n 'email' => 'required|email|max:200|unique:users,email',\n 'password' => 'required|min:20|max:300',\n 'name' => ['required', 'min:2', 'max:400'],\n ];\n }\n\n}\n")),(0,i.yg)("p",null,"And here's how you would use this Request class within a Controller:"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},"public function __invoke(RegisterUserRequest $request)\n{\n $user = app(RegisterUserAction::class)->run($request);\n \n return $this->transform($user, UserTransformer::class);\n}\n")),(0,i.yg)("p",null,"In this example,\nthe validation rules defined in ",(0,i.yg)("inlineCode",{parentName:"p"},"RegisterUserRequest")," will be automatically applied\nbefore the ",(0,i.yg)("inlineCode",{parentName:"p"},"__invoke")," method is executed.\nIf the validation fails, an appropriate error response will be generated."),(0,i.yg)("h2",{id:"request-properties"},"Request Properties"),(0,i.yg)("p",null,"Apiato introduces new properties to the Request Class that enhance its functionality."),(0,i.yg)("h3",{id:"access"},"access"),(0,i.yg)("p",null,"The ",(0,i.yg)("inlineCode",{parentName:"p"},"$access")," property allows you to define Roles and Permissions that can access a specific endpoint.\nIt's used by the ",(0,i.yg)("inlineCode",{parentName:"p"},"hasAccess")," method to check if a user has the required Roles and Permissions to use that endpoint."),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},"class DemoRequest extends ParentRequest\n{\n protected array $access = [\n 'permissions' => 'delete-users',\n 'roles' => 'manager'\n ];\n\n public function authorize(): bool\n {\n return $this->check([\n 'hasAccess',\n ]);\n }\n}\n")),(0,i.yg)("p",null,"You can also use the ",(0,i.yg)("inlineCode",{parentName:"p"},"array notation")," or ",(0,i.yg)("inlineCode",{parentName:"p"},"pipe")," to define multiple Roles and Permissions."),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},"class DemoRequest extends ParentRequest\n{\n protected $access = [\n 'permissions' => ['delete-users', 'another-permissions'],\n 'roles' => 'manager|admin',\n ];\n \n // ...\n}\n")),(0,i.yg)("admonition",{type:"tip"},(0,i.yg)("p",{parentName:"admonition"},"If there's no need to set any roles or permissions,\nyou can simply set the ",(0,i.yg)("inlineCode",{parentName:"p"},"$permissions")," or ",(0,i.yg)("inlineCode",{parentName:"p"},"$roles")," property to an empty string ",(0,i.yg)("inlineCode",{parentName:"p"},"''"),", an empty array ",(0,i.yg)("inlineCode",{parentName:"p"},"[]"),", or ",(0,i.yg)("inlineCode",{parentName:"p"},"null"),".")),(0,i.yg)("h3",{id:"decode"},"decode"),(0,i.yg)("p",null,"The ",(0,i.yg)("inlineCode",{parentName:"p"},"$decode")," property is used to handle the decoding of Hashed IDs from the incoming Request."),(0,i.yg)("p",null,"When you enable the ",(0,i.yg)("a",{parentName:"p",href:"/docs/next/security/hash-id"},"Hash ID")," feature, your application can receive Hashed IDs from users.\nThese Hashed IDs need to be decoded before they can be effectively validated.\nApiato facilitates this process\nby providing a property in its Requests class where you can specify which Hashed IDs need to be decoded.\nThis ensures that the validation procedure seamlessly integrates with Hashed IDs."),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},"class DemoRequest extends ParentRequest\n{\n protected array $decode = [\n 'user_id',\n 'item_id',\n ];\n \n // ...\n}\n")),(0,i.yg)("admonition",{type:"note"},(0,i.yg)("p",{parentName:"admonition"},"Keep in mind that validation rules relying on your ID, such as ",(0,i.yg)("inlineCode",{parentName:"p"},"exists:users,id"),",\nwill not function correctly unless you decode the ID before passing it to the validation process.")),(0,i.yg)("h3",{id:"urlparameters"},"urlParameters"),(0,i.yg)("p",null,"The ",(0,i.yg)("inlineCode",{parentName:"p"},"$urlParameters")," property simplifies the process of applying validation rules to URL parameters."),(0,i.yg)("p",null,"By default, Laravel doesn't provide validation for URL parameters (",(0,i.yg)("inlineCode",{parentName:"p"},"/stores/999/items"),").\nHowever, by using the ",(0,i.yg)("inlineCode",{parentName:"p"},"$urlParameters")," property, you can enable validation for these parameters.\nBy specifying the desired URL parameters within this property,\nyou not only enable validation but also gain direct access to these parameters from the Request object."),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},"// URL: /stores/{id}/items\n// GET /stores/999/items\nclass DemoRequest extends ParentRequest\n{\n protected array $urlParameters = [\n 'id',\n ];\n\n public function rules(): array\n {\n return [\n 'id' => 'integer', // url parameter\n ];\n }\n}\n")),(0,i.yg)("h2",{id:"helper-methods"},"Helper Methods"),(0,i.yg)("h3",{id:"check"},"check"),(0,i.yg)("p",null,"The ",(0,i.yg)("inlineCode",{parentName:"p"},"check")," method is used to authorize the user to access the endpoint.\nIt accepts an array of methods names that will be called to check if the user has access or not.\nEach of those methods must return a boolean.\nTake a look at the following example:"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},"use App\\Ship\\Parents\\Requests\\Request as ParentRequest;\n\nclass DemoRequest extends ParentRequest\n{\n use IsAuthorTrait;\n\n // ...\n\n public function authorize(): bool\n {\n return $this->check([\n 'hasAccess|isOwner',\n 'isKing',\n ]);\n }\n}\n")),(0,i.yg)("p",null,"Here we are passing the the ",(0,i.yg)("inlineCode",{parentName:"p"},"hasAccess"),", ",(0,i.yg)("inlineCode",{parentName:"p"},"isOwner")," and ",(0,i.yg)("inlineCode",{parentName:"p"},"isKing")," methods to the ",(0,i.yg)("inlineCode",{parentName:"p"},"check")," method.\nThen the ",(0,i.yg)("inlineCode",{parentName:"p"},"check")," method follows the following rules and checks if the user has access or not:"),(0,i.yg)("ul",null,(0,i.yg)("li",{parentName:"ul"},"The separator ",(0,i.yg)("inlineCode",{parentName:"li"},"|")," between the methods indicates an ",(0,i.yg)("inlineCode",{parentName:"li"},"OR")," operation."),(0,i.yg)("li",{parentName:"ul"},"The default operation between all methods in the array is ",(0,i.yg)("inlineCode",{parentName:"li"},"AND"),".")),(0,i.yg)("p",null,"So in the above example, the call to the ",(0,i.yg)("inlineCode",{parentName:"p"},"check")," method will be translated to:"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},"return ($this->hasAccess() || $this->isOwner()) && $this->isKing();\n")),(0,i.yg)("p",null,"And if the result of this operation is ",(0,i.yg)("inlineCode",{parentName:"p"},"true")," then the user will be authorized to access the endpoint."),(0,i.yg)("admonition",{type:"note"},(0,i.yg)("ul",{parentName:"admonition"},(0,i.yg)("li",{parentName:"ul"},(0,i.yg)("inlineCode",{parentName:"li"},"hasAccess")," method is a ",(0,i.yg)("a",{parentName:"li",href:"#hasaccess"},"built-in authorization method"),"."),(0,i.yg)("li",{parentName:"ul"},(0,i.yg)("inlineCode",{parentName:"li"},"isOwner")," and ",(0,i.yg)("inlineCode",{parentName:"li"},"isKing")," methods are ",(0,i.yg)("a",{parentName:"li",href:"#custom-authorize-methods"},"custom authorization methods")))),(0,i.yg)("h3",{id:"hasaccess"},"hasAccess"),(0,i.yg)("p",null,"The ",(0,i.yg)("inlineCode",{parentName:"p"},"hasAccess")," method assesses a user's access rights based on the Request's ",(0,i.yg)("inlineCode",{parentName:"p"},"$access")," property.\nIf the user has any of the specified Roles or Permissions, the method will return ",(0,i.yg)("inlineCode",{parentName:"p"},"true")," otherwise it will\nreturn ",(0,i.yg)("inlineCode",{parentName:"p"},"false"),"."),(0,i.yg)("h3",{id:"sanitizeinput"},"sanitizeInput"),(0,i.yg)("p",null,"The ",(0,i.yg)("inlineCode",{parentName:"p"},"sanitizeInput")," method is employed to cleanse request data before its utilization within the application."),(0,i.yg)("p",null,"Particularly useful for ",(0,i.yg)("inlineCode",{parentName:"p"},"PATCH")," requests,\nwhere you may want\nto submit only the fields\nintended for modification to minimize traffic or perform partial updates to the corresponding database resource.\nTraditional checks for the presence or absence of specific keys in the request can lead to extensive ",(0,i.yg)("inlineCode",{parentName:"p"},"if")," blocks,\nsuch as:"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},"if ($request->has('data.name')) {\n $data['name'] = $request->input('data.name');\n}\n")),(0,i.yg)("p",null,"To circumvent these ",(0,i.yg)("inlineCode",{parentName:"p"},"if")," blocks, you might utilize ",(0,i.yg)("inlineCode",{parentName:"p"},"array_filter($data)")," to remove empty fields from the request.\nHowever, be aware that in PHP, both ",(0,i.yg)("inlineCode",{parentName:"p"},"false")," and an empty string ",(0,i.yg)("inlineCode",{parentName:"p"},"''")," are considered as ",(0,i.yg)("inlineCode",{parentName:"p"},"empty"),"."),(0,i.yg)("p",null,"For streamlining data sanitization when using ",(0,i.yg)("inlineCode",{parentName:"p"},"application/json")," instead of ",(0,i.yg)("inlineCode",{parentName:"p"},"x-www-form-urlencoded"),",\nApiato provides the convenient ",(0,i.yg)("inlineCode",{parentName:"p"},"sanitizeInput")," method."),(0,i.yg)("p",null,"Consider the following request:"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-json"},'{\n "data": {\n "name": "Demo",\n "description": "Some description",\n "is_private": false,\n "address": "",\n "foo": {\n "number": 1,\n "bar": "bar"\n }\n },\n "meta": "some meta data"\n}\n')),(0,i.yg)("p",null,"The ",(0,i.yg)("inlineCode",{parentName:"p"},"sanitizeInput")," method enables you to specify a list of fields,\nemploying dot notation, to be accessed and extracted from the request."),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},"$data = $request->sanitizeInput([\n 'data.description',\n 'data.is_private',\n 'data.address',\n 'data.foo.number',\n 'email', // will be ignored\n 'meta',\n]);\n")),(0,i.yg)("p",null,"The extracted data will appear as follows:"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},'[\n "data" => [\n "description" => "Some description"\n "is_private" => false,\n "address" => null, // empty string is converted to null by Laravel\n "foo" => [\n "number" => 1,\n ]\n ],\n "meta" => "some meta data",\n]\n')),(0,i.yg)("p",null,"Note that ",(0,i.yg)("inlineCode",{parentName:"p"},"email")," is excluded from the sanitized array, as it was absent in the request.\nAdditionally, any other fields not specified are omitted.\nIn essence, the method filters the request, retaining only the defined values."),(0,i.yg)("p",null,"You can also assign default values during the data sanitization process:"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},"$sanitizedData = $request->sanitizeInput([\n 'name' => 'John', // If name is not provided, the default value will be set\n 'product.company.address' => 'Somewhere in the world', // dot notation is supported\n 'email',\n 'password'\n]);\n")),(0,i.yg)("h3",{id:"getinputbykey"},"getInputByKey"),(0,i.yg)("p",null,"The ",(0,i.yg)("inlineCode",{parentName:"p"},"getInputByKey")," method retrieves data from the ",(0,i.yg)("inlineCode",{parentName:"p"},"request")," by specifying the field name.\nSimilar to ",(0,i.yg)("inlineCode",{parentName:"p"},"$request->input('key.here')"),", this method operates on the ",(0,i.yg)("inlineCode",{parentName:"p"},"decoded")," values instead of the original data."),(0,i.yg)("p",null,"Consider the following request:"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-json"},'{\n "id": "XbPW7awNkzl83LD6"\n}\n')),(0,i.yg)("p",null,"While ",(0,i.yg)("inlineCode",{parentName:"p"},"$request->input('id')")," would return ",(0,i.yg)("inlineCode",{parentName:"p"},'"XbPW7awNkzl83LD6"'),",\n",(0,i.yg)("inlineCode",{parentName:"p"},"$request->getInputByKey('id')")," would return the decoded value\n(e.g., ",(0,i.yg)("inlineCode",{parentName:"p"},"4"),")."),(0,i.yg)("p",null,"Moreover, you can set a ",(0,i.yg)("inlineCode",{parentName:"p"},"default")," value to be returned if the key is absent or unset, like this:"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},"$request->getInputByKey('data.name', 'Undefined')\n")),(0,i.yg)("h3",{id:"mapinput"},"mapInput"),(0,i.yg)("p",null,"In certain cases, you might need to remap input from the request to different fields.\nWhile manual field mapping is possible, you can also leverage the ",(0,i.yg)("inlineCode",{parentName:"p"},"mapInput"),' method for this purpose.\nThis helper method allows you to "redefine" keys within the request, making subsequent processing easier.'),(0,i.yg)("p",null,"Consider the following request:"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-json"},'{\n "data": {\n "name": "John Doe"\n }\n}\n')),(0,i.yg)("p",null,"However, for processing purposes, you require the ",(0,i.yg)("inlineCode",{parentName:"p"},"username")," field instead of ",(0,i.yg)("inlineCode",{parentName:"p"},"data.name"),"."),(0,i.yg)("p",null,"You can use the helper as follows:"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},"$request->mapInput([\n 'data.name' => 'username',\n]);\n")),(0,i.yg)("p",null,"The resulting structure would be:"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-json"},'{\n "username": "John Doe"\n}\n')),(0,i.yg)("p",null,"And you can access the value as follows:"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},"$request->input('username');\n")),(0,i.yg)("h3",{id:"injectdata"},"injectData"),(0,i.yg)("p",null,"The ",(0,i.yg)("inlineCode",{parentName:"p"},"injectData")," method allows you to inject data into the request.\nThis can be particularly helpful during testing\nwhen you wish to provide data directly to the request instead of sending it through the request body."),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},"$request = RegisterUserRequest::injectData($data);\n")),(0,i.yg)("h3",{id:"withurlparameters"},"withUrlParameters"),(0,i.yg)("p",null,"The ",(0,i.yg)("inlineCode",{parentName:"p"},"withUrlParameters")," method enables you to inject URL parameters into the request.\nThis is especially useful when you need to include properties in the request that are not part of the request body\nbut are required for the request to be processed.\nThis method is often used in conjunction with the ",(0,i.yg)("inlineCode",{parentName:"p"},"injectData")," method."),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},"$request = RegisterUserRequest::injectData($data)\n ->withUrlParameters(['id' => 123]);\n")),(0,i.yg)("h2",{id:"custom-authorize-methods"},"Custom Authorize Methods"),(0,i.yg)("p",null,"The recommended approach for adding custom authorization functions is by using a Trait,\nwhich can be included in your Request classes."),(0,i.yg)("p",null,"For instance,\nlet's\ncreate an ",(0,i.yg)("inlineCode",{parentName:"p"},"IsAuthorTrait")," Trait with a single method\nnamed ",(0,i.yg)("inlineCode",{parentName:"p"},"isAuthor")," to determine if the current user holds the role of an author."),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},"trait IsAuthorTrait\n{\n public function isAuthor(): bool\n {\n return $this->user()->hasRole('author');\n }\n}\n")),(0,i.yg)("p",null,"Subsequently, you can apply the ",(0,i.yg)("inlineCode",{parentName:"p"},"IsAuthorTrait")," Trait to a Request class,\nallowing the utilization of the ",(0,i.yg)("inlineCode",{parentName:"p"},"isAuthor")," function within the authorization process."),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},"use App\\Ship\\Parents\\Requests\\Request as ParentRequest;\n\nclass DemoRequest extends ParentRequest\n{\n use IsAuthorTrait;\n\n // ...\n\n public function authorize(): bool\n {\n return $this->check([\n 'isAuthor',\n ]);\n }\n}\n")),(0,i.yg)("h2",{id:"bypass-authorization"},"Bypass Authorization"),(0,i.yg)("p",null,"To grant certain Roles access to all endpoints within the system without the need\nto define the role in each Request object,\nyou can follow this approach.\nThis is particularly beneficial when you want to provide unrestricted access to users with the ",(0,i.yg)("inlineCode",{parentName:"p"},"admin")," role.\nTo implement this, define the relevant roles in ",(0,i.yg)("inlineCode",{parentName:"p"},"app/Ship/Configs/apiato.php")," as shown below:"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},"'requests' => [\n 'allow-roles-to-access-all-routes' => ['admin'],\n],\n")),(0,i.yg)("h2",{id:"force-accept-header"},"Force Accept Header"),(0,i.yg)("p",null,"Typically, when making calls to a JSON API, you should include the ",(0,i.yg)("inlineCode",{parentName:"p"},"accept: application/json")," HTTP header.\nIn Apiato, you have the option to either enforce users to send this header or allow them to skip it."),(0,i.yg)("p",null,"To enforce the ",(0,i.yg)("inlineCode",{parentName:"p"},"accept: application/json")," header,\nnavigate to the ",(0,i.yg)("inlineCode",{parentName:"p"},"app/Ship/Configs/apiato.php")," configuration file and set the ",(0,i.yg)("inlineCode",{parentName:"p"},"force-accept-header")," to ",(0,i.yg)("inlineCode",{parentName:"p"},"true"),"."),(0,i.yg)("p",null,"Conversely, if you wish to allow users to skip this header, set ",(0,i.yg)("inlineCode",{parentName:"p"},"force-accept-header")," to ",(0,i.yg)("inlineCode",{parentName:"p"},"false"),"."),(0,i.yg)("admonition",{type:"info"},(0,i.yg)("p",{parentName:"admonition"},"Forcing the accept header is disabled by default.")),(0,i.yg)("h2",{id:"etag"},"Etag"),(0,i.yg)("p",null,"The ",(0,i.yg)("strong",{parentName:"p"},"ETag")," or ",(0,i.yg)("strong",{parentName:"p"},"entity tag")," is part of HTTP, the protocol for the World Wide Web.\nIt is one of several mechanisms that HTTP provides for Web cache validation,\nwhich allows a client to make conditional requests.\nThis mechanism allows caches to be more efficient and saves bandwidth,\nas a Web server does not need to send a full response if the content has not changed.\n(",(0,i.yg)("a",{parentName:"p",href:"https://en.wikipedia.org/wiki/HTTP_ETag"},"Wikipedia"),")"),(0,i.yg)("p",null,"Apiato offers support for Etag through the ",(0,i.yg)("inlineCode",{parentName:"p"},"Apiato\\Core\\Middlewares\\HttpProcessETagHeadersMiddleware")," middleware,\nwhich employs the Shallow technique.\nThis middleware can be particularly valuable in reducing bandwidth usage for clients, especially on mobile devices."),(0,i.yg)("p",null,"Please note that this feature is ",(0,i.yg)("strong",{parentName:"p"},"disabled by default"),". To enable it, follow these steps:"),(0,i.yg)("ol",null,(0,i.yg)("li",{parentName:"ol"},"Navigate to the ",(0,i.yg)("inlineCode",{parentName:"li"},"app/Ship/Configs/apiato.php")," configuration file."),(0,i.yg)("li",{parentName:"ol"},"Inside the configuration file, locate the ",(0,i.yg)("inlineCode",{parentName:"li"},"use-etag")," configuration parameter."),(0,i.yg)("li",{parentName:"ol"},"Set the ",(0,i.yg)("inlineCode",{parentName:"li"},"use-etag")," parameter to ",(0,i.yg)("inlineCode",{parentName:"li"},"true"),".")),(0,i.yg)("p",null,"Keep in mind that for this feature to function correctly, the client must include the ",(0,i.yg)("inlineCode",{parentName:"p"},"If-None-Match")," HTTP header,\nwhich corresponds to the ETag value, in their request."))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/3ffc09c4.c2453e03.js b/assets/js/3ffc09c4.0e2414c3.js similarity index 94% rename from assets/js/3ffc09c4.c2453e03.js rename to assets/js/3ffc09c4.0e2414c3.js index 7bcd9bc7f..c2561d152 100644 --- a/assets/js/3ffc09c4.c2453e03.js +++ b/assets/js/3ffc09c4.0e2414c3.js @@ -1 +1 @@ -"use strict";(self.webpackChunkdocumentation=self.webpackChunkdocumentation||[]).push([[6580],{95788:(e,t,n)=>{n.d(t,{Iu:()=>c,yg:()=>y});var o=n(11504);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function i(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var l=o.createContext({}),p=function(e){var t=o.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},c=function(e){var t=p(e.components);return o.createElement(l.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},m=o.forwardRef((function(e,t){var n=e.components,r=e.mdxType,a=e.originalType,l=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),u=p(n),m=r,y=u["".concat(l,".").concat(m)]||u[m]||d[m]||a;return n?o.createElement(y,i(i({ref:t},c),{},{components:n})):o.createElement(y,i({ref:t},c))}));function y(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var a=n.length,i=new Array(a);i[0]=m;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[u]="string"==typeof e?e:r,i[1]=s;for(var p=2;p{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>i,default:()=>d,frontMatter:()=>a,metadata:()=>s,toc:()=>p});var o=n(45072),r=(n(11504),n(95788));const a={sidebar_position:3,title:"Authorization",tags:["component","optional-component","authorization","request","policy"]},i=void 0,s={unversionedId:"security/authorization",id:"security/authorization",title:"Authorization",description:"Apiato provides a Role-Based Access Control (RBAC) through its Authorization Container.",source:"@site/docs/security/authorization.mdx",sourceDirName:"security",slug:"/security/authorization",permalink:"/docs/next/security/authorization",draft:!1,editUrl:"https://github.com/apiato/documentation/tree/master/docs/security/authorization.mdx",tags:[{label:"component",permalink:"/docs/next/tags/component"},{label:"optional-component",permalink:"/docs/next/tags/optional-component"},{label:"authorization",permalink:"/docs/next/tags/authorization"},{label:"request",permalink:"/docs/next/tags/request"},{label:"policy",permalink:"/docs/next/tags/policy"}],version:"current",lastUpdatedBy:"Mohammad Alavi",lastUpdatedAt:1697511591,formattedLastUpdatedAt:"Oct 17, 2023",sidebarPosition:3,frontMatter:{sidebar_position:3,title:"Authorization",tags:["component","optional-component","authorization","request","policy"]},sidebar:"tutorialSidebar",previous:{title:"Authentication",permalink:"/docs/next/security/authentication"},next:{title:"Email Verification",permalink:"/docs/next/security/email-varification"}},l={},p=[{value:"How it works",id:"how-it-works",level:2},{value:"Code Example",id:"code-example",level:2}],c={toc:p},u="wrapper";function d(e){let{components:t,...n}=e;return(0,r.yg)(u,(0,o.c)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,r.yg)("p",null,"Apiato provides a Role-Based Access Control (RBAC) through its Authorization Container.\nBehind the scenes,\nApiato uses the ",(0,r.yg)("a",{parentName:"p",href:"https://github.com/spatie/laravel-permission"},"Laravel Permission")," package."),(0,r.yg)("h2",{id:"how-it-works"},"How it works"),(0,r.yg)("p",null,"Authorization in Apiato is indeed straightforward and easy.\nIt operates by linking permissions to roles and then assigning roles to users."),(0,r.yg)("p",null,"To implement the authorization process, follow these steps:"),(0,r.yg)("ol",null,(0,r.yg)("li",{parentName:"ol"},"Create Roles and Permissions"),(0,r.yg)("li",{parentName:"ol"},"Attach Permissions to Roles"),(0,r.yg)("li",{parentName:"ol"},"Attach Roles and/or Permissions to Users"),(0,r.yg)("li",{parentName:"ol"},"Protect Endpoints with Permissions and/or Roles")),(0,r.yg)("p",null,"To ",(0,r.yg)("a",{parentName:"p",href:"/docs/next/components/main-components/requests#request-properties"},"protect your endpoints"),",\nyou have to specify the required permissions and/or roles in the ",(0,r.yg)("inlineCode",{parentName:"p"},"Request")," class.\nIn doing so, you can check whether the current user has the necessary access rights to reach a particular endpoint.\nBy verifying permissions and roles at the request level,\nyou ensure that unauthorized users are denied access before any further processing takes place."),(0,r.yg)("admonition",{title:"Default Roles & Permissions",type:"info"},(0,r.yg)("p",{parentName:"admonition"},"Apiato comes with some default Roles and Permissions.\nYou can find them in ",(0,r.yg)("inlineCode",{parentName:"p"},"app/Containers/AppSection/Authorization/Data/Seeders"),".\nYou can use them as a starting point, or delete them and create your own.")),(0,r.yg)("h2",{id:"code-example"},"Code Example"),(0,r.yg)("p",null,"Protecting the ",(0,r.yg)("strong",{parentName:"p"},"delete user")," endpoint with ",(0,r.yg)("inlineCode",{parentName:"p"},"delete-users")," permission:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-php"},"use App\\Ship\\Parents\\Requests\\Request as ParentRequest;\n\nclass DeleteUserRequest extends ParentRequest\n{\n protected array $access = [\n 'permissions' => 'delete-users',\n 'roles' => '',\n ];\n\n public function authorize(): bool\n {\n return $this->check([\n 'hasAccess',\n ]);\n }\n}\n")),(0,r.yg)("p",null,"Authorization failed JSON response:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-json"},'{\n "message": "This action is unauthorized.",\n "errors": []\n}\n')))}d.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkdocumentation=self.webpackChunkdocumentation||[]).push([[6580],{95788:(e,t,n)=>{n.d(t,{Iu:()=>c,yg:()=>y});var o=n(11504);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function i(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var l=o.createContext({}),p=function(e){var t=o.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},c=function(e){var t=p(e.components);return o.createElement(l.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},m=o.forwardRef((function(e,t){var n=e.components,r=e.mdxType,a=e.originalType,l=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),u=p(n),m=r,y=u["".concat(l,".").concat(m)]||u[m]||d[m]||a;return n?o.createElement(y,i(i({ref:t},c),{},{components:n})):o.createElement(y,i({ref:t},c))}));function y(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var a=n.length,i=new Array(a);i[0]=m;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[u]="string"==typeof e?e:r,i[1]=s;for(var p=2;p{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>i,default:()=>d,frontMatter:()=>a,metadata:()=>s,toc:()=>p});var o=n(45072),r=(n(11504),n(95788));const a={sidebar_position:3,title:"Authorization",tags:["component","optional-component","authorization","request","policy"]},i=void 0,s={unversionedId:"security/authorization",id:"security/authorization",title:"Authorization",description:"Apiato provides a Role-Based Access Control (RBAC) through its Authorization Container.",source:"@site/docs/security/authorization.mdx",sourceDirName:"security",slug:"/security/authorization",permalink:"/docs/next/security/authorization",draft:!1,editUrl:"https://github.com/apiato/documentation/tree/master/docs/security/authorization.mdx",tags:[{label:"component",permalink:"/docs/next/tags/component"},{label:"optional-component",permalink:"/docs/next/tags/optional-component"},{label:"authorization",permalink:"/docs/next/tags/authorization"},{label:"request",permalink:"/docs/next/tags/request"},{label:"policy",permalink:"/docs/next/tags/policy"}],version:"current",lastUpdatedBy:"Mohammad Alavi",lastUpdatedAt:1707555342,formattedLastUpdatedAt:"Feb 10, 2024",sidebarPosition:3,frontMatter:{sidebar_position:3,title:"Authorization",tags:["component","optional-component","authorization","request","policy"]},sidebar:"tutorialSidebar",previous:{title:"Authentication",permalink:"/docs/next/security/authentication"},next:{title:"Email Verification",permalink:"/docs/next/security/email-varification"}},l={},p=[{value:"How it works",id:"how-it-works",level:2},{value:"Code Example",id:"code-example",level:2}],c={toc:p},u="wrapper";function d(e){let{components:t,...n}=e;return(0,r.yg)(u,(0,o.c)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,r.yg)("p",null,"Apiato provides a Role-Based Access Control (RBAC) through its Authorization Container.\nBehind the scenes,\nApiato uses the ",(0,r.yg)("a",{parentName:"p",href:"https://github.com/spatie/laravel-permission"},"Laravel Permission")," package."),(0,r.yg)("h2",{id:"how-it-works"},"How it works"),(0,r.yg)("p",null,"Authorization in Apiato is indeed straightforward and easy.\nIt operates by linking permissions to roles and then assigning roles to users."),(0,r.yg)("p",null,"To implement the authorization process, follow these steps:"),(0,r.yg)("ol",null,(0,r.yg)("li",{parentName:"ol"},"Create Roles and Permissions"),(0,r.yg)("li",{parentName:"ol"},"Attach Permissions to Roles"),(0,r.yg)("li",{parentName:"ol"},"Attach Roles and/or Permissions to Users"),(0,r.yg)("li",{parentName:"ol"},"Protect Endpoints with Permissions and/or Roles")),(0,r.yg)("p",null,"To ",(0,r.yg)("a",{parentName:"p",href:"/docs/next/components/main-components/requests#request-properties"},"protect your endpoints"),",\nyou have to specify the required permissions and/or roles in the ",(0,r.yg)("inlineCode",{parentName:"p"},"Request")," class.\nIn doing so, you can check whether the current user has the necessary access rights to reach a particular endpoint.\nBy verifying permissions and roles at the request level,\nyou ensure that unauthorized users are denied access before any further processing takes place."),(0,r.yg)("admonition",{title:"Default Roles & Permissions",type:"info"},(0,r.yg)("p",{parentName:"admonition"},"Apiato comes with some default Roles and Permissions.\nYou can find them in ",(0,r.yg)("inlineCode",{parentName:"p"},"app/Containers/AppSection/Authorization/Data/Seeders"),".\nYou can use them as a starting point, or delete them and create your own.")),(0,r.yg)("h2",{id:"code-example"},"Code Example"),(0,r.yg)("p",null,"Protecting the ",(0,r.yg)("strong",{parentName:"p"},"delete user")," endpoint with ",(0,r.yg)("inlineCode",{parentName:"p"},"delete-users")," permission:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-php"},"use App\\Ship\\Parents\\Requests\\Request as ParentRequest;\n\nclass DeleteUserRequest extends ParentRequest\n{\n protected array $access = [\n 'permissions' => 'delete-users',\n 'roles' => '',\n ];\n\n public function authorize(): bool\n {\n return $this->hasAccess();\n }\n}\n")),(0,r.yg)("p",null,"Authorization failed JSON response:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-json"},'{\n "message": "This action is unauthorized.",\n "errors": []\n}\n')))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/66d590de.93f0783d.js b/assets/js/66d590de.93f0783d.js deleted file mode 100644 index fb54c277f..000000000 --- a/assets/js/66d590de.93f0783d.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkdocumentation=self.webpackChunkdocumentation||[]).push([[3188],{95788:(e,n,t)=>{t.d(n,{Iu:()=>u,yg:()=>h});var a=t(11504);function i(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function r(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);n&&(a=a.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,a)}return t}function o(e){for(var n=1;n=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var l=a.createContext({}),p=function(e){var n=a.useContext(l),t=n;return e&&(t="function"==typeof e?e(n):o(o({},n),e)),t},u=function(e){var n=p(e.components);return a.createElement(l.Provider,{value:n},e.children)},d="mdxType",c={inlineCode:"code",wrapper:function(e){var n=e.children;return a.createElement(a.Fragment,{},n)}},m=a.forwardRef((function(e,n){var t=e.components,i=e.mdxType,r=e.originalType,l=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),d=p(t),m=i,h=d["".concat(l,".").concat(m)]||d[m]||c[m]||r;return t?a.createElement(h,o(o({ref:n},u),{},{components:t})):a.createElement(h,o({ref:n},u))}));function h(e,n){var t=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var r=t.length,o=new Array(r);o[0]=m;var s={};for(var l in n)hasOwnProperty.call(n,l)&&(s[l]=n[l]);s.originalType=e,s[d]="string"==typeof e?e:i,o[1]=s;for(var p=2;p{t.r(n),t.d(n,{assets:()=>l,contentTitle:()=>o,default:()=>c,frontMatter:()=>r,metadata:()=>s,toc:()=>p});var a=t(45072),i=(t(11504),t(95788));const r={sidebar_position:3,title:"Requests",tags:["component","main-component","request","route","controller","action"]},o=void 0,s={unversionedId:"components/main-components/requests",id:"version-12.x/components/main-components/requests",title:"Requests",description:"Requests components are a way to interact with the current HTTP request",source:"@site/versioned_docs/version-12.x/components/main-components/requests.md",sourceDirName:"components/main-components",slug:"/components/main-components/requests",permalink:"/docs/components/main-components/requests",draft:!1,editUrl:"https://github.com/apiato/documentation/tree/master/versioned_docs/version-12.x/components/main-components/requests.md",tags:[{label:"component",permalink:"/docs/tags/component"},{label:"main-component",permalink:"/docs/tags/main-component"},{label:"request",permalink:"/docs/tags/request"},{label:"route",permalink:"/docs/tags/route"},{label:"controller",permalink:"/docs/tags/controller"},{label:"action",permalink:"/docs/tags/action"}],version:"12.x",lastUpdatedBy:"Mohammad Alavi",lastUpdatedAt:1704287783,formattedLastUpdatedAt:"Jan 3, 2024",sidebarPosition:3,frontMatter:{sidebar_position:3,title:"Requests",tags:["component","main-component","request","route","controller","action"]},sidebar:"tutorialSidebar",previous:{title:"Controllers",permalink:"/docs/components/main-components/controllers"},next:{title:"Actions",permalink:"/docs/components/main-components/actions"}},l={},p=[{value:"Definition & Principles",id:"definition--principles",level:2},{value:"Rules",id:"rules",level:2},{value:"Folder Structure",id:"folder-structure",level:2},{value:"Code Example",id:"code-example",level:2},{value:"Validation",id:"validation",level:2},{value:"Request Properties",id:"request-properties",level:2},{value:"access",id:"access",level:3},{value:"decode",id:"decode",level:3},{value:"urlParameters",id:"urlparameters",level:3},{value:"Helper Methods",id:"helper-methods",level:2},{value:"check",id:"check",level:3},{value:"hasAccess",id:"hasaccess",level:3},{value:"sanitizeInput",id:"sanitizeinput",level:3},{value:"getInputByKey",id:"getinputbykey",level:3},{value:"mapInput",id:"mapinput",level:3},{value:"injectData",id:"injectdata",level:3},{value:"withUrlParameters",id:"withurlparameters",level:3},{value:"Custom Authorize Methods",id:"custom-authorize-methods",level:2},{value:"Bypass Authorization",id:"bypass-authorization",level:2},{value:"Force Accept Header",id:"force-accept-header",level:2},{value:"Etag",id:"etag",level:2}],u={toc:p},d="wrapper";function c(e){let{components:n,...t}=e;return(0,i.yg)(d,(0,a.c)({},u,t,{components:n,mdxType:"MDXLayout"}),(0,i.yg)("p",null,(0,i.yg)("a",{parentName:"p",href:"https://laravel.com/docs/requests"},"Requests")," components are a way to interact with the current HTTP request\nbeing handled by your application as well as retrieve the input,\ncookies, and files that were submitted with the request."),(0,i.yg)("p",null,"To generate new requests you may use the ",(0,i.yg)("inlineCode",{parentName:"p"},"apiato:generate:request")," interactive command:"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre"},"php artisan apiato:generate:request\n")),(0,i.yg)("h2",{id:"definition--principles"},"Definition & Principles"),(0,i.yg)("p",null,"Read ",(0,i.yg)("a",{parentName:"p",href:"https://github.com/Mahmoudz/Porto#definitions--principles"},(0,i.yg)("strong",{parentName:"a"},"Porto SAP Documentation (#Requests)")),"."),(0,i.yg)("h2",{id:"rules"},"Rules"),(0,i.yg)("ul",null,(0,i.yg)("li",{parentName:"ul"},"Validation rules MUST be defined in the ",(0,i.yg)("inlineCode",{parentName:"li"},"rules")," method."),(0,i.yg)("li",{parentName:"ul"},"All API Requests MUST be placed in the ",(0,i.yg)("inlineCode",{parentName:"li"},"app/Containers/{Section}/{Container}/UI/API/Requests")," directory."),(0,i.yg)("li",{parentName:"ul"},"All Web Requests MUST be placed in the ",(0,i.yg)("inlineCode",{parentName:"li"},"app/Containers/{Section}/{Container}/UI/WEB/Requests")," directory."),(0,i.yg)("li",{parentName:"ul"},"All Requests:",(0,i.yg)("ul",{parentName:"li"},(0,i.yg)("li",{parentName:"ul"},"MUST extend the ",(0,i.yg)("inlineCode",{parentName:"li"},"App\\Ship\\Parents\\Requests\\Request")," class.",(0,i.yg)("ul",{parentName:"li"},(0,i.yg)("li",{parentName:"ul"},"The parent extension SHOULD be aliased as ",(0,i.yg)("inlineCode",{parentName:"li"},"ParentRequest"),"."))),(0,i.yg)("li",{parentName:"ul"},"MUST have a public ",(0,i.yg)("inlineCode",{parentName:"li"},"rules")," method, returning an array of validation rules."),(0,i.yg)("li",{parentName:"ul"},"MUST have a public ",(0,i.yg)("inlineCode",{parentName:"li"},"authorize")," method, returning a boolean value.")))),(0,i.yg)("h2",{id:"folder-structure"},"Folder Structure"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-markdown"},"app\n\u2514\u2500\u2500 Containers\n \u2514\u2500\u2500 Section\n \u2514\u2500\u2500 Container\n \u2514\u2500\u2500 UI\n \u251c\u2500\u2500 API\n \u2502 \u2514\u2500\u2500 Requests\n \u2502 \u251c\u2500\u2500 CreateUserRequest.php\n \u2502 \u251c\u2500\u2500 DeleteUserRequest.php\n \u2502 \u2514\u2500\u2500 ...\n \u2514\u2500\u2500 WEB\n \u2514\u2500\u2500 Requests\n \u251c\u2500\u2500 Login.php\n \u251c\u2500\u2500 Logout.php\n \u2514\u2500\u2500 ...\n")),(0,i.yg)("h2",{id:"code-example"},"Code Example"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},"use App\\Ship\\Parents\\Requests\\Request as ParentRequest;\n\nclass DemoRequest extends ParentRequest\n{\n protected array $access = [];\n protected array $decode = [];\n protected array $urlParameters = [];\n\n public function rules(): array\n {\n return [\n 'field' => 'min:3|max:50',\n ];\n }\n\n public function authorize(): bool\n {\n return true;\n }\n}\n")),(0,i.yg)("h2",{id:"validation"},"Validation"),(0,i.yg)("p",null,"In Apiato,\nvalidation of incoming requests follows the\nLaravel ",(0,i.yg)("a",{parentName:"p",href:"https://laravel.com/docs/validation#form-request-validation"},"Form Request Validation")," approach."),(0,i.yg)("p",null,"Validation rules are defined within the respective Request class.\nThese rules are automatically enforced when a Request is injected into a Controller."),(0,i.yg)("p",null,"Here's an example of a Request class with validation rules:"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},"use App\\Ship\\Parents\\Requests\\Request as ParentRequest;\n\nclass RegisterUserRequest extends ParentRequest\n{\n public function rules(): array\n {\n return [\n 'email' => 'required|email|max:200|unique:users,email',\n 'password' => 'required|min:20|max:300',\n 'name' => ['required', 'min:2', 'max:400'],\n ];\n }\n\n}\n")),(0,i.yg)("p",null,"And here's how you would use this Request class within a Controller:"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},"public function __invoke(RegisterUserRequest $request)\n{\n $user = app(RegisterUserAction::class)->run($request);\n \n return $this->transform($user, UserTransformer::class);\n}\n")),(0,i.yg)("p",null,"In this example,\nthe validation rules defined in ",(0,i.yg)("inlineCode",{parentName:"p"},"RegisterUserRequest")," will be automatically applied\nbefore the ",(0,i.yg)("inlineCode",{parentName:"p"},"__invoke")," method is executed.\nIf the validation fails, an appropriate error response will be generated."),(0,i.yg)("h2",{id:"request-properties"},"Request Properties"),(0,i.yg)("p",null,"Apiato introduces new properties to the Request Class that enhance its functionality."),(0,i.yg)("h3",{id:"access"},"access"),(0,i.yg)("p",null,"The ",(0,i.yg)("inlineCode",{parentName:"p"},"$access")," property allows you to define Roles and Permissions that can access a specific endpoint.\nIt's used by the ",(0,i.yg)("inlineCode",{parentName:"p"},"hasAccess")," method to check if a user has the required Roles and Permissions to use that endpoint."),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},"class DemoRequest extends ParentRequest\n{\n protected array $access = [\n 'permissions' => 'delete-users',\n 'roles' => 'manager'\n ];\n\n public function authorize(): bool\n {\n return $this->check([\n 'hasAccess',\n ]);\n }\n}\n")),(0,i.yg)("p",null,"You can also use the ",(0,i.yg)("inlineCode",{parentName:"p"},"array notation")," or ",(0,i.yg)("inlineCode",{parentName:"p"},"pipe")," to define multiple Roles and Permissions."),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},"class DemoRequest extends ParentRequest\n{\n protected $access = [\n 'permissions' => ['delete-users', 'another-permissions'],\n 'roles' => 'manager|admin',\n ];\n \n // ...\n}\n")),(0,i.yg)("admonition",{type:"tip"},(0,i.yg)("p",{parentName:"admonition"},"If there's no need to set any roles or permissions,\nyou can simply set the ",(0,i.yg)("inlineCode",{parentName:"p"},"$permissions")," or ",(0,i.yg)("inlineCode",{parentName:"p"},"$roles")," property to an empty string ",(0,i.yg)("inlineCode",{parentName:"p"},"''"),", an empty array ",(0,i.yg)("inlineCode",{parentName:"p"},"[]"),", or ",(0,i.yg)("inlineCode",{parentName:"p"},"null"),".")),(0,i.yg)("h3",{id:"decode"},"decode"),(0,i.yg)("p",null,"The ",(0,i.yg)("inlineCode",{parentName:"p"},"$decode")," property is used to handle the decoding of Hashed IDs from the incoming Request."),(0,i.yg)("p",null,"When you enable the ",(0,i.yg)("a",{parentName:"p",href:"/docs/security/hash-id"},"Hash ID")," feature, your application can receive Hashed IDs from users.\nThese Hashed IDs need to be decoded before they can be effectively validated.\nApiato facilitates this process\nby providing a property in its Requests class where you can specify which Hashed IDs need to be decoded.\nThis ensures that the validation procedure seamlessly integrates with Hashed IDs."),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},"class DemoRequest extends ParentRequest\n{\n protected array $decode = [\n 'user_id',\n 'item_id',\n ];\n \n // ...\n}\n")),(0,i.yg)("admonition",{type:"note"},(0,i.yg)("p",{parentName:"admonition"},"Keep in mind that validation rules relying on your ID, such as ",(0,i.yg)("inlineCode",{parentName:"p"},"exists:users,id"),",\nwill not function correctly unless you decode the ID before passing it to the validation process.")),(0,i.yg)("h3",{id:"urlparameters"},"urlParameters"),(0,i.yg)("p",null,"The ",(0,i.yg)("inlineCode",{parentName:"p"},"$urlParameters")," property simplifies the process of applying validation rules to URL parameters."),(0,i.yg)("p",null,"By default, Laravel doesn't provide validation for URL parameters (",(0,i.yg)("inlineCode",{parentName:"p"},"/stores/999/items"),").\nHowever, by using the ",(0,i.yg)("inlineCode",{parentName:"p"},"$urlParameters")," property, you can enable validation for these parameters.\nBy specifying the desired URL parameters within this property,\nyou not only enable validation but also gain direct access to these parameters from the Request object."),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},"// URL: /stores/{id}/items\n// GET /stores/999/items\nclass DemoRequest extends ParentRequest\n{\n protected array $urlParameters = [\n 'id',\n ];\n\n public function rules(): array\n {\n return [\n 'id' => 'integer', // url parameter\n ];\n }\n}\n")),(0,i.yg)("h2",{id:"helper-methods"},"Helper Methods"),(0,i.yg)("h3",{id:"check"},"check"),(0,i.yg)("p",null,"The ",(0,i.yg)("inlineCode",{parentName:"p"},"check")," method is used to authorize the user to access the endpoint.\nIt accepts an array of methods names that will be called to check if the user has access or not.\nEach of those methods must return a boolean.\nTake a look at the following example:"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},"use App\\Ship\\Parents\\Requests\\Request as ParentRequest;\n\nclass DemoRequest extends ParentRequest\n{\n use IsAuthorTrait;\n\n // ...\n\n public function authorize(): bool\n {\n return $this->check([\n 'hasAccess|isOwner',\n 'isKing',\n ]);\n }\n}\n")),(0,i.yg)("p",null,"Here we are passing the the ",(0,i.yg)("inlineCode",{parentName:"p"},"hasAccess"),", ",(0,i.yg)("inlineCode",{parentName:"p"},"isOwner")," and ",(0,i.yg)("inlineCode",{parentName:"p"},"isKing")," methods to the ",(0,i.yg)("inlineCode",{parentName:"p"},"check")," method.\nThen the ",(0,i.yg)("inlineCode",{parentName:"p"},"check")," method follows the following rules and checks if the user has access or not:"),(0,i.yg)("ul",null,(0,i.yg)("li",{parentName:"ul"},"The separator ",(0,i.yg)("inlineCode",{parentName:"li"},"|")," between the methods indicates an ",(0,i.yg)("inlineCode",{parentName:"li"},"OR")," operation."),(0,i.yg)("li",{parentName:"ul"},"The default operation between all methods in the array is ",(0,i.yg)("inlineCode",{parentName:"li"},"AND"),".")),(0,i.yg)("p",null,"So in the above example, the call to the ",(0,i.yg)("inlineCode",{parentName:"p"},"check")," method will be translated to:"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},"return ($this->hasAccess() || $this->isOwner()) && $this->isKing();\n")),(0,i.yg)("p",null,"And if the result of this operation is ",(0,i.yg)("inlineCode",{parentName:"p"},"true")," then the user will be authorized to access the endpoint."),(0,i.yg)("admonition",{type:"note"},(0,i.yg)("ul",{parentName:"admonition"},(0,i.yg)("li",{parentName:"ul"},(0,i.yg)("inlineCode",{parentName:"li"},"hasAccess")," method is a ",(0,i.yg)("a",{parentName:"li",href:"#hasaccess"},"built-in authorization method"),"."),(0,i.yg)("li",{parentName:"ul"},(0,i.yg)("inlineCode",{parentName:"li"},"isOwner")," and ",(0,i.yg)("inlineCode",{parentName:"li"},"isKing")," methods are ",(0,i.yg)("a",{parentName:"li",href:"#custom-authorize-methods"},"custom authorization methods")))),(0,i.yg)("h3",{id:"hasaccess"},"hasAccess"),(0,i.yg)("p",null,"The ",(0,i.yg)("inlineCode",{parentName:"p"},"hasAccess")," method assesses a user's access rights based on the Request's ",(0,i.yg)("inlineCode",{parentName:"p"},"$access")," property.\nIf the user has any of the specified Roles or Permissions, the method will return ",(0,i.yg)("inlineCode",{parentName:"p"},"true")," otherwise it will\nreturn ",(0,i.yg)("inlineCode",{parentName:"p"},"false"),"."),(0,i.yg)("h3",{id:"sanitizeinput"},"sanitizeInput"),(0,i.yg)("p",null,"The ",(0,i.yg)("inlineCode",{parentName:"p"},"sanitizeInput")," method is employed to cleanse request data before its utilization within the application."),(0,i.yg)("p",null,"Particularly useful for ",(0,i.yg)("inlineCode",{parentName:"p"},"PATCH")," requests,\nwhere you may want\nto submit only the fields\nintended for modification to minimize traffic or perform partial updates to the corresponding database resource.\nTraditional checks for the presence or absence of specific keys in the request can lead to extensive ",(0,i.yg)("inlineCode",{parentName:"p"},"if")," blocks,\nsuch as:"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},"if ($request->has('data.name')) {\n $data['name'] = $request->input('data.name');\n}\n")),(0,i.yg)("p",null,"To circumvent these ",(0,i.yg)("inlineCode",{parentName:"p"},"if")," blocks, you might utilize ",(0,i.yg)("inlineCode",{parentName:"p"},"array_filter($data)")," to remove empty fields from the request.\nHowever, be aware that in PHP, both ",(0,i.yg)("inlineCode",{parentName:"p"},"false")," and an empty string ",(0,i.yg)("inlineCode",{parentName:"p"},"''")," are considered as ",(0,i.yg)("inlineCode",{parentName:"p"},"empty"),"."),(0,i.yg)("p",null,"For streamlining data sanitization when using ",(0,i.yg)("inlineCode",{parentName:"p"},"application/json")," instead of ",(0,i.yg)("inlineCode",{parentName:"p"},"x-www-form-urlencoded"),",\nApiato provides the convenient ",(0,i.yg)("inlineCode",{parentName:"p"},"sanitizeInput")," method."),(0,i.yg)("p",null,"Consider the following request:"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-json"},'{\n "data": {\n "name": "Demo",\n "description": "Some description",\n "is_private": false,\n "address": "",\n "foo": {\n "number": 1,\n "bar": "bar"\n }\n },\n "meta": "some meta data"\n}\n')),(0,i.yg)("p",null,"The ",(0,i.yg)("inlineCode",{parentName:"p"},"sanitizeInput")," method enables you to specify a list of fields,\nemploying dot notation, to be accessed and extracted from the request."),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},"$data = $request->sanitizeInput([\n 'data.description',\n 'data.is_private',\n 'data.address',\n 'data.foo.number',\n 'email', // will be ignored\n 'meta',\n]);\n")),(0,i.yg)("p",null,"The extracted data will appear as follows:"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},'[\n "data" => [\n "description" => "Some description"\n "is_private" => false,\n "address" => null, // empty string is converted to null by Laravel\n "foo" => [\n "number" => 1,\n ]\n ],\n "meta" => "some meta data",\n]\n')),(0,i.yg)("p",null,"Note that ",(0,i.yg)("inlineCode",{parentName:"p"},"email")," is excluded from the sanitized array, as it was absent in the request.\nAdditionally, any other fields not specified are omitted.\nIn essence, the method filters the request, retaining only the defined values."),(0,i.yg)("p",null,"You can also assign default values during the data sanitization process:"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},"$sanitizedData = $request->sanitizeInput([\n 'name' => 'John', // If name is not provided, the default value will be set\n 'product.company.address' => 'Somewhere in the world', // dot notation is supported\n 'email',\n 'password'\n]);\n")),(0,i.yg)("h3",{id:"getinputbykey"},"getInputByKey"),(0,i.yg)("p",null,"The ",(0,i.yg)("inlineCode",{parentName:"p"},"getInputByKey")," method retrieves data from the ",(0,i.yg)("inlineCode",{parentName:"p"},"request")," by specifying the field name.\nSimilar to ",(0,i.yg)("inlineCode",{parentName:"p"},"$request->input('key.here')"),", this method operates on the ",(0,i.yg)("inlineCode",{parentName:"p"},"decoded")," values instead of the original data."),(0,i.yg)("p",null,"Consider the following request:"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-json"},'{\n "id": "XbPW7awNkzl83LD6"\n}\n')),(0,i.yg)("p",null,"While ",(0,i.yg)("inlineCode",{parentName:"p"},"$request->input('id')")," would return ",(0,i.yg)("inlineCode",{parentName:"p"},'"XbPW7awNkzl83LD6"'),",\n",(0,i.yg)("inlineCode",{parentName:"p"},"$request->getInputByKey('id')")," would return the decoded value\n(e.g., ",(0,i.yg)("inlineCode",{parentName:"p"},"4"),")."),(0,i.yg)("p",null,"Moreover, you can set a ",(0,i.yg)("inlineCode",{parentName:"p"},"default")," value to be returned if the key is absent or unset, like this:"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},"$request->getInputByKey('data.name', 'Undefined')\n")),(0,i.yg)("h3",{id:"mapinput"},"mapInput"),(0,i.yg)("p",null,"In certain cases, you might need to remap input from the request to different fields.\nWhile manual field mapping is possible, you can also leverage the ",(0,i.yg)("inlineCode",{parentName:"p"},"mapInput"),' method for this purpose.\nThis helper method allows you to "redefine" keys within the request, making subsequent processing easier.'),(0,i.yg)("p",null,"Consider the following request:"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-json"},'{\n "data": {\n "name": "John Doe"\n }\n}\n')),(0,i.yg)("p",null,"However, for processing purposes, you require the ",(0,i.yg)("inlineCode",{parentName:"p"},"username")," field instead of ",(0,i.yg)("inlineCode",{parentName:"p"},"data.name"),"."),(0,i.yg)("p",null,"You can use the helper as follows:"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},"$request->mapInput([\n 'data.name' => 'username',\n]);\n")),(0,i.yg)("p",null,"The resulting structure would be:"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-json"},'{\n "username": "John Doe"\n}\n')),(0,i.yg)("p",null,"And you can access the value as follows:"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},"$request->input('username');\n")),(0,i.yg)("h3",{id:"injectdata"},"injectData"),(0,i.yg)("p",null,"The ",(0,i.yg)("inlineCode",{parentName:"p"},"injectData")," method allows you to inject data into the request.\nThis can be particularly helpful during testing\nwhen you wish to provide data directly to the request instead of sending it through the request body."),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},"$request = RegisterUserRequest::injectData($data);\n")),(0,i.yg)("h3",{id:"withurlparameters"},"withUrlParameters"),(0,i.yg)("p",null,"The ",(0,i.yg)("inlineCode",{parentName:"p"},"withUrlParameters")," method enables you to inject URL parameters into the request.\nThis is especially useful when you need to include properties in the request that are not part of the request body\nbut are required for the request to be processed.\nThis method is often used in conjunction with the ",(0,i.yg)("inlineCode",{parentName:"p"},"injectData")," method."),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},"$request = RegisterUserRequest::injectData($data)\n ->withUrlParameters(['id' => 123]);\n")),(0,i.yg)("h2",{id:"custom-authorize-methods"},"Custom Authorize Methods"),(0,i.yg)("p",null,"The recommended approach for adding custom authorization functions is by using a Trait,\nwhich can be included in your Request classes."),(0,i.yg)("p",null,"For instance,\nlet's\ncreate an ",(0,i.yg)("inlineCode",{parentName:"p"},"IsAuthorTrait")," Trait with a single method\nnamed ",(0,i.yg)("inlineCode",{parentName:"p"},"isAuthor")," to determine if the current user holds the role of an author."),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},"trait IsAuthorTrait\n{\n public function isAuthor(): bool\n {\n return $this->user()->hasRole('author');\n }\n}\n")),(0,i.yg)("p",null,"Subsequently, you can apply the ",(0,i.yg)("inlineCode",{parentName:"p"},"IsAuthorTrait")," Trait to a Request class,\nallowing the utilization of the ",(0,i.yg)("inlineCode",{parentName:"p"},"isAuthor")," function within the authorization process."),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},"use App\\Ship\\Parents\\Requests\\Request as ParentRequest;\n\nclass DemoRequest extends ParentRequest\n{\n use IsAuthorTrait;\n\n // ...\n\n public function authorize(): bool\n {\n return $this->check([\n 'isAuthor',\n ]);\n }\n}\n")),(0,i.yg)("h2",{id:"bypass-authorization"},"Bypass Authorization"),(0,i.yg)("p",null,"To grant certain Roles access to all endpoints within the system without the need\nto define the role in each Request object,\nyou can follow this approach.\nThis is particularly beneficial when you want to provide unrestricted access to users with the ",(0,i.yg)("inlineCode",{parentName:"p"},"admin")," role.\nTo implement this, define the relevant roles in ",(0,i.yg)("inlineCode",{parentName:"p"},"app/Ship/Configs/apiato.php")," as shown below:"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-php"},"'requests' => [\n 'allow-roles-to-access-all-routes' => ['admin'],\n],\n")),(0,i.yg)("h2",{id:"force-accept-header"},"Force Accept Header"),(0,i.yg)("p",null,"Typically, when making calls to a JSON API, you should include the ",(0,i.yg)("inlineCode",{parentName:"p"},"accept: application/json")," HTTP header.\nIn Apiato, you have the option to either enforce users to send this header or allow them to skip it."),(0,i.yg)("p",null,"To enforce the ",(0,i.yg)("inlineCode",{parentName:"p"},"accept: application/json")," header,\nnavigate to the ",(0,i.yg)("inlineCode",{parentName:"p"},"app/Ship/Configs/apiato.php")," configuration file and set the ",(0,i.yg)("inlineCode",{parentName:"p"},"force-accept-header")," to ",(0,i.yg)("inlineCode",{parentName:"p"},"true"),"."),(0,i.yg)("p",null,"Conversely, if you wish to allow users to skip this header, set ",(0,i.yg)("inlineCode",{parentName:"p"},"force-accept-header")," to ",(0,i.yg)("inlineCode",{parentName:"p"},"false"),"."),(0,i.yg)("admonition",{type:"info"},(0,i.yg)("p",{parentName:"admonition"},"Forcing the accept header is disabled by default.")),(0,i.yg)("h2",{id:"etag"},"Etag"),(0,i.yg)("p",null,"The ",(0,i.yg)("strong",{parentName:"p"},"ETag")," or ",(0,i.yg)("strong",{parentName:"p"},"entity tag")," is part of HTTP, the protocol for the World Wide Web.\nIt is one of several mechanisms that HTTP provides for Web cache validation,\nwhich allows a client to make conditional requests.\nThis mechanism allows caches to be more efficient and saves bandwidth,\nas a Web server does not need to send a full response if the content has not changed.\n(",(0,i.yg)("a",{parentName:"p",href:"https://en.wikipedia.org/wiki/HTTP_ETag"},"Wikipedia"),")"),(0,i.yg)("p",null,"Apiato offers support for Etag through the ",(0,i.yg)("inlineCode",{parentName:"p"},"Apiato\\Core\\Middlewares\\HttpProcessETagHeadersMiddleware")," middleware,\nwhich employs the Shallow technique.\nThis middleware can be particularly valuable in reducing bandwidth usage for clients, especially on mobile devices."),(0,i.yg)("p",null,"Please note that this feature is ",(0,i.yg)("strong",{parentName:"p"},"disabled by default"),". To enable it, follow these steps:"),(0,i.yg)("ol",null,(0,i.yg)("li",{parentName:"ol"},"Navigate to the ",(0,i.yg)("inlineCode",{parentName:"li"},"app/Ship/Configs/apiato.php")," configuration file."),(0,i.yg)("li",{parentName:"ol"},"Inside the configuration file, locate the ",(0,i.yg)("inlineCode",{parentName:"li"},"use-etag")," configuration parameter."),(0,i.yg)("li",{parentName:"ol"},"Set the ",(0,i.yg)("inlineCode",{parentName:"li"},"use-etag")," parameter to ",(0,i.yg)("inlineCode",{parentName:"li"},"true"),".")),(0,i.yg)("p",null,"Keep in mind that for this feature to function correctly, the client must include the ",(0,i.yg)("inlineCode",{parentName:"p"},"If-None-Match")," HTTP header,\nwhich corresponds to the ETag value, in their request."))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/66d590de.eb8f2ff4.js b/assets/js/66d590de.eb8f2ff4.js new file mode 100644 index 000000000..91cf11a1b --- /dev/null +++ b/assets/js/66d590de.eb8f2ff4.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkdocumentation=self.webpackChunkdocumentation||[]).push([[3188],{95788:(e,n,t)=>{t.d(n,{Iu:()=>u,yg:()=>g});var a=t(11504);function r(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function i(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);n&&(a=a.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,a)}return t}function o(e){for(var n=1;n=0||(r[t]=e[t]);return r}(e,n);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(r[t]=e[t])}return r}var l=a.createContext({}),p=function(e){var n=a.useContext(l),t=n;return e&&(t="function"==typeof e?e(n):o(o({},n),e)),t},u=function(e){var n=p(e.components);return a.createElement(l.Provider,{value:n},e.children)},d="mdxType",c={inlineCode:"code",wrapper:function(e){var n=e.children;return a.createElement(a.Fragment,{},n)}},m=a.forwardRef((function(e,n){var t=e.components,r=e.mdxType,i=e.originalType,l=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),d=p(t),m=r,g=d["".concat(l,".").concat(m)]||d[m]||c[m]||i;return t?a.createElement(g,o(o({ref:n},u),{},{components:t})):a.createElement(g,o({ref:n},u))}));function g(e,n){var t=arguments,r=n&&n.mdxType;if("string"==typeof e||r){var i=t.length,o=new Array(i);o[0]=m;var s={};for(var l in n)hasOwnProperty.call(n,l)&&(s[l]=n[l]);s.originalType=e,s[d]="string"==typeof e?e:r,o[1]=s;for(var p=2;p{t.r(n),t.d(n,{assets:()=>l,contentTitle:()=>o,default:()=>c,frontMatter:()=>i,metadata:()=>s,toc:()=>p});var a=t(45072),r=(t(11504),t(95788));const i={sidebar_position:3,title:"Requests",tags:["component","main-component","request","route","controller","action"]},o=void 0,s={unversionedId:"components/main-components/requests",id:"version-12.x/components/main-components/requests",title:"Requests",description:"Requests components are a way to interact with the current HTTP request",source:"@site/versioned_docs/version-12.x/components/main-components/requests.md",sourceDirName:"components/main-components",slug:"/components/main-components/requests",permalink:"/docs/components/main-components/requests",draft:!1,editUrl:"https://github.com/apiato/documentation/tree/master/versioned_docs/version-12.x/components/main-components/requests.md",tags:[{label:"component",permalink:"/docs/tags/component"},{label:"main-component",permalink:"/docs/tags/main-component"},{label:"request",permalink:"/docs/tags/request"},{label:"route",permalink:"/docs/tags/route"},{label:"controller",permalink:"/docs/tags/controller"},{label:"action",permalink:"/docs/tags/action"}],version:"12.x",lastUpdatedBy:"Mohammad Alavi",lastUpdatedAt:1707555342,formattedLastUpdatedAt:"Feb 10, 2024",sidebarPosition:3,frontMatter:{sidebar_position:3,title:"Requests",tags:["component","main-component","request","route","controller","action"]},sidebar:"tutorialSidebar",previous:{title:"Controllers",permalink:"/docs/components/main-components/controllers"},next:{title:"Actions",permalink:"/docs/components/main-components/actions"}},l={},p=[{value:"Definition & Principles",id:"definition--principles",level:2},{value:"Rules",id:"rules",level:2},{value:"Folder Structure",id:"folder-structure",level:2},{value:"Code Example",id:"code-example",level:2},{value:"Validation",id:"validation",level:2},{value:"Request Properties",id:"request-properties",level:2},{value:"access",id:"access",level:3},{value:"decode",id:"decode",level:3},{value:"urlParameters",id:"urlparameters",level:3},{value:"Helper Methods",id:"helper-methods",level:2},{value:"hasAccess",id:"hasaccess",level:3},{value:"sanitizeInput",id:"sanitizeinput",level:3},{value:"getInputByKey",id:"getinputbykey",level:3},{value:"mapInput",id:"mapinput",level:3},{value:"injectData",id:"injectdata",level:3},{value:"withUrlParameters",id:"withurlparameters",level:3},{value:"Bypassing Authorization",id:"bypassing-authorization",level:2},{value:"Force Accept Header",id:"force-accept-header",level:2},{value:"Etag",id:"etag",level:2}],u={toc:p},d="wrapper";function c(e){let{components:n,...t}=e;return(0,r.yg)(d,(0,a.c)({},u,t,{components:n,mdxType:"MDXLayout"}),(0,r.yg)("p",null,(0,r.yg)("a",{parentName:"p",href:"https://laravel.com/docs/requests"},"Requests")," components are a way to interact with the current HTTP request\nbeing handled by your application as well as retrieve the input,\ncookies, and files that were submitted with the request."),(0,r.yg)("p",null,"To generate new requests you may use the ",(0,r.yg)("inlineCode",{parentName:"p"},"apiato:generate:request")," interactive command:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre"},"php artisan apiato:generate:request\n")),(0,r.yg)("h2",{id:"definition--principles"},"Definition & Principles"),(0,r.yg)("p",null,"Read ",(0,r.yg)("a",{parentName:"p",href:"https://github.com/Mahmoudz/Porto#definitions--principles"},(0,r.yg)("strong",{parentName:"a"},"Porto SAP Documentation (#Requests)")),"."),(0,r.yg)("h2",{id:"rules"},"Rules"),(0,r.yg)("ul",null,(0,r.yg)("li",{parentName:"ul"},"Validation rules MUST be defined in the ",(0,r.yg)("inlineCode",{parentName:"li"},"rules")," method."),(0,r.yg)("li",{parentName:"ul"},"All API Requests MUST be placed in the ",(0,r.yg)("inlineCode",{parentName:"li"},"app/Containers/{Section}/{Container}/UI/API/Requests")," directory."),(0,r.yg)("li",{parentName:"ul"},"All Web Requests MUST be placed in the ",(0,r.yg)("inlineCode",{parentName:"li"},"app/Containers/{Section}/{Container}/UI/WEB/Requests")," directory."),(0,r.yg)("li",{parentName:"ul"},"All Requests:",(0,r.yg)("ul",{parentName:"li"},(0,r.yg)("li",{parentName:"ul"},"MUST extend the ",(0,r.yg)("inlineCode",{parentName:"li"},"App\\Ship\\Parents\\Requests\\Request")," class.",(0,r.yg)("ul",{parentName:"li"},(0,r.yg)("li",{parentName:"ul"},"The parent extension SHOULD be aliased as ",(0,r.yg)("inlineCode",{parentName:"li"},"ParentRequest"),"."))),(0,r.yg)("li",{parentName:"ul"},"MUST have a public ",(0,r.yg)("inlineCode",{parentName:"li"},"rules")," method, returning an array of validation rules."),(0,r.yg)("li",{parentName:"ul"},"MUST have a public ",(0,r.yg)("inlineCode",{parentName:"li"},"authorize")," method, returning a boolean value.")))),(0,r.yg)("h2",{id:"folder-structure"},"Folder Structure"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-markdown"},"app\n\u2514\u2500\u2500 Containers\n \u2514\u2500\u2500 Section\n \u2514\u2500\u2500 Container\n \u2514\u2500\u2500 UI\n \u251c\u2500\u2500 API\n \u2502 \u2514\u2500\u2500 Requests\n \u2502 \u251c\u2500\u2500 CreateUserRequest.php\n \u2502 \u251c\u2500\u2500 DeleteUserRequest.php\n \u2502 \u2514\u2500\u2500 ...\n \u2514\u2500\u2500 WEB\n \u2514\u2500\u2500 Requests\n \u251c\u2500\u2500 Login.php\n \u251c\u2500\u2500 Logout.php\n \u2514\u2500\u2500 ...\n")),(0,r.yg)("h2",{id:"code-example"},"Code Example"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-php"},"use App\\Ship\\Parents\\Requests\\Request as ParentRequest;\n\nclass DemoRequest extends ParentRequest\n{\n protected array $access = [];\n protected array $decode = [];\n protected array $urlParameters = [];\n\n public function rules(): array\n {\n return [\n 'field' => 'min:3|max:50',\n ];\n }\n\n public function authorize(): bool\n {\n return true;\n }\n}\n")),(0,r.yg)("h2",{id:"validation"},"Validation"),(0,r.yg)("p",null,"In Apiato,\nvalidation of incoming requests follows the\nLaravel ",(0,r.yg)("a",{parentName:"p",href:"https://laravel.com/docs/validation#form-request-validation"},"Form Request Validation")," approach."),(0,r.yg)("p",null,"Validation rules are defined within the respective Request class.\nThese rules are automatically enforced when a Request is injected into a Controller."),(0,r.yg)("p",null,"Here's an example of a Request class with validation rules:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-php"},"use App\\Ship\\Parents\\Requests\\Request as ParentRequest;\n\nclass RegisterUserRequest extends ParentRequest\n{\n public function rules(): array\n {\n return [\n 'email' => 'required|email|max:200|unique:users,email',\n 'password' => 'required|min:20|max:300',\n 'name' => ['required', 'min:2', 'max:400'],\n ];\n }\n\n}\n")),(0,r.yg)("p",null,"And here's how you would use this Request class within a Controller:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-php"},"public function __invoke(RegisterUserRequest $request)\n{\n $user = app(RegisterUserAction::class)->run($request);\n \n return $this->transform($user, UserTransformer::class);\n}\n")),(0,r.yg)("p",null,"In this example,\nthe validation rules defined in ",(0,r.yg)("inlineCode",{parentName:"p"},"RegisterUserRequest")," will be automatically applied\nbefore the ",(0,r.yg)("inlineCode",{parentName:"p"},"__invoke")," method is executed.\nIf the validation fails, an appropriate error response will be generated."),(0,r.yg)("h2",{id:"request-properties"},"Request Properties"),(0,r.yg)("p",null,"Apiato introduces new properties to the Request Class that enhance its functionality."),(0,r.yg)("h3",{id:"access"},"access"),(0,r.yg)("p",null,"The ",(0,r.yg)("inlineCode",{parentName:"p"},"$access")," property allows you to define Roles and Permissions that can access a specific endpoint.\nIt's used by the ",(0,r.yg)("inlineCode",{parentName:"p"},"hasAccess")," method to check if a user has the required Roles and Permissions to use that endpoint."),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-php"},"class DemoRequest extends ParentRequest\n{\n protected array $access = [\n 'permissions' => 'delete-users',\n 'roles' => 'manager'\n ];\n\n public function authorize(): bool\n {\n return $this->hasAccess();\n }\n}\n")),(0,r.yg)("p",null,"You can also use the ",(0,r.yg)("inlineCode",{parentName:"p"},"array notation")," or ",(0,r.yg)("inlineCode",{parentName:"p"},"pipe")," to define multiple Roles and Permissions."),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-php"},"class DemoRequest extends ParentRequest\n{\n protected $access = [\n 'permissions' => ['delete-users', 'another-permissions'],\n 'roles' => 'manager|admin',\n ];\n \n // ...\n}\n")),(0,r.yg)("admonition",{type:"tip"},(0,r.yg)("p",{parentName:"admonition"},"If there's no need to set any roles or permissions,\nyou can simply set the ",(0,r.yg)("inlineCode",{parentName:"p"},"$permissions")," or ",(0,r.yg)("inlineCode",{parentName:"p"},"$roles")," property to an empty string ",(0,r.yg)("inlineCode",{parentName:"p"},"''"),", an empty array ",(0,r.yg)("inlineCode",{parentName:"p"},"[]"),", or ",(0,r.yg)("inlineCode",{parentName:"p"},"null"),".")),(0,r.yg)("h3",{id:"decode"},"decode"),(0,r.yg)("p",null,"The ",(0,r.yg)("inlineCode",{parentName:"p"},"$decode")," property is used to handle the decoding of Hashed IDs from the incoming Request."),(0,r.yg)("p",null,"When you enable the ",(0,r.yg)("a",{parentName:"p",href:"/docs/security/hash-id"},"Hash ID")," feature, your application can receive Hashed IDs from users.\nThese Hashed IDs need to be decoded before they can be effectively validated.\nApiato facilitates this process\nby providing a property in its Requests class where you can specify which Hashed IDs need to be decoded.\nThis ensures that the validation procedure seamlessly integrates with Hashed IDs."),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-php"},"class DemoRequest extends ParentRequest\n{\n protected array $decode = [\n 'user_id',\n 'item_id',\n ];\n \n // ...\n}\n")),(0,r.yg)("admonition",{type:"note"},(0,r.yg)("p",{parentName:"admonition"},"Keep in mind that validation rules relying on your ID, such as ",(0,r.yg)("inlineCode",{parentName:"p"},"exists:users,id"),",\nwill not function correctly unless you decode the ID before passing it to the validation process.")),(0,r.yg)("h3",{id:"urlparameters"},"urlParameters"),(0,r.yg)("p",null,"The ",(0,r.yg)("inlineCode",{parentName:"p"},"$urlParameters")," property simplifies the process of applying validation rules to URL parameters."),(0,r.yg)("p",null,"By default, Laravel doesn't provide validation for URL parameters (",(0,r.yg)("inlineCode",{parentName:"p"},"/stores/999/items"),").\nHowever, by using the ",(0,r.yg)("inlineCode",{parentName:"p"},"$urlParameters")," property, you can enable validation for these parameters.\nBy specifying the desired URL parameters within this property,\nyou not only enable validation but also gain direct access to these parameters from the Request object."),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-php"},"// URL: /stores/{id}/items\n// GET /stores/999/items\nclass DemoRequest extends ParentRequest\n{\n protected array $urlParameters = [\n 'id',\n ];\n\n public function rules(): array\n {\n return [\n 'id' => 'integer', // url parameter\n ];\n }\n}\n")),(0,r.yg)("h2",{id:"helper-methods"},"Helper Methods"),(0,r.yg)("h3",{id:"hasaccess"},"hasAccess"),(0,r.yg)("p",null,"The ",(0,r.yg)("inlineCode",{parentName:"p"},"hasAccess")," method assesses a user's access rights based on the Request's ",(0,r.yg)("inlineCode",{parentName:"p"},"$access")," property.\nIf the user has any of the specified Roles or Permissions, the method will return ",(0,r.yg)("inlineCode",{parentName:"p"},"true")," otherwise it will\nreturn ",(0,r.yg)("inlineCode",{parentName:"p"},"false"),"."),(0,r.yg)("h3",{id:"sanitizeinput"},"sanitizeInput"),(0,r.yg)("p",null,"The ",(0,r.yg)("inlineCode",{parentName:"p"},"sanitizeInput")," method is employed to cleanse request data before its utilization within the application."),(0,r.yg)("p",null,"Particularly useful for ",(0,r.yg)("inlineCode",{parentName:"p"},"PATCH")," requests,\nwhere you may want\nto submit only the fields\nintended for modification to minimize traffic or perform partial updates to the corresponding database resource.\nTraditional checks for the presence or absence of specific keys in the request can lead to extensive ",(0,r.yg)("inlineCode",{parentName:"p"},"if")," blocks,\nsuch as:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-php"},"if ($request->has('data.name')) {\n $data['name'] = $request->input('data.name');\n}\n")),(0,r.yg)("p",null,"To circumvent these ",(0,r.yg)("inlineCode",{parentName:"p"},"if")," blocks, you might utilize ",(0,r.yg)("inlineCode",{parentName:"p"},"array_filter($data)")," to remove empty fields from the request.\nHowever, be aware that in PHP, both ",(0,r.yg)("inlineCode",{parentName:"p"},"false")," and an empty string ",(0,r.yg)("inlineCode",{parentName:"p"},"''")," are considered as ",(0,r.yg)("inlineCode",{parentName:"p"},"empty"),"."),(0,r.yg)("p",null,"For streamlining data sanitization when using ",(0,r.yg)("inlineCode",{parentName:"p"},"application/json")," instead of ",(0,r.yg)("inlineCode",{parentName:"p"},"x-www-form-urlencoded"),",\nApiato provides the convenient ",(0,r.yg)("inlineCode",{parentName:"p"},"sanitizeInput")," method."),(0,r.yg)("p",null,"Consider the following request:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-json"},'{\n "data": {\n "name": "Demo",\n "description": "Some description",\n "is_private": false,\n "address": "",\n "foo": {\n "number": 1,\n "bar": "bar"\n }\n },\n "meta": "some meta data"\n}\n')),(0,r.yg)("p",null,"The ",(0,r.yg)("inlineCode",{parentName:"p"},"sanitizeInput")," method enables you to specify a list of fields,\nemploying dot notation, to be accessed and extracted from the request."),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-php"},"$data = $request->sanitizeInput([\n 'data.description',\n 'data.is_private',\n 'data.address',\n 'data.foo.number',\n 'email', // will be ignored\n 'meta',\n]);\n")),(0,r.yg)("p",null,"The extracted data will appear as follows:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-php"},'[\n "data" => [\n "description" => "Some description"\n "is_private" => false,\n "address" => null, // empty string is converted to null by Laravel\n "foo" => [\n "number" => 1,\n ]\n ],\n "meta" => "some meta data",\n]\n')),(0,r.yg)("p",null,"Note that ",(0,r.yg)("inlineCode",{parentName:"p"},"email")," is excluded from the sanitized array, as it was absent in the request.\nAdditionally, any other fields not specified are omitted.\nIn essence, the method filters the request, retaining only the defined values."),(0,r.yg)("p",null,"You can also assign default values during the data sanitization process:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-php"},"$sanitizedData = $request->sanitizeInput([\n 'name' => 'John', // If name is not provided, the default value will be set\n 'product.company.address' => 'Somewhere in the world', // dot notation is supported\n 'email',\n 'password'\n]);\n")),(0,r.yg)("h3",{id:"getinputbykey"},"getInputByKey"),(0,r.yg)("p",null,"The ",(0,r.yg)("inlineCode",{parentName:"p"},"getInputByKey")," method retrieves data from the ",(0,r.yg)("inlineCode",{parentName:"p"},"request")," by specifying the field name.\nSimilar to ",(0,r.yg)("inlineCode",{parentName:"p"},"$request->input('key.here')"),", this method operates on the ",(0,r.yg)("inlineCode",{parentName:"p"},"decoded")," values instead of the original data."),(0,r.yg)("p",null,"Consider the following request:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-json"},'{\n "id": "XbPW7awNkzl83LD6"\n}\n')),(0,r.yg)("p",null,"While ",(0,r.yg)("inlineCode",{parentName:"p"},"$request->input('id')")," would return ",(0,r.yg)("inlineCode",{parentName:"p"},'"XbPW7awNkzl83LD6"'),",\n",(0,r.yg)("inlineCode",{parentName:"p"},"$request->getInputByKey('id')")," would return the decoded value\n(e.g., ",(0,r.yg)("inlineCode",{parentName:"p"},"4"),")."),(0,r.yg)("p",null,"Moreover, you can set a ",(0,r.yg)("inlineCode",{parentName:"p"},"default")," value to be returned if the key is absent or unset, like this:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-php"},"$request->getInputByKey('data.name', 'Undefined')\n")),(0,r.yg)("h3",{id:"mapinput"},"mapInput"),(0,r.yg)("p",null,"In certain cases, you might need to remap input from the request to different fields.\nWhile manual field mapping is possible, you can also leverage the ",(0,r.yg)("inlineCode",{parentName:"p"},"mapInput"),' method for this purpose.\nThis helper method allows you to "redefine" keys within the request, making subsequent processing easier.'),(0,r.yg)("p",null,"Consider the following request:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-json"},'{\n "data": {\n "name": "John Doe"\n }\n}\n')),(0,r.yg)("p",null,"However, for processing purposes, you require the ",(0,r.yg)("inlineCode",{parentName:"p"},"username")," field instead of ",(0,r.yg)("inlineCode",{parentName:"p"},"data.name"),"."),(0,r.yg)("p",null,"You can use the helper as follows:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-php"},"$request->mapInput([\n 'data.name' => 'username',\n]);\n")),(0,r.yg)("p",null,"The resulting structure would be:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-json"},'{\n "username": "John Doe"\n}\n')),(0,r.yg)("p",null,"And you can access the value as follows:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-php"},"$request->input('username');\n")),(0,r.yg)("h3",{id:"injectdata"},"injectData"),(0,r.yg)("p",null,"The ",(0,r.yg)("inlineCode",{parentName:"p"},"injectData")," method allows you to inject data into the request.\nThis can be particularly helpful during testing\nwhen you wish to provide data directly to the request instead of sending it through the request body."),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-php"},"$request = RegisterUserRequest::injectData($data);\n")),(0,r.yg)("h3",{id:"withurlparameters"},"withUrlParameters"),(0,r.yg)("p",null,"The ",(0,r.yg)("inlineCode",{parentName:"p"},"withUrlParameters")," method enables you to inject URL parameters into the request.\nThis is especially useful when you need to include properties in the request that are not part of the request body\nbut are required for the request to be processed.\nThis method is often used in conjunction with the ",(0,r.yg)("inlineCode",{parentName:"p"},"injectData")," method."),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-php"},"$request = RegisterUserRequest::injectData($data)\n ->withUrlParameters(['id' => 123]);\n")),(0,r.yg)("h2",{id:"bypassing-authorization"},"Bypassing Authorization"),(0,r.yg)("p",null,"To grant certain Roles access to all endpoints within the system without the need\nto define the role in each Request object,\nyou can follow this approach.\nThis is particularly beneficial when you want to provide unrestricted access to users with the ",(0,r.yg)("inlineCode",{parentName:"p"},"admin")," role.\nTo implement this, define the relevant roles in ",(0,r.yg)("inlineCode",{parentName:"p"},"app/Ship/Configs/apiato.php")," as shown below:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-php"},"'requests' => [\n 'allow-roles-to-access-all-routes' => ['admin'],\n],\n")),(0,r.yg)("h2",{id:"force-accept-header"},"Force Accept Header"),(0,r.yg)("p",null,"Typically, when making calls to a JSON API, you should include the ",(0,r.yg)("inlineCode",{parentName:"p"},"accept: application/json")," HTTP header.\nIn Apiato, you have the option to either enforce users to send this header or allow them to skip it."),(0,r.yg)("p",null,"To enforce the ",(0,r.yg)("inlineCode",{parentName:"p"},"accept: application/json")," header,\nnavigate to the ",(0,r.yg)("inlineCode",{parentName:"p"},"app/Ship/Configs/apiato.php")," configuration file and set the ",(0,r.yg)("inlineCode",{parentName:"p"},"force-accept-header")," to ",(0,r.yg)("inlineCode",{parentName:"p"},"true"),"."),(0,r.yg)("p",null,"Conversely, if you wish to allow users to skip this header, set ",(0,r.yg)("inlineCode",{parentName:"p"},"force-accept-header")," to ",(0,r.yg)("inlineCode",{parentName:"p"},"false"),"."),(0,r.yg)("admonition",{type:"info"},(0,r.yg)("p",{parentName:"admonition"},"Forcing the accept header is disabled by default.")),(0,r.yg)("h2",{id:"etag"},"Etag"),(0,r.yg)("p",null,"The ",(0,r.yg)("strong",{parentName:"p"},"ETag")," or ",(0,r.yg)("strong",{parentName:"p"},"entity tag")," is part of HTTP, the protocol for the World Wide Web.\nIt is one of several mechanisms that HTTP provides for Web cache validation,\nwhich allows a client to make conditional requests.\nThis mechanism allows caches to be more efficient and saves bandwidth,\nas a Web server does not need to send a full response if the content has not changed.\n(",(0,r.yg)("a",{parentName:"p",href:"https://en.wikipedia.org/wiki/HTTP_ETag"},"Wikipedia"),")"),(0,r.yg)("p",null,"Apiato offers support for Etag through the ",(0,r.yg)("inlineCode",{parentName:"p"},"Apiato\\Core\\Middlewares\\HttpProcessETagHeadersMiddleware")," middleware,\nwhich employs the Shallow technique.\nThis middleware can be particularly valuable in reducing bandwidth usage for clients, especially on mobile devices."),(0,r.yg)("p",null,"Please note that this feature is ",(0,r.yg)("strong",{parentName:"p"},"disabled by default"),". To enable it, follow these steps:"),(0,r.yg)("ol",null,(0,r.yg)("li",{parentName:"ol"},"Navigate to the ",(0,r.yg)("inlineCode",{parentName:"li"},"app/Ship/Configs/apiato.php")," configuration file."),(0,r.yg)("li",{parentName:"ol"},"Inside the configuration file, locate the ",(0,r.yg)("inlineCode",{parentName:"li"},"use-etag")," configuration parameter."),(0,r.yg)("li",{parentName:"ol"},"Set the ",(0,r.yg)("inlineCode",{parentName:"li"},"use-etag")," parameter to ",(0,r.yg)("inlineCode",{parentName:"li"},"true"),".")),(0,r.yg)("p",null,"Keep in mind that for this feature to function correctly, the client must include the ",(0,r.yg)("inlineCode",{parentName:"p"},"If-None-Match")," HTTP header,\nwhich corresponds to the ETag value, in their request."))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/runtime~main.54e3bf55.js b/assets/js/runtime~main.5a8dd5a8.js similarity index 98% rename from assets/js/runtime~main.54e3bf55.js rename to assets/js/runtime~main.5a8dd5a8.js index 8f39d1522..6ea07ef0d 100644 --- a/assets/js/runtime~main.54e3bf55.js +++ b/assets/js/runtime~main.5a8dd5a8.js @@ -1 +1 @@ -(()=>{"use strict";var e,c,a,f,b,d={},t={};function r(e){var c=t[e];if(void 0!==c)return c.exports;var a=t[e]={exports:{}};return d[e].call(a.exports,a,a.exports,r),a.exports}r.m=d,e=[],r.O=(c,a,f,b)=>{if(!a){var d=1/0;for(i=0;i=b)&&Object.keys(r.O).every((e=>r.O[e](a[o])))?a.splice(o--,1):(t=!1,b0&&e[i-1][2]>b;i--)e[i]=e[i-1];e[i]=[a,f,b]},r.n=e=>{var c=e&&e.__esModule?()=>e.default:()=>e;return r.d(c,{a:c}),c},a=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,r.t=function(e,f){if(1&f&&(e=this(e)),8&f)return e;if("object"==typeof e&&e){if(4&f&&e.__esModule)return e;if(16&f&&"function"==typeof e.then)return e}var b=Object.create(null);r.r(b);var d={};c=c||[null,a({}),a([]),a(a)];for(var t=2&f&&e;"object"==typeof t&&!~c.indexOf(t);t=a(t))Object.getOwnPropertyNames(t).forEach((c=>d[c]=()=>e[c]));return d.default=()=>e,r.d(b,d),b},r.d=(e,c)=>{for(var a in c)r.o(c,a)&&!r.o(e,a)&&Object.defineProperty(e,a,{enumerable:!0,get:c[a]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce(((c,a)=>(r.f[a](e,c),c)),[])),r.u=e=>"assets/js/"+({64:"10691446",66:"d85472ef",68:"986c3199",76:"8c9fccb5",82:"5a824d24",88:"245df310",127:"af8bcf34",192:"ad753fe5",204:"fbb5ccd8",224:"3a8a9a02",240:"31a3eb6f",276:"20bb4ef4",300:"ed111213",304:"2c8be122",308:"77c0bb11",340:"2c32570e",384:"a29806db",388:"53f60aaa",392:"abe60d8e",400:"f3323e34",464:"245b95ea",532:"1675a853",556:"ffafab28",576:"f4ed0a4d",592:"41e9fc02",608:"add102e2",640:"63cffe9f",716:"c6d7e964",726:"9dd4b0ef",760:"c21f0827",780:"b8242e5d",784:"19031211",792:"4683e51a",804:"bfdf68d4",824:"e5c09629",836:"5040bcd3",866:"a765b786",940:"db5219b9",984:"a291d650",1028:"0c2c09cf",1040:"a55de8d9",1044:"6855260d",1144:"d6d55995",1152:"4ce02702",1156:"4ee994fc",1236:"7c71f28e",1260:"38a9fada",1264:"114e21b0",1360:"2529077f",1380:"9f93909f",1424:"03d23ece",1448:"3c75101b",1504:"3919a682",1512:"fdca9218",1532:"ff4e10c4",1538:"ec989c0b",1580:"321c3473",1632:"1c74e866",1640:"69995e4b",1724:"0bfb57e8",1804:"fcdb2c3c",1808:"1958aa06",1824:"77aa4d93",1860:"10f6dc74",1912:"1ebbf8ad",1932:"023af9ff",1960:"fc7b1962",2e3:"0d59295f",2008:"2a5e16ec",2020:"31910907",2030:"8013ae06",2032:"93d36f00",2068:"53ff84c0",2134:"303f2916",2140:"360898bf",2152:"1d2ace06",2164:"e848ef61",2168:"8b4b94e0",2180:"d3ed5dd0",2208:"1db39f58",2224:"4b4e43c1",2240:"540a99bf",2252:"a8497d64",2280:"6459b84b",2288:"c8555b7b",2324:"dbdbfb09",2352:"149faf5c",2360:"b5a6ea24",2392:"99ff9a84",2492:"02e9e60d",2496:"abba3c1a",2504:"38e3e5e2",2508:"3c8cc128",2514:"f981018f",2520:"02dc5f66",2528:"0fdfb154",2540:"e08582aa",2548:"6ca79f64",2556:"92611ec4",2560:"33939c05",2620:"abd7c6e1",2632:"c4f5d8e4",2692:"99dcebb4",2704:"a193a73b",2724:"c8795c05",2728:"28b502c2",2760:"128066bb",2788:"4504fa21",2804:"66b63dfc",2813:"54071611",2844:"630dcb4d",2856:"b626b9c9",2860:"c417dca4",2900:"36ca3315",2904:"892cf132",2908:"aaf1cba5",2956:"9c612400",2968:"7b4e6d79",2988:"a10ffd78",3072:"427fd2ae",3088:"15e12a2f",3096:"1c647d6d",3104:"90c91afe",3116:"a7d0887a",3128:"9fcd2aaf",3140:"92ea068e",3164:"ace976d6",3168:"7c884429",3184:"b2c1c78c",3188:"66d590de",3240:"6e7ac859",3264:"6095db2e",3268:"e8f086b4",3284:"60321f1e",3300:"9ec0fefb",3330:"8e61a622",3336:"47a03c7f",3340:"2f4205f9",3348:"effa6628",3400:"df203c0f",3404:"57aa3e64",3408:"06abd5b6",3436:"934030ed",3440:"b7b5f722",3448:"9f7214f0",3512:"e63fc1b2",3548:"1746ec49",3564:"aa3c5b40",3638:"1c2e3411",3656:"d72ff326",3662:"40d100db",3668:"fd1ff340",3676:"dbe067dd",3732:"3dd44646",3752:"45778f4a",3764:"18ee9ead",3800:"0b3ceb4d",3808:"9ba65a7f",3872:"67ad9935",3876:"c02ab877",3878:"42656b86",3884:"82d76014",3888:"b9d99630",3936:"da8b188d",3944:"0a783a8b",3972:"2e6078dc",4006:"f2b76d46",4028:"d8325a2c",4040:"cc5c6d64",4050:"76f67042",4088:"20702474",4108:"d4124539",4128:"33f68dcb",4164:"7c142331",4184:"9d6a5804",4196:"70b24a51",4235:"f119515c",4236:"2af38141",4272:"a247f8ec",4296:"55960ee5",4300:"2f6f9645",4304:"31874089",4376:"75d21731",4412:"4d3c06b1",4416:"4a69c104",4424:"5ecea96b",4432:"ef449f35",4436:"7c87e584",4443:"92b2ca3c",4464:"491bc18c",4466:"819f8db2",4492:"3720c009",4536:"84164204",4540:"332d52b4",4624:"b4ea3a21",4664:"c3ec0a52",4700:"956e9f49",4704:"693f6ee0",4740:"374b0855",4772:"27c465e4",4776:"40185eb5",4820:"3fa6a9b5",4888:"cf3ee67c",4891:"aca4b983",4901:"87d519de",4920:"fe4c7294",4932:"9650c219",4936:"27299a3b",4944:"9cc140f3",4950:"54828236",4956:"b5a0697e",4964:"9522a145",4968:"a49f1700",4984:"e1c5dcea",4992:"c4cc7857",5016:"e8ac06ec",5108:"1626590c",5120:"05614d2f",5160:"c4023386",5203:"35891e33",5222:"b50f730b",5244:"30644fd9",5248:"b3eefa80",5308:"dae7336e",5316:"4015baac",5328:"2e3626b2",5344:"13f612ac",5420:"8882a989",5456:"826b2a47",5464:"ec13b678",5488:"4a272307",5496:"2457aad7",5544:"2f1df01f",5566:"8fce0176",5572:"7d1f259c",5596:"ef04c7c8",5606:"df6a485b",5620:"f1acbeca",5631:"eb3c6d3c",5660:"881a9bc1",5674:"b221159f",5696:"935f2afb",5728:"2ac485cd",5748:"0d75c27e",5752:"eeb62e5b",5800:"4890df88",5816:"28451e1a",5836:"c2998ab0",5882:"687b8652",5892:"30dc594d",5928:"1f987ab0",5976:"8c90118e",6e3:"c51d05bf",6011:"13d06b8c",6020:"5e76b55a",6092:"47355b93",6136:"8e0e9e2b",6176:"bacc2700",6180:"33b191bc",6262:"9cd87160",6266:"b1c5cbf2",6284:"89918c0c",6320:"23c8336c",6419:"7458314f",6428:"53df34ca",6442:"5ba01c12",6513:"de0ecdfa",6560:"1b1d605d",6568:"f187f30c",6580:"3ffc09c4",6668:"18c33990",6692:"cf7e6749",6720:"0e1a79a5",6724:"6127c255",6752:"17896441",6764:"c16bfc7e",6768:"834e3df2",6796:"8dd1624d",6808:"d055169c",6860:"2a15d269",6864:"5ea00916",6872:"db5d8c25",6876:"4d7c6870",6904:"1647ec57",6956:"28055a57",6964:"5e923e9d",6976:"144669f6",6992:"53fc899c",7040:"31053c79",7108:"450e6318",7110:"738bceb6",7120:"4e202c12",7144:"da105351",7164:"f0ea2bc1",7184:"963bb5ab",7204:"e8e0ef40",7208:"d52025df",7222:"7b0d43d2",7228:"d34bd87a",7243:"fd3f6a3d",7244:"9c985f15",7256:"4bf0522b",7272:"1b07933a",7280:"a1a6e4a2",7312:"959be4b4",7320:"f6807fb4",7332:"98999a3e",7336:"22ecef17",7348:"5ac833b0",7384:"d994236d",7412:"2b026185",7428:"653187f9",7432:"29d4a56b",7440:"0d8578a6",7456:"b65ccb97",7492:"327f2b8d",7516:"4d0c2aca",7540:"a63c68ec",7556:"bd783ed9",7604:"4d96365a",7608:"44a2c628",7688:"3b074962",7720:"8f47f31d",7736:"4610191c",7764:"d5f37c55",7776:"59ee4ecb",7792:"ddec9574",7820:"dae33245",7824:"69bce674",7844:"9bbc65ac",7868:"26084ccc",7872:"f1121274",7968:"3d9aab4f",7988:"28a91100",8008:"c7c4bc80",8040:"fa1390c3",8044:"61086f7d",8056:"9e660ab4",8112:"415f74f4",8120:"96944d2a",8140:"9944c6d1",8180:"3f06413e",8212:"6777c82a",8216:"deb44a68",8264:"14935a59",8268:"27268bf4",8276:"ecd5baba",8292:"8375e767",8368:"e5ac4d56",8376:"8ecb0387",8388:"f32194d4",8392:"4707ca8c",8403:"c3fa5730",8436:"af738211",8476:"a71c172d",8487:"13140ae3",8488:"518c31a4",8502:"62e78712",8532:"018bfed5",8564:"df8fe5f1",8592:"2ddf079f",8620:"525560b7",8632:"55ea0897",8666:"86b21c86",8756:"01255979",8800:"1b728867",8808:"ef35623d",8848:"0688ea12",8888:"7ced6537",8980:"3ce8e004",9e3:"76827885",9008:"9840276f",9020:"92ca3f12",9072:"93746dcf",9076:"94056865",9146:"f7bd22f2",9160:"1f4f2990",9188:"411cd95c",9192:"db1fb617",9220:"19bbdee1",9232:"7dbacb84",9240:"aa32d378",9248:"7768a617",9260:"9e0bb3f7",9300:"2f3ae6a8",9344:"3b382b4c",9380:"9465b6bc",9384:"fba107f3",9404:"df59c461",9424:"4047e3d8",9450:"044a7006",9480:"0aef6e2f",9496:"1abe76cc",9516:"dbbc6b2c",9540:"a8254ede",9548:"b2aa2b43",9568:"70943eb8",9572:"a8bcf301",9620:"bb0f6255",9624:"329f7802",9632:"cb5f486b",9656:"1be78505",9684:"c1bf43f6",9700:"2aaa4378",9720:"bb32ce55",9724:"58f8f5d8",9728:"bab8e2ef",9752:"a4ce67ea",9760:"1d502ec8",9764:"43ff30f2",9784:"1fa6cfaa",9890:"37833312",9936:"568e42b9",9958:"136a2cf4",9976:"bddd0f35",9988:"94009283",9998:"6aabc5ba"}[e]||e)+"."+{64:"e03d843e",66:"a2a3175d",68:"7c140542",76:"2f3d9000",82:"2a5423d2",88:"f7d4bf9d",127:"1d6a9505",148:"ce04d052",192:"fd6826d6",204:"ad77b4ec",224:"7772fd45",240:"b6868629",276:"b7de9d84",300:"cccd7d35",304:"fbf95519",308:"671143b6",340:"318f27cc",384:"498ba1c0",388:"56f0cb36",392:"35bdac05",400:"4396eb91",464:"aa166608",532:"0381da54",556:"b33d6a55",576:"0d707963",592:"2f1afd17",608:"e5e0fd43",640:"aba14ab8",716:"f5900e0e",726:"f197447c",760:"0e887fd3",780:"d0c6391c",784:"bd623f85",792:"61e31e0f",804:"8b672dfc",824:"fbdc73f1",836:"9cd0536c",866:"a4dbd854",940:"35d55f91",984:"182e8ab7",1028:"0eb1472f",1040:"f29019cb",1044:"ce14a136",1144:"32ab05a6",1152:"9920f714",1156:"0b95df45",1236:"f021ef13",1260:"9afecba6",1264:"bf69cc0e",1360:"a3662a76",1380:"27eb7eb5",1424:"58d6faa5",1448:"0791c839",1504:"230ce4cf",1512:"8a41043d",1532:"dc911aa0",1538:"3bdc0b28",1580:"6c016d82",1632:"208267e7",1640:"837e4187",1676:"b182d0d6",1724:"5792d227",1804:"5480251d",1808:"ab36ddc0",1824:"51db2f08",1848:"f5e87e7f",1860:"5a72fd3f",1912:"643ce78d",1932:"ee07d96e",1960:"1594c6f2",2e3:"699756de",2008:"3aaf1f24",2020:"9c57aed8",2030:"5e749070",2032:"9389e9bf",2068:"781a3f32",2134:"ad02c85e",2140:"942ca312",2152:"872dae6c",2164:"84cb0b3c",2168:"d285c023",2180:"3c1a886a",2208:"816a0c7a",2224:"e351b1e9",2240:"bbb57199",2252:"2abf6129",2280:"a79374b0",2288:"26026ac3",2324:"e10dcb78",2352:"96897cd4",2360:"a30bb4e7",2392:"4ca1309f",2492:"94dc7312",2496:"bd1c8254",2504:"764c53b0",2508:"8dcc6b4b",2514:"cdb6dc50",2520:"a9450ec4",2528:"e06e4790",2540:"6c562438",2548:"237fe638",2556:"cbdd35f2",2560:"f2391da7",2592:"f79bfc25",2620:"a49e91d2",2632:"9e99648f",2692:"302f60c4",2704:"29b855cd",2724:"4c24023b",2728:"1130f732",2760:"9a294ff5",2788:"e4f7a7d3",2804:"125db56d",2813:"fdad7756",2844:"5b53eb95",2856:"1fe563d7",2860:"0540c3dc",2900:"314f4d1e",2904:"414c2fba",2908:"9899e33d",2956:"bccbe11f",2968:"0990cec0",2988:"706b078c",3072:"6a03a665",3088:"dd7086b6",3096:"935fdd63",3104:"b6b9a45d",3116:"abb8d70b",3128:"158d4028",3140:"860a029e",3164:"2f79d741",3168:"0909b306",3184:"f7aedccd",3188:"93f0783d",3240:"414ed64e",3264:"b392a7b5",3268:"b1f67458",3284:"3c85b27d",3300:"95181847",3330:"79061c76",3336:"821a5c56",3340:"cebf54b5",3348:"6be50f37",3400:"a1d2b02a",3404:"a4bdb4d0",3408:"8cf76dba",3436:"f2ea5a74",3440:"a897bd73",3448:"cb396bc4",3512:"34d0593e",3548:"d06b6769",3564:"fed6dfcf",3638:"a1e97033",3656:"f11edc6f",3662:"cb95af95",3668:"03ec2f2e",3676:"e7848cf0",3732:"53479fd9",3752:"d9ff558f",3764:"b9026d53",3800:"dfb95537",3808:"e6112df6",3872:"08497ea6",3876:"bd68bdeb",3878:"3312c963",3884:"1ba336e9",3888:"1bf95994",3936:"0951d6c3",3944:"be235f34",3972:"895e18e8",4006:"b60f28ef",4028:"6c371536",4040:"35b13b21",4050:"2fcd8697",4088:"68f7594b",4108:"d0e7efcb",4128:"425cd3d1",4164:"7076e2de",4184:"cf4b1a8b",4196:"d6a9cb1c",4235:"8aa4f1f8",4236:"039d38db",4272:"ff3c3554",4296:"f8f72c84",4300:"5abb1c55",4304:"cfdd4711",4376:"f295987b",4412:"8c636985",4416:"03c614df",4424:"49d57c0a",4432:"9fb2e433",4436:"2708aaf1",4443:"41ed0f79",4464:"8453a132",4466:"60d25c4d",4492:"057ff55e",4536:"6a531817",4540:"4e4cb182",4624:"cf2cd490",4664:"55f90633",4700:"166464d2",4704:"e1fe6f49",4740:"4950ec81",4772:"4f6c8e90",4776:"b4aa1eca",4820:"b5a6b9f9",4888:"82ae85c2",4891:"a3b3efa7",4901:"ac12d733",4920:"8beb6b75",4932:"813cbebd",4936:"93f5a6c6",4944:"88fd156f",4950:"5ae5254a",4956:"3aad476d",4964:"357238f0",4968:"6b974736",4984:"24dc9b4d",4992:"a9f4c793",5016:"5700b32f",5108:"8f5f9a80",5120:"ee6b3e70",5160:"9abfb881",5203:"37464699",5222:"b4b2f3ae",5244:"5758235c",5248:"b3c460a0",5308:"79fa30cd",5316:"6b98909d",5328:"5a9e1a76",5344:"d645b040",5420:"bc1f28ba",5456:"615fb125",5464:"47ac9494",5488:"b13f013e",5496:"3865d741",5544:"8720b9ef",5566:"366af879",5572:"3cd92a5f",5596:"1b2409bd",5606:"ddad1a20",5620:"950a6f83",5631:"59487b1d",5660:"c7c44aae",5674:"ea702f9c",5696:"f1eb8702",5728:"60b84ce1",5748:"846988bb",5752:"e08db8e5",5800:"1c683c72",5816:"ee085898",5836:"0664c43a",5882:"fd7d220b",5892:"d937092d",5928:"236cc305",5976:"10833c6f",6e3:"9973b90b",6011:"deea3b76",6020:"38044a2a",6092:"470a4b8e",6136:"72d78faa",6176:"3514dd94",6180:"7ead1419",6262:"d0f1292f",6266:"c89a7d60",6284:"5d47c466",6320:"41ddab3c",6419:"31fba5b3",6428:"f7d3ad96",6442:"fd5b9d86",6513:"ef782f41",6560:"4a6c51d5",6568:"400f22f2",6579:"5388cc08",6580:"c2453e03",6668:"4ddc7406",6692:"901f95da",6720:"8a9f16a1",6724:"06724ddb",6752:"dd61cb89",6764:"a0617972",6768:"8bb4739b",6796:"fbf17ae0",6808:"400e7bda",6860:"7a363877",6864:"102e1842",6872:"7f1d9688",6876:"7cc387d7",6904:"77f851e7",6956:"5a1f67d4",6964:"0cecdb26",6976:"226d4280",6992:"a530b4b0",7040:"d43d962b",7108:"ca99a9e4",7110:"a4138606",7120:"d6f82427",7144:"86c72098",7164:"3c8d8ede",7184:"c9ec90d4",7204:"81af1be0",7208:"44d941b2",7222:"ffe2646b",7228:"382982f4",7243:"8fd7c45b",7244:"d16a39fb",7256:"0fac1a2c",7272:"66c0a11c",7280:"b267951c",7312:"ee6f90bf",7320:"d362c5fe",7332:"7594329c",7336:"d4512163",7348:"5e0fc434",7384:"a5774bf9",7412:"7656bfed",7428:"fb13f307",7432:"5602b489",7440:"593f400d",7456:"74f458fe",7492:"542b781f",7516:"77a4fbcd",7540:"d77a4e47",7556:"7f76249c",7604:"1f0dfb0e",7608:"1d3eecd3",7688:"7aac35f5",7720:"d76fc684",7736:"7b6d5cc7",7764:"a0cbcab9",7776:"b3deb321",7792:"24db1b25",7820:"e971379b",7824:"a533f647",7844:"9ead78e2",7868:"859d0b47",7872:"65b0ccd8",7968:"563dbc3a",7988:"51232986",8008:"3d19ea33",8040:"53d02a75",8044:"976591e0",8056:"360399f4",8112:"bba7e25c",8120:"ede04743",8140:"e956a914",8180:"638ee0e5",8212:"4bbc8683",8216:"e475845a",8264:"c177acf1",8268:"ce379bdb",8276:"7b522167",8292:"e839c448",8368:"8d0ee5bd",8376:"0b77a5a6",8388:"5e3effd9",8392:"45075116",8403:"fc02a093",8436:"4a8e4069",8476:"973d031d",8487:"2371cdca",8488:"6ca68d19",8502:"08ab2486",8532:"aa370902",8564:"d38ff788",8592:"5baf8af7",8620:"58792d8f",8632:"daa2beca",8666:"fb70c63e",8756:"1b47a78f",8800:"faa9ad6a",8808:"2c2c7e65",8848:"1e279d76",8879:"e84a6a97",8888:"c0030abc",8980:"91af5075",9e3:"2c4ff459",9008:"1a5bfc05",9020:"6f0032f5",9072:"a0cbb623",9076:"85f07285",9146:"2950aab1",9160:"8bcc2ebe",9188:"df287e97",9192:"768ca3cc",9220:"c1f30c23",9232:"0e6c6273",9240:"6cf233c1",9248:"1c899cfc",9260:"105deba4",9300:"28a597e3",9344:"a8e564e8",9380:"d5f98963",9384:"0bbcd97d",9404:"01a5ddca",9424:"dc77bd11",9450:"a7708eac",9480:"e8ca5839",9496:"dbedb6d1",9516:"567a7ecb",9540:"338c6b28",9548:"8186f861",9568:"b09195de",9572:"d49697f3",9620:"7d49434f",9624:"b97a436a",9632:"ac178506",9656:"c22080b2",9684:"a3c2ccff",9700:"622a44d4",9720:"0d349797",9724:"d8fef2af",9728:"6cc5f542",9752:"f528e65a",9760:"d5ccd140",9764:"a314a6d6",9772:"344f2223",9784:"876c4e45",9890:"79d9dc82",9936:"e94b3327",9958:"10df8a44",9976:"763b09e8",9980:"02d2fcef",9988:"3e1bcea1",9998:"625892ff"}[e]+".js",r.miniCssF=e=>{},r.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),r.o=(e,c)=>Object.prototype.hasOwnProperty.call(e,c),f={},b="documentation:",r.l=(e,c,a,d)=>{if(f[e])f[e].push(c);else{var t,o;if(void 0!==a)for(var n=document.getElementsByTagName("script"),i=0;i{t.onerror=t.onload=null,clearTimeout(s);var b=f[e];if(delete f[e],t.parentNode&&t.parentNode.removeChild(t),b&&b.forEach((e=>e(a))),c)return c(a)},s=setTimeout(l.bind(null,void 0,{type:"timeout",target:t}),12e4);t.onerror=l.bind(null,t.onerror),t.onload=l.bind(null,t.onload),o&&document.head.appendChild(t)}},r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.p="/",r.gca=function(e){return e={10691446:"64",17896441:"6752",19031211:"784",20702474:"4088",31874089:"4304",31910907:"2020",37833312:"9890",54071611:"2813",54828236:"4950",76827885:"9000",84164204:"4536",94009283:"9988",94056865:"9076",d85472ef:"66","986c3199":"68","8c9fccb5":"76","5a824d24":"82","245df310":"88",af8bcf34:"127",ad753fe5:"192",fbb5ccd8:"204","3a8a9a02":"224","31a3eb6f":"240","20bb4ef4":"276",ed111213:"300","2c8be122":"304","77c0bb11":"308","2c32570e":"340",a29806db:"384","53f60aaa":"388",abe60d8e:"392",f3323e34:"400","245b95ea":"464","1675a853":"532",ffafab28:"556",f4ed0a4d:"576","41e9fc02":"592",add102e2:"608","63cffe9f":"640",c6d7e964:"716","9dd4b0ef":"726",c21f0827:"760",b8242e5d:"780","4683e51a":"792",bfdf68d4:"804",e5c09629:"824","5040bcd3":"836",a765b786:"866",db5219b9:"940",a291d650:"984","0c2c09cf":"1028",a55de8d9:"1040","6855260d":"1044",d6d55995:"1144","4ce02702":"1152","4ee994fc":"1156","7c71f28e":"1236","38a9fada":"1260","114e21b0":"1264","2529077f":"1360","9f93909f":"1380","03d23ece":"1424","3c75101b":"1448","3919a682":"1504",fdca9218:"1512",ff4e10c4:"1532",ec989c0b:"1538","321c3473":"1580","1c74e866":"1632","69995e4b":"1640","0bfb57e8":"1724",fcdb2c3c:"1804","1958aa06":"1808","77aa4d93":"1824","10f6dc74":"1860","1ebbf8ad":"1912","023af9ff":"1932",fc7b1962:"1960","0d59295f":"2000","2a5e16ec":"2008","8013ae06":"2030","93d36f00":"2032","53ff84c0":"2068","303f2916":"2134","360898bf":"2140","1d2ace06":"2152",e848ef61:"2164","8b4b94e0":"2168",d3ed5dd0:"2180","1db39f58":"2208","4b4e43c1":"2224","540a99bf":"2240",a8497d64:"2252","6459b84b":"2280",c8555b7b:"2288",dbdbfb09:"2324","149faf5c":"2352",b5a6ea24:"2360","99ff9a84":"2392","02e9e60d":"2492",abba3c1a:"2496","38e3e5e2":"2504","3c8cc128":"2508",f981018f:"2514","02dc5f66":"2520","0fdfb154":"2528",e08582aa:"2540","6ca79f64":"2548","92611ec4":"2556","33939c05":"2560",abd7c6e1:"2620",c4f5d8e4:"2632","99dcebb4":"2692",a193a73b:"2704",c8795c05:"2724","28b502c2":"2728","128066bb":"2760","4504fa21":"2788","66b63dfc":"2804","630dcb4d":"2844",b626b9c9:"2856",c417dca4:"2860","36ca3315":"2900","892cf132":"2904",aaf1cba5:"2908","9c612400":"2956","7b4e6d79":"2968",a10ffd78:"2988","427fd2ae":"3072","15e12a2f":"3088","1c647d6d":"3096","90c91afe":"3104",a7d0887a:"3116","9fcd2aaf":"3128","92ea068e":"3140",ace976d6:"3164","7c884429":"3168",b2c1c78c:"3184","66d590de":"3188","6e7ac859":"3240","6095db2e":"3264",e8f086b4:"3268","60321f1e":"3284","9ec0fefb":"3300","8e61a622":"3330","47a03c7f":"3336","2f4205f9":"3340",effa6628:"3348",df203c0f:"3400","57aa3e64":"3404","06abd5b6":"3408","934030ed":"3436",b7b5f722:"3440","9f7214f0":"3448",e63fc1b2:"3512","1746ec49":"3548",aa3c5b40:"3564","1c2e3411":"3638",d72ff326:"3656","40d100db":"3662",fd1ff340:"3668",dbe067dd:"3676","3dd44646":"3732","45778f4a":"3752","18ee9ead":"3764","0b3ceb4d":"3800","9ba65a7f":"3808","67ad9935":"3872",c02ab877:"3876","42656b86":"3878","82d76014":"3884",b9d99630:"3888",da8b188d:"3936","0a783a8b":"3944","2e6078dc":"3972",f2b76d46:"4006",d8325a2c:"4028",cc5c6d64:"4040","76f67042":"4050",d4124539:"4108","33f68dcb":"4128","7c142331":"4164","9d6a5804":"4184","70b24a51":"4196",f119515c:"4235","2af38141":"4236",a247f8ec:"4272","55960ee5":"4296","2f6f9645":"4300","75d21731":"4376","4d3c06b1":"4412","4a69c104":"4416","5ecea96b":"4424",ef449f35:"4432","7c87e584":"4436","92b2ca3c":"4443","491bc18c":"4464","819f8db2":"4466","3720c009":"4492","332d52b4":"4540",b4ea3a21:"4624",c3ec0a52:"4664","956e9f49":"4700","693f6ee0":"4704","374b0855":"4740","27c465e4":"4772","40185eb5":"4776","3fa6a9b5":"4820",cf3ee67c:"4888",aca4b983:"4891","87d519de":"4901",fe4c7294:"4920","9650c219":"4932","27299a3b":"4936","9cc140f3":"4944",b5a0697e:"4956","9522a145":"4964",a49f1700:"4968",e1c5dcea:"4984",c4cc7857:"4992",e8ac06ec:"5016","1626590c":"5108","05614d2f":"5120",c4023386:"5160","35891e33":"5203",b50f730b:"5222","30644fd9":"5244",b3eefa80:"5248",dae7336e:"5308","4015baac":"5316","2e3626b2":"5328","13f612ac":"5344","8882a989":"5420","826b2a47":"5456",ec13b678:"5464","4a272307":"5488","2457aad7":"5496","2f1df01f":"5544","8fce0176":"5566","7d1f259c":"5572",ef04c7c8:"5596",df6a485b:"5606",f1acbeca:"5620",eb3c6d3c:"5631","881a9bc1":"5660",b221159f:"5674","935f2afb":"5696","2ac485cd":"5728","0d75c27e":"5748",eeb62e5b:"5752","4890df88":"5800","28451e1a":"5816",c2998ab0:"5836","687b8652":"5882","30dc594d":"5892","1f987ab0":"5928","8c90118e":"5976",c51d05bf:"6000","13d06b8c":"6011","5e76b55a":"6020","47355b93":"6092","8e0e9e2b":"6136",bacc2700:"6176","33b191bc":"6180","9cd87160":"6262",b1c5cbf2:"6266","89918c0c":"6284","23c8336c":"6320","7458314f":"6419","53df34ca":"6428","5ba01c12":"6442",de0ecdfa:"6513","1b1d605d":"6560",f187f30c:"6568","3ffc09c4":"6580","18c33990":"6668",cf7e6749:"6692","0e1a79a5":"6720","6127c255":"6724",c16bfc7e:"6764","834e3df2":"6768","8dd1624d":"6796",d055169c:"6808","2a15d269":"6860","5ea00916":"6864",db5d8c25:"6872","4d7c6870":"6876","1647ec57":"6904","28055a57":"6956","5e923e9d":"6964","144669f6":"6976","53fc899c":"6992","31053c79":"7040","450e6318":"7108","738bceb6":"7110","4e202c12":"7120",da105351:"7144",f0ea2bc1:"7164","963bb5ab":"7184",e8e0ef40:"7204",d52025df:"7208","7b0d43d2":"7222",d34bd87a:"7228",fd3f6a3d:"7243","9c985f15":"7244","4bf0522b":"7256","1b07933a":"7272",a1a6e4a2:"7280","959be4b4":"7312",f6807fb4:"7320","98999a3e":"7332","22ecef17":"7336","5ac833b0":"7348",d994236d:"7384","2b026185":"7412","653187f9":"7428","29d4a56b":"7432","0d8578a6":"7440",b65ccb97:"7456","327f2b8d":"7492","4d0c2aca":"7516",a63c68ec:"7540",bd783ed9:"7556","4d96365a":"7604","44a2c628":"7608","3b074962":"7688","8f47f31d":"7720","4610191c":"7736",d5f37c55:"7764","59ee4ecb":"7776",ddec9574:"7792",dae33245:"7820","69bce674":"7824","9bbc65ac":"7844","26084ccc":"7868",f1121274:"7872","3d9aab4f":"7968","28a91100":"7988",c7c4bc80:"8008",fa1390c3:"8040","61086f7d":"8044","9e660ab4":"8056","415f74f4":"8112","96944d2a":"8120","9944c6d1":"8140","3f06413e":"8180","6777c82a":"8212",deb44a68:"8216","14935a59":"8264","27268bf4":"8268",ecd5baba:"8276","8375e767":"8292",e5ac4d56:"8368","8ecb0387":"8376",f32194d4:"8388","4707ca8c":"8392",c3fa5730:"8403",af738211:"8436",a71c172d:"8476","13140ae3":"8487","518c31a4":"8488","62e78712":"8502","018bfed5":"8532",df8fe5f1:"8564","2ddf079f":"8592","525560b7":"8620","55ea0897":"8632","86b21c86":"8666","01255979":"8756","1b728867":"8800",ef35623d:"8808","0688ea12":"8848","7ced6537":"8888","3ce8e004":"8980","9840276f":"9008","92ca3f12":"9020","93746dcf":"9072",f7bd22f2:"9146","1f4f2990":"9160","411cd95c":"9188",db1fb617:"9192","19bbdee1":"9220","7dbacb84":"9232",aa32d378:"9240","7768a617":"9248","9e0bb3f7":"9260","2f3ae6a8":"9300","3b382b4c":"9344","9465b6bc":"9380",fba107f3:"9384",df59c461:"9404","4047e3d8":"9424","044a7006":"9450","0aef6e2f":"9480","1abe76cc":"9496",dbbc6b2c:"9516",a8254ede:"9540",b2aa2b43:"9548","70943eb8":"9568",a8bcf301:"9572",bb0f6255:"9620","329f7802":"9624",cb5f486b:"9632","1be78505":"9656",c1bf43f6:"9684","2aaa4378":"9700",bb32ce55:"9720","58f8f5d8":"9724",bab8e2ef:"9728",a4ce67ea:"9752","1d502ec8":"9760","43ff30f2":"9764","1fa6cfaa":"9784","568e42b9":"9936","136a2cf4":"9958",bddd0f35:"9976","6aabc5ba":"9998"}[e]||e,r.p+r.u(e)},(()=>{var e={296:0,2176:0};r.f.j=(c,a)=>{var f=r.o(e,c)?e[c]:void 0;if(0!==f)if(f)a.push(f[2]);else if(/^2(17|9)6$/.test(c))e[c]=0;else{var b=new Promise(((a,b)=>f=e[c]=[a,b]));a.push(f[2]=b);var d=r.p+r.u(c),t=new Error;r.l(d,(a=>{if(r.o(e,c)&&(0!==(f=e[c])&&(e[c]=void 0),f)){var b=a&&("load"===a.type?"missing":a.type),d=a&&a.target&&a.target.src;t.message="Loading chunk "+c+" failed.\n("+b+": "+d+")",t.name="ChunkLoadError",t.type=b,t.request=d,f[1](t)}}),"chunk-"+c,c)}},r.O.j=c=>0===e[c];var c=(c,a)=>{var f,b,d=a[0],t=a[1],o=a[2],n=0;if(d.some((c=>0!==e[c]))){for(f in t)r.o(t,f)&&(r.m[f]=t[f]);if(o)var i=o(r)}for(c&&c(a);n{"use strict";var e,c,a,f,b,d={},t={};function r(e){var c=t[e];if(void 0!==c)return c.exports;var a=t[e]={exports:{}};return d[e].call(a.exports,a,a.exports,r),a.exports}r.m=d,e=[],r.O=(c,a,f,b)=>{if(!a){var d=1/0;for(i=0;i=b)&&Object.keys(r.O).every((e=>r.O[e](a[o])))?a.splice(o--,1):(t=!1,b0&&e[i-1][2]>b;i--)e[i]=e[i-1];e[i]=[a,f,b]},r.n=e=>{var c=e&&e.__esModule?()=>e.default:()=>e;return r.d(c,{a:c}),c},a=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,r.t=function(e,f){if(1&f&&(e=this(e)),8&f)return e;if("object"==typeof e&&e){if(4&f&&e.__esModule)return e;if(16&f&&"function"==typeof e.then)return e}var b=Object.create(null);r.r(b);var d={};c=c||[null,a({}),a([]),a(a)];for(var t=2&f&&e;"object"==typeof t&&!~c.indexOf(t);t=a(t))Object.getOwnPropertyNames(t).forEach((c=>d[c]=()=>e[c]));return d.default=()=>e,r.d(b,d),b},r.d=(e,c)=>{for(var a in c)r.o(c,a)&&!r.o(e,a)&&Object.defineProperty(e,a,{enumerable:!0,get:c[a]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce(((c,a)=>(r.f[a](e,c),c)),[])),r.u=e=>"assets/js/"+({64:"10691446",66:"d85472ef",68:"986c3199",76:"8c9fccb5",82:"5a824d24",88:"245df310",127:"af8bcf34",192:"ad753fe5",204:"fbb5ccd8",224:"3a8a9a02",240:"31a3eb6f",276:"20bb4ef4",300:"ed111213",304:"2c8be122",308:"77c0bb11",340:"2c32570e",384:"a29806db",388:"53f60aaa",392:"abe60d8e",400:"f3323e34",464:"245b95ea",532:"1675a853",556:"ffafab28",576:"f4ed0a4d",592:"41e9fc02",608:"add102e2",640:"63cffe9f",716:"c6d7e964",726:"9dd4b0ef",760:"c21f0827",780:"b8242e5d",784:"19031211",792:"4683e51a",804:"bfdf68d4",824:"e5c09629",836:"5040bcd3",866:"a765b786",940:"db5219b9",984:"a291d650",1028:"0c2c09cf",1040:"a55de8d9",1044:"6855260d",1144:"d6d55995",1152:"4ce02702",1156:"4ee994fc",1236:"7c71f28e",1260:"38a9fada",1264:"114e21b0",1360:"2529077f",1380:"9f93909f",1424:"03d23ece",1448:"3c75101b",1504:"3919a682",1512:"fdca9218",1532:"ff4e10c4",1538:"ec989c0b",1580:"321c3473",1632:"1c74e866",1640:"69995e4b",1724:"0bfb57e8",1804:"fcdb2c3c",1808:"1958aa06",1824:"77aa4d93",1860:"10f6dc74",1912:"1ebbf8ad",1932:"023af9ff",1960:"fc7b1962",2e3:"0d59295f",2008:"2a5e16ec",2020:"31910907",2030:"8013ae06",2032:"93d36f00",2068:"53ff84c0",2134:"303f2916",2140:"360898bf",2152:"1d2ace06",2164:"e848ef61",2168:"8b4b94e0",2180:"d3ed5dd0",2208:"1db39f58",2224:"4b4e43c1",2240:"540a99bf",2252:"a8497d64",2280:"6459b84b",2288:"c8555b7b",2324:"dbdbfb09",2352:"149faf5c",2360:"b5a6ea24",2392:"99ff9a84",2492:"02e9e60d",2496:"abba3c1a",2504:"38e3e5e2",2508:"3c8cc128",2514:"f981018f",2520:"02dc5f66",2528:"0fdfb154",2540:"e08582aa",2548:"6ca79f64",2556:"92611ec4",2560:"33939c05",2620:"abd7c6e1",2632:"c4f5d8e4",2692:"99dcebb4",2704:"a193a73b",2724:"c8795c05",2728:"28b502c2",2760:"128066bb",2788:"4504fa21",2804:"66b63dfc",2813:"54071611",2844:"630dcb4d",2856:"b626b9c9",2860:"c417dca4",2900:"36ca3315",2904:"892cf132",2908:"aaf1cba5",2956:"9c612400",2968:"7b4e6d79",2988:"a10ffd78",3072:"427fd2ae",3088:"15e12a2f",3096:"1c647d6d",3104:"90c91afe",3116:"a7d0887a",3128:"9fcd2aaf",3140:"92ea068e",3164:"ace976d6",3168:"7c884429",3184:"b2c1c78c",3188:"66d590de",3240:"6e7ac859",3264:"6095db2e",3268:"e8f086b4",3284:"60321f1e",3300:"9ec0fefb",3330:"8e61a622",3336:"47a03c7f",3340:"2f4205f9",3348:"effa6628",3400:"df203c0f",3404:"57aa3e64",3408:"06abd5b6",3436:"934030ed",3440:"b7b5f722",3448:"9f7214f0",3512:"e63fc1b2",3548:"1746ec49",3564:"aa3c5b40",3638:"1c2e3411",3656:"d72ff326",3662:"40d100db",3668:"fd1ff340",3676:"dbe067dd",3732:"3dd44646",3752:"45778f4a",3764:"18ee9ead",3800:"0b3ceb4d",3808:"9ba65a7f",3872:"67ad9935",3876:"c02ab877",3878:"42656b86",3884:"82d76014",3888:"b9d99630",3936:"da8b188d",3944:"0a783a8b",3972:"2e6078dc",4006:"f2b76d46",4028:"d8325a2c",4040:"cc5c6d64",4050:"76f67042",4088:"20702474",4108:"d4124539",4128:"33f68dcb",4164:"7c142331",4184:"9d6a5804",4196:"70b24a51",4235:"f119515c",4236:"2af38141",4272:"a247f8ec",4296:"55960ee5",4300:"2f6f9645",4304:"31874089",4376:"75d21731",4412:"4d3c06b1",4416:"4a69c104",4424:"5ecea96b",4432:"ef449f35",4436:"7c87e584",4443:"92b2ca3c",4464:"491bc18c",4466:"819f8db2",4492:"3720c009",4536:"84164204",4540:"332d52b4",4624:"b4ea3a21",4664:"c3ec0a52",4700:"956e9f49",4704:"693f6ee0",4740:"374b0855",4772:"27c465e4",4776:"40185eb5",4820:"3fa6a9b5",4888:"cf3ee67c",4891:"aca4b983",4901:"87d519de",4920:"fe4c7294",4932:"9650c219",4936:"27299a3b",4944:"9cc140f3",4950:"54828236",4956:"b5a0697e",4964:"9522a145",4968:"a49f1700",4984:"e1c5dcea",4992:"c4cc7857",5016:"e8ac06ec",5108:"1626590c",5120:"05614d2f",5160:"c4023386",5203:"35891e33",5222:"b50f730b",5244:"30644fd9",5248:"b3eefa80",5308:"dae7336e",5316:"4015baac",5328:"2e3626b2",5344:"13f612ac",5420:"8882a989",5456:"826b2a47",5464:"ec13b678",5488:"4a272307",5496:"2457aad7",5544:"2f1df01f",5566:"8fce0176",5572:"7d1f259c",5596:"ef04c7c8",5606:"df6a485b",5620:"f1acbeca",5631:"eb3c6d3c",5660:"881a9bc1",5674:"b221159f",5696:"935f2afb",5728:"2ac485cd",5748:"0d75c27e",5752:"eeb62e5b",5800:"4890df88",5816:"28451e1a",5836:"c2998ab0",5882:"687b8652",5892:"30dc594d",5928:"1f987ab0",5976:"8c90118e",6e3:"c51d05bf",6011:"13d06b8c",6020:"5e76b55a",6092:"47355b93",6136:"8e0e9e2b",6176:"bacc2700",6180:"33b191bc",6262:"9cd87160",6266:"b1c5cbf2",6284:"89918c0c",6320:"23c8336c",6419:"7458314f",6428:"53df34ca",6442:"5ba01c12",6513:"de0ecdfa",6560:"1b1d605d",6568:"f187f30c",6580:"3ffc09c4",6668:"18c33990",6692:"cf7e6749",6720:"0e1a79a5",6724:"6127c255",6752:"17896441",6764:"c16bfc7e",6768:"834e3df2",6796:"8dd1624d",6808:"d055169c",6860:"2a15d269",6864:"5ea00916",6872:"db5d8c25",6876:"4d7c6870",6904:"1647ec57",6956:"28055a57",6964:"5e923e9d",6976:"144669f6",6992:"53fc899c",7040:"31053c79",7108:"450e6318",7110:"738bceb6",7120:"4e202c12",7144:"da105351",7164:"f0ea2bc1",7184:"963bb5ab",7204:"e8e0ef40",7208:"d52025df",7222:"7b0d43d2",7228:"d34bd87a",7243:"fd3f6a3d",7244:"9c985f15",7256:"4bf0522b",7272:"1b07933a",7280:"a1a6e4a2",7312:"959be4b4",7320:"f6807fb4",7332:"98999a3e",7336:"22ecef17",7348:"5ac833b0",7384:"d994236d",7412:"2b026185",7428:"653187f9",7432:"29d4a56b",7440:"0d8578a6",7456:"b65ccb97",7492:"327f2b8d",7516:"4d0c2aca",7540:"a63c68ec",7556:"bd783ed9",7604:"4d96365a",7608:"44a2c628",7688:"3b074962",7720:"8f47f31d",7736:"4610191c",7764:"d5f37c55",7776:"59ee4ecb",7792:"ddec9574",7820:"dae33245",7824:"69bce674",7844:"9bbc65ac",7868:"26084ccc",7872:"f1121274",7968:"3d9aab4f",7988:"28a91100",8008:"c7c4bc80",8040:"fa1390c3",8044:"61086f7d",8056:"9e660ab4",8112:"415f74f4",8120:"96944d2a",8140:"9944c6d1",8180:"3f06413e",8212:"6777c82a",8216:"deb44a68",8264:"14935a59",8268:"27268bf4",8276:"ecd5baba",8292:"8375e767",8368:"e5ac4d56",8376:"8ecb0387",8388:"f32194d4",8392:"4707ca8c",8403:"c3fa5730",8436:"af738211",8476:"a71c172d",8487:"13140ae3",8488:"518c31a4",8502:"62e78712",8532:"018bfed5",8564:"df8fe5f1",8592:"2ddf079f",8620:"525560b7",8632:"55ea0897",8666:"86b21c86",8756:"01255979",8800:"1b728867",8808:"ef35623d",8848:"0688ea12",8888:"7ced6537",8980:"3ce8e004",9e3:"76827885",9008:"9840276f",9020:"92ca3f12",9072:"93746dcf",9076:"94056865",9146:"f7bd22f2",9160:"1f4f2990",9188:"411cd95c",9192:"db1fb617",9220:"19bbdee1",9232:"7dbacb84",9240:"aa32d378",9248:"7768a617",9260:"9e0bb3f7",9300:"2f3ae6a8",9344:"3b382b4c",9380:"9465b6bc",9384:"fba107f3",9404:"df59c461",9424:"4047e3d8",9450:"044a7006",9480:"0aef6e2f",9496:"1abe76cc",9516:"dbbc6b2c",9540:"a8254ede",9548:"b2aa2b43",9568:"70943eb8",9572:"a8bcf301",9620:"bb0f6255",9624:"329f7802",9632:"cb5f486b",9656:"1be78505",9684:"c1bf43f6",9700:"2aaa4378",9720:"bb32ce55",9724:"58f8f5d8",9728:"bab8e2ef",9752:"a4ce67ea",9760:"1d502ec8",9764:"43ff30f2",9784:"1fa6cfaa",9890:"37833312",9936:"568e42b9",9958:"136a2cf4",9976:"bddd0f35",9988:"94009283",9998:"6aabc5ba"}[e]||e)+"."+{64:"e03d843e",66:"a2a3175d",68:"7c140542",76:"2f3d9000",82:"2a5423d2",88:"f7d4bf9d",127:"1d6a9505",148:"ce04d052",192:"fd6826d6",204:"ad77b4ec",224:"7772fd45",240:"b6868629",276:"b7de9d84",300:"cccd7d35",304:"fbf95519",308:"671143b6",340:"318f27cc",384:"498ba1c0",388:"56f0cb36",392:"35bdac05",400:"4396eb91",464:"aa166608",532:"0381da54",556:"b33d6a55",576:"0d707963",592:"2f1afd17",608:"e5e0fd43",640:"aba14ab8",716:"f5900e0e",726:"f197447c",760:"0e887fd3",780:"d0c6391c",784:"bd623f85",792:"61e31e0f",804:"8b672dfc",824:"fbdc73f1",836:"9cd0536c",866:"a4dbd854",940:"35d55f91",984:"182e8ab7",1028:"0eb1472f",1040:"f29019cb",1044:"ce14a136",1144:"32ab05a6",1152:"9920f714",1156:"0b95df45",1236:"f021ef13",1260:"9afecba6",1264:"bf69cc0e",1360:"a3662a76",1380:"27eb7eb5",1424:"58d6faa5",1448:"0791c839",1504:"230ce4cf",1512:"8a41043d",1532:"dc911aa0",1538:"3bdc0b28",1580:"6c016d82",1632:"208267e7",1640:"837e4187",1676:"b182d0d6",1724:"5792d227",1804:"5480251d",1808:"ab36ddc0",1824:"51db2f08",1848:"f5e87e7f",1860:"5a72fd3f",1912:"643ce78d",1932:"ee07d96e",1960:"1594c6f2",2e3:"699756de",2008:"3aaf1f24",2020:"9c57aed8",2030:"5e749070",2032:"9389e9bf",2068:"781a3f32",2134:"ad02c85e",2140:"942ca312",2152:"872dae6c",2164:"84cb0b3c",2168:"d285c023",2180:"3c1a886a",2208:"816a0c7a",2224:"e351b1e9",2240:"bbb57199",2252:"2abf6129",2280:"a79374b0",2288:"26026ac3",2324:"e10dcb78",2352:"96897cd4",2360:"a30bb4e7",2392:"4ca1309f",2492:"94dc7312",2496:"bd1c8254",2504:"764c53b0",2508:"8dcc6b4b",2514:"cdb6dc50",2520:"a9450ec4",2528:"e06e4790",2540:"6c562438",2548:"237fe638",2556:"cbdd35f2",2560:"f2391da7",2592:"f79bfc25",2620:"a49e91d2",2632:"9e99648f",2692:"302f60c4",2704:"29b855cd",2724:"4c24023b",2728:"1130f732",2760:"9a294ff5",2788:"e4f7a7d3",2804:"125db56d",2813:"fdad7756",2844:"5b53eb95",2856:"1fe563d7",2860:"0540c3dc",2900:"314f4d1e",2904:"414c2fba",2908:"9899e33d",2956:"bccbe11f",2968:"0990cec0",2988:"706b078c",3072:"6a03a665",3088:"dd7086b6",3096:"935fdd63",3104:"b6b9a45d",3116:"abb8d70b",3128:"158d4028",3140:"860a029e",3164:"2f79d741",3168:"0909b306",3184:"f7aedccd",3188:"eb8f2ff4",3240:"414ed64e",3264:"b392a7b5",3268:"b1f67458",3284:"3c85b27d",3300:"95181847",3330:"79061c76",3336:"821a5c56",3340:"cebf54b5",3348:"6be50f37",3400:"a1d2b02a",3404:"a4bdb4d0",3408:"8cf76dba",3436:"f2ea5a74",3440:"a897bd73",3448:"cb396bc4",3512:"34d0593e",3548:"d06b6769",3564:"fed6dfcf",3638:"a1e97033",3656:"f11edc6f",3662:"cb95af95",3668:"03ec2f2e",3676:"e7848cf0",3732:"53479fd9",3752:"d9ff558f",3764:"b9026d53",3800:"dfb95537",3808:"e6112df6",3872:"08497ea6",3876:"bd68bdeb",3878:"3312c963",3884:"1ba336e9",3888:"1bf95994",3936:"0951d6c3",3944:"be235f34",3972:"895e18e8",4006:"b60f28ef",4028:"6c371536",4040:"35b13b21",4050:"2fcd8697",4088:"68f7594b",4108:"d0e7efcb",4128:"425cd3d1",4164:"7076e2de",4184:"cf4b1a8b",4196:"d6a9cb1c",4235:"8aa4f1f8",4236:"039d38db",4272:"ff3c3554",4296:"f8f72c84",4300:"5abb1c55",4304:"cfdd4711",4376:"f295987b",4412:"8c636985",4416:"03c614df",4424:"49d57c0a",4432:"9fb2e433",4436:"2708aaf1",4443:"41ed0f79",4464:"8453a132",4466:"60d25c4d",4492:"057ff55e",4536:"6a531817",4540:"4e4cb182",4624:"cf2cd490",4664:"55f90633",4700:"166464d2",4704:"e1fe6f49",4740:"4950ec81",4772:"4f6c8e90",4776:"b4aa1eca",4820:"b5a6b9f9",4888:"82ae85c2",4891:"a3b3efa7",4901:"ac12d733",4920:"8beb6b75",4932:"813cbebd",4936:"93f5a6c6",4944:"88fd156f",4950:"5ae5254a",4956:"3aad476d",4964:"357238f0",4968:"6b974736",4984:"24dc9b4d",4992:"a9f4c793",5016:"5700b32f",5108:"8f5f9a80",5120:"ee6b3e70",5160:"9abfb881",5203:"37464699",5222:"b4b2f3ae",5244:"5758235c",5248:"b3c460a0",5308:"79fa30cd",5316:"6b98909d",5328:"1acba910",5344:"d645b040",5420:"bc1f28ba",5456:"615fb125",5464:"47ac9494",5488:"b13f013e",5496:"3865d741",5544:"8720b9ef",5566:"366af879",5572:"3cd92a5f",5596:"1b2409bd",5606:"ddad1a20",5620:"950a6f83",5631:"59487b1d",5660:"c7c44aae",5674:"ea702f9c",5696:"f1eb8702",5728:"60b84ce1",5748:"846988bb",5752:"e08db8e5",5800:"1c683c72",5816:"ee085898",5836:"0664c43a",5882:"fd7d220b",5892:"d937092d",5928:"236cc305",5976:"10833c6f",6e3:"9973b90b",6011:"16ae6aff",6020:"38044a2a",6092:"470a4b8e",6136:"72d78faa",6176:"3514dd94",6180:"7ead1419",6262:"d0f1292f",6266:"c89a7d60",6284:"5d47c466",6320:"41ddab3c",6419:"31fba5b3",6428:"f7d3ad96",6442:"fd5b9d86",6513:"ef782f41",6560:"4a6c51d5",6568:"400f22f2",6579:"5388cc08",6580:"0e2414c3",6668:"4ddc7406",6692:"901f95da",6720:"8a9f16a1",6724:"06724ddb",6752:"dd61cb89",6764:"a0617972",6768:"8bb4739b",6796:"fbf17ae0",6808:"400e7bda",6860:"7a363877",6864:"102e1842",6872:"7f1d9688",6876:"7cc387d7",6904:"77f851e7",6956:"5a1f67d4",6964:"0cecdb26",6976:"226d4280",6992:"a530b4b0",7040:"d43d962b",7108:"ca99a9e4",7110:"a4138606",7120:"d6f82427",7144:"86c72098",7164:"3c8d8ede",7184:"c9ec90d4",7204:"81af1be0",7208:"44d941b2",7222:"ffe2646b",7228:"382982f4",7243:"8fd7c45b",7244:"d16a39fb",7256:"0fac1a2c",7272:"66c0a11c",7280:"b267951c",7312:"ee6f90bf",7320:"d362c5fe",7332:"7594329c",7336:"d4512163",7348:"5e0fc434",7384:"a5774bf9",7412:"7656bfed",7428:"fb13f307",7432:"5602b489",7440:"593f400d",7456:"74f458fe",7492:"542b781f",7516:"77a4fbcd",7540:"d77a4e47",7556:"7f76249c",7604:"1f0dfb0e",7608:"1d3eecd3",7688:"7aac35f5",7720:"d76fc684",7736:"7b6d5cc7",7764:"a0cbcab9",7776:"b3deb321",7792:"24db1b25",7820:"e971379b",7824:"a533f647",7844:"9ead78e2",7868:"859d0b47",7872:"65b0ccd8",7968:"563dbc3a",7988:"51232986",8008:"3d19ea33",8040:"53d02a75",8044:"976591e0",8056:"360399f4",8112:"bba7e25c",8120:"ede04743",8140:"e956a914",8180:"638ee0e5",8212:"4bbc8683",8216:"e475845a",8264:"c177acf1",8268:"ce379bdb",8276:"7b522167",8292:"e839c448",8368:"8d0ee5bd",8376:"0b77a5a6",8388:"5e3effd9",8392:"45075116",8403:"fc02a093",8436:"4a8e4069",8476:"973d031d",8487:"2371cdca",8488:"6ca68d19",8502:"08ab2486",8532:"aa370902",8564:"d38ff788",8592:"5baf8af7",8620:"58792d8f",8632:"daa2beca",8666:"fb70c63e",8756:"1b47a78f",8800:"faa9ad6a",8808:"2c2c7e65",8848:"1e279d76",8879:"e84a6a97",8888:"c0030abc",8980:"91af5075",9e3:"2c4ff459",9008:"1a5bfc05",9020:"6f0032f5",9072:"a0cbb623",9076:"85f07285",9146:"2950aab1",9160:"8bcc2ebe",9188:"df287e97",9192:"768ca3cc",9220:"c1f30c23",9232:"0e6c6273",9240:"6cf233c1",9248:"1c899cfc",9260:"105deba4",9300:"28a597e3",9344:"a8e564e8",9380:"d5f98963",9384:"0bbcd97d",9404:"01a5ddca",9424:"dc77bd11",9450:"a7708eac",9480:"e8ca5839",9496:"dbedb6d1",9516:"567a7ecb",9540:"338c6b28",9548:"8186f861",9568:"b09195de",9572:"d49697f3",9620:"7d49434f",9624:"b97a436a",9632:"ac178506",9656:"c22080b2",9684:"a3c2ccff",9700:"622a44d4",9720:"0d349797",9724:"d8fef2af",9728:"6cc5f542",9752:"f528e65a",9760:"d5ccd140",9764:"a314a6d6",9772:"344f2223",9784:"876c4e45",9890:"79d9dc82",9936:"e94b3327",9958:"10df8a44",9976:"763b09e8",9980:"02d2fcef",9988:"3e1bcea1",9998:"625892ff"}[e]+".js",r.miniCssF=e=>{},r.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),r.o=(e,c)=>Object.prototype.hasOwnProperty.call(e,c),f={},b="documentation:",r.l=(e,c,a,d)=>{if(f[e])f[e].push(c);else{var t,o;if(void 0!==a)for(var n=document.getElementsByTagName("script"),i=0;i{t.onerror=t.onload=null,clearTimeout(s);var b=f[e];if(delete f[e],t.parentNode&&t.parentNode.removeChild(t),b&&b.forEach((e=>e(a))),c)return c(a)},s=setTimeout(l.bind(null,void 0,{type:"timeout",target:t}),12e4);t.onerror=l.bind(null,t.onerror),t.onload=l.bind(null,t.onload),o&&document.head.appendChild(t)}},r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.p="/",r.gca=function(e){return e={10691446:"64",17896441:"6752",19031211:"784",20702474:"4088",31874089:"4304",31910907:"2020",37833312:"9890",54071611:"2813",54828236:"4950",76827885:"9000",84164204:"4536",94009283:"9988",94056865:"9076",d85472ef:"66","986c3199":"68","8c9fccb5":"76","5a824d24":"82","245df310":"88",af8bcf34:"127",ad753fe5:"192",fbb5ccd8:"204","3a8a9a02":"224","31a3eb6f":"240","20bb4ef4":"276",ed111213:"300","2c8be122":"304","77c0bb11":"308","2c32570e":"340",a29806db:"384","53f60aaa":"388",abe60d8e:"392",f3323e34:"400","245b95ea":"464","1675a853":"532",ffafab28:"556",f4ed0a4d:"576","41e9fc02":"592",add102e2:"608","63cffe9f":"640",c6d7e964:"716","9dd4b0ef":"726",c21f0827:"760",b8242e5d:"780","4683e51a":"792",bfdf68d4:"804",e5c09629:"824","5040bcd3":"836",a765b786:"866",db5219b9:"940",a291d650:"984","0c2c09cf":"1028",a55de8d9:"1040","6855260d":"1044",d6d55995:"1144","4ce02702":"1152","4ee994fc":"1156","7c71f28e":"1236","38a9fada":"1260","114e21b0":"1264","2529077f":"1360","9f93909f":"1380","03d23ece":"1424","3c75101b":"1448","3919a682":"1504",fdca9218:"1512",ff4e10c4:"1532",ec989c0b:"1538","321c3473":"1580","1c74e866":"1632","69995e4b":"1640","0bfb57e8":"1724",fcdb2c3c:"1804","1958aa06":"1808","77aa4d93":"1824","10f6dc74":"1860","1ebbf8ad":"1912","023af9ff":"1932",fc7b1962:"1960","0d59295f":"2000","2a5e16ec":"2008","8013ae06":"2030","93d36f00":"2032","53ff84c0":"2068","303f2916":"2134","360898bf":"2140","1d2ace06":"2152",e848ef61:"2164","8b4b94e0":"2168",d3ed5dd0:"2180","1db39f58":"2208","4b4e43c1":"2224","540a99bf":"2240",a8497d64:"2252","6459b84b":"2280",c8555b7b:"2288",dbdbfb09:"2324","149faf5c":"2352",b5a6ea24:"2360","99ff9a84":"2392","02e9e60d":"2492",abba3c1a:"2496","38e3e5e2":"2504","3c8cc128":"2508",f981018f:"2514","02dc5f66":"2520","0fdfb154":"2528",e08582aa:"2540","6ca79f64":"2548","92611ec4":"2556","33939c05":"2560",abd7c6e1:"2620",c4f5d8e4:"2632","99dcebb4":"2692",a193a73b:"2704",c8795c05:"2724","28b502c2":"2728","128066bb":"2760","4504fa21":"2788","66b63dfc":"2804","630dcb4d":"2844",b626b9c9:"2856",c417dca4:"2860","36ca3315":"2900","892cf132":"2904",aaf1cba5:"2908","9c612400":"2956","7b4e6d79":"2968",a10ffd78:"2988","427fd2ae":"3072","15e12a2f":"3088","1c647d6d":"3096","90c91afe":"3104",a7d0887a:"3116","9fcd2aaf":"3128","92ea068e":"3140",ace976d6:"3164","7c884429":"3168",b2c1c78c:"3184","66d590de":"3188","6e7ac859":"3240","6095db2e":"3264",e8f086b4:"3268","60321f1e":"3284","9ec0fefb":"3300","8e61a622":"3330","47a03c7f":"3336","2f4205f9":"3340",effa6628:"3348",df203c0f:"3400","57aa3e64":"3404","06abd5b6":"3408","934030ed":"3436",b7b5f722:"3440","9f7214f0":"3448",e63fc1b2:"3512","1746ec49":"3548",aa3c5b40:"3564","1c2e3411":"3638",d72ff326:"3656","40d100db":"3662",fd1ff340:"3668",dbe067dd:"3676","3dd44646":"3732","45778f4a":"3752","18ee9ead":"3764","0b3ceb4d":"3800","9ba65a7f":"3808","67ad9935":"3872",c02ab877:"3876","42656b86":"3878","82d76014":"3884",b9d99630:"3888",da8b188d:"3936","0a783a8b":"3944","2e6078dc":"3972",f2b76d46:"4006",d8325a2c:"4028",cc5c6d64:"4040","76f67042":"4050",d4124539:"4108","33f68dcb":"4128","7c142331":"4164","9d6a5804":"4184","70b24a51":"4196",f119515c:"4235","2af38141":"4236",a247f8ec:"4272","55960ee5":"4296","2f6f9645":"4300","75d21731":"4376","4d3c06b1":"4412","4a69c104":"4416","5ecea96b":"4424",ef449f35:"4432","7c87e584":"4436","92b2ca3c":"4443","491bc18c":"4464","819f8db2":"4466","3720c009":"4492","332d52b4":"4540",b4ea3a21:"4624",c3ec0a52:"4664","956e9f49":"4700","693f6ee0":"4704","374b0855":"4740","27c465e4":"4772","40185eb5":"4776","3fa6a9b5":"4820",cf3ee67c:"4888",aca4b983:"4891","87d519de":"4901",fe4c7294:"4920","9650c219":"4932","27299a3b":"4936","9cc140f3":"4944",b5a0697e:"4956","9522a145":"4964",a49f1700:"4968",e1c5dcea:"4984",c4cc7857:"4992",e8ac06ec:"5016","1626590c":"5108","05614d2f":"5120",c4023386:"5160","35891e33":"5203",b50f730b:"5222","30644fd9":"5244",b3eefa80:"5248",dae7336e:"5308","4015baac":"5316","2e3626b2":"5328","13f612ac":"5344","8882a989":"5420","826b2a47":"5456",ec13b678:"5464","4a272307":"5488","2457aad7":"5496","2f1df01f":"5544","8fce0176":"5566","7d1f259c":"5572",ef04c7c8:"5596",df6a485b:"5606",f1acbeca:"5620",eb3c6d3c:"5631","881a9bc1":"5660",b221159f:"5674","935f2afb":"5696","2ac485cd":"5728","0d75c27e":"5748",eeb62e5b:"5752","4890df88":"5800","28451e1a":"5816",c2998ab0:"5836","687b8652":"5882","30dc594d":"5892","1f987ab0":"5928","8c90118e":"5976",c51d05bf:"6000","13d06b8c":"6011","5e76b55a":"6020","47355b93":"6092","8e0e9e2b":"6136",bacc2700:"6176","33b191bc":"6180","9cd87160":"6262",b1c5cbf2:"6266","89918c0c":"6284","23c8336c":"6320","7458314f":"6419","53df34ca":"6428","5ba01c12":"6442",de0ecdfa:"6513","1b1d605d":"6560",f187f30c:"6568","3ffc09c4":"6580","18c33990":"6668",cf7e6749:"6692","0e1a79a5":"6720","6127c255":"6724",c16bfc7e:"6764","834e3df2":"6768","8dd1624d":"6796",d055169c:"6808","2a15d269":"6860","5ea00916":"6864",db5d8c25:"6872","4d7c6870":"6876","1647ec57":"6904","28055a57":"6956","5e923e9d":"6964","144669f6":"6976","53fc899c":"6992","31053c79":"7040","450e6318":"7108","738bceb6":"7110","4e202c12":"7120",da105351:"7144",f0ea2bc1:"7164","963bb5ab":"7184",e8e0ef40:"7204",d52025df:"7208","7b0d43d2":"7222",d34bd87a:"7228",fd3f6a3d:"7243","9c985f15":"7244","4bf0522b":"7256","1b07933a":"7272",a1a6e4a2:"7280","959be4b4":"7312",f6807fb4:"7320","98999a3e":"7332","22ecef17":"7336","5ac833b0":"7348",d994236d:"7384","2b026185":"7412","653187f9":"7428","29d4a56b":"7432","0d8578a6":"7440",b65ccb97:"7456","327f2b8d":"7492","4d0c2aca":"7516",a63c68ec:"7540",bd783ed9:"7556","4d96365a":"7604","44a2c628":"7608","3b074962":"7688","8f47f31d":"7720","4610191c":"7736",d5f37c55:"7764","59ee4ecb":"7776",ddec9574:"7792",dae33245:"7820","69bce674":"7824","9bbc65ac":"7844","26084ccc":"7868",f1121274:"7872","3d9aab4f":"7968","28a91100":"7988",c7c4bc80:"8008",fa1390c3:"8040","61086f7d":"8044","9e660ab4":"8056","415f74f4":"8112","96944d2a":"8120","9944c6d1":"8140","3f06413e":"8180","6777c82a":"8212",deb44a68:"8216","14935a59":"8264","27268bf4":"8268",ecd5baba:"8276","8375e767":"8292",e5ac4d56:"8368","8ecb0387":"8376",f32194d4:"8388","4707ca8c":"8392",c3fa5730:"8403",af738211:"8436",a71c172d:"8476","13140ae3":"8487","518c31a4":"8488","62e78712":"8502","018bfed5":"8532",df8fe5f1:"8564","2ddf079f":"8592","525560b7":"8620","55ea0897":"8632","86b21c86":"8666","01255979":"8756","1b728867":"8800",ef35623d:"8808","0688ea12":"8848","7ced6537":"8888","3ce8e004":"8980","9840276f":"9008","92ca3f12":"9020","93746dcf":"9072",f7bd22f2:"9146","1f4f2990":"9160","411cd95c":"9188",db1fb617:"9192","19bbdee1":"9220","7dbacb84":"9232",aa32d378:"9240","7768a617":"9248","9e0bb3f7":"9260","2f3ae6a8":"9300","3b382b4c":"9344","9465b6bc":"9380",fba107f3:"9384",df59c461:"9404","4047e3d8":"9424","044a7006":"9450","0aef6e2f":"9480","1abe76cc":"9496",dbbc6b2c:"9516",a8254ede:"9540",b2aa2b43:"9548","70943eb8":"9568",a8bcf301:"9572",bb0f6255:"9620","329f7802":"9624",cb5f486b:"9632","1be78505":"9656",c1bf43f6:"9684","2aaa4378":"9700",bb32ce55:"9720","58f8f5d8":"9724",bab8e2ef:"9728",a4ce67ea:"9752","1d502ec8":"9760","43ff30f2":"9764","1fa6cfaa":"9784","568e42b9":"9936","136a2cf4":"9958",bddd0f35:"9976","6aabc5ba":"9998"}[e]||e,r.p+r.u(e)},(()=>{var e={296:0,2176:0};r.f.j=(c,a)=>{var f=r.o(e,c)?e[c]:void 0;if(0!==f)if(f)a.push(f[2]);else if(/^2(17|9)6$/.test(c))e[c]=0;else{var b=new Promise(((a,b)=>f=e[c]=[a,b]));a.push(f[2]=b);var d=r.p+r.u(c),t=new Error;r.l(d,(a=>{if(r.o(e,c)&&(0!==(f=e[c])&&(e[c]=void 0),f)){var b=a&&("load"===a.type?"missing":a.type),d=a&&a.target&&a.target.src;t.message="Loading chunk "+c+" failed.\n("+b+": "+d+")",t.name="ChunkLoadError",t.type=b,t.request=d,f[1](t)}}),"chunk-"+c,c)}},r.O.j=c=>0===e[c];var c=(c,a)=>{var f,b,d=a[0],t=a[1],o=a[2],n=0;if(d.some((c=>0!==e[c]))){for(f in t)r.o(t,f)&&(r.m[f]=t[f]);if(o)var i=o(r)}for(c&&c(a);n Debugger | Apiato - + @@ -12,7 +12,7 @@
Version: 10.x

Debugger

Apiato provides a simple and easy way to monitor and log all the HTTP requests coming to your application.

The request monitor can be very useful when testing and debugging your frontend Apps which consume your API. Especially when the frontend apps (Mobile, Web,...) are built by other developers who are far from you.

The requests monitoring is provided via theRequestsMonitorMiddleware middleware.

Installation

composer require apiato/debugger-container
tip

This container is installed by default with an Apiato fresh installation.

Enable Requests Logging

Set REQUESTS_DEBUG and APP_DEBUG to true in .env file .

Usage

Log will be written to storage/logs/debugger.log

Debugger Customization

Instructions

This container works out of the box perfectly but if you want to change its configs or modify the codes you MUST follow these steps:

1- Copy the container from Vendor to AppSection (or any of your custom sections) of your project
2- Fix the namespaces
3- Remove apiato/debugger-container dependency from project root composer.json

Change the Default Log File

By default, everything is logged in the debugger.log file, to change the default file go to Debugger/Configs/debugger.php config file and set the file name:

/*

|--------------------------------------------------------------------------
| Log File
|--------------------------------------------------------------------------
|
| What to name the log file in the `storage/log` path.
|
*/

'log_file' => 'debugger.log',

Run in Testing Environments

Request monitoring will not run in testing environments, to enable it you need to manually edit the Middleware.

- + \ No newline at end of file diff --git a/docs/10.x/additional-features/apiato-containers/documentation/index.html b/docs/10.x/additional-features/apiato-containers/documentation/index.html index 5ed70f290..c9170c491 100644 --- a/docs/10.x/additional-features/apiato-containers/documentation/index.html +++ b/docs/10.x/additional-features/apiato-containers/documentation/index.html @@ -4,7 +4,7 @@ Documentation | Apiato - + @@ -17,7 +17,7 @@ 3- Remove apiato/documentation-generator-container dependency from project root composer.json
4- Update section_name & html_files in container configs
5- Update apidoc.json files in ApiDocJs/private & public folders and fix the filename

{
"header": {
"filename": "Containers/NEW_SECTION_NAME/Documentation/UI/WEB/Views/documentation/header.md"
}
}

Edit Default Generated Values in Templates

Apiato by defaults generates 2 API documentations, each one has its own apidoc.json file. Both can be modified from the Documentation Containers in Containers/Vendor/Documentation/ApiDocJs/

Change the Documentations URL's

Edit the config file of the Documentation Container Containers/Vendor/Documentation/Configs/vendor-documentation.php

Edit the Documentation Header

The header is usually the Overview of your API. It contains Info about authenticating users, making requests, responses, potential errors, rate limiting, pagination, query parameters and anything you want.

All this information is written in app/Containers/Vendor/Documentation/ApiDocJs/shared/header.template.md file, and the same file is used as header for both private and public documentations.

To edit the content just open the markdown file in any markdown editor and edit it.

You will notice some variables like {{rate-limit}} and {{token-expires}}. Those are replaced when running apiato:apidoc with real values from your application configuration files.

Feel free to extend them to include more info about your API from the app/Containers/Vendor/Documentation/Tasks/RenderTemplatesTask.php class.

Localization for Documentation Header

Default, the documentation title is in English en localization.

See which locales are supported by going in app/Containers/Vendor/Documentation/ApiDocJs/shared/

There will be some header.template.{locale}.md files in the folder.

You can change the language by adding APIDOC_LOCALE=ru to the .env file.

If you didn't find a file with your locale, you can create it. You need to modify its source code and create new file like header.template.cn.md

- + \ No newline at end of file diff --git a/docs/10.x/additional-features/apiato-containers/localization/index.html b/docs/10.x/additional-features/apiato-containers/localization/index.html index 9d6d9e220..7bbd7ff51 100644 --- a/docs/10.x/additional-features/apiato-containers/localization/index.html +++ b/docs/10.x/additional-features/apiato-containers/localization/index.html @@ -4,7 +4,7 @@ Localization | Apiato - + @@ -38,7 +38,7 @@ language in this specific language (e.g., locale_name => Deutsch). Furthermore, the language name is outputted in the applications default name (e.g., configured in app.locale). This would result in default_name => German.

The same applies to the regions that are defined (e.g., de-DE). Consequently, this results in locale_name => Deutschland and default_name = Germany.

Tests

To change the default language in your tests requests. You can set the env language in the phpunit.xml file.

- + \ No newline at end of file diff --git a/docs/10.x/additional-features/apiato-containers/overview/index.html b/docs/10.x/additional-features/apiato-containers/overview/index.html index 2be27fc86..a3a34066b 100644 --- a/docs/10.x/additional-features/apiato-containers/overview/index.html +++ b/docs/10.x/additional-features/apiato-containers/overview/index.html @@ -4,14 +4,14 @@ Overview | Apiato - +
Version: 10.x

Overview

Apiato ships with a few pre-defined and pre-configured containers. Some of these containers (e.g. Documentation & Debugger) are installed by default with a fresh Apiato project.

- + \ No newline at end of file diff --git a/docs/10.x/additional-features/apiato-containers/payments/index.html b/docs/10.x/additional-features/apiato-containers/payments/index.html index 2fe738b2e..1067774af 100644 --- a/docs/10.x/additional-features/apiato-containers/payments/index.html +++ b/docs/10.x/additional-features/apiato-containers/payments/index.html @@ -4,7 +4,7 @@ Payments | Apiato - + @@ -38,7 +38,7 @@ need to create your own ChargeWithFooTask. This class, however, needs to implement the PaymentChargerInterface distributed via the Payment container. This interface, in turn, requires you to implement the charge() method.

This method needs to connect to the FooService, create the payment and return a PaymentTransaction model.

Finally, you need to register the new service. This can be done in the Payment\Configs\vendor-payment.php file. For the vendor-payment.gateways key, add the new entry for your Foo Payment Gateway. This may look like this:

    // ...
'foo' => [
'container' => 'Foo',
'charge_task' => \App\Containers\Foo\Tasks\ChargeWithFooTask::class,
],
// ...

Basically, this entry points to the charger_task that handles, how to charge a User with the specific Payment Gateway.

That's all!

Mocking the real payment call for Testing

// mock the ChargeWithStripeService external API call
$this->mockIt(ChargeWithStripeService::class)->shouldReceive('charge')->andReturn([
'payment_method' => 'stripe',
'description' => $payId
]);

// mock the ChargeWithPaypalService external API call
$this->mockIt(ChargeWithPaypalService::class)->shouldReceive('charge')->andReturn([
'payment_method' => 'paypal',
'description' => $payId
]);

Checkout the Tests Helpers page for more information about Testing.

- + \ No newline at end of file diff --git a/docs/10.x/additional-features/apiato-containers/settings/index.html b/docs/10.x/additional-features/apiato-containers/settings/index.html index 8269a4f9a..c88556332 100644 --- a/docs/10.x/additional-features/apiato-containers/settings/index.html +++ b/docs/10.x/additional-features/apiato-containers/settings/index.html @@ -4,7 +4,7 @@ Settings | Apiato - + @@ -12,7 +12,7 @@
Version: 10.x

Settings

Installation

composer require apiato/settings-container

Now run php artisan migrate

Seed the default settings

Instructions

This container works out of the box perfectly but if you want to change its configs or modify the codes you MUST follow these steps:

1- Copy the container from Vendor to AppSection (or any of your custom sections) of your project
2- Fix the namespaces
3- Remove apiato/settings-container dependency from project root composer.json

Seed default settings in app/Containers/YourSection/Settings/Database/Seeders/DefaultSystemSettingsSeeder.php

Read settings

$value = $this->findSettingsByKeyTask->run('whateverSettingsName');

You can search for settings by Key as in the example above, or create a class for each setting as follows:

$value = $this->findWhateverSettingsTask->run();
- + \ No newline at end of file diff --git a/docs/10.x/additional-features/apiato-containers/social-authentication/index.html b/docs/10.x/additional-features/apiato-containers/social-authentication/index.html index 7848b2e3e..0a4ce272b 100644 --- a/docs/10.x/additional-features/apiato-containers/social-authentication/index.html +++ b/docs/10.x/additional-features/apiato-containers/social-authentication/index.html @@ -4,7 +4,7 @@ Social Authentication | Apiato - + @@ -21,7 +21,7 @@ 2- Fix the namespaces
3- Remove apiato/social-auth-container dependency from project root composer.json

1) Pick an Auth Provider from the supported providers by Socialite.
2) Go to app/Containers/YourSection/Socialauth/Tasks/FindUserSocialProfileTask.php and support your provider.

- + \ No newline at end of file diff --git a/docs/10.x/additional-features/community-containers/overview/index.html b/docs/10.x/additional-features/community-containers/overview/index.html index 7d464c8a2..b22dcc040 100644 --- a/docs/10.x/additional-features/community-containers/overview/index.html +++ b/docs/10.x/additional-features/community-containers/overview/index.html @@ -4,7 +4,7 @@ Overview | Apiato - + @@ -13,7 +13,7 @@ features in form of a respective container using Container Installer. Although these containers may work perfectly out of the box, they only serve as some kind of "blueprint" in order to get you kickstart your own application. These containers may not contain "production-ready code". Please revise them carefully.

note

These containers are developed by Apiato community and provided as is.

Feel free to add your containers to this list.

How to contribute

  1. Add a good documentation to your container.
  2. Make a PR and add your repository

Community Containers

NameDescriptionLink
ActivityLogLog Activities of Users and ModelsGitHub
NoteA Container handling Notes (e.g., ToDo Lists) for Eloquent Models.GitHub
MFAA Container to manage 2 Factor Authentication works with any Authenticator app.GitHub
SanctumImplementation of Laravel Sanctum in Apiato.GitHub
- + \ No newline at end of file diff --git a/docs/10.x/contribution-guide/index.html b/docs/10.x/contribution-guide/index.html index b6883ac44..ccef58370 100644 --- a/docs/10.x/contribution-guide/index.html +++ b/docs/10.x/contribution-guide/index.html @@ -4,7 +4,7 @@ Contribution Guide | Apiato - + @@ -42,7 +42,7 @@ 3 - Make sure you write a complete Changelog, in the release description. 4 - Change the default branch on GitHub to that new branch. 5 - If you updated the documentation, and you should! then visit the documentation repository and merge the PR into master.

- + \ No newline at end of file diff --git a/docs/10.x/core-features/api-versioning/index.html b/docs/10.x/core-features/api-versioning/index.html index 4cf44b564..4aa3dc271 100644 --- a/docs/10.x/core-features/api-versioning/index.html +++ b/docs/10.x/core-features/api-versioning/index.html @@ -4,13 +4,13 @@ API Versioning | Apiato - +
Version: 10.x

API Versioning

Since Laravel does not support API versioning, Apiato provide a very easy way to implement versioning for your API.

How it works

Create:

When creating a new API endpoint, specify the version number in the route file name following this naming format {endpoint-name}.{version-number}.{documentation-name}.php.

Example:

  • MakeOrder.v1.public.php
  • MakeOrder.v2.public.php
  • ListOrders.v1.private.php

Use:

Automatically the endpoint inside that route file will be accessible by adding the version number to the URL.

Example:

  • http://api.apiato.test/v1/register
  • http://api.apiato.test/v1/orders
  • http://api.apiato.test/v2/stores/123

Version the API in header instead of URL

First remove the URL version prefix:

  1. Edit app/Ship/Configs/apiato.php, set prefix to 'enable_version_prefix' => 'false',.
  2. Implement the Header versioning anyway you prefer. (this is not implemented in Apiato yet. Consider a contribution).
- + \ No newline at end of file diff --git a/docs/10.x/core-features/authentication/index.html b/docs/10.x/core-features/authentication/index.html index 66e0250e3..8d760b44f 100644 --- a/docs/10.x/core-features/authentication/index.html +++ b/docs/10.x/core-features/authentication/index.html @@ -4,7 +4,7 @@ Authentication | Apiato - + @@ -58,7 +58,7 @@ It will email you a link and when you make a request to that link it will call the /password-reset endpoint.

Note: For security reason, make sure the reset password URL is set in app/Containers/AppSection/User/Configs/appSection-user.php and given to the client App to be sent as parameter when calling the /password-forgot.

Note: You must set up the email to get this function to work, however for testing purposes set the MAIL_DRIVER=log in your .env file in order to the see the email content in the log file storage/logs/laravel.log.

Social Authentication

For Social Authentication visit the Social Authentication page.

- + \ No newline at end of file diff --git a/docs/10.x/core-features/authorization/index.html b/docs/10.x/core-features/authorization/index.html index 0cfc6e11f..876c30afa 100644 --- a/docs/10.x/core-features/authorization/index.html +++ b/docs/10.x/core-features/authorization/index.html @@ -4,13 +4,13 @@ Authorization | Apiato - +
Version: 10.x

Authorization

Apiato provides a Role-Based Access Control (RBAC) through its Authorization Container.

Behind the scenes apiato is using the Laravel's authorization functionality that was introduced in version 5.1.11 with the helper package laravel-permission. So you can always refer to the correspond documentation for more information.

How it works

Authorization in apiato is very simple and easy.

1) Create some Roles and permissions. By default, an admin role and some permissions are provided by Apiato. You can find the code in app/Containers/AppSection/Authorization/Data/Seeders/* directory.

2) Attach some permissions to the roles.

3) Now start creating users (or use existing users), to assign them to the new created Roles.

4) Finally, you need to protect your endpoints by Permissions (or/and Roles). The right place to do that is the Requests class.

Example protecting the (delete user) endpoint with delete-users permission:

class DeleteUserRequest extends Request
{
protected array $access = [
'permissions' => 'delete-users',
'roles' => '',
];

public function authorize(): bool
{
return $this->check([
'hasAccess',
]);
}
}

For detailed explanation of this example, please visit the Requests Page.

Responses

Authorization failed JSON response:

{
"message": "This action is unauthorized."
}

Assign Roles & Permission to the Testing User

You will need to set $access property in your test class, check out the Tests Helpers page for more details.

Seeding some users (Admins)

By default, Apiato comes with a Super Admin.

This Super Admin Credentials are:

This Admin seeded by app/Containers/Authorization/Data/Seeders/AuthorizationDefaultUsersSeeder_3.php.

The Default Super User, has a default role admin.

The admin default role has no permissions given to it.

To give permissions to the admin role (or any other role), you can use the dedicated endpoints (from your custom Admin Interface) or use this command php artisan apiato:permissions:toRole admin to give it all the permissions in the system.

Checkout each container Seeders directory app/Containers/AppSection/{container-name}/Data/Seeders/, to edit the default Users, Roles and Permissions.

Roles & Permissions guards

By default, Apiato uses a single guard called web for all it's roles and permissions, you can add/edit this behavior and support multiple guards at any time. Refer to the laravel-permission package for more details.

- + \ No newline at end of file diff --git a/docs/10.x/core-features/code-generator/index.html b/docs/10.x/core-features/code-generator/index.html index d077a9d8a..6ad42f799 100644 --- a/docs/10.x/core-features/code-generator/index.html +++ b/docs/10.x/core-features/code-generator/index.html @@ -4,7 +4,7 @@ Code Generator | Apiato - + @@ -17,7 +17,7 @@ the file and the folder structure needs to be the same as in vendor/apiato/core/Generator/Stubs.

Say, if you like to change the action -> create.stub, simply copy the file to app/Ship/Generators/CustomStubs/actions/create.stub and start adapting it to your needs.

If you run the respective command (e.g., in this case php artisan apiato:generate:action) and choose Create type this would read your specific create.stub file instead of the pre-defined one!

Contributing

If you would like to add your own generators, please check out the Contribution Guide.

- + \ No newline at end of file diff --git a/docs/10.x/core-features/data-caching/index.html b/docs/10.x/core-features/data-caching/index.html index 195b47cf4..eb14959bb 100644 --- a/docs/10.x/core-features/data-caching/index.html +++ b/docs/10.x/core-features/data-caching/index.html @@ -4,13 +4,13 @@ Data Caching | Apiato - +
Version: 10.x

Data Caching

Enable / Disable Eloquent Query Caching

info

This feature is disabled By default.

To enable it, go to app/Ship/Configs/repository.php config file and set cache > enabled => true, or set it from the .env file using ELOQUENT_QUERY_CACHE.

More details can be found here.

Users can skip the query caching and request new data by passing specific parameter to the Endpoint. Checkout its documentation here.

Change different caching settings

You can use different cache setting for each repository.

To set cache settings on each repository, first the caching must be enabled, second you need to set some properties on the repository class to override the default values.

For more details about all the properties refer to the L5 repository package documentation.

Note: you don't need to use the CacheableRepository trait or implement the CacheableInterface since they both exist on the Abstract repository class (App\Ship\Parents\Repositories\Repository).

- + \ No newline at end of file diff --git a/docs/10.x/core-features/default-endpoints/index.html b/docs/10.x/core-features/default-endpoints/index.html index 8f2f9f658..80f57b4bc 100644 --- a/docs/10.x/core-features/default-endpoints/index.html +++ b/docs/10.x/core-features/default-endpoints/index.html @@ -4,13 +4,13 @@ Default Endpoints | Apiato - +
Version: 10.x

Default Endpoints

Apiato comes loaded with many useful API endpoints for speeding up the development process.

You can see the endpoints in three ways:

  • In Terminal, by running php artisan route:list -c.
  • In Browser, by generating the beautiful detailed documentation. See API Docs Generator.
  • In Code, by navigating to the Routes folder of each container's UI.
- + \ No newline at end of file diff --git a/docs/10.x/core-features/etag/index.html b/docs/10.x/core-features/etag/index.html index 5aa6e381c..fc9a3f489 100644 --- a/docs/10.x/core-features/etag/index.html +++ b/docs/10.x/core-features/etag/index.html @@ -4,7 +4,7 @@ ETag | Apiato - + @@ -12,7 +12,7 @@
Version: 10.x

ETag

ETag Middleware

Apiato provides an ETag Middleware (app/Ship/Middlewares/Http/ProcessETagHeadersMiddleware.php) that implements the Shallow technique. It can be used to reduce bandwidth on the client side (especially for Mobile devices).

Enable / Disable ETag

info

This feature is disabled By default.

To enable it go to app/Ship/Configs/apiato.php and set use-etag to true. Of course your client should send the If-None-Match HTTP Header (= etag) in his request for this feature to work properly.

- + \ No newline at end of file diff --git a/docs/10.x/core-features/hash-id/index.html b/docs/10.x/core-features/hash-id/index.html index 5ec0fcf58..a66f8c76d 100644 --- a/docs/10.x/core-features/hash-id/index.html +++ b/docs/10.x/core-features/hash-id/index.html @@ -4,13 +4,13 @@ Hash ID | Apiato - +
Version: 10.x

Hash ID

Hashing your internal ID's, is a very helpful feature for security reasons (to prevent some hack attacks) and business reasons (to hide the real total records from your competitors).

Enable Hash ID

Set the HASH_ID=true in the .env file.

Also, with the feature make sure to always use the getHashedKey() on any model, whenever you need to return an ID (mainly from transformers) weather hashed ID or not.

Example:


'id' => $user->getHashedKey(),

Note: if the feature is set to false HASH_ID=false the getHashedKey() will return the normal ID.

Usage

There are 2 ways an ID's can be passed to your system via the API:

In URL example: www.apiato.test/items/abcdef.

In parameters example: [GET] or [POST] www.apiato.test/items?id=abcdef.

in both cases you will need to inform your API about what's coming form the Request class.

Checkout the Requests page. After setting the $decode and $urlParameters properties on your Request class, the ID will be automatically decoded for you to apply validation rules on it or/and use it from your controller. ($request->id will return the decoded ID)

Configuration

You can change the default length and characters used in the ID from the config file app/Ship/Configs/hashids.phpor in the .env file by editing the HASH_ID_LENGTH value.

You can set the HASH_ID_KEY in the .env file to any random string. You can generate this from any of the online random string generators, or run head /dev/urandom | tr -dc 'A-Za-z0-9!"#$%&'\''()*+,-./:;<=>?@[\]^_{|}~' | head -c 32 ; echo on the linux command-line. Apiato defaults to the APP_KEY should this not be set.

The HASH_ID_KEY acts as the salt during hashing of the ID. This should never be changed in production as it renders all previously generated IDs impossible to decode.

Testing

In your tests you must hash the ID's before making the calls, because if you tell your Request class to decode an ID for you, it will throw an exception when the ID is not encoded.

for Parameter ID's

Always use getHashedKey() on your models when you want to get the ID

Example:

$data = [
'roles_ids' => [
$role1->getHashedKey(),
$role2->getHashedKey(),
],
'user_id' => $randomUser->getHashedKey(),
];
$response = $this->makeCall($data);

Or you can do this manually Hashids::encode($id);.

for URL ID's

You can use this helper function injectId($id, $skipEncoding = false, $replace = '{id}').

Example:

$response = $this->injectId($admin->id)->makeCall();

More details on the Tests Helpers page.

Availability

You can use the Apiato\Core\Traits\HashIdTrait on any model or class, in order to have the encode and decode functions.

By default, you have access to these functions $this->encode($id) and $this->decode($id) from all your Test classes and Controllers.

- + \ No newline at end of file diff --git a/docs/10.x/core-features/pagination/index.html b/docs/10.x/core-features/pagination/index.html index 3426fb12d..b237e1626 100644 --- a/docs/10.x/core-features/pagination/index.html +++ b/docs/10.x/core-features/pagination/index.html @@ -4,7 +4,7 @@ Pagination | Apiato - + @@ -16,7 +16,7 @@ you can do it either project wide or per repository. After that a request can get all the data (with no pagination applied) by applying limit=0.

This will return all matching entities:
api.domain.test/endpoint?limit=0

Project Wide

Set PAGINATION_SKIP=true in .env file.

Per Repository

Override the $allowDisablePagination property in your specific Repository class.

note

Per repository configs override the global config and have precedence.

- + \ No newline at end of file diff --git a/docs/10.x/core-features/profiler/index.html b/docs/10.x/core-features/profiler/index.html index 88b98891f..7ae039aa0 100644 --- a/docs/10.x/core-features/profiler/index.html +++ b/docs/10.x/core-features/profiler/index.html @@ -4,14 +4,14 @@ Profiler | Apiato - +
Version: 10.x

Profiler

Profiling is very important to optimize the performance of your application, and help you better understand what happens when a request is received, as well as it can speed up the debugging process.

Apiato uses the third-party package laravel-debugbar (which uses the PHP Debug Bar), to collect the profiling data.

By default, the laravel-debugbar package displays the profiling data in the browser. However, Apiato uses a middleware app/Ship/Middlewares/Http/ProfilerMiddleware.php to append the profiling data to the response.

Sample Profiler response

{
// Actual Response Here...
"_profiler": {
"__meta": {
"id": "X167f293230e3457f1bbd95d9c82aba4a",
"datetime": "2017-09-22 18:45:27",
"utime": 1506105927.799299,
"method": "GET",
"uri": "/",
"ip": "172.20.0.1"
},
"messages": {
"count": 0,
"messages": []
},
"time": {
"start": 1506105922.742068,
"end": 1506105927.799333,
"duration": 5.057265043258667,
"duration_str": "5.06s",
"measures": [
{
"label": "Booting",
"start": 1506105922.742068,
"relative_start": 0,
"end": 1506105923.524004,
"relative_end": 1506105923.524004,
"duration": 0.7819359302520752,
"duration_str": "781.94ms",
"params": [],
"collector": null
},
{
"label": "Application",
"start": 1506105923.535343,
"relative_start": 0.7932748794555664,
"end": 1506105927.799336,
"relative_end": 0.00000286102294921875,
"duration": 4.26399302482605,
"duration_str": "4.26s",
"params": [],
"collector": null
}
]
},
"memory": {
"peak_usage": 13234248,
"peak_usage_str": "12.62MB"
},
"exceptions": {
"count": 0,
"exceptions": []
},
"route": {
"uri": "GET /",
"middleware": "api, throttle:30,1",
"domain": "http://api.apiato.test",
"as": "apis_root_page",
"controller": "App\\Containers\\Welcome\\UI\\API\\Controllers\\Controller@apiRoot",
"namespace": "App\\Containers\\Welcome\\UI\\API\\Controllers",
"prefix": "/",
"where": [],
"file": "app/Containers/Welcome/UI/API/Controllers/Controller.php:20-25"
},
"queries": {
"nb_statements": 0,
"nb_failed_statements": 0,
"accumulated_duration": 0,
"accumulated_duration_str": "0μs",
"statements": []
},
"swiftmailer_mails": {
"count": 0,
"mails": []
},
"logs": {
"count": 3,
"messages": [
{
"message": "...",
"message_html": null,
"is_string": false,
"label": "error",
"time": 1506105927.694807
},
{
"message": "...",
"message_html": null,
"is_string": false,
"label": "error",
"time": 1506105927.694811
},
{
"message": "[2017-09-18 17:38:15] testing.INFO: New User registration. ID = 970ylqvaogmxnbdr | Email = apiato@mail.test. Thank you for signing up.\n</div>\n</body>\n</html>\n \n",
"message_html": null,
"is_string": false,
"label": "info",
"time": 1506105927.694812
}
]
},
"auth": {
"guards": {
"web": "array:2 [\n \"name\" => \"Guest\"\n \"user\" => array:1 [\n \"guest\" => true\n ]\n]",
"api": "array:2 [\n \"name\" => \"Guest\"\n \"user\" => array:1 [\n \"guest\" => true\n ]\n]"
},
"names": ""
},
"gate": {
"count": 0,
"messages": []
}
}
}

Configuration

By default, the profiler feature is turned off. To turn it on edit the .env file and set DEBUGBAR_ENABLED=true.

To control and modify the profiler response, you need to edit this config file app/Ship/Configs/debugbar.php.

- + \ No newline at end of file diff --git a/docs/10.x/core-features/query-parameters/index.html b/docs/10.x/core-features/query-parameters/index.html index 6023c5ea9..66bad36c3 100644 --- a/docs/10.x/core-features/query-parameters/index.html +++ b/docs/10.x/core-features/query-parameters/index.html @@ -4,7 +4,7 @@ Query Parameters | Apiato - + @@ -25,7 +25,7 @@ accepts driver as relationship ($availableIncludes in Transformer).

Nested Includes

It is also possible to request "nested includes". Extend the example from above. Imagine, that a Driver may also have a relationship to an Address object. You can access this information as well by calling ?include=driver,driver.address.

Of course, the address include is defined in the respective DriverTransformer that is used here.

Where to define the includes:

Every Transformer can have 2 types of includes $availableIncludes and $defaultIncludes:

    protected $availableIncludes = [
'products',
'store',
'recipients',
];

protected $defaultIncludes = [
'invoice',
];

$defaultIncludes will not be listed in the response, only the $availableIncludes will be.

Visit the Transformers page for more details.

Skip caching

(provided by the L5 Repository)

Note: You need to turn the Eloquent Query Caching ON for this feature to work. ELOQUENT_QUERY_CACHE=true in .env.

To run a new query and force disabling the cache on certain endpoints, you can use this parameter

?skipCache=true

It's not recommended to keep skipping cache as it has bad impact on the performance.

Configuration

Most of these parameters are provided by the L5 Repository and configurable from the Ship/Configs/repository.php file. Some of them are built in house, or inherited from other packages such as Fractal.

See the Query parameters from the User Developer perspective

1) Generate the Default API documentation

2) Visit the documentation URL

More details in the API Docs Generator page.

More Information

For more details on these parameters check out these links:

- + \ No newline at end of file diff --git a/docs/10.x/core-features/rate-limiting/index.html b/docs/10.x/core-features/rate-limiting/index.html index d3584f80d..8324468ef 100644 --- a/docs/10.x/core-features/rate-limiting/index.html +++ b/docs/10.x/core-features/rate-limiting/index.html @@ -4,14 +4,14 @@ Rate Limiting | Apiato - +
Version: 10.x

Rate Limiting

Apiato uses the default Laravel middleware for rate limiting (throttling).

All REST API requests are throttled to prevent abuse and ensure stability. The exact number of calls that your application can make per day varies based on the type of request you are making.

The rate limit window is 1 minute per endpoint, with most individual calls allowing for 30 requests in each window.

In other words, each user is allowed to make 30 calls per endpoint every 1 minute. (For each unique access token).

To update these values go to app/Ship/Configs/apiato.php config file, or to the ENV file.

'throttle' => [
'enabled' => env('API_RATE_LIMIT_ENABLED', true),
'attempts' => env('API_RATE_LIMIT_ATTEMPTS', '30'),
'expires' => env('API_RATE_LIMIT_EXPIRES', '1'),
]
API_RATE_LIMIT_ENABLED=true
API_RATE_LIMIT_ATTEMPTS=30
API_RATE_LIMIT_EXPIRES=1

For how many hits you can preform on an endpoint, you can always check the header:

X-RateLimit-Limit →30
X-RateLimit-Remaining →29

Enable/Disable Rate Limiting:

The API rate limiting middleware is enabled and applied to all the Container Endpoints by default.

To disable it set API_RATE_LIMIT_ENABLED to false in the .env file.

- + \ No newline at end of file diff --git a/docs/10.x/core-features/useful-commands/index.html b/docs/10.x/core-features/useful-commands/index.html index 78d41546f..7423cf505 100644 --- a/docs/10.x/core-features/useful-commands/index.html +++ b/docs/10.x/core-features/useful-commands/index.html @@ -4,14 +4,14 @@ Useful Commands | Apiato - +
Version: 10.x

Useful Commands

Apiato is loaded with many useful commands to help you speed up the development process. You can see list of all commands, by typing php artisan and look for Apiato section.

Available Commands

  • php artisan apiato Display the current Apiato version.
  • php artisan apiato:apidoc Generate API Documentations with apidoc from your routes Docblock. More details.
  • php artisan apiato:create:admin Create a new User with the ADMIN role
  • php artisan apiato:generate:{component} Generate a specific component for the framework (e.g., Action, Task, ...). For more details on the Code Generator click here.
  • php artisan apiato:list:actions List all Actions in the Application.
  • php artisan apiato:list:tasks List all Tasks in the Application.
  • php artisan apiato:permissions:toRole Give all system Permissions to a specific Role.
  • php artisan apiato:seed-deploy Seeds your custom deployment data from app/Ship/Seeders/SeedDeploymentData.php.
  • php artisan apiato:seed-test Seeds your custom testing data from app/Ship/Seeders/SeedTestingData.php.
  • php artisan apiato:welcome Just saying welcome from a container.
- + \ No newline at end of file diff --git a/docs/10.x/core-features/user-registration/index.html b/docs/10.x/core-features/user-registration/index.html index 4489698fa..4cd06d407 100644 --- a/docs/10.x/core-features/user-registration/index.html +++ b/docs/10.x/core-features/user-registration/index.html @@ -4,13 +4,13 @@ User Registration | Apiato - +
Version: 10.x

User Registration

Register users by credentials (email and passwords)

Call the http://api.apiato.test/v1/register endpoint (you can find its documentation after generating the API Docs.

Check out the registerUser endpoint in the API Routes files.

This will register a new User and respond with user object.

Registration request:

curl --request POST \
--url http://api.apiato.test/v1/register \
--header 'accept: application/json' \
--header 'content-type: application/x-www-form-urlencoded' \
--data 'email=john%40doe.com&password=password&name=John%20Doe'

Registration response:

{
"data": {
"object": "User",
"id": "XbPW7awNkzl83LD6",
"name": "John Doe",
"email": "john@doe.com",
"email_verified_at": null,
"gender": null,
"birth": null,
"created_at": "2021-04-15T14:17:24.000000Z",
"updated_at": "2021-04-15T14:17:24.000000Z",
"readable_created_at": "1 second ago",
"readable_updated_at": "1 second ago"
},
"meta": {
"include": [
"roles"
],
"custom": []
}
}

Note: After registration in order to get the user access token you will have to send another call to http://api.example.com/v1/oauth/token with following fields and values

username => your_username e.g. admin@admin.com
password => your_password
grant_type => password
client_id => your_client_id
client_secret => your_client_secret

For third-party clients you must have client ID and secret first. You can generate them by creating new client in your app using Laravel Passport.

For first-party clients you can use a proxy to add those fields on requests coming from your trusted client. For an example on how to do it look at ProxyLoginForWebClientAction Action in Authentication Container.

Register users by Social Account

(Facebook, Twitter, Google..)

Checkout the Social Authentication Page for how to Sign up with Social Account.

- + \ No newline at end of file diff --git a/docs/10.x/core-features/validation/index.html b/docs/10.x/core-features/validation/index.html index bcaae678d..19e3a78b5 100644 --- a/docs/10.x/core-features/validation/index.html +++ b/docs/10.x/core-features/validation/index.html @@ -4,13 +4,13 @@ Validation | Apiato - +
Version: 10.x

Validation

Apiato uses the powerful Laravel validation system.

In Apiato, validation must be defined in Request component, since every request might have different rules.

Validation rules are automatically applied, once injecting the Request in the Controller.

Requests help validating User data, accessibility, ownership and more.

Example Request with Validation rules:

namespace App\Containers\AppSection\User\UI\API\Requests;

use App\Ship\Parents\Requests\Request;

class RegisterUserRequest extends Request
{
/**
* @return array
*/
public function rules()
{
return [
'email' => 'required|email|max:200|unique:users,email',
'password' => 'required|min:20|max:300',
'name' => 'required|min:2|max:400',
];
}

}

Usage from Controller Example:

    public function registerUser(RegisterUserRequest $request)
{
$user = app(RegisterUserAction::class)->run($request);
return $this->transform($user, UserTransformer::class);
}

Responses

Validation Error response format:

Single Field:

{
"message": "The given data was invalid.",
"errors": {
"email": [
"The email has already been taken."
]
}
}

Multiple Fields:

{
"message": "The given data was invalid.",
"errors": {
"email": [
"The email has already been taken."
],
"password": [
"The password field is required."
]
}
}

More details about requests in the Requests Page.

- + \ No newline at end of file diff --git a/docs/10.x/faq/index.html b/docs/10.x/faq/index.html index f7e1ab35b..cf6c76ade 100644 --- a/docs/10.x/faq/index.html +++ b/docs/10.x/faq/index.html @@ -4,7 +4,7 @@ Frequently Asked Questions | Apiato - + @@ -37,7 +37,7 @@ Discord.

Lastly, if you got your question answered, consider sharing it, if you believe it can help others. You can submit a PR adding the questions and answer here on the FAQ page. Or leave it somewhere on the repository or on the chat room. Thanks in advance :)

- + \ No newline at end of file diff --git a/docs/10.x/getting-started/container-installer/index.html b/docs/10.x/getting-started/container-installer/index.html index 3d8d8127d..28666defb 100644 --- a/docs/10.x/getting-started/container-installer/index.html +++ b/docs/10.x/getting-started/container-installer/index.html @@ -4,7 +4,7 @@ Container Installer | Apiato - + @@ -20,7 +20,7 @@ that allows installing/updating containers.
  • You must provide the key extra.apiato.container.name. This key indicates the name of the folder (e.g., container) when installing the package to the /app/ContainersVendorSection folder. In the shown example, the container would be installed to app/Containers/VendorSection/Foo.
  • - + \ No newline at end of file diff --git a/docs/10.x/getting-started/conventions-and-principles/index.html b/docs/10.x/getting-started/conventions-and-principles/index.html index e02d8e98b..144d0ba17 100644 --- a/docs/10.x/getting-started/conventions-and-principles/index.html +++ b/docs/10.x/getting-started/conventions-and-principles/index.html @@ -4,13 +4,13 @@ Conventions | Apiato - +
    Version: 10.x

    Conventions

    HTTP Methods usage in RESTful API's

    • GET (SELECT): retrieve a specific resource from the server, or a listing of resources.
    • POST (CREATE): create a new resource on the server.
    • PUT (UPDATE): update a resource on the server, providing the entire resource.
    • PATCH (UPDATE): update a resource on the server, providing only changed attributes.
    • DELETE (DELETE): remove a resource from the server.

    Naming Conventions for Routes & Actions

    • GetAllResource: to fetch all resources.
    • FindResourceByID: to search for single resource by its unique identifier.
    • CreateResource: to create a new resource.
    • UpdateResource: to update/edit existing resource.
    • DeleteResource: to delete a resource.

    General guidelines and principles for RESTful URLs

    • A URL identifies a resource.
    • URLs should include nouns, not verbs.
    • Use plural nouns only for consistency (no singular nouns).
    • Use HTTP verbs (GET, POST, PUT, DELETE) to operate on the collections and elements.
    • You should not need to go deeper than resource/identifier/resource.
    • Put the version number at the base of your URL, for example http://apiato.test/v1/path/to/resource.
    • If an input data changes the logic of the endpoint, it should be passed in the URL. If not can go in the header "like Auth Token".
    • Don't use query parameters to alter state.
    • Don't use mixed-case paths if you can help it; lowercase is best.
    • Don't use implementation-specific extensions in your URIs (.php, .py, .pl, etc.)
    • Limit your URI space as much as possible. And keep path segments short.
    • Don't put metadata in the body of a response that should be in a header

    Good URL examples

    • Find a single Car by its unique identifier (ID):
      • GET http://www.api.apiato.test/v1/cars/123
    • Get all Cars:
      • GET http://www.api.apiato.test/v1/cars
    • Find/Search cars by one or more fields:
      • GET http://www.api.apiato.test/v1/cars?search=maker:mercedes
      • GET http://www.api.apiato.test/v1/cars?search=maker:mercedes;color:white
    • Order and Sort query result:
      • GET http://www.api.apiato.test/v1/cars?orderBy=created_at&sortedBy=desc
      • GET http://www.api.apiato.test/v1/cars?search=maker:mercedes&orderBy=created_at&sortedBy=desc
    • Specify optional fields:
      • GET http://www.api.apiato.test/v1/cars?filter=id;name;status
      • GET http://www.api.apiato.test/v1/cars/123?filter=id;name;status
    • Get all Drivers belonging to a Car:
      • GET http://www.api.apiato.test/v1/cars/123/drivers
      • GET http://www.api.apiato.test/v1/cars/123/drivers/123/addresses
    • Include Drivers objects relationship with the car response:
      • GET http://www.api.apiato.test/v1/cars/123?include=drivers
      • GET http://www.api.apiato.test/v1/cars/123?include=drivers,owner
    • Add new Car:
      • POST http://www.api.apiato.test/v1/cars
    • Add new Driver to a Car:
      • POST http://www.api.apiato.test/v1/cars/123/drivers

    General principles for HTTP methods

    • Don't ever use GET to alter state; to prevent Googlebot from corrupting your data. And use GET as much as possible.
    • Don't use PUT unless you are updating an entire resource. And unless you can also legitimately do a GET on the same URI.
    • Don't use POST to retrieve information that is long-lived or that might be reasonable to cache.
    • Don't perform an operation that is not idempotent with PUT.
    • Use GET for things like calculations, unless your input is large, in which case use POST.
    • Use POST in preference to PUT when in doubt.
    • Use POST whenever you have to do something that feels RPC-like.
    • Use PUT for classes of resources that are larger or hierarchical.
    • Use DELETE in preference to POST to remove resources.
    - + \ No newline at end of file diff --git a/docs/10.x/getting-started/installation/index.html b/docs/10.x/getting-started/installation/index.html index 33aeabc3c..0dc0612ef 100644 --- a/docs/10.x/getting-started/installation/index.html +++ b/docs/10.x/getting-started/installation/index.html @@ -4,7 +4,7 @@ Installation | Apiato - + @@ -29,7 +29,7 @@ try running this command homestead halt && homestead up --provision.

    Using anything else

    If you're not into virtualization solutions, you can set up your environment directly on your machine. Check the software's requirements list.

    Let's Play

    Now let's see it in action

    Open your web browser and visit:

    • http://apiato.test You should see an HTML page, with Apiato in the middle.
    • http://api.apiato.test You should see a response like this:
    [
    "Welcome to Apiato"
    ]

    Open your HTTP client and call:

    • http://api.apiato.test/ You should see a JSON response with message: "Welcome to apiato.",
    • http://api.apiato.test/v1 You should see a JSON response with message: "Welcome to apiato (API V1).",

    Make some HTTP calls to the API:

    To make the calls you can use Postman, HTTPIE or any other tool you prefer.

    Let's test the (user registration) endpoint http://api.apiato.test/v1/register with cURL:

    curl -X POST -H "Accept: application/json" -H "Cache-Control: no-cache" -F "email=John@Doe.me" -F "password=so-secret" -F "name=John Doe" "http://api.apiato.test/v1/register"

    You should get a response like this:

    Header:

    Access-Control-Allow-Origin → ...
    Cache-Control → ...
    Connection → keep-alive
    Content-Language → en
    Content-Type → application/json
    Date → Wed, 11 Apr 2000 22:55:88 GMT
    Server → nginx
    Transfer-Encoding → chunked
    Vary → Origin
    X-Powered-By → PHP/7.7.7
    X-RateLimit-Limit → 30
    X-RateLimit-Remaining → 29

    Body:

    {
    "data": {
    "object": "User",
    "id": "7VgmkMw7rR2pWO5j",
    "name": "John Doe",
    "email": "John@Doe.me",
    "email_verified_at": null,
    "gender": null,
    "birth": null,
    "created_at": "2021-04-12T13:33:24.000000Z",
    "updated_at": "2021-04-12T13:33:24.000000Z",
    "readable_created_at": "1 second ago",
    "readable_updated_at": "1 second ago"
    },
    "meta": {
    "include": [
    "roles"
    ],
    "custom": []
    }
    }
    - + \ No newline at end of file diff --git a/docs/10.x/getting-started/markdown-features/index.html b/docs/10.x/getting-started/markdown-features/index.html index c1967174d..70acc2456 100644 --- a/docs/10.x/getting-started/markdown-features/index.html +++ b/docs/10.x/getting-started/markdown-features/index.html @@ -4,13 +4,13 @@ Markdown Features | Apiato - +
    Version: 10.x

    Markdown Features

    Docusaurus supports the Markdown syntax and has some additional features.

    Front Matter

    Markdown documents can have associated metadata at the top called Front Matter:

    ---
    id: my-doc
    title: My document title
    description: My document description
    sidebar_label: My doc
    ---

    Markdown content

    Regular Markdown links are supported using url paths or relative file paths.

    Let's see how to [Create a page].
    Let's see how to [Create a page].

    Let's see how to [Create a page].

    Markdown images

    Regular Markdown images are supported.

    Add an image at static/img/logo.png and use this Markdown declaration:

    ![Docusaurus logo](/img/logo.png)

    Docusaurus logo

    Code Blocks

    Markdown code blocks are supported with Syntax highlighting.

    ```jsx title="src/components/HelloDocusaurus.js"
    function HelloDocusaurus() {
    return (
    <h1>Hello, Docusaurus!</h1>
    )
    }
    ```
    src/components/HelloDocusaurus.js
    function HelloDocusaurus() {
    return <h1>Hello, Docusaurus!</h1>;
    }

    Admonitions

    Docusaurus has a special syntax to create admonitions and callouts:

    :::tip My tip

    Use this awesome feature option

    :::

    :::danger Take care

    This action is dangerous

    :::
    My tip

    Use this awesome feature option

    Take care

    This action is dangerous

    React components

    Thanks to MDX, you can make your doc more interactive and use React components inside Markdown:

    export const Highlight = ({children, color}) => (
    <span
    style={{
    backgroundColor: color,
    borderRadius: '2px',
    color: 'red',
    padding: '0.2rem',
    }}>
    {children}
    </span>
    );

    <Highlight color="#25c2a0">Docusaurus green</Highlight> and <Highlight color="#1877F2">Facebook blue</Highlight> are my favorite colors.
    Docusaurus green and Facebook blue are my favorite colors.
    - + \ No newline at end of file diff --git a/docs/10.x/getting-started/requests/index.html b/docs/10.x/getting-started/requests/index.html index c7d0eb02d..218aae4fe 100644 --- a/docs/10.x/getting-started/requests/index.html +++ b/docs/10.x/getting-started/requests/index.html @@ -4,7 +4,7 @@ Requests | Apiato - + @@ -17,7 +17,7 @@ you can force your users to send application/json by setting 'force-accept-header' => true, in app/Ship/Configs/apiato.php or allow them to skip it completely by setting the 'force-accept-header' => false,. By default this flag is set to false.

    Calling Endpoints

    Calling unprotected endpoint example:

    curl -X POST -H "Accept: application/json" -H "Content-Type: application/x-www-form-urlencoded; -F "email=admin@admin.com" -F "password=admin" -F "=" "http://api.domain.test/v2/register"

    Calling protected endpoint (passing Bearer Token) example:

    curl -X GET -H "Accept: application/json" -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..." -H "http://api.domain.test/v1/users"
    - + \ No newline at end of file diff --git a/docs/10.x/getting-started/responses/index.html b/docs/10.x/getting-started/responses/index.html index 7d8182616..fe19375a4 100644 --- a/docs/10.x/getting-started/responses/index.html +++ b/docs/10.x/getting-started/responses/index.html @@ -4,7 +4,7 @@ Responses | Apiato - + @@ -17,7 +17,7 @@ If no $resourceKey is defined at the Model, the ShortClassName is used as key. For example, the ShortClassName of the App\Containers\AppSection\User\Models\User::class is User.

    Error Responses formats

    Visit each feature, e.g. the Authentication and there you will see how an unauthenticated response looks like, same for Authorization, Validation and so on.

    Building a Responses from the Controller:

    Checkout the Controller response builder helper functions.

    - + \ No newline at end of file diff --git a/docs/10.x/getting-started/samples/index.html b/docs/10.x/getting-started/samples/index.html index ea2798166..a672ce7a1 100644 --- a/docs/10.x/getting-started/samples/index.html +++ b/docs/10.x/getting-started/samples/index.html @@ -4,7 +4,7 @@ Samples | Apiato - + @@ -12,7 +12,7 @@
    Version: 10.x

    Samples

    The basic flow

    When an HTTP request is received, it first hits your predefined Endpoint (each endpoint live in its own Route file).

    Sample Route Endpoint

    Route::get('/hello', [Controller::class, 'sayHello']);

    After the user makes a request to the endpoint [GET] www.api.apiato.com/v1/hello it calls the defined controller function (sayHello).

    Sample Controller Function

    class Controller extends ApiController
    {
    public function sayHello(SayHelloRequest $request)
    {
    $helloMessage = app(SayHelloAction::class)->run();

    $this->json([
    $helloMessage
    ]);
    }
    }

    This function takes a Request class SayHelloRequest to automatically checks if the user has the right access to this endpoint. Only if the user has access, it proceeds to the function body.

    Then the function calls an Action (SayHelloAction) to perform the business logic.

    Sample Action

    class SayHelloAction extends Action
    {
    public function run()
    {
    return 'Hello World!';
    }
    }

    The Action can do anything then return a result (could be an Object, a String or anything).

    When the Action finishes its job, the controller function gets ready to build a response.

    Json responses can be built using the helper function json ($this->json(['foo' => 'bar']);).

    Sample User Response

    [
    "Hello World!"
    ]
    - + \ No newline at end of file diff --git a/docs/10.x/getting-started/software-architectural-patterns/index.html b/docs/10.x/getting-started/software-architectural-patterns/index.html index 35f6ef505..f77c3c555 100644 --- a/docs/10.x/getting-started/software-architectural-patterns/index.html +++ b/docs/10.x/getting-started/software-architectural-patterns/index.html @@ -4,7 +4,7 @@ Architecture | Apiato - + @@ -14,7 +14,7 @@ App\Containers\SectionName\Printer).
  • Container MAY be named anything, however a good practice is to name it to its most important Model name. Example: If the User Story is (User can create a Store and Store can have Items) then we you could have 3 Containers (User, Store and Item).
  • - + \ No newline at end of file diff --git a/docs/10.x/index.html b/docs/10.x/index.html index e32b20abc..c3b223233 100644 --- a/docs/10.x/index.html +++ b/docs/10.x/index.html @@ -4,13 +4,13 @@ Requirements | Apiato - +
    Version: 10.x

    Requirements

    Requirements

    • GIT
    • PHP >= 7.4 (8.0 is recommended)
    • Composer
    • PHP Extensions:
      • OpenSSL PHP Extension
      • PDO PHP Extension
      • Mbstring PHP Extension
      • Tokenizer PHP Extension
      • BCMath PHP Extension (required when the Hash ID feature is enabled)
      • Intl Extension (required when you use the Localization Container)
    - + \ No newline at end of file diff --git a/docs/10.x/main-components/actions/index.html b/docs/10.x/main-components/actions/index.html index a54a7a811..efc1f635b 100644 --- a/docs/10.x/main-components/actions/index.html +++ b/docs/10.x/main-components/actions/index.html @@ -4,13 +4,13 @@ Actions | Apiato - +
    Version: 10.x

    Actions

    Definition & Principles

    Read Porto SAP Documentation (#Actions).

    Rules

    • All Actions MUST extend App\Ship\Parents\Actions\Action.

    Folder Structure

     - app
    - Containers
    - {section-name}
    - {container-name}
    - Actions
    - CreateUserAction.php
    - DeleteUserAction.php
    - ...

    Code Sample

    Action

    class CreateAdminAction extends Action
    {
    public function run(string $email, string $password, string $name, bool $isClient = false): User
    {
    $admin = app(CreateUserByCredentialsTask::class)->run(
    $isClient,
    $email,
    $password,
    $name
    );

    app(AssignUserToRoleTask::class)->run($admin, ['admin']);

    return $admin;
    }
    }

    Calling multiple Tasks

    class DemoAction extends Action
    {
    public function run($xxx, $yyy, $zzz)
    {
    $foo = app(Sample111Task::class)->run($xxx, $yyy);
    $bar = app(Sample222Task::class)->run($zzz);
    }
    }

    Usage from a Controller

        public function deleteUser(DeleteUserRequest $request)
    {
    $user = app(DeleteUserAction::class)->run($request);
    return $this->deleted($user);
    }
    tip

    The same Action MAY be called by multiple Controllers (Web, Api, Cli).

    - + \ No newline at end of file diff --git a/docs/10.x/main-components/controllers/index.html b/docs/10.x/main-components/controllers/index.html index 770e774a5..e5f528abf 100644 --- a/docs/10.x/main-components/controllers/index.html +++ b/docs/10.x/main-components/controllers/index.html @@ -4,7 +4,7 @@ Controllers | Apiato - + @@ -17,7 +17,7 @@ This function allows including metadata in the response.

    $metaData = ['total_credits', 10000];

    return $this->withMeta($metaData)->transform($receipt, ReceiptTransformer::class);

    json This function allows passing array data to be represented as json.

    return $this->json([
    'foo': 'bar'
    ])

    Other functions

    • accepted
    • deleted
    • noContent
    • // Some functions might not be documented, so refer to the vendor/apiato/core/Traits/ResponseTrait.php and see the public functions.
    - + \ No newline at end of file diff --git a/docs/10.x/main-components/exceptions/index.html b/docs/10.x/main-components/exceptions/index.html index 12c7ca767..5d032a84c 100644 --- a/docs/10.x/main-components/exceptions/index.html +++ b/docs/10.x/main-components/exceptions/index.html @@ -4,13 +4,13 @@ Exceptions | Apiato - +
    Version: 10.x

    Exceptions

    Definition & Principles

    Read Porto SAP Documentation (#Exceptions).

    Rules

    • All Exceptions MUST extend App\Ship\Parents\Exceptions\Exception.
    • Shared (general) Exceptions between all Containers SHOULD be created in the Exceptions Ship folder (app/Ship/Exceptions/*).
    • Every Exception SHOULD have two properties code and message. You can override those values while throwing the error.

    Folder Structure

     - App
    - Containers
    - {section-name}
    - {container-name}
    - Exceptions
    - AccountFailedException.php
    - ...

    - Ship
    - Exceptions
    - CreateResourceFailedException.php
    - NotFoundException.php
    - ...

    Code Samples

    Demo Exception

    class DemoException extends Exception
    {
    public $code = Response::HTTP_CONFLICT;
    public $message = 'This is a demo exception.';
    }

    Usage from anywhere

    throw new AccountFailedException();

    Usage with errors

    throw (new AccountFailedException())->withErrors(['email' => 'Email already in use']);
    throw (new AccountFailedException())->withErrors(['email' => ['Email already in use', 'Another message']]);

    Usage with errors and localization

    For localization, you can use the Localization Container

    // translation strings are automatically translated if the translations are found.
    throw (new AccountFailedException())->withErrors(['email' => 'appSection@user::exceptions.email-taken']);

    Response:

    {
    "message": "The exception error message.",
    "errors": {
    "email": [
    "The email has already been taken."
    ]
    }
    }

    Usage with Log for Debugging

    throw (new AccountFailedException())->debug($e); // debug() accepts string or \Exception instance

    Usage and overriding the default

    throw new AccountFailedException('I am the message to be displayed to the user');
    - + \ No newline at end of file diff --git a/docs/10.x/main-components/models/index.html b/docs/10.x/main-components/models/index.html index 5ba664018..b6080de12 100644 --- a/docs/10.x/main-components/models/index.html +++ b/docs/10.x/main-components/models/index.html @@ -4,13 +4,13 @@ Models | Apiato - +
    Version: 10.x

    Models

    Definition & Principles

    Read Porto SAP Documentation (#Models).

    Rules

    • All Models MUST extend from App\Ship\Parents\Models\Model.
    • If the name of a model differs from the Container name you have to implement model() method in the repository - more details.

    Folder Structure

     - App
    - Containers
    - {section-name}
    - {container-name}
    - Models
    - User.php
    - UserId.php
    - ...

    Code Sample

    class Demo extends Model
    {
    protected $table = 'demos';

    protected $fillable = [
    'label',
    'user_id'
    ];

    protected $hidden = [
    'token',
    ];

    protected $casts = [
    'total_credits' => 'float',
    ];

    protected $dates = [
    'created_at',
    'updated_at',
    ];

    public function user()
    {
    return $this->belongsTo(\App\Containers\AppSection\User\Models\User::class);
    }
    }

    Notice the Demo Model has a relationship with User Model, which lives in another Container.

    Casts

    The casts attribute can be used to parse any of the model's attributes to a specific type. In the code sample below we can cast total_credits to float.

    More information about the applicable cast-types can be found in the laravel eloquent-mutators documentation.

    You can place any dates inside the $dates to parse those automatically.

    - + \ No newline at end of file diff --git a/docs/10.x/main-components/requests/index.html b/docs/10.x/main-components/requests/index.html index a06236ba0..bc7662aaf 100644 --- a/docs/10.x/main-components/requests/index.html +++ b/docs/10.x/main-components/requests/index.html @@ -4,7 +4,7 @@ Requests | Apiato - + @@ -25,7 +25,7 @@ function.

    This helper, in turn, allows to "redefine" keys in the request for subsequent processing. Consider the following example request:

    {
    "data" : {
    "name" : "John Doe"
    }
    }

    Your Task to process this data, however, requests the field data.name as data.username. You can call the helper like this:

    $request->mapInput([
    'data.name' => 'data.username',
    ]);

    The resulting structure would look like this:

    {
    "data" : {
    "username" : "John Doe"
    }
    }

    Storing Data on the Request

    During the Request life-cycle you may want to store some data on the request object and pass it to other SubActions (or Tasks).

    To store some data on the request use:

    $request->keep(['someKey' => $someValue]);

    To retrieve the data back at any time during the request life-cycle use:

    $someValue = $request->retrieve('someKey');

    Unit Testing for Actions (Request)

    Since we're passing Request objects to Actions. When writing unit tests we need to create fake Request just to pass it to the Action with some fake data.

    // creating
    $request = RegisterUserRequest::injectData($data);

    Example One:

    $data = [
    'email' => 'john@doe.test',
    'name' => 'John Doe',
    'password' => 'so-secret',
    ];

    // create request object with some data
    $request = RegisterUserRequest::injectData($data);

    // create instance of the Action
    $action = app(RegisterUserAction::class)->run($request);

    // do any kind of assertions..
    $this->assertInstanceOf(User::class, $user);

    Example Two (With Authenticated User):

    $data = [
    'store_id' => $this->encode($store->id),
    'items' => $orderItems,
    'recipient' => $receipient,
    ];

    $user = User::factory()->create();

    $request = MakeOrderRequest::injectData($data, $user);

    $order = app(MakeOrderAction::class)->run($request);
    - + \ No newline at end of file diff --git a/docs/10.x/main-components/routes/index.html b/docs/10.x/main-components/routes/index.html index 89eb656fe..c8644f87c 100644 --- a/docs/10.x/main-components/routes/index.html +++ b/docs/10.x/main-components/routes/index.html @@ -4,13 +4,13 @@ Routes | Apiato - +
    Version: 10.x

    Routes

    Definition & Principles

    Read Porto SAP Documentation (#Routes).

    Rules

    • API Route files MUST be named according to their API's version, exposure and functionality. e.g. CreateOrder.v1.public.php, FulfillOrder.v2.public.php, CancelOrder.v1.private.php...

    • Web Route files are pretty similar to API web files, but they can be named anything.

    Folder Structure

     - app
    - Containers
    - {section-name}
    - {container-name}
    - UI
    - API
    - Routes
    - CreateItem.v1.public.php
    - DeleteItem.v1.public.php
    - CreateItem.v2.public.php
    - DeleteItem.v1.private.php
    - ApproveItem.v1.private.php
    - ...
    - WEB
    - Routes
    - main.php
    - ...

    Code Sample

    Web & API route

    Routes are defined exactly like the way you defined them in Laravel.

    Route::post('hello', [Controller::class, 'sayHello']);

    Protected Route (API)

    Route::get('users', [Controller::class, 'listAllUsers'])
    ->middleware(['auth:api']);

    Protect your Endpoints:

    Checkout the Authorization Page.

    Difference between Public & Private routes files

    Apiato has 2 types of endpoint, Public (External) mainly for third parties clients, and Private (Internal) for your own Apps. This will help to generate separate documentations for each and keep your internal API private.

    - + \ No newline at end of file diff --git a/docs/10.x/main-components/subactions/index.html b/docs/10.x/main-components/subactions/index.html index 79eae79af..841d2db70 100644 --- a/docs/10.x/main-components/subactions/index.html +++ b/docs/10.x/main-components/subactions/index.html @@ -4,13 +4,13 @@ Sub Actions | Apiato - +
    Version: 10.x

    Sub Actions

    Definition & Principles

    Read Porto SAP Documentation (#Sub-Actions).

    Rules

    • All SubActions MUST extend from App\Ship\Parents\Actions\SubAction.

    Folder Structure

     - app
    - Containers
    - {section-name}
    - {container-name}
    - Actions
    - ValidateAddressSubAction.php
    - BuildOrderSubAction.php
    - ...

    Code Sample

    ExampleSubAction

    class ExampleSubAction extends SubAction
    {
    public function run(SomeRequest $request)
    {
    app(SomeTask::class)->run($request);
    }
    }
    note

    Every feature available for Actions, are also available in SubActions.

    - + \ No newline at end of file diff --git a/docs/10.x/main-components/tasks/index.html b/docs/10.x/main-components/tasks/index.html index 985ef3391..b4781bde5 100644 --- a/docs/10.x/main-components/tasks/index.html +++ b/docs/10.x/main-components/tasks/index.html @@ -4,13 +4,13 @@ Tasks | Apiato - +
    Version: 10.x

    Tasks

    Definition & Principles

    Read Porto SAP Documentation (#Tasks).

    Rules

    • All Tasks MUST extend from App\Ship\Parents\Tasks\Task.

    Folder Structure

     - app
    - Containers
    - {section-name}
    - {container-name}
    - Tasks
    - ConfirmUserEmailTask.php
    - GenerateEmailConfirmationUrlTask.php
    - SendConfirmationEmailTask.php
    - ValidateConfirmationCodeTask.php
    - SetUserEmailTask.php
    - ...

    Code Sample

    Task

    class FindUserByIdTask extends Task
    {
    private $userRepository;

    public function __construct(UserRepository $userRepository)
    {
    $this->userRepository = $userRepository;
    }

    public function run($id)
    {
    try {
    $user = $this->userRepository->find($id);
    } catch (Exception $e) {
    throw new UserNotFoundException();
    }

    return $user;
    }
    }

    Task usage from an Action

    class ValidateUserEmailByConfirmationCodeAction extends Action
    {
    public function run($userId, $code)
    {
    app(ValidateConfirmationCodeTask::class)->run($userId, $code);
    $user = app(FindUserByIdTask::class)->run($userId);
    app(ConfirmUserEmailTask::class)->run($user);
    }
    }
    - + \ No newline at end of file diff --git a/docs/10.x/main-components/transformers/index.html b/docs/10.x/main-components/transformers/index.html index 8d5015ba0..5d0beb4c8 100644 --- a/docs/10.x/main-components/transformers/index.html +++ b/docs/10.x/main-components/transformers/index.html @@ -4,13 +4,13 @@ Transformers | Apiato - +
    Version: 10.x

    Transformers

    Definition & Principles

    Read Porto SAP Documentation (#Transformers).

    Rules

    • All API responses MUST be formatted via a Transformer.

    • Every Transformer SHOULD extend from App\Ship\Parents\Transformers\Transformer.

    • Each Transformer MUST have a transform() function.

    Folder Structure

     - app
    - Containers
    - {section-name}
    - {container-name}
    - UI
    - API
    - Transformers
    - UserTransformer.php
    - ...

    Code Samples

    Reward Transformer with Country relation

    class ItemTransformer extends Transformer
    {
    protected $availableIncludes = [
    'images',
    ];

    protected $defaultIncludes = [
    'roles',
    ];

    public function transform(Item $item)
    {
    $response = [
    'object' => $item->getResourceKey(),
    'id' => $item->getHashedKey(),
    'name' => $item->name,
    'description' => $item->description,
    'price' => (float)$item->price,
    'weight' => (float)$item->weight,
    'created_at' => $item->created_at,
    'updated_at' => $item->updated_at,
    'readable_created_at' => $item->created_at->diffForHumans(),
    'readable_updated_at' => $item->updated_at->diffForHumans(),
    ];

    // add more or modify data for Admins only
    $response = $this->ifAdmin([
    'real_id' => $item->id,
    'deleted_at' => $item->deleted_at,
    ], $response);

    return $response;
    }

    public function includeImages(Item $item)
    {
    return $this->collection($item->images, new ItemImageTransformer());
    }

    public function includeRoles(User $user)
    {
    return $this->collection($user->roles, new RoleTransformer());
    }
    }

    Usage from Controller (Single Item)

    $user = $this->getUser();

    $this->transform($user, UserTransformer::class);

    // more options are available

    Relationships (include)

    Loading relationships in Transformer (calling other Transformers):

    This can be done in 2 ways:

    1. By the User, he can specify what relations to return in response.

    2. By the Developer, define what relations to include at run time.

    From Front-end

    You can request data with their relationships directly from the API call using include=tags,user but first the Transformer need to have the availableIncludes defined with their functions like this:

    class AccountTransformer extends Transformer
    {
    protected $availableIncludes = [
    'tags',
    'user',
    ];

    public function transform(Account $account)
    {
    return [
    'id' => (int)$account->id,
    'url' => $account->url,
    'username' => $account->username,
    'secret' => $account->secret,
    'note' => $account->note,
    ];
    }

    public function includeTags(Account $account)
    {
    // use collection with `multi` relationship
    return $this->collection($account->tags, new TagTransformer());
    }

    public function includeUser(Account $account)
    {
    // use `item` with single relationship
    return $this->item($account->user, new UserTransformer());
    }
    }

    Now to get the Tags with the response when Accounts are requested pass the ?include=tags parameter with the [GET] request.

    To get Tags with User use the comma separator: ?include=tags,user.

    From Back-end

    From the controller you can dynamically set the DefaultInclude using (setDefaultIncludes) anytime you want.

    return $this->transform($rewards, ProductsTransformer::class)->setDefaultIncludes(['tags']);

    You need to have includeTags function defined on the transformer. Look at the full examples above.

    If you want to include a relation with every response from this transformer you can define the relation directly in the transformer on ($defaultIncludes)

    protected $availableIncludes = [
    'users',
    ];

    protected $defaultIncludes = [
    'tags',
    ];

    // ..

    You need to have includeUser and includeTags functions defined on the transformer. Look at the full examples above.

    Transformer Available helper functions:

    • user() : returns current authenticated user object.

    • ifAdmin($adminResponse, $clientResponse) : merges normal client response with the admin extra or modified results, when current authenticated user is Admin. Look at the full examples above.

    For more information about the Transformers read this.

    - + \ No newline at end of file diff --git a/docs/10.x/main-components/views/index.html b/docs/10.x/main-components/views/index.html index 0b72f64e0..eb3587df2 100644 --- a/docs/10.x/main-components/views/index.html +++ b/docs/10.x/main-components/views/index.html @@ -4,13 +4,13 @@ Views | Apiato - +
    Version: 10.x

    Views

    Definition & Principles

    Read Porto SAP Documentation (#Views).

    Rules

    • Views SHOULD be created inside the Containers, and they will be automatically available for use in the Web Controllers.

    Folder Structure

     - app
    - Containers
    - {section-name}
    - {container-name}
    - UI
    - WEB
    - Views
    - welcome.php
    - profile.php
    - ...

    Code Sample

    Welcome page View

    <!DOCTYPE html>
    <html>
    <head>
    <title>Welcome</title>
    </head>
    <body>
    <div class="container">
    <div class="content">
    <div class="title">Welcome</div>
    </div>
    </div>
    </body>
    </html>

    Usage From Controller

    class Controller extends WebController
    {
    public function sayWelcome()
    {
    return view('just-welcome');
    }
    }

    Namespaces

    • By default, all Views are namespaced as the camelCase of its Section name + @ + camelCase of its Container name.

    For example, a view named welcome-page inside MySection > MyContainer can be accessed like this: view(mySection@myContainer::welcome-page)

    If you try to access it without the namespace view('just-welcome'), it will not find your View.

    note

    View files in Ship folder are exception to this and will be namespaced with the word "ship" instead of section name, e.g. view(ship::welcome-page)

    - + \ No newline at end of file diff --git a/docs/10.x/miscellaneous/tasks-queuing/index.html b/docs/10.x/miscellaneous/tasks-queuing/index.html index eae59616d..ec1c40a6d 100644 --- a/docs/10.x/miscellaneous/tasks-queuing/index.html +++ b/docs/10.x/miscellaneous/tasks-queuing/index.html @@ -4,7 +4,7 @@ Tasks Queuing | Apiato - + @@ -18,7 +18,7 @@

    The only addition to the Laravel's queues in Apiato, is that by default, apiato detects which queue driver you are planning to use (based on the configs), to create the migration files required, in case type database is used.

    if (Config::get('queue.default') == 'database')
    {
    // do something
    }

    (refer to app/Ship/Migrations/ folder for more details).

    Beanstalkd

    In order to use Beanstalkd as your queue driver, you need to require the "pda/pheanstalk": "^4.0" package first. You can include this in any composer.json file you want.

    Further reading

    More info at Laravel Docs.

    - + \ No newline at end of file diff --git a/docs/10.x/miscellaneous/tasks-scheduling/index.html b/docs/10.x/miscellaneous/tasks-scheduling/index.html index 890e3d7f2..5d845807c 100644 --- a/docs/10.x/miscellaneous/tasks-scheduling/index.html +++ b/docs/10.x/miscellaneous/tasks-scheduling/index.html @@ -4,7 +4,7 @@ Tasks Scheduling | Apiato - + @@ -18,7 +18,7 @@ See the Commands Page.

    Once you have your command ready, go to app/Ship/Kernels/ConsoleKernel.php and start adding the commands you need to schedule inside the schedule function.

    Example:

    protected function schedule(Schedule $schedule)
    {
    $schedule->command('apiato:welcome')->everyMinute();
    $schedule->job(new myJob)->hourly();
    $schedule->exec('touch me.txt')->dailyAt('12:00');
    // ...
    }

    More details here.

    note

    You do not need to register the commands with the $commands property or point to them in the commands() function. Apiato will do that automatically for you.

    Further reading

    More info at Laravel Docs.

    - + \ No newline at end of file diff --git a/docs/10.x/miscellaneous/tests-helpers/index.html b/docs/10.x/miscellaneous/tests-helpers/index.html index f3277dae2..921470ab9 100644 --- a/docs/10.x/miscellaneous/tests-helpers/index.html +++ b/docs/10.x/miscellaneous/tests-helpers/index.html @@ -4,7 +4,7 @@ Tests Helpers | Apiato - + @@ -24,7 +24,7 @@ testing data.

    1. Go to app/Ship/Seeder/SeedTestingData.php seeder class, and create your live testing data.

    2. Run this command php artisan apiato:seed-test

    Debugging with PsySH

    For better debugging and development, you can open a runtime developer console while executing your test.

    Using PsySH (interactive debugger and REPL "read-eval-print loop" for PHP). The package is required by the Laravel Tinker Package.

    To use it set the breakpoint eval(\Psy\sh()); anywhere you want in any Actions, Controllers, Tasks... and run your test normally.

    - + \ No newline at end of file diff --git a/docs/10.x/optional-components/commands/index.html b/docs/10.x/optional-components/commands/index.html index 064dcf470..98400dcb9 100644 --- a/docs/10.x/optional-components/commands/index.html +++ b/docs/10.x/optional-components/commands/index.html @@ -4,13 +4,13 @@ Commands | Apiato - +
    Version: 10.x

    Commands

    Definition

    • Commands are a Laravel artisan command. Laravel has its own default commands, and you can create your own as well.
    • Commands provide a way to interact with the Laravel app.
    • A Command can be scheduled by a Task scheduler, like Cron Job or by the Laravel built-in wrapper of the Cron Job "laravel scheduler".
    • Commands could be Closure based or Classes.
    • "dispatch" is the term that is usually used to call a Command.

    Principles

    • Containers MAY or MAY NOT have one or more Commands.

    • Every Command SHOULD call an Action to perform its job, and should not contain any business logic.

    • Ship may contain Application general Commands.

    Rules

    • All Commands MUST extend from App\Ship\Parents\Commands\ConsoleCommand.

    Folder Structure

    - app
    - Containers
    - {section-name}
    - {container-name}
    - UI
    - CLI
    - Commands
    - SayHelloCommand.php
    - ...
    - Ship
    - Commands
    - GeneralCommand.php
    - ...

    Code Samples

    A Simple Command

    class HelloWorldCommand extends ConsoleCommand
    {
    protected $signature = 'hello:world';
    protected $description = 'Hello World!';

    public function handle()
    {
    echo "Hello World :)\n";
    }
    }

    Usage from CLI (Terminal)

    php artisan hello:world

    Schedule Commands Execution

    To Schedule the execution of a Command checkout the Tasks Scheduling page.

    Define Consoles Routes

    To define Console route go to app/Ship/Commands/Routes.php.

    Further reading

    More info at Laravel Docs.

    - + \ No newline at end of file diff --git a/docs/10.x/optional-components/configs/index.html b/docs/10.x/optional-components/configs/index.html index 8cf21b6b9..cce65fe8d 100644 --- a/docs/10.x/optional-components/configs/index.html +++ b/docs/10.x/optional-components/configs/index.html @@ -4,13 +4,13 @@ Configs | Apiato - +
    Version: 10.x

    Configs

    Definition

    Configs are files that contain configurations.

    In each Apiato container, there are two types of config files:

    • the container specific config file (a config file that contains the container specific configurations).
    • the container third party packages config files (a config file that belongs to a third party package, required by the composer file of the container).

    Principles

    • Your custom config files and third party packages config files, should be placed in the Container, unless it's too generic then it can be placed on the Ship Layer.
    • Containers can have as many config files as they need.

    Rules

    • When publishing a third party package config file, move it manually to its container or to the Ship Configs folder in case it is generic.
    • Framework config files (provided by Laravel) lives at the default config directory on the root of the project.
    • You SHOULD NOT add any config file to the root config directory.
    • The container specific config file, MUST be named this way:
      camelCase of its Section name + - + camelCase of its Container name, to prevent conflicts between third party packages and container specific packages.
      For example, config file inside MySection > MyContainer should be named like this: mySection-myContainer.php

    Folder Structure

    - app
    - Containers
    {section-name}
    - {container-name}
    - Configs
    - {section-name}-{container-name}.php
    - package-config-file1.php
    - ...
    - Ship
    - Configs
    - apiato.php
    - ...
    - config
    - app.php
    - ...

    Code Samples

    Example simple Config file

    // app/Containers/{SectionName}/{ContainerName}/Configs/{section-name}-{container-name}.php
    return [

    /*
    |--------------------------------------------------------------------------
    | Default Namespace
    |--------------------------------------------------------------------------
    */
    'namespace' => 'App',

    // some other config params here...

    You can access the respective configuration key like this:

    $value = Config::get('{section-name}-{container-name}.namespace');     // returns 'App'
    $value = config('{section-name}-{container-name}.namespace'); // same, but using laravel helper function
    Further reading

    More info at Laravel Docs.

    - + \ No newline at end of file diff --git a/docs/10.x/optional-components/criterias/index.html b/docs/10.x/optional-components/criterias/index.html index defe27f47..569bc8658 100644 --- a/docs/10.x/optional-components/criterias/index.html +++ b/docs/10.x/optional-components/criterias/index.html @@ -4,13 +4,13 @@ Criterias | Apiato - +
    Version: 10.x

    Criterias

    Definition

    Criterias are classes that hold and apply query condition when retrieving data from the database through a Repository.

    Without using a Criteria class, you can add your query conditions to a Repository or to a Model as scope, but with Criterias, your query conditions can be shared across multiple Models and Repositories. It allows you to define the query condition once and use it anywhere in the App.

    Principles

    • Every Container MAY have its own Criterias. However, shared Criterias SHOULD be created in the Ship layer.

    • A Criteria MUST not contain any extra code, if it needs data, the data SHOULD be passed to it from the Actions or the Task. It SHOULD not call any Task for data.

    Rules

    • All Criterias MUST extend from App\Ship\Parents\Criterias\Criteria.

    • Every Criteria SHOULD have an apply() function.

    Folder Structure

     - app
    - Containers
    - {section-name}
    - {container-name}
    - Data
    - Criterias
    - ColourRedCriteria.php
    - RaceCarsCriteria.php
    - ...
    - Ship
    - Criterias
    - CreatedTodayCriteria.php
    - NotNullCriteria.php
    - ...

    Code Samples

    A Shared Criteria

    class OrderByCreationDateDescendingCriteria extends Criteria
    {
    public function apply($model, PrettusRepositoryInterface $repository)
    {
    return $model->orderBy('created_at', 'desc');
    }
    }

    Usage from Task

    public function run()
    {
    $this->userRepository->pushCriteria(new OrderByCreationDateDescendingCriteria());
    return $this->userRepository->paginate();
    }

    Criteria Accepting Data Input

    class ThisUserCriteria extends Criteria
    {
    private $userId;

    public function __construct($userId)
    {
    $this->userId = $userId;
    }

    public function apply($model, PrettusRepositoryInterface $repository)
    {
    return $model->where('user_id', '=', $this->userId);
    }
    }

    Passing Data from Task to Criteria

    public function run($user)
    {
    $this->accountRepository->pushCriteria(new ThisUserCriteria($user->id));
    return $this->accountRepository->paginate();
    }

    Further reading

    More info at Laravel Docs.

    - + \ No newline at end of file diff --git a/docs/10.x/optional-components/events/index.html b/docs/10.x/optional-components/events/index.html index 71fd59bce..68d485a8f 100644 --- a/docs/10.x/optional-components/events/index.html +++ b/docs/10.x/optional-components/events/index.html @@ -4,13 +4,13 @@ Events | Apiato - +
    Version: 10.x

    Events

    Definition

    • Events provide a simple observer implementation, allowing you to subscribe and listen for various events that occur in your application.
    • Events are classes that can be fired from anywhere in your application.
    • An event class will usually be bound to one, or many Events Listeners Classes or has those Listeners registered to listen to it.
    • "fire" is the term that is usually used to call an Event.

    Principles

    • Events can be fired from Actions and or Tasks. It's preferable to choose one place only. (Tasks are recommended).
    • Events SHOULD be created inside the Containers. However, general Events CAN be created in the Ship layer.

    Rules

    • Event classes CAN be placed inside the Containers in Events folders or on the Ship for the general Events.
    • All Events MUST extend from App\Ship\Parents\Events\Event.

    Folder Structure

    - App
    - Containers
    - {section-name}
    - {container-name}
    - Events
    - SomethingHappenedEvent.php
    - ...
    - Listeners
    - ListenToMusicListener.php
    - ...

    - Ship
    - Events
    - GlobalStateChanged.php
    - SomethingBiiigHappenedEvent.php
    - ...

    Usage

    In Laravel, you can create and register events in multiple way. Read Laravel documentation to learn more about Events.

    Your custom EventServiceProvider needs to be registered in the containers MainServiceProvider as well.

    Broadcasting

    To define Broadcasting route go to app/Ship/Boardcasts/Routes.php.

    Further reading

    More info at Laravel Docs.

    - + \ No newline at end of file diff --git a/docs/10.x/optional-components/factories/index.html b/docs/10.x/optional-components/factories/index.html index c8ffad2ac..dd045bb4f 100644 --- a/docs/10.x/optional-components/factories/index.html +++ b/docs/10.x/optional-components/factories/index.html @@ -4,13 +4,13 @@ Factories | Apiato - +
    Version: 10.x

    Factories

    Definition

    Factories (are a short name for Model Factories).

    Factories are used to generate some fake data with the help of Faker to be used for testing purposes.

    Factories are mainly used from Tests.

    Principles

    • Factories SHOULD be created in the Containers.

    Rules

    • All Factories MUST extend from App\Ship\Parents\Factories\Factory.

    Folder Structure

     - app
    - Containers
    - {section-name}
    - {container-name}
    - Data
    - Factories
    - UserFactory.php
    - ...

    Code Samples

    A User Model Factory

    class UserFactory extends Factory
    {
    protected $model = User::class;

    public function definition(): array
    {
    static $password;

    return [
    'name' => $this->faker->name,
    'email' => $this->faker->unique()->safeEmail,
    'password' => $password ?: $password = Hash::make('testing-password'),
    'email_verified_at' => now(),
    'remember_token' => Str::random(10),
    'is_admin' => false,
    ];
    }
    }

    Usage from Tests or Anywhere Else

    // creating 4 users
    User::factory()->count(4)->create();
    Further reading

    More info at Laravel Docs.

    - + \ No newline at end of file diff --git a/docs/10.x/optional-components/helpers/index.html b/docs/10.x/optional-components/helpers/index.html index 95bc347bb..3f06a109f 100644 --- a/docs/10.x/optional-components/helpers/index.html +++ b/docs/10.x/optional-components/helpers/index.html @@ -4,13 +4,13 @@ Helpers | Apiato - +
    Version: 10.x

    Helpers

    Definition

    • Helpers are global PHP functions that you can call from anywhere in your application.
    • Helper files are simple PHP files that hold functions.

    Principles

    • Helpers SHOULD be created inside the Containers. However, general Helpers CAN be created in the Ship layer.
    • You can create as many helper files as you need, per container.
    • You can implement as many helper functions as you need, per helper file.
    • All Helper files will be autoloaded by the framework.

    Rules

    • Helpers CAN be placed inside the Containers in Helpers folder or on the Ship for the general Helpers.

    Folder Structure

    - App
    - Containers
    - {section-name}
    - {container-name}
    - Helpers
    - helpers.php
    - mix.php
    - ...

    - Ship
    - Helpers
    - helpers.php
    - mix.php
    - ...

    Usage

    if (!function_exists('add')) {
    function add(int $firstNumber, int $secondNumber): int
    {
    return $firstNumber + $secondNumber;
    }
    }
    - + \ No newline at end of file diff --git a/docs/10.x/optional-components/jobs/index.html b/docs/10.x/optional-components/jobs/index.html index 598153a7d..3026ed182 100644 --- a/docs/10.x/optional-components/jobs/index.html +++ b/docs/10.x/optional-components/jobs/index.html @@ -4,13 +4,13 @@ Jobs | Apiato - +
    Version: 10.x

    Jobs

    Definition

    • Jobs are simple classes that can do one thing or multiple related things.
    • Job is a name given to a class that is usually created to be queued (it's execution is usually deferred for later, after the execution of previous Jobs are completed).
    • Jobs can be scheduled to be executed later by a queuing mechanism (a queue system like beanstalkd).
    • When a Job class is dispatched, it performs its specific job and dies.
    • Laravel's queue worker will process every Job as it's pushed onto the queue.

    Principles

    • A Container MAY have more than one Job.

    Rules

    • All Jobs MUST extend from App\Ship\Parents\Jobs\Job.

    Folder Structure

    - app
    - Containers
    - {section-name}
    - {container-name}
    - Jobs
    - DoSomethingJob.php
    - DoSomethingElseJob.php

    Code Samples

    DemoJob

    class DemoJob extends Job
    {
    private $something;

    public function __construct(array $someData)
    {
    $this->something = $someData;
    }

    public function handle()
    {
    foreach ($this->something as $thing) {
    // do whatever you like
    }
    }
    }

    Check the parent Job class.

    Usage from Action

    // using helper function
    dispatch(new DemoJob($someData));

    // manually
    app(\Illuminate\Contracts\Bus\Dispatcher\Dispatcher::class)->dispatch(New DemoJob($someData));

    Execute Jobs Execution

    For running your Jobs checkout the Tasks Queuing page.

    Further reading

    More info at Laravel Docs.

    - + \ No newline at end of file diff --git a/docs/10.x/optional-components/languages/index.html b/docs/10.x/optional-components/languages/index.html index 9c1397ab5..90a1bc5a9 100644 --- a/docs/10.x/optional-components/languages/index.html +++ b/docs/10.x/optional-components/languages/index.html @@ -4,14 +4,14 @@ Languages | Apiato - +
    Version: 10.x

    Languages

    Definition

    Languages are not real Components, they are just files that holds translations.

    Rules

    • Languages CAN be placed inside the Containers. However, the default laravel resources/lang languages files are still loaded and can be used as well.

    • All Translations are namespaced as the camelCase of its Section name + @ + camelCase of its Container name.
      For example, translation key inside a translation file named messages inside MySection > MyContainer can be accessed like this: __(mySection@myContainer::messages.welcome)

    Folder Structure

    - app
    - Containers
    - {section-name}
    - {container-name}
    - Resources
    - Languages
    - en
    - messages.php
    - users.php
    - ar
    - messages.php
    - users.php

    Usage

    Nothing much to show here, here's how you use translated strings:

    __('mySection@myContainer::messages.welcome');
    Further reading

    More info at Localization.

    - + \ No newline at end of file diff --git a/docs/10.x/optional-components/mails/index.html b/docs/10.x/optional-components/mails/index.html index bbec7e13f..b85a553d9 100644 --- a/docs/10.x/optional-components/mails/index.html +++ b/docs/10.x/optional-components/mails/index.html @@ -4,13 +4,13 @@ Mails | Apiato - +
    Version: 10.x

    Mails

    Definition

    The Mail component allows you to describe an email and send it whenever needed.

    Principles

    • Containers MAY or MAY NOT have one or more Mail.

    • Ship may contain general Mails.

    Rules

    • All Notifications MUST extend from App\Ship\Parents\Mails\Mail.
    • Email Templates must be placed inside the Mail directory in a Templates directory app/Containers/{section}/{container}/Mails/Templates.

    Folder Structure

    - app
    - Containers
    - {section-name}
    - {container-name}
    - Mails
    - UserRegisteredMail.php
    - ...
    - Templates
    - user-registered.blade.php
    - ...
    - Ship
    - Mails
    - SomeMail.php
    - ...
    - Templates
    - some-template.blade.php
    - ...

    Code Samples

    A simple Mail

    class UserRegisteredMail extends Mail implements ShouldQueue
    {
    use Queueable;

    protected $user;

    public function __construct(User $user)
    {
    $this->user = $user;
    }

    public function build()
    {
    return $this->view('appSection@user::user-registered')
    ->to($this->user->email, $this->user->name)
    ->with([
    'name' => $this->user->name,
    ]);
    }
    }

    Usage from an Action

    Notifications can be sent from Actions or Tasks using the Mail Facade.

    Mail::send(new UserRegisteredMail($user));

    Email Templates

    Templates should be placed inside a folder Templates inside the Mail folder.

    To access a Mail template (same like accessing a web view) you must call the camelCase of its Section name + @ + camelCase of its Container name.

    In the example below we're using the user-registered.blade.php template in the AppSection Section > User Container.

    $this->view('appSection@user::user-registered');

    Configure Emails

    Open the .env file and set the from mail and address. This will be used globally whenever the from function is not called in the Mail.

    MAIL_FROM_ADDRESS=test@test.test
    MAIL_FROM_NAME="apiato"

    To use different email address in some classes add ->to($this->email, $this->name) to the build function in your Mail class.

    By default Apiato is configured to use Log Driver MAIL_DRIVER=log, you can change that from the .env file.

    Queueing A Notification

    To queue a notification you should use Illuminate\Bus\Queueable and implement Illuminate\Contracts\Queue\ShouldQueue.

    Further reading

    More info at Laravel Docs.

    - + \ No newline at end of file diff --git a/docs/10.x/optional-components/middlewares/index.html b/docs/10.x/optional-components/middlewares/index.html index 37e03c9a6..46a271522 100644 --- a/docs/10.x/optional-components/middlewares/index.html +++ b/docs/10.x/optional-components/middlewares/index.html @@ -4,7 +4,7 @@ Middlewares | Apiato - + @@ -12,7 +12,7 @@
    Version: 10.x

    Middlewares

    Definition

    Middleware provide a convenient mechanism for filtering HTTP requests entering your application.

    You can enable and disable Middlewares as you wish.

    Principles

    • There are two types of Middlewares, General (applied on all the Routes by default) and Endpoints Middlewares (applied on some Endpoints).

    • The Middlewares CAN be placed in Ship layer or Container layer depending on its roles.

    Rules

    • If a Middleware is written inside a Container then it MUST be registered inside that Container.

    • To register Middlewares in a Container the container needs to have a MiddlewareServiceProvider, and like all other Container Providers it MUST be registered in the MainServiceProvider of that Container.

    • General Middlewares SHOULD live in the Ship layer app/Ship/Middlewares/* and are registered in the app/Ship/Kernels/HttpKernel.

    • Third Party packages Middleware CAN be registered in Containers or on the Ship layer (wherever they make more sense). For example the jwt.auth middleware "provided by the JWT package" should be registered in the Authentication Container (Containers/AppSection/Authentication/Providers/MiddlewareServiceProvider.php).

    Folder Structure

     - App
    - Containers
    - {section-name}
    - {container-name}
    - Middlewares
    - WebAuthentication.php
    - Ship
    - Middleware
    - Http
    - EncryptCookies.php
    - VerifyCsrfToken.php

    Code Sample

    Middleware Registration Inside the Container Example

    class MiddlewareServiceProvider extends MiddlewareProvider
    {
    protected array $middlewares = [
    // ..
    ];

    protected array $middlewareGroups = [
    'web' => [
    // ..
    ],
    'api' => [
    // ..
    ],
    ];

    protected array $routeMiddleware = [
    // apiato User Authentication middleware for Web Pages
    'guest' => RedirectIfAuthenticated::class
    ];
    }

    Middleware Registration Inside the Ship Layer (HTTP Kernel)

    class HttpKernel extends LaravelHttpKernel
    {
    /**
    * The application's global HTTP middleware stack.
    *
    * These middleware are run during every request to your application.
    *
    * @var array
    */
    protected $middleware = [
    // Laravel middleware's
    // \App\Http\Middleware\TrustHosts::class,
    TrustProxies::class,
    HandleCors::class,
    PreventRequestsDuringMaintenance::class,
    ValidatePostSize::class,
    TrimStrings::class,
    ConvertEmptyStringsToNull::class,
    ];

    /**
    * The application's route middleware groups.
    *
    * @var array
    */
    protected $middlewareGroups = [
    'web' => [
    EncryptCookies::class,
    AddQueuedCookiesToResponse::class,
    StartSession::class,
    // \Illuminate\Session\Middleware\AuthenticateSession::class,
    ShareErrorsFromSession::class,
    VerifyCsrfToken::class,
    SubstituteBindings::class,
    ],

    'api' => [
    // Note: The "throttle" Middleware is registered by the RoutesLoaderTrait in the Core
    SubstituteBindings::class,
    ValidateJsonContent::class,
    ProcessETagHeadersMiddleware::class,
    ProfilerMiddleware::class,
    ],
    ];

    /**
    * The application's route middleware.
    *
    * These middleware may be assigned to groups or used individually.
    *
    * @var array
    */
    protected $routeMiddleware = [
    'auth' => Authenticate::class,
    // 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
    'cache.headers' => SetCacheHeaders::class,
    // Note: The "can" Middleware is registered by MiddlewareServiceProvider in Authorization Container
    // 'can' => \Illuminate\Auth\Middleware\Authorize::class,
    // Note: The "guest" Middleware is registered by MiddlewareServiceProvider in Authentication Container
    // 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
    'password.confirm' => RequirePassword::class,
    'signed' => ValidateSignature::class,
    'throttle' => ThrottleRequests::class,
    'verified' => EnsureEmailIsVerified::class,
    ];

    /**
    * The priority-sorted list of middleware.
    *
    * Forces non-global middleware to always be in the given order.
    *
    * @var string[]
    */
    protected $middlewarePriority = [
    EncryptCookies::class,
    StartSession::class,
    ShareErrorsFromSession::class,
    Authenticate::class,
    ThrottleRequests::class,
    AuthenticateSession::class,
    SubstituteBindings::class,
    Authorize::class,
    ];
    }
    Further reading

    More info at Laravel Docs.

    - + \ No newline at end of file diff --git a/docs/10.x/optional-components/migrations/index.html b/docs/10.x/optional-components/migrations/index.html index b11560316..39a25af1b 100644 --- a/docs/10.x/optional-components/migrations/index.html +++ b/docs/10.x/optional-components/migrations/index.html @@ -4,13 +4,13 @@ Migrations | Apiato - +
    Version: 10.x

    Migrations

    Definition

    Migrations (are the short name for Database Migrations).

    Migrations are the version control of your database. They are very useful for generating and documenting the database tables.

    Principles

    • Migrations SHOULD be created inside the Containers folders.

    • Migrations will be autoloaded by the framework.

    Rules

    • No need to publish the DB Migrations. Just run the artisan migrate command and Laravel will read the Migrations from the Containers.

    Folder Structure

       - app
    - Containers
    - {section-name}
    - {container-name}
    - Data
    - Migrations
    - 2200_01_01_000001_create_something_table.php
    - ...

    Code Samples

    User CreateDemoTable Migrations

    class CreateDemoTable extends Migration
    {
    public function up()
    {
    Schema::create('demos', function (Blueprint $table) {
    $table->increments('id');
    // ...
    $table->timestamps();
    $table->softDeletes();
    });
    }

    public function down()
    {
    Schema::drop('demos');
    }
    }

    Further reading

    More info at Laravel Docs.

    - + \ No newline at end of file diff --git a/docs/10.x/optional-components/notifications/index.html b/docs/10.x/optional-components/notifications/index.html index 49d298900..1cd732939 100644 --- a/docs/10.x/optional-components/notifications/index.html +++ b/docs/10.x/optional-components/notifications/index.html @@ -4,7 +4,7 @@ Notifications | Apiato - + @@ -12,7 +12,7 @@
    Version: 10.x

    Notifications

    Definition

    Notifications allow you to inform the user about a state changes in your application.

    The Laravel notifications supports sending notifications across a variety of channels (mail, SMS, Slack, Database...).

    When using the Database channel, the notifications will be stored in a database to be displayed in your client interface.

    Principles

    • Containers MAY or MAY NOT have one or more Notification.

    • Ship MAY contain Application general Notifications.

    Rules

    • All Notifications MUST extend from App\Ship\Parents\Notifications\Notification.

    Folder Structure

    - app
    - Containers
    - {select-name}
    - {container-name}
    - Notifications
    - UserRegisteredNotification.php
    - ...
    - Ship
    - Notifications
    - SystemFailureNotification.php
    - ...

    Code Samples

    A Simple Notification

    class BirthdayReminderNotification extends Notification implements ShouldQueue
    {
    use Queueable;

    protected $notificationMessage;

    public function __construct($notificationMessage)
    {
    $this->notificationMessage = $notificationMessage;
    }

    public function toArray($notifiable)
    {
    return [
    'content' => $this->notificationMessage,
    ];
    }

    public function toMail($notifiable)
    {
    // $notifiable is the object you want to notify "e.g. user"
    return (new MailMessage)
    ->subject("Hello World")
    ->line("Hi, $notifiable->name")
    ->line($this->notificationMessage);
    }

    public function toSms($notifiable)
    {
    // ...
    }

    // ...
    }

    Usage from an Action or Task

    Notifications can be sent from Actions or Tasks using the Notification Facade.

    \Notification::send($user, new BirthdayReminderNotification($notificationMessage));

    Alternatively you can use the Illuminate\Notifications\Notifiable trait on the notifiable object "e.g. User" and then call it as follows:

    // call notify, found on the Notifiable trait
    $user->notify(new BirthdayReminderNotification($notificationMessage));

    Select Channels

    To select a notification channel, apiato have the app/Ship/Configs/notification.php config file where you can define the array of supported channels "e.g. SMS, Email, WebPush...", to be used for all your notifications.

    If you want to override the configuration for some notifications classes, or if you prefer to define the channels within each notification class itself, you can override the via function public function via($notifiable) in the notification class and define your channels.

    Checkout laravel notification channels for list of supported integrations.

    Queueing a Notification

    To queue a notification you should use Illuminate\Bus\Queueable and implement Illuminate\Contracts\Queue\ShouldQueue.

    Use DB channel

    Generally you need to generate the notification migration php artisan notifications:table, then run php artisan migrate, however just running the migration command will do the job, since Apiato already adds the _create_notifications_table.php in the default migrations files directory app/Ship/Migrations/.

    Further reading

    More info at Laravel Docs.

    - + \ No newline at end of file diff --git a/docs/10.x/optional-components/providers/index.html b/docs/10.x/optional-components/providers/index.html index 76ebafaa7..7a5bfa0af 100644 --- a/docs/10.x/optional-components/providers/index.html +++ b/docs/10.x/optional-components/providers/index.html @@ -4,7 +4,7 @@ Providers | Apiato - + @@ -14,7 +14,7 @@ In apiato those providers have been renamed and moved to the Ship Layer app/Ship/Parents/Providers/*:

    • AppServiceProvider
    • RouteServiceProvider
    • AuthServiceProvider
    • BroadcastServiceProvider
    • EventsServiceProvider
    note

    You should not touch those providers, instead you have to extend them from a containers providers in order to modify them. Example: the app/Containers/AppSection/Authentication/Providers/AuthProvider.php is extending the AuthServiceProvider to modify it.

    Those providers are not auto registered by default, thus writing any code there will not be available, unless you extend them. Once extended the child Provider should be registered in its Container Main Provider, which makes its parent available.

    This rule does not apply to the RouteServiceProvider since it's required by Apiato, this Provider is registered by the ShipProvider.

    Check How Service Providers are auto-loaded.

    Further reading

    More info at Laravel Docs.

    - + \ No newline at end of file diff --git a/docs/10.x/optional-components/repositories/index.html b/docs/10.x/optional-components/repositories/index.html index 6b30afef1..d442b5989 100644 --- a/docs/10.x/optional-components/repositories/index.html +++ b/docs/10.x/optional-components/repositories/index.html @@ -4,13 +4,13 @@ Repositories | Apiato - +
    Version: 10.x

    Repositories

    Definition

    The Repository classes are an implementation of the Repository Design Pattern.

    Their major roles are separating the business logic from the data (or the data access Task).

    Repositories save and retrieves Models to/from the underlying storage mechanism.

    The Repository is used to separate the logic that retrieves the data and maps it to a Model, from the business logic that acts on the Model.

    Principles

    • Every Model SHOULD have a Repository.

    • A Model SHOULD always get accessed through its Repository. (Never accessed directly).

    Rules

    • All Repositories MUST extend from App\Ship\Parents\Repositories\Repository. Extending from this class will give you access to methods like (find, create, update and much more).

    • Repository name should be same as it's model name (model: Foo -> repository: FooRepository).

    • If a Repository belongs to a Model whose name is not equal to its Container name, then the Repository implement model() method like this.

    Folder Structure

     - app
    - Containers
    - {section-name}
    - {container-name}
    - Data
    - Repositories
    - UserRepository.php
    - ...

    Code Samples

    Demo Repository

    class DemoRepository extends Repository
    {
    protected $fieldSearchable = [
    'name' => 'like',
    'email' => '=',
    ];
    }

    Usage

    // paginate the data by 10
    $users = $userRepository->paginate(10);

    // search by 1 field
    $cars = $carRepository->findByField('colour', $colour);

    // searching multiple fields
    $offer = $offerRepository->findWhere([
    'offer_id' => $offer_id,
    'user_id' => $user_id,
    ])->first();

    //....

    Different Model and Container Name

    The model() method must be implemented when the model has different name than the container.

    class DemoRepository extends Repository
    {
    // ...

    public function model(): string
    {
    return Demo::class;
    }
    }

    Other Properties:

    API Query Parameters Property

    To enable query parameters (?search=text,...) in your API you need to set the property $fieldSearchable on the Repository class, to instruct the querying on your model. More details.

        protected $fieldSearchable = [
    'name' => 'like',
    'email' => '=',
    ];

    All other Properties

    Apiato uses the l5-repository package, to provide a lot of powerful features to the repository class.

    Further reading

    To learn more about all the properties you can use, visit the andersao/l5-repository package documentation.

    - + \ No newline at end of file diff --git a/docs/10.x/optional-components/seeders/index.html b/docs/10.x/optional-components/seeders/index.html index c2aad4dc0..ea9b756b3 100644 --- a/docs/10.x/optional-components/seeders/index.html +++ b/docs/10.x/optional-components/seeders/index.html @@ -4,13 +4,13 @@ Seeders | Apiato - +
    Version: 10.x

    Seeders

    Definition

    Seeders (are a short name for Database Seeders).

    Seeders are classes made to seed the database with real data, this data usually should exist in the Application after the installation (Example: the default Users Roles and Permissions or the list of Countries).

    Principles

    • Seeders SHOULD be created in the Containers. (If the container is using a package that publishes a Seeder class, this class should be manually placed in the Container that make use of it. Do not rely on the package to place it in its right location).

    Rules

    • Seeders should be in the right directory inside the container to be loaded.

    • To avoid any conflict between containers seeders classes, you SHOULD always prepend the Seeders of each container with the container name. (Example: UserPermissionsSeeder, ItemPermissionsSeeder).

      note

      If 2 seeders classes have the same name but live in different containers, one of them will not be loaded. In these situations you can also prepend the seeder name with the section name

    • If you wish to order the seeding of the classes, you can just append _1, _2 to your classes.

    Folder Structure

     - App
    - Containers
    - {section-name}
    - {container-name}
    - Data
    - Seeders
    - ContainerNameRolesSeeder_1.php
    - ContainerNamePermissionsSeeder_2.php
    - ...

    Code Samples

    Demo Seeder

    class DemoSeeder_1 extends Seeder
    {
    public function run()
    {
    app(CreateRoleTask::class)->run('admin', 'Administrator', 'Administrator Role', 999);
    // ...
    }
    }
    note

    Same Seeder class is allowed to contain seeding for multiple Models.

    Run the Seeders

    After registering the Seeders you can run this command:

    php artisan db:seed

    Migrate & seed at the same time

    php artisan migrate --seed

    Testing Seeder Command

    It's useful sometimes to create a big set of testing data. apiato facilitates this task:

    1. Open app/Ship/Seeders/SeedTestingData.php and write your testing data here.
    2. Run this command any time you want this data available (example at staging servers):
    php artisan apiato:seed-test
    Further reading

    More info at Laravel Docs.

    - + \ No newline at end of file diff --git a/docs/10.x/optional-components/tests/index.html b/docs/10.x/optional-components/tests/index.html index 7453e7e9c..9a3bb8bbd 100644 --- a/docs/10.x/optional-components/tests/index.html +++ b/docs/10.x/optional-components/tests/index.html @@ -4,13 +4,13 @@ Tests | Apiato - +
    Version: 10.x

    Tests

    Definition

    Tests classes are created to test if the Application classes are working as expected.

    The two most essential Test types for this architecture are the Unit Tests and the Functional Tests. However, Integration and Acceptance Tests can be used as well.

    Principles

    • Containers MAY be covered by all types of Tests.

    • Use Functional Tests to test Container Routes are doing what's expected from them.

    • Use Unit Tests to test Container Actions and Tasks are doing what's expected from them.

    Rules

    • All Container Test classes SHOULD extend from a Container Internal TestCase class {container-name}/Tests/TestCase.php. The container TestCase MUST extend main TestCase on Ship layer App\Ship\Parents\Tests\PhpUnit\TestCase. (Adding functions to the container TestCase allows sharing those functions between all Test classes of the Container).

    Folder Structure

     - app
    - Containers
    - {section-name}
    - {container-name}
    - Tests
    - TestCase.php // the container test case
    - Unit
    - CreateUserTest.php
    - UpdateUserTest.php
    - ...
    - UI
    - API
    - Tests
    - Functional
    - LoginTest.php
    - LogoutTest.php
    - ...
    - WEB
    - Tests
    - Functional
    - LoginTest.php
    - LogoutTest.php
    - ...
    - CLI
    - Tests
    - Functional
    - BackupDataTest.php
    - ...

    Code Sample

    class DeleteUserTest extends TestCase
    {
    protected $endpoint = 'delete@v1/users/{id}';

    protected array $access = [
    'roles' => '',
    'permissions' => 'delete-users',
    ];

    public function testDeleteExistingUser()
    {
    $user = $this->getTestingUser();

    $response = $this->injectId($user->id)->makeCall();

    $response->assertStatus(204);
    }
    }

    See the Tests Helpers Page

    - + \ No newline at end of file diff --git a/docs/10.x/optional-components/values/index.html b/docs/10.x/optional-components/values/index.html index fc6e41ec0..2aeaaffd6 100644 --- a/docs/10.x/optional-components/values/index.html +++ b/docs/10.x/optional-components/values/index.html @@ -4,7 +4,7 @@ Values | Apiato - + @@ -12,7 +12,7 @@
    Version: 10.x

    Values

    Definition & Principles

    Values are short names for the known "Value Objects" which are simple Objects, pretty similar to Models in the concept of representing data, but they do not get stored in the DB, thus they don't have ID's. They also do not hold functionality or change any state, they just hold data.

    A Value Object is an immutable object that is defined by its encapsulated attributes. We create Value Object when we need it to represent/serve/manipulate some data (attached as attributes), and we'll kill it later when we don't need it anymore, to recreate it again when needed.

    Rules

    • All Models MUST extend from App\Ship\Parents\Values\Value.

    Folder Structure

    - App
    - Containers
    - {section-name}
    - {container-name}
    - Values
    - Output.php
    - Region.php
    - ...

    Code Sample

    class Location extends Value
    {
    private $x = null;
    private $y = null;
    protected $resourceKey = 'locales';

    public function __construct($x, $y)
    {
    $this->x = $x;
    $this->y = $y;
    }

    public function getCoordinatesAsString()
    {
    return $this->x . ' - ' . $this->y;
    }
    }
    - + \ No newline at end of file diff --git a/docs/10.x/upgrade-guide/index.html b/docs/10.x/upgrade-guide/index.html index 8f208e01e..17c7e64c5 100644 --- a/docs/10.x/upgrade-guide/index.html +++ b/docs/10.x/upgrade-guide/index.html @@ -4,7 +4,7 @@ Upgrade Guide | Apiato - + @@ -23,7 +23,7 @@ and the term New Project (referring to the new freshly installed Apiato 5.0).

    1) Download and install Apiato 5.0. See Application Setup.

    2) Delete the Containers directory app/Containers from the new project.

    3) Move the Containers directory app/Containers from the old project to the new project.

    4) Open this file app/Ship/composer.json in your old project and only copy the required dependencies, from the old project to the same file in the new project.

    5) Again, open the app/Ship/composer.json file in the new project, and remove the following dependencies: guzzlehttp/guzzle, prettus/l5-repository, barryvdh/laravel-cors, spatie/laravel-fractal, vinkla/hashids and johannesschobel/apiato-container-installer.

    6) Move and replace the following directories from the old project to the new project: config, public, resources, database and storage.

    7) Open config/app.php and replace App\Ship\Engine\Providers\PortoServiceProvider::class with Apiato\Core\Providers\ApiatoProvider::class.

    8) Move .gitignore, phpunit.xml and .env files, from the old project to the new project.

    9) Open the .env file on the new project and append this to it API_RATE_LIMIT_ENABLED=true.

    10) Open phpunit.xml file of the new project and delete this line from the file <file>./app/Ship/Engine/Loaders/FactoryMixer/FactoriesLoader.php</file>.

    11) If you had live testing data in your old project inside app/Ship/Seeders/Data/Testing/Seeders/TestingDataSeeder.php file, then copy that file content and past it in the new project inside app/Ship/Seeders/SeedTestingData.php. You will need to rename the class (not the file) from TestingDataSeeder to SeedTestingData, and you will need to update the namespace from namespace App\Ship\Seeders\Data\Testing\Seeders; to namespace App\Ship\Seeders;.

    12) If you ever used the HashIdTrait, you need to search and replace this namespace App\Ship\Engine\Traits\HashIdTrait with this Apiato\Core\Traits\HashIdTrait.

    13) Run composer update. If you got any error at this step, try to solve it or open an Issue.

    14) Move the .git directory from the old project to the new one. Add all changes git add . then commit git commit -m 'upgrade Apiato from 4.1 to 5.0'.

    15) Run your tests vendor/bin/phpunit.

    That's it :)

    How to manually upgrade older versions to 4.1?

    Use the Manual Upgrading Guide below.

    Manual Upgrading Guide

    These commands and examples, are compatible with the Apiato 8.0 upgrade. You can just copy/past.

    1) Checkout a new branch from your stable branch, to perform the upgrade.

    git checkout -b upgrade-apiato

    2) Configure a new remote (upstream) that points to the official Apiato repository.

    git remote add upstream https://github.com/apiato/apiato

    Verify the new upstream repository was added, by listing the current configured remote repositories.

    git remote -vv

    origin git@bitbucket.org:username/my-awesome-api.git (fetch)
    origin git@bitbucket.org:username/my-awesome-api.git (push)
    upstream git@github.com:apiato/apiato.git (fetch)
    upstream git@github.com:apiato/apiato.git (push)

    3) Checkout a new branch to hold the latest Apiato changes. This branch will be merged into your upgrade-apiato branch created above.

    git checkout -b apiato-{version}
    // Example: git checkout -b apiato-8.0

    4) Configure this branch to track an upstream specific branch.

    Replace {upstream-branch-name} with the branch name you want to upgrade to (for example 8.0).

    git fetch upstream {upstream-branch-name}
    // Example: git fetch upstream 8.0

    git branch --set-upstream-to upstream/{upstream-branch-name}
    // Example: git branch --set-upstream-to upstream/8.0

    Verify your local branch is tracking the Apiato specified upstream branch.

    git branch -vv

    apiato 77b4d945 [upstream/{upstream-branch-name}] ...
    master 77d302aa [origin/master] ...

    5) Make this branch identical to the remote upstream branch

    git reset --hard upstream/{upstream-branch-name}
    // Example: git reset --hard upstream/8.0

    Verify this branch now contains the latest changes from the upstream branch.

    git log

    6) Switch back to the upgrade-apiato branch

    git checkout upgrade-apiato

    7) Now lets merge the 2 branches. This step can be done in two ways:

    Option A: Merge all the changes together and solve the conflicts if any. (Recommended)

    You can execute the next command with different different parameters, below are 2 options to pick whatever feels safer to you. Do not execute both of them.

    A1: This will overwrite your changes with the upstream changes. (Try this first and if your tests failed then you can try the second one).

    git merge --allow-unrelated-histories --strategy-option=theirs apiato-{version}
    // Example: git merge --allow-unrelated-histories --strategy-option=theirs apiato-8.0

    A2: This will let you solve all the conflicts manually. (Can be the most secure choice, but it's time consuming as well.)

    git merge --allow-unrelated-histories apiato-{version}
    // Example: git merge --allow-unrelated-histories apiato-8.0

    Option B: Manually cherry pick the commits you likes to have:

    git log {upstream-branch-name}

    (copy each commit ID, one by one)

    git cherry-pick {commit-ID}

    (if you get any conflict solve it and keep moving)

    8) Compare your .env with the new .env-example and update it.

    9) Check everything is working. By running Composer install first then re-running your tests.

    • Read the changelog releases page.
    • You may want to update your custom containers dependencies, simply follow the composer install error outputs and bump each failing dependency. (Hint: visit each package releases page, and use the version which supports the supported version of Laravel).
    • You may need to fix the failing tests.
    composer install  &&  vendor/bin/phpunit

    10) Finally, merge the upgrade-apiato (which contains the upgraded changes) with your stable branch (could be master).

    git checkout master
    git merge upgrade-apiato

    php artisan -V

    Enjoy :)

    - + \ No newline at end of file diff --git a/docs/11.x/additional-features/documentation/index.html b/docs/11.x/additional-features/documentation/index.html index f2f694d56..357a990ed 100644 --- a/docs/11.x/additional-features/documentation/index.html +++ b/docs/11.x/additional-features/documentation/index.html @@ -4,7 +4,7 @@ Documentation | Apiato - + @@ -15,7 +15,7 @@ access-private-docs-permission values in documentation config. By default, users need access-private-docs permission to access private docs.

    Edit Default Generated Values in Templates

    Apiato by defaults generates 2 API documentations, each one has its own apidoc.json file. Both can be modified from the Documentation Container in Containers/Vendor/Documentation/ApiDocJs/ and need Source code modification.

    Edit the Documentation Header

    The header is usually the Overview of your API. It contains Info about authenticating users, making requests, responses, potential errors, rate limiting, pagination, query parameters and anything you want.

    All this information is written in app/Containers/Vendor/Documentation/ApiDocJs/shared/header.template.md file, and the same file is used as header for both private and public documentations.

    To edit its content you need to modify its source code and open the markdown file in any markdown editor and edit it.

    You will notice some variables like {{rate-limit}} and {{token-expires}}. Those are replaced when running apiato:apidoc with real values from your application configuration files.

    Feel free to extend them to include more info about your API from the app/Containers/Vendor/Documentation/Tasks/RenderTemplatesTask.php class.

    Localization for Documentation Header

    Default, the documentation title is in English en localization.

    See which locales are supported by going in app/Containers/Vendor/Documentation/ApiDocJs/shared/

    There will be some header.template.{locale}.md files in the folder.

    You can change the language by adding APIDOC_LOCALE=ru to the .env file.

    If you didn't find a file with your locale, you can create it. You need to modify its source code and create new file like header.template.cn.md

    - + \ No newline at end of file diff --git a/docs/11.x/additional-features/localization/index.html b/docs/11.x/additional-features/localization/index.html index e454dd81c..0da93d0c8 100644 --- a/docs/11.x/additional-features/localization/index.html +++ b/docs/11.x/additional-features/localization/index.html @@ -4,7 +4,7 @@ Localization | Apiato - + @@ -37,7 +37,7 @@ language in this specific language (e.g., locale_name => Deutsch). Furthermore, the language name is outputted in the applications default name (e.g., configured in app.locale). This would result in default_name => German.

    The same applies to the regions that are defined (e.g., de-DE). Consequently, this results in locale_name => Deutschland and default_name = Germany.

    Tests

    To change the default language in your tests requests. You can set the env language in the phpunit.xml file.

    - + \ No newline at end of file diff --git a/docs/11.x/additional-features/overview/index.html b/docs/11.x/additional-features/overview/index.html index 9709f2d2c..f91a7b7f2 100644 --- a/docs/11.x/additional-features/overview/index.html +++ b/docs/11.x/additional-features/overview/index.html @@ -4,14 +4,14 @@ Overview | Apiato - +
    Version: 11.x

    Overview

    Apiato ships with a few pre-defined and pre-configured containers. Some of these containers (e.g. Documentation) are installed by default with a fresh Apiato project.

    - + \ No newline at end of file diff --git a/docs/11.x/additional-features/social-authentication/index.html b/docs/11.x/additional-features/social-authentication/index.html index d32f2072d..4fb5536be 100644 --- a/docs/11.x/additional-features/social-authentication/index.html +++ b/docs/11.x/additional-features/social-authentication/index.html @@ -4,7 +4,7 @@ Social Authentication | Apiato - + @@ -18,7 +18,7 @@ visit app/Containers/Vendor/Socialauth/UI/API/Routes/AuthenticateAll.v1.private.php.

  • The endpoint above should return the User and his Personal Access Token.

  • Example Google Response:

    {
    "data": {
    // user data
    .
    .
    .
    // additional social data if you have updated your transformer as mentioned above
    "social_auth_provider": "google",
    "social_id": "113834952367767922133",
    "social_avatar": {
    "avatar": "https:\/\/lh6.googleusercontent.com\/-OSItz6IHbSw\/AAA\/AMZuucltEs\/s96-c\/photo.jpg",
    "original": "https:\/\/lh6.googleusercontent.com\/-OSItz6IHbSw\/AAA\/AMZuucltEs\/s96-c\/photo.jpg"
    }
    },
    "meta": {
    "include": [
    "roles"
    ],
    "custom": {
    "token_type": "personal",
    "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9...."
    }
    }
    }
    tip

    For testing purposes Apiato provides two web endpoints

    1. http://apiato.test/auth/{provider}/redirect which act as a client (step 3 above)
    2. http://apiato.test/auth/{provider}/callback which you can use in your provider's developer dashboard for callback url.
      Use those endpoints from your browser (replace the provider with any of the supported providers facebook, twitter,...) to get the oauth info and user data respectively.

    Social Authentication Container Customization

    You can customize this container by publishing its config and modifying its values

    php artisan vendor:publish

    Config file will be copied to app/Ship/Configs/vendor-socialAuth.php

    Support new Auth Provider

    1. Publish the configs
    2. Create your new auth provider by implementing App\Containers\Vendor\SocialAuth\Contracts\SocialAuthProvider interface.
      To get an idea about how to implement your own provider you can check out supported providers here App\Containers\Vendor\SocialAuth\SocialAuthProviders.
    3. Add your new provider to providers array in the vendor-socialAuth config.
        'providers' => [
    ...
    'something' => Location\Of\Your\Provider\SomthingSocialAuthProvider::class,
    ],

    Changing default used Repository, Transformer & DB user table name

    This container depends on Apiato's default user repository, transformer & database user table name. If you changed those defaults you can update and provide them in the configs.

    - + \ No newline at end of file diff --git a/docs/11.x/contribution-guide/index.html b/docs/11.x/contribution-guide/index.html index b72781130..f74e5776e 100644 --- a/docs/11.x/contribution-guide/index.html +++ b/docs/11.x/contribution-guide/index.html @@ -4,7 +4,7 @@ Contribution Guide | Apiato - + @@ -42,7 +42,7 @@ 3 - Make sure you write a complete Changelog, in the release description. 4 - Change the default branch on GitHub to that new branch. 5 - If you updated the documentation, and you should! then visit the documentation repository and merge the PR into master.

    - + \ No newline at end of file diff --git a/docs/11.x/core-features/api-versioning/index.html b/docs/11.x/core-features/api-versioning/index.html index 0caf7bf06..f99e41ff9 100644 --- a/docs/11.x/core-features/api-versioning/index.html +++ b/docs/11.x/core-features/api-versioning/index.html @@ -4,13 +4,13 @@ API Versioning | Apiato - +
    Version: 11.x

    API Versioning

    Since Laravel does not support API versioning, Apiato provide a very easy way to implement versioning for your API.

    How it works

    Create:

    When creating a new API endpoint, specify the version number in the route file name following this naming format {endpoint-name}.{version-number}.{documentation-name}.php.

    Example:

    • MakeOrder.v1.public.php
    • MakeOrder.v2.public.php
    • ListOrders.v1.private.php

    Use:

    Automatically the endpoint inside that route file will be accessible by adding the version number to the URL.

    Example:

    • http://api.apiato.test/v1/register
    • http://api.apiato.test/v1/orders
    • http://api.apiato.test/v2/stores/123

    Version the API in header instead of URL

    First remove the URL version prefix:

    1. Edit app/Ship/Configs/apiato.php, set prefix to 'enable_version_prefix' => 'false',.
    2. Implement the Header versioning anyway you prefer. (this is not implemented in Apiato yet. Consider a contribution).
    - + \ No newline at end of file diff --git a/docs/11.x/core-features/authentication/index.html b/docs/11.x/core-features/authentication/index.html index f07ec70aa..3d6f17a27 100644 --- a/docs/11.x/core-features/authentication/index.html +++ b/docs/11.x/core-features/authentication/index.html @@ -4,7 +4,7 @@ Authentication | Apiato - + @@ -70,7 +70,7 @@ http://myapp.com/reset-password?email=mohammad.alavi1990@gmail.com&token=51f8d80182f3785648c9b9dc7162719d158fc418b3cca86c14963638ec83d663

    3) And when user click on that link it will go to your front end app reset password page. And then from there you should get the user's new password and call the /password-reset endpoint with all the required fields to reset the password.

    note

    You must set up the email to get this function to work, however for testing purposes set the MAIL_MAILER=log in your .env file in order to the see the email content in the log file storage/logs/laravel.log.

    Social Authentication

    For Social Authentication visit the Social Authentication page.

    - + \ No newline at end of file diff --git a/docs/11.x/core-features/authorization/index.html b/docs/11.x/core-features/authorization/index.html index 8e4c25763..fd9862a2b 100644 --- a/docs/11.x/core-features/authorization/index.html +++ b/docs/11.x/core-features/authorization/index.html @@ -4,13 +4,13 @@ Authorization | Apiato - +
    Version: 11.x

    Authorization

    Apiato provides a Role-Based Access Control (RBAC) through its Authorization Container.

    Behind the scenes apiato is using the Laravel's authorization functionality that was introduced in version 5.1.11 with the helper package laravel-permission. So you can always refer to the correspond documentation for more information.

    How it works

    Authorization in apiato is very simple and easy.

    1) Create some Roles and permissions. By default, an admin role and some permissions are provided by Apiato. You can find the code in app/Containers/AppSection/Authorization/Data/Seeders/* directory.

    2) Attach some permissions to the roles.

    3) Now start creating users (or use existing users), to assign them to the new created Roles.

    4) Finally, you need to protect your endpoints by Permissions (or/and Roles). The right place to do that is the Requests class.

    Example protecting the (delete user) endpoint with delete-users permission:

    class DeleteUserRequest extends Request
    {
    protected array $access = [
    'permissions' => 'delete-users',
    'roles' => '',
    ];

    public function authorize(): bool
    {
    return $this->check([
    'hasAccess',
    ]);
    }
    }

    For detailed explanation of this example, please visit the Requests Page.

    Responses

    Authorization failed JSON response:

    {
    "message": "This action is unauthorized."
    }

    Assign Roles & Permission to the Testing User

    You will need to set $access property in your test class, check out the Tests Helpers page for more details.

    Seeding some users (Admins)

    By default, Apiato comes with a Super Admin.

    This Super Admin Credentials are:

    This Admin seeded by app/Containers/Authorization/Data/Seeders/AuthorizationDefaultUsersSeeder_3.php.

    The Default Super User, has a default role admin.

    The admin default role has no permissions given to it.

    To give permissions to the admin role (or any other role), you can use the dedicated endpoints (from your custom Admin Interface).

    Checkout each container Seeders directory app/Containers/AppSection/{container-name}/Data/Seeders/, to edit the default Users, Roles and Permissions.

    Roles & Permissions guards

    By default, Apiato uses a single guard called web for all it's roles and permissions, you can add/edit this behavior and support multiple guards at any time. Refer to the laravel-permission package for more details.

    - + \ No newline at end of file diff --git a/docs/11.x/core-features/code-generator/index.html b/docs/11.x/core-features/code-generator/index.html index 2617c6e0d..922923de4 100644 --- a/docs/11.x/core-features/code-generator/index.html +++ b/docs/11.x/core-features/code-generator/index.html @@ -4,7 +4,7 @@ Code Generator | Apiato - + @@ -17,7 +17,7 @@ the file and the folder structure needs to be the same as in vendor/apiato/core/Generator/Stubs.

    Say, if you like to change the action -> create.stub, simply copy the file to app/Ship/Generators/CustomStubs/actions/create.stub and start adapting it to your needs.

    If you run the respective command (e.g., in this case php artisan apiato:generate:action) and choose Create type this would read your specific create.stub file instead of the pre-defined one!

    Contributing

    If you would like to add your own generators, please check out the Contribution Guide.

    - + \ No newline at end of file diff --git a/docs/11.x/core-features/data-caching/index.html b/docs/11.x/core-features/data-caching/index.html index 92c651e41..313c68147 100644 --- a/docs/11.x/core-features/data-caching/index.html +++ b/docs/11.x/core-features/data-caching/index.html @@ -4,13 +4,13 @@ Data Caching | Apiato - +
    Version: 11.x

    Data Caching

    Enable / Disable Eloquent Query Caching

    info

    This feature is disabled By default.

    To enable it, go to app/Ship/Configs/repository.php config file and set cache > enabled => true, or set it from the .env file using ELOQUENT_QUERY_CACHE.

    More details can be found here.

    Users can skip the query caching and request new data by passing specific parameter to the Endpoint. Checkout its documentation here.

    Change different caching settings

    You can use different cache setting for each repository.

    To set cache settings on each repository, first the caching must be enabled, second you need to set some properties on the repository class to override the default values.

    For more details about all the properties refer to the L5 repository package documentation.

    Note: you don't need to use the CacheableRepository trait or implement the CacheableInterface since they both exist on the Abstract repository class (App\Ship\Parents\Repositories\Repository).

    - + \ No newline at end of file diff --git a/docs/11.x/core-features/default-endpoints/index.html b/docs/11.x/core-features/default-endpoints/index.html index 3ebb03a8b..298092dc6 100644 --- a/docs/11.x/core-features/default-endpoints/index.html +++ b/docs/11.x/core-features/default-endpoints/index.html @@ -4,13 +4,13 @@ Default Endpoints | Apiato - +
    Version: 11.x

    Default Endpoints

    Apiato comes loaded with many useful API endpoints for speeding up the development process.

    You can see the endpoints in three ways:

    • In Terminal, by running php artisan route:list -c.
    • In Browser, by generating the beautiful detailed documentation. See API Docs Generator.
    • In Code, by navigating to the Routes folder of each container's UI.
    - + \ No newline at end of file diff --git a/docs/11.x/core-features/etag/index.html b/docs/11.x/core-features/etag/index.html index c70e517e5..bba93828e 100644 --- a/docs/11.x/core-features/etag/index.html +++ b/docs/11.x/core-features/etag/index.html @@ -4,7 +4,7 @@ ETag | Apiato - + @@ -12,7 +12,7 @@
    Version: 11.x

    ETag

    ETag Middleware

    Apiato provides an ETag Middleware (ProcessETagHeadersMiddleware) that implements the Shallow technique. It can be used to reduce bandwidth on the client side (especially for Mobile devices).

    Enable / Disable ETag

    This feature is disabled By default. To enable it go to app/Ship/Configs/apiato.php and set use-etag to true. Of course your client should send the If-None-Match HTTP Header (= etag) in his request for this feature to work properly.

    - + \ No newline at end of file diff --git a/docs/11.x/core-features/hash-id/index.html b/docs/11.x/core-features/hash-id/index.html index 1a9323ba3..f1e83bc47 100644 --- a/docs/11.x/core-features/hash-id/index.html +++ b/docs/11.x/core-features/hash-id/index.html @@ -4,13 +4,13 @@ Hash ID | Apiato - +
    Version: 11.x

    Hash ID

    Hashing your internal ID's, is a very helpful feature for security reasons (to prevent some hack attacks) and business reasons (to hide the real total records from your competitors).

    Enable Hash ID

    Set the HASH_ID=true in the .env file.

    Also, with the feature make sure to always use the getHashedKey() on any model, whenever you need to return an ID (mainly from transformers) weather hashed ID or not.

    Example:


    'id' => $user->getHashedKey(),

    Note: if the feature is set to false HASH_ID=false the getHashedKey() will return the normal ID.

    Usage

    There are 2 ways an ID's can be passed to your system via the API:

    In URL example: www.apiato.test/items/abcdef.

    In parameters example: [GET] or [POST] www.apiato.test/items?id=abcdef.

    in both cases you will need to inform your API about what's coming form the Request class.

    Checkout the Requests page. After setting the $decode and $urlParameters properties on your Request class, the ID will be automatically decoded for you to apply validation rules on it or/and use it from your controller. ($request->id will return the decoded ID)

    Configuration

    You can change the default length and characters used in the ID from the config file app/Ship/Configs/hashids.phpor in the .env file by editing the HASH_ID_LENGTH value.

    You can set the HASH_ID_KEY in the .env file to any random string. You can generate this from any of the online random string generators, or run head /dev/urandom | tr -dc 'A-Za-z0-9!"#$%&'\''()*+,-./:;<=>?@[\]^_{|}~' | head -c 32 ; echo on the linux command-line. Apiato defaults to the APP_KEY should this not be set.

    The HASH_ID_KEY acts as the salt during hashing of the ID. This should never be changed in production as it renders all previously generated IDs impossible to decode.

    Testing

    In your tests you must hash the ID's before making the calls, because if you tell your Request class to decode an ID for you, it will throw an exception when the ID is not encoded.

    for Parameter ID's

    Always use getHashedKey() on your models when you want to get the ID

    Example:

    $data = [
    'roles_ids' => [
    $role1->getHashedKey(),
    $role2->getHashedKey(),
    ],
    'user_id' => $randomUser->getHashedKey(),
    ];
    $response = $this->makeCall($data);

    Or you can do this manually Hashids::encode($id);.

    for URL ID's

    You can use this helper function injectId($id, $skipEncoding = false, $replace = '{id}').

    Example:

    $response = $this->injectId($admin->id)->makeCall();

    More details on the Tests Helpers page.

    Availability

    You can use the Apiato\Core\Traits\HashIdTrait on any model or class, in order to have the encode and decode functions.

    By default, you have access to these functions $this->encode($id) and $this->decode($id) from all your Test classes and Controllers.

    - + \ No newline at end of file diff --git a/docs/11.x/core-features/pagination/index.html b/docs/11.x/core-features/pagination/index.html index 8bc8f0c62..05326dcb7 100644 --- a/docs/11.x/core-features/pagination/index.html +++ b/docs/11.x/core-features/pagination/index.html @@ -4,7 +4,7 @@ Pagination | Apiato - + @@ -16,7 +16,7 @@ you can do it either project wide or per repository. After that a request can get all the data (with no pagination applied) by applying limit=0.

    This will return all matching entities:
    api.domain.test/endpoint?limit=0

    Project Wide

    Set PAGINATION_SKIP=true in .env file.

    Per Repository

    Override the $allowDisablePagination property in your specific Repository class.

    note

    Per repository configs override the global config and have precedence.

    - + \ No newline at end of file diff --git a/docs/11.x/core-features/profiler/index.html b/docs/11.x/core-features/profiler/index.html index b3c103a81..588e8f43e 100644 --- a/docs/11.x/core-features/profiler/index.html +++ b/docs/11.x/core-features/profiler/index.html @@ -4,14 +4,14 @@ Profiler | Apiato - +
    Version: 11.x

    Profiler

    Profiling is very important to optimize the performance of your application, and help you better understand what happens when a request is received, as well as it can speed up the debugging process.

    Apiato uses the third-party package laravel-debugbar (which uses the PHP Debug Bar), to collect the profiling data.

    By default, the laravel-debugbar package displays the profiling data in the browser. However, Apiato uses a middleware (ProfilerMiddleware) to append the profiling data to the response.

    Sample Profiler response

    {
    // Actual Response Here...
    "_profiler": {
    "__meta": {
    "id": "X167f293230e3457f1bbd95d9c82aba4a",
    "datetime": "2017-09-22 18:45:27",
    "utime": 1506105927.799299,
    "method": "GET",
    "uri": "/",
    "ip": "172.20.0.1"
    },
    "messages": {
    "count": 0,
    "messages": []
    },
    "time": {
    "start": 1506105922.742068,
    "end": 1506105927.799333,
    "duration": 5.057265043258667,
    "duration_str": "5.06s",
    "measures": [
    {
    "label": "Booting",
    "start": 1506105922.742068,
    "relative_start": 0,
    "end": 1506105923.524004,
    "relative_end": 1506105923.524004,
    "duration": 0.7819359302520752,
    "duration_str": "781.94ms",
    "params": [],
    "collector": null
    },
    {
    "label": "Application",
    "start": 1506105923.535343,
    "relative_start": 0.7932748794555664,
    "end": 1506105927.799336,
    "relative_end": 0.00000286102294921875,
    "duration": 4.26399302482605,
    "duration_str": "4.26s",
    "params": [],
    "collector": null
    }
    ]
    },
    "memory": {
    "peak_usage": 13234248,
    "peak_usage_str": "12.62MB"
    },
    "exceptions": {
    "count": 0,
    "exceptions": []
    },
    "route": {
    "uri": "GET /",
    "middleware": "api, throttle:30,1",
    "domain": "http://api.apiato.test",
    "as": "apis_root_page",
    "controller": "App\\Containers\\Welcome\\UI\\API\\Controllers\\Controller@apiRoot",
    "namespace": "App\\Containers\\Welcome\\UI\\API\\Controllers",
    "prefix": "/",
    "where": [],
    "file": "app/Containers/Welcome/UI/API/Controllers/Controller.php:20-25"
    },
    "queries": {
    "nb_statements": 0,
    "nb_failed_statements": 0,
    "accumulated_duration": 0,
    "accumulated_duration_str": "0μs",
    "statements": []
    },
    "swiftmailer_mails": {
    "count": 0,
    "mails": []
    },
    "logs": {
    "count": 3,
    "messages": [
    {
    "message": "...",
    "message_html": null,
    "is_string": false,
    "label": "error",
    "time": 1506105927.694807
    },
    {
    "message": "...",
    "message_html": null,
    "is_string": false,
    "label": "error",
    "time": 1506105927.694811
    },
    {
    "message": "[2017-09-18 17:38:15] testing.INFO: New User registration. ID = 970ylqvaogmxnbdr | Email = apiato@mail.test. Thank you for signing up.\n</div>\n</body>\n</html>\n \n",
    "message_html": null,
    "is_string": false,
    "label": "info",
    "time": 1506105927.694812
    }
    ]
    },
    "auth": {
    "guards": {
    "web": "array:2 [\n \"name\" => \"Guest\"\n \"user\" => array:1 [\n \"guest\" => true\n ]\n]",
    "api": "array:2 [\n \"name\" => \"Guest\"\n \"user\" => array:1 [\n \"guest\" => true\n ]\n]"
    },
    "names": ""
    },
    "gate": {
    "count": 0,
    "messages": []
    }
    }
    }

    Configuration

    By default, the profiler feature is turned off. To turn it on edit the .env file and set DEBUGBAR_ENABLED=true.

    To control and modify the profiler response, you need to edit this config file app/Ship/Configs/debugbar.php.

    - + \ No newline at end of file diff --git a/docs/11.x/core-features/query-parameters/index.html b/docs/11.x/core-features/query-parameters/index.html index ee4688cde..fe1f9b704 100644 --- a/docs/11.x/core-features/query-parameters/index.html +++ b/docs/11.x/core-features/query-parameters/index.html @@ -4,7 +4,7 @@ Query Parameters | Apiato - + @@ -25,7 +25,7 @@ accepts driver as relationship ($availableIncludes in Transformer).

    Nested Includes

    It is also possible to request "nested includes". Extend the example from above. Imagine, that a Driver may also have a relationship to an Address object. You can access this information as well by calling ?include=driver,driver.address.

    Of course, the address include is defined in the respective DriverTransformer that is used here.

    Where to define the includes:

    Every Transformer can have 2 types of includes $availableIncludes and $defaultIncludes:

        protected $availableIncludes = [
    'products',
    'store',
    'recipients',
    ];

    protected $defaultIncludes = [
    'invoice',
    ];

    $defaultIncludes will not be listed in the response, only the $availableIncludes will be.

    Visit the Transformers page for more details.

    Skip caching

    (provided by the L5 Repository)

    Note: You need to turn the Eloquent Query Caching ON for this feature to work. ELOQUENT_QUERY_CACHE=true in .env.

    To run a new query and force disabling the cache on certain endpoints, you can use this parameter

    ?skipCache=true

    It's not recommended to keep skipping cache as it has bad impact on the performance.

    Configuration

    Most of these parameters are provided by the L5 Repository and configurable from the Ship/Configs/repository.php file. Some of them are built in house, or inherited from other packages such as Fractal.

    See the Query parameters from the User Developer perspective

    1) Generate the Default API documentation

    2) Visit the documentation URL

    More details in the API Docs Generator page.

    More Information

    For more details on these parameters check out these links:

    - + \ No newline at end of file diff --git a/docs/11.x/core-features/rate-limiting/index.html b/docs/11.x/core-features/rate-limiting/index.html index 2757ac313..5ff46aa24 100644 --- a/docs/11.x/core-features/rate-limiting/index.html +++ b/docs/11.x/core-features/rate-limiting/index.html @@ -4,14 +4,14 @@ Rate Limiting | Apiato - +
    Version: 11.x

    Rate Limiting

    Apiato uses the default Laravel middleware for rate limiting (throttling).

    All REST API requests are throttled to prevent abuse and ensure stability. The exact number of calls that your application can make per day varies based on the type of request you are making.

    The rate limit window is 1 minute per endpoint, with most individual calls allowing for 30 requests in each window.

    In other words, each user is allowed to make 30 calls per endpoint every 1 minute. (For each unique access token).

    To update these values go to app/Ship/Configs/apiato.php config file, or to the ENV file.

    'throttle' => [
    'enabled' => env('GLOBAL_API_RATE_LIMIT_ENABLED', true),
    'attempts' => env('GLOBAL_API_RATE_LIMIT_ATTEMPTS_PER_MIN', '30'),
    'expires' => env('GLOBAL_API_RATE_LIMIT_EXPIRES_IN_MIN', '1'),
    ]
    GLOBAL_API_RATE_LIMIT_ENABLED=true
    GLOBAL_API_RATE_LIMIT_ATTEMPTS_PER_MIN=30
    GLOBAL_API_RATE_LIMIT_EXPIRES_IN_MIN=1

    For how many hits you can preform on an endpoint, you can always check the header:

    X-RateLimit-Limit →30
    X-RateLimit-Remaining →29

    Enable/Disable Rate Limiting:

    The global API rate limiting middleware name is api and is enabled and applied to all the Container API Endpoints by default.

    Disable on specific endpoint:

    This middleware can be bypassed using withoutMiddleware() method on a specific route.

    Disable on all endpoints (globally):

    To disable it set GLOBAL_API_RATE_LIMIT_ENABLED to false in the .env file.

    - + \ No newline at end of file diff --git a/docs/11.x/core-features/useful-commands/index.html b/docs/11.x/core-features/useful-commands/index.html index 87f038ee8..67fc40ff2 100644 --- a/docs/11.x/core-features/useful-commands/index.html +++ b/docs/11.x/core-features/useful-commands/index.html @@ -4,14 +4,14 @@ Useful Commands | Apiato - +
    Version: 11.x

    Useful Commands

    Apiato is loaded with many useful commands to help you speed up the development process. You can see list of all commands, by typing php artisan and look for Apiato section.

    Available Commands

    • php artisan apiato Display the current Apiato version.
    • php artisan apiato:apidoc Generate API Documentations with apidoc from your routes Docblock. More details.
    • php artisan apiato:create:admin Create a new User with the ADMIN role
    • php artisan apiato:generate:{component} Generate a specific component for the framework (e.g., Action, Task, ...). For more details on the Code Generator click here.
    • php artisan apiato:list:actions List all Actions in the Application.
    • php artisan apiato:list:tasks List all Tasks in the Application.
    • php artisan apiato:seed-deploy Seeds your custom deployment data from app/Ship/Seeders/SeedDeploymentData.php.
    • php artisan apiato:seed-test Seeds your custom testing data from app/Ship/Seeders/SeedTestingData.php.
    • php artisan apiato:welcome Just saying welcome from a container.
    - + \ No newline at end of file diff --git a/docs/11.x/core-features/user-registration/index.html b/docs/11.x/core-features/user-registration/index.html index bd0b24a97..958673cb6 100644 --- a/docs/11.x/core-features/user-registration/index.html +++ b/docs/11.x/core-features/user-registration/index.html @@ -4,13 +4,13 @@ User Registration | Apiato - +
    Version: 11.x

    User Registration

    Register users by credentials (email and passwords)

    Call the http://api.apiato.test/v1/register endpoint (you can find its documentation after generating the API Docs.

    Check out the registerUser endpoint in the API Routes files.

    This will register a new User and respond with user object.

    Registration request:

    curl --request POST \
    --url http://api.apiato.test/v1/register \
    --header 'accept: application/json' \
    --header 'content-type: application/x-www-form-urlencoded' \
    --data 'email=john%40doe.com&password=password&name=John%20Doe'

    Registration response:

    {
    "data": {
    "object": "User",
    "id": "XbPW7awNkzl83LD6",
    "name": "John Doe",
    "email": "john@doe.com",
    "email_verified_at": null,
    "gender": null,
    "birth": null,
    "created_at": "2021-04-15T14:17:24.000000Z",
    "updated_at": "2021-04-15T14:17:24.000000Z",
    "readable_created_at": "1 second ago",
    "readable_updated_at": "1 second ago"
    },
    "meta": {
    "include": [
    "roles"
    ],
    "custom": []
    }
    }

    Note: After registration in order to get the user access token you will have to send another call to http://api.example.com/v1/oauth/token with following fields and values

    username => your_username e.g. admin@admin.com
    password => your_password
    grant_type => password
    client_id => your_client_id
    client_secret => your_client_secret

    For third-party clients you must have client ID and secret first. You can generate them by creating new client in your app using Laravel Passport.

    For first-party clients you can use a proxy to add those fields on requests coming from your trusted client. For an example on how to do it look at ProxyLoginForWebClientAction Action in Authentication Container.

    Register users by Social Account

    (Facebook, Twitter, Google..)

    Checkout the Social Authentication Page for how to Sign up with Social Account.

    - + \ No newline at end of file diff --git a/docs/11.x/core-features/validation/index.html b/docs/11.x/core-features/validation/index.html index ee048ee6c..2f17e9540 100644 --- a/docs/11.x/core-features/validation/index.html +++ b/docs/11.x/core-features/validation/index.html @@ -4,13 +4,13 @@ Validation | Apiato - +
    Version: 11.x

    Validation

    Apiato uses the powerful Laravel validation system.

    In Apiato, validation must be defined in Request component, since every request might have different rules.

    Validation rules are automatically applied, once injecting the Request in the Controller.

    Requests help validating User data, accessibility, ownership and more.

    Example Request with Validation rules:

    namespace App\Containers\AppSection\User\UI\API\Requests;

    use App\Ship\Parents\Requests\Request;

    class RegisterUserRequest extends Request
    {
    /**
    * @return array
    */
    public function rules()
    {
    return [
    'email' => 'required|email|max:200|unique:users,email',
    'password' => 'required|min:20|max:300',
    'name' => 'required|min:2|max:400',
    ];
    }

    }

    Usage from Controller Example:

        public function registerUser(RegisterUserRequest $request)
    {
    $user = app(RegisterUserAction::class)->run($request);
    return $this->transform($user, UserTransformer::class);
    }

    Responses

    Validation Error response format:

    Single Field:

    {
    "message": "The given data was invalid.",
    "errors": {
    "email": [
    "The email has already been taken."
    ]
    }
    }

    Multiple Fields:

    {
    "message": "The given data was invalid.",
    "errors": {
    "email": [
    "The email has already been taken."
    ],
    "password": [
    "The password field is required."
    ]
    }
    }

    More details about requests in the Requests Page.

    - + \ No newline at end of file diff --git a/docs/11.x/faq/index.html b/docs/11.x/faq/index.html index b7a725793..c2d4b7d03 100644 --- a/docs/11.x/faq/index.html +++ b/docs/11.x/faq/index.html @@ -4,7 +4,7 @@ Frequently Asked Questions | Apiato - + @@ -37,7 +37,7 @@ Discord.

    Lastly, if you got your question answered, consider sharing it, if you believe it can help others. You can submit a PR adding the questions and answer here on the FAQ page. Or leave it somewhere on the repository or on the chat room. Thanks in advance :)

    - + \ No newline at end of file diff --git a/docs/11.x/getting-started/container-installer/index.html b/docs/11.x/getting-started/container-installer/index.html index aae3cbaf7..0e1ad6126 100644 --- a/docs/11.x/getting-started/container-installer/index.html +++ b/docs/11.x/getting-started/container-installer/index.html @@ -4,7 +4,7 @@ Container Installer | Apiato - + @@ -20,7 +20,7 @@ that allows installing/updating containers.
  • You must provide the key extra.apiato.container.name. This key indicates the name of the folder (e.g., container) when installing the package to the /app/Containers/Vendor folder. In the shown example, the container would be installed to app/Containers/Vendor/Foo.
  • - + \ No newline at end of file diff --git a/docs/11.x/getting-started/conventions-and-principles/index.html b/docs/11.x/getting-started/conventions-and-principles/index.html index 2677cc51f..9d90dd6db 100644 --- a/docs/11.x/getting-started/conventions-and-principles/index.html +++ b/docs/11.x/getting-started/conventions-and-principles/index.html @@ -4,13 +4,13 @@ Conventions | Apiato - +
    Version: 11.x

    Conventions

    HTTP Methods usage in RESTful API's

    • GET (SELECT): retrieve a specific resource from the server, or a listing of resources.
    • POST (CREATE): create a new resource on the server.
    • PUT (UPDATE): update a resource on the server, providing the entire resource.
    • PATCH (UPDATE): update a resource on the server, providing only changed attributes.
    • DELETE (DELETE): remove a resource from the server.

    Naming Conventions for Routes & Actions

    • GetAllResource: to fetch all resources.
    • FindResourceByID: to search for single resource by its unique identifier.
    • CreateResource: to create a new resource.
    • UpdateResource: to update/edit existing resource.
    • DeleteResource: to delete a resource.

    General guidelines and principles for RESTful URLs

    • A URL identifies a resource.
    • URLs should include nouns, not verbs.
    • Use plural nouns only for consistency (no singular nouns).
    • Use HTTP verbs (GET, POST, PUT, DELETE) to operate on the collections and elements.
    • You should not need to go deeper than resource/identifier/resource.
    • Put the version number at the base of your URL, for example http://apiato.test/v1/path/to/resource.
    • If an input data changes the logic of the endpoint, it should be passed in the URL. If not can go in the header "like Auth Token".
    • Don't use query parameters to alter state.
    • Don't use mixed-case paths if you can help it; lowercase is best.
    • Don't use implementation-specific extensions in your URIs (.php, .py, .pl, etc.)
    • Limit your URI space as much as possible. And keep path segments short.
    • Don't put metadata in the body of a response that should be in a header

    Good URL examples

    • Find a single Car by its unique identifier (ID):
      • GET http://www.api.apiato.test/v1/cars/123
    • Get all Cars:
      • GET http://www.api.apiato.test/v1/cars
    • Find/Search cars by one or more fields:
      • GET http://www.api.apiato.test/v1/cars?search=maker:mercedes
      • GET http://www.api.apiato.test/v1/cars?search=maker:mercedes;color:white
    • Order and Sort query result:
      • GET http://www.api.apiato.test/v1/cars?orderBy=created_at&sortedBy=desc
      • GET http://www.api.apiato.test/v1/cars?search=maker:mercedes&orderBy=created_at&sortedBy=desc
    • Specify optional fields:
      • GET http://www.api.apiato.test/v1/cars?filter=id;name;status
      • GET http://www.api.apiato.test/v1/cars/123?filter=id;name;status
    • Get all Drivers belonging to a Car:
      • GET http://www.api.apiato.test/v1/cars/123/drivers
      • GET http://www.api.apiato.test/v1/cars/123/drivers/123/addresses
    • Include Drivers objects relationship with the car response:
      • GET http://www.api.apiato.test/v1/cars/123?include=drivers
      • GET http://www.api.apiato.test/v1/cars/123?include=drivers,owner
    • Add new Car:
      • POST http://www.api.apiato.test/v1/cars
    • Add new Driver to a Car:
      • POST http://www.api.apiato.test/v1/cars/123/drivers

    General principles for HTTP methods

    • Don't ever use GET to alter state; to prevent Googlebot from corrupting your data. And use GET as much as possible.
    • Don't use PUT unless you are updating an entire resource. And unless you can also legitimately do a GET on the same URI.
    • Don't use POST to retrieve information that is long-lived or that might be reasonable to cache.
    • Don't perform an operation that is not idempotent with PUT.
    • Use GET for things like calculations, unless your input is large, in which case use POST.
    • Use POST in preference to PUT when in doubt.
    • Use POST whenever you have to do something that feels RPC-like.
    • Use PUT for classes of resources that are larger or hierarchical.
    • Use DELETE in preference to POST to remove resources.
    - + \ No newline at end of file diff --git a/docs/11.x/getting-started/installation/index.html b/docs/11.x/getting-started/installation/index.html index e404a5b66..06f1c8dbf 100644 --- a/docs/11.x/getting-started/installation/index.html +++ b/docs/11.x/getting-started/installation/index.html @@ -4,7 +4,7 @@ Installation | Apiato - + @@ -28,7 +28,7 @@ try running this command homestead halt && homestead up --provision.

    Using anything else

    If you're not into virtualization solutions, you can set up your environment directly on your machine. Check the software's requirements list.

    Let's Play

    Now let's see it in action

    Open your web browser and visit:

    • http://apiato.test You should see an HTML page, with Apiato in the middle.
    • http://api.apiato.test You should see a response like this:
    [
    "Welcome to Apiato"
    ]

    Open your HTTP client and call:

    • http://api.apiato.test/ You should see a JSON response with message: "Welcome to apiato.",
    • http://api.apiato.test/v1 You should see a JSON response with message: "Welcome to apiato (API V1).",

    Make some HTTP calls to the API:

    To make the calls you can use Postman, HTTPIE or any other tool you prefer.

    Let's test the (user registration) endpoint http://api.apiato.test/v1/register with cURL:

    curl -X POST -H "Accept: application/json" -H "Cache-Control: no-cache" -F "email=John@Doe.me" -F "password=so-secret" -F "name=John Doe" "http://api.apiato.test/v1/register"

    You should get a response like this:

    Header:

    Access-Control-Allow-Origin → ...
    Cache-Control → ...
    Connection → keep-alive
    Content-Language → en
    Content-Type → application/json
    Date → Wed, 11 Apr 2000 22:55:88 GMT
    Server → nginx
    Transfer-Encoding → chunked
    Vary → Origin
    X-Powered-By → PHP/7.7.7
    X-RateLimit-Limit → 30
    X-RateLimit-Remaining → 29

    Body:

    {
    "data": {
    "object": "User",
    "id": "7VgmkMw7rR2pWO5j",
    "name": "John Doe",
    "email": "John@Doe.me",
    "email_verified_at": null,
    "gender": null,
    "birth": null,
    "created_at": "2021-04-12T13:33:24.000000Z",
    "updated_at": "2021-04-12T13:33:24.000000Z",
    "readable_created_at": "1 second ago",
    "readable_updated_at": "1 second ago"
    },
    "meta": {
    "include": [
    "roles"
    ],
    "custom": []
    }
    }
    - + \ No newline at end of file diff --git a/docs/11.x/getting-started/markdown-features/index.html b/docs/11.x/getting-started/markdown-features/index.html index df534a980..3545c59cf 100644 --- a/docs/11.x/getting-started/markdown-features/index.html +++ b/docs/11.x/getting-started/markdown-features/index.html @@ -4,13 +4,13 @@ Markdown Features | Apiato - +
    Version: 11.x

    Markdown Features

    Docusaurus supports the Markdown syntax and has some additional features.

    Front Matter

    Markdown documents can have associated metadata at the top called Front Matter:

    ---
    id: my-doc
    title: My document title
    description: My document description
    sidebar_label: My doc
    ---

    Markdown content

    Regular Markdown links are supported using url paths or relative file paths.

    Let's see how to [Create a page].
    Let's see how to [Create a page].

    Let's see how to [Create a page].

    Markdown images

    Regular Markdown images are supported.

    Add an image at static/img/logo.png and use this Markdown declaration:

    ![Docusaurus logo](/img/logo.png)

    Docusaurus logo

    Code Blocks

    Markdown code blocks are supported with Syntax highlighting.

    ```jsx title="src/components/HelloDocusaurus.js"
    function HelloDocusaurus() {
    return (
    <h1>Hello, Docusaurus!</h1>
    )
    }
    ```
    src/components/HelloDocusaurus.js
    function HelloDocusaurus() {
    return <h1>Hello, Docusaurus!</h1>;
    }

    Admonitions

    Docusaurus has a special syntax to create admonitions and callouts:

    :::tip My tip

    Use this awesome feature option

    :::

    :::danger Take care

    This action is dangerous

    :::
    My tip

    Use this awesome feature option

    Take care

    This action is dangerous

    React components

    Thanks to MDX, you can make your doc more interactive and use React components inside Markdown:

    export const Highlight = ({children, color}) => (
    <span
    style={{
    backgroundColor: color,
    borderRadius: '2px',
    color: 'red',
    padding: '0.2rem',
    }}>
    {children}
    </span>
    );

    <Highlight color="#25c2a0">Docusaurus green</Highlight> and <Highlight color="#1877F2">Facebook blue</Highlight> are my favorite colors.
    Docusaurus green and Facebook blue are my favorite colors.
    - + \ No newline at end of file diff --git a/docs/11.x/getting-started/requests/index.html b/docs/11.x/getting-started/requests/index.html index cbc46655d..91df9d7a5 100644 --- a/docs/11.x/getting-started/requests/index.html +++ b/docs/11.x/getting-started/requests/index.html @@ -4,7 +4,7 @@ Requests | Apiato - + @@ -17,7 +17,7 @@ you can force your users to send application/json by setting 'force-accept-header' => true, in app/Ship/Configs/apiato.php or allow them to skip it completely by setting the 'force-accept-header' => false,. By default this flag is set to false.

    Calling Endpoints

    Calling unprotected endpoint example:

    curl -X POST -H "Accept: application/json" -H "Content-Type: application/x-www-form-urlencoded; -F "email=admin@admin.com" -F "password=admin" -F "=" "http://api.domain.test/v2/register"

    Calling protected endpoint (passing Bearer Token) example:

    curl -X GET -H "Accept: application/json" -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..." -H "http://api.domain.test/v1/users"
    - + \ No newline at end of file diff --git a/docs/11.x/getting-started/responses/index.html b/docs/11.x/getting-started/responses/index.html index 9714c99d9..8a0d30333 100644 --- a/docs/11.x/getting-started/responses/index.html +++ b/docs/11.x/getting-started/responses/index.html @@ -4,7 +4,7 @@ Responses | Apiato - + @@ -17,7 +17,7 @@ If no $resourceKey is defined at the Model, the ShortClassName is used as key. For example, the ShortClassName of the App\Containers\AppSection\User\Models\User::class is User.

    Error Responses formats

    Visit each feature, e.g. the Authentication and there you will see how an unauthenticated response looks like, same for Authorization, Validation and so on.

    Building a Responses from the Controller:

    Checkout the Controller response builder helper functions.

    - + \ No newline at end of file diff --git a/docs/11.x/getting-started/samples/index.html b/docs/11.x/getting-started/samples/index.html index 6494a694b..83e6c6ecd 100644 --- a/docs/11.x/getting-started/samples/index.html +++ b/docs/11.x/getting-started/samples/index.html @@ -4,7 +4,7 @@ Samples | Apiato - + @@ -12,7 +12,7 @@
    Version: 11.x

    Samples

    The basic flow

    When an HTTP request is received, it first hits your predefined Endpoint (each endpoint live in its own Route file).

    Sample Route Endpoint

    Route::get('/hello', [Controller::class, 'sayHello']);

    After the user makes a request to the endpoint [GET] www.api.apiato.com/v1/hello it calls the defined controller function (sayHello).

    Sample Controller Function

    class Controller extends ApiController
    {
    public function sayHello(SayHelloRequest $request)
    {
    $helloMessage = app(SayHelloAction::class)->run();

    $this->json([
    $helloMessage
    ]);
    }
    }

    This function takes a Request class SayHelloRequest to automatically checks if the user has the right access to this endpoint. Only if the user has access, it proceeds to the function body.

    Then the function calls an Action (SayHelloAction) to perform the business logic.

    Sample Action

    class SayHelloAction extends Action
    {
    public function run()
    {
    return 'Hello World!';
    }
    }

    The Action can do anything then return a result (could be an Object, a String or anything).

    When the Action finishes its job, the controller function gets ready to build a response.

    Json responses can be built using the helper function json ($this->json(['foo' => 'bar']);).

    Sample User Response

    [
    "Hello World!"
    ]
    - + \ No newline at end of file diff --git a/docs/11.x/getting-started/software-architectural-patterns/index.html b/docs/11.x/getting-started/software-architectural-patterns/index.html index 4111b910f..9f4cf43e2 100644 --- a/docs/11.x/getting-started/software-architectural-patterns/index.html +++ b/docs/11.x/getting-started/software-architectural-patterns/index.html @@ -4,7 +4,7 @@ Architecture | Apiato - + @@ -14,7 +14,7 @@ App\Containers\SectionName\Printer).
  • Container MAY be named anything, however a good practice is to name it to its most important Model name. Example: If the User Story is (User can create a Store and Store can have Items) then we you could have 3 Containers (User, Store and Item).
  • - + \ No newline at end of file diff --git a/docs/11.x/index.html b/docs/11.x/index.html index db839c61f..018f13e42 100644 --- a/docs/11.x/index.html +++ b/docs/11.x/index.html @@ -4,13 +4,13 @@ Requirements | Apiato - +
    Version: 11.x

    Requirements

    Requirements

    • GIT
    • PHP >= 8.0.2
    • Composer
    • PHP Extensions:
      • OpenSSL PHP Extension
      • PDO PHP Extension
      • Mbstring PHP Extension
      • Tokenizer PHP Extension
      • BCMath PHP Extension (required when the Hash ID feature is enabled)
      • Intl Extension (required when you use the Localization Container)
    - + \ No newline at end of file diff --git a/docs/11.x/main-components/actions/index.html b/docs/11.x/main-components/actions/index.html index dd7d90cf5..fb97a4bff 100644 --- a/docs/11.x/main-components/actions/index.html +++ b/docs/11.x/main-components/actions/index.html @@ -4,7 +4,7 @@ Actions | Apiato - + @@ -17,7 +17,7 @@ automatically rolled-back from the database. However, respective operations on the file system (e.g., you may also have uploaded a profile picture for this Team already) need to be performed manually!

    Typically, you may want to use the transactionalRun() on the Controller level!

    - + \ No newline at end of file diff --git a/docs/11.x/main-components/controllers/index.html b/docs/11.x/main-components/controllers/index.html index edf808ce8..6ebfadfae 100644 --- a/docs/11.x/main-components/controllers/index.html +++ b/docs/11.x/main-components/controllers/index.html @@ -4,7 +4,7 @@ Controllers | Apiato - + @@ -17,7 +17,7 @@ This function allows including metadata in the response.

    $metaData = ['total_credits', 10000];

    return $this->withMeta($metaData)->transform($receipt, ReceiptTransformer::class);

    json This function allows passing array data to be represented as json.

    return $this->json([
    'foo': 'bar'
    ])

    Other functions

    • accepted
    • deleted
    • noContent
    • // Some functions might not be documented, so refer to the vendor/apiato/core/Traits/ResponseTrait.php and see the public functions.
    - + \ No newline at end of file diff --git a/docs/11.x/main-components/exceptions/index.html b/docs/11.x/main-components/exceptions/index.html index f67393f9a..3bb672275 100644 --- a/docs/11.x/main-components/exceptions/index.html +++ b/docs/11.x/main-components/exceptions/index.html @@ -4,13 +4,13 @@ Exceptions | Apiato - +
    Version: 11.x

    Exceptions

    Definition & Principles

    Read Porto SAP Documentation (#Exceptions).

    Rules

    • All Exceptions MUST extend App\Ship\Parents\Exceptions\Exception.
    • Shared (general) Exceptions between all Containers SHOULD be created in the Exceptions Ship folder (app/Ship/Exceptions/*).
    • Every Exception SHOULD have two properties code and message. You can override those values while throwing the error.

    Folder Structure

     - App
    - Containers
    - {section-name}
    - {container-name}
    - Exceptions
    - AccountFailedException.php
    - ...

    - Ship
    - Exceptions
    - CreateResourceFailedException.php
    - NotFoundException.php
    - ...

    Code Samples

    Demo Exception

    class DemoException extends Exception
    {
    public $code = Response::HTTP_CONFLICT;
    public $message = 'This is a demo exception.';
    }

    Usage from anywhere

    throw new AccountFailedException();

    Usage with errors

    throw (new AccountFailedException())->withErrors(['email' => 'Email already in use']);
    throw (new AccountFailedException())->withErrors(['email' => ['Email already in use', 'Another message']]);

    Usage with errors and localization

    For localization, you can use the Localization Container

    // translation strings are automatically translated if the translations are found.
    throw (new AccountFailedException())->withErrors(['email' => 'appSection@user::exceptions.email-taken']);

    Response:

    {
    "message": "The exception error message.",
    "errors": {
    "email": [
    "The email has already been taken."
    ]
    }
    }

    Usage with Log for Debugging

    throw (new AccountFailedException())->debug($e); // debug() accepts string or \Exception instance

    Usage and overriding the default

    throw new AccountFailedException('I am the message to be displayed to the user');
    - + \ No newline at end of file diff --git a/docs/11.x/main-components/models/index.html b/docs/11.x/main-components/models/index.html index 4e8cb2c21..bb4c3a055 100644 --- a/docs/11.x/main-components/models/index.html +++ b/docs/11.x/main-components/models/index.html @@ -4,13 +4,13 @@ Models | Apiato - +
    Version: 11.x

    Models

    Definition & Principles

    Read Porto SAP Documentation (#Models).

    Rules

    • All Models MUST extend from App\Ship\Parents\Models\Model.
    • If the name of a model differs from the Container name you have to implement model() method in the repository - more details.

    Folder Structure

     - App
    - Containers
    - {section-name}
    - {container-name}
    - Models
    - User.php
    - UserId.php
    - ...

    Code Sample

    class Demo extends Model
    {
    protected $table = 'demos';

    protected $fillable = [
    'label',
    'user_id'
    ];

    protected $hidden = [
    'token',
    ];

    protected $casts = [
    'total_credits' => 'float',
    ];

    protected $dates = [
    'created_at',
    'updated_at',
    ];

    public function user()
    {
    return $this->belongsTo(\App\Containers\AppSection\User\Models\User::class);
    }
    }

    Notice the Demo Model has a relationship with User Model, which lives in another Container.

    Casts

    The casts attribute can be used to parse any of the model's attributes to a specific type. In the code sample below we can cast total_credits to float.

    More information about the applicable cast-types can be found in the laravel eloquent-mutators documentation.

    You can place any dates inside the $dates to parse those automatically.

    - + \ No newline at end of file diff --git a/docs/11.x/main-components/requests/index.html b/docs/11.x/main-components/requests/index.html index f01b90bf5..d24af941a 100644 --- a/docs/11.x/main-components/requests/index.html +++ b/docs/11.x/main-components/requests/index.html @@ -4,7 +4,7 @@ Requests | Apiato - + @@ -25,7 +25,7 @@ function.

    This helper, in turn, allows to "redefine" keys in the request for subsequent processing. Consider the following example request:

    {
    "data" : {
    "name" : "John Doe"
    }
    }

    Your Task to process this data, however, requests the field data.name as data.username. You can call the helper like this:

    $request->mapInput([
    'data.name' => 'data.username',
    ]);

    The resulting structure would look like this:

    {
    "data" : {
    "username" : "John Doe"
    }
    }

    Storing Data on the Request

    During the Request life-cycle you may want to store some data on the request object and pass it to other SubActions (or Tasks).

    To store some data on the request use:

    $request->keep(['someKey' => $someValue]);

    To retrieve the data back at any time during the request life-cycle use:

    $someValue = $request->retrieve('someKey');

    Unit Testing for Actions (Request)

    Since we're passing Request objects to Actions. When writing unit tests we need to create fake Request just to pass it to the Action with some fake data.

    // creating
    $request = RegisterUserRequest::injectData($data);

    Example One:

    $data = [
    'email' => 'john@doe.test',
    'name' => 'John Doe',
    'password' => 'so-secret',
    ];

    // create request object with some data
    $request = RegisterUserRequest::injectData($data);

    // create instance of the Action
    $action = app(RegisterUserAction::class)->run($request);

    // do any kind of assertions..
    $this->assertInstanceOf(User::class, $user);

    Example Two (With Authenticated User):

    $data = [
    'store_id' => $this->encode($store->id),
    'items' => $orderItems,
    'recipient' => $receipient,
    ];

    $user = User::factory()->create();

    $request = MakeOrderRequest::injectData($data, $user);

    $order = app(MakeOrderAction::class)->run($request);
    - + \ No newline at end of file diff --git a/docs/11.x/main-components/routes/index.html b/docs/11.x/main-components/routes/index.html index 9a8437b47..c3a3752e5 100644 --- a/docs/11.x/main-components/routes/index.html +++ b/docs/11.x/main-components/routes/index.html @@ -4,13 +4,13 @@ Routes | Apiato - +
    Version: 11.x

    Routes

    Definition & Principles

    Read Porto SAP Documentation (#Routes).

    Rules

    • API Route files MUST be named according to their API's version, exposure and functionality. e.g. CreateOrder.v1.public.php, FulfillOrder.v2.public.php, CancelOrder.v1.private.php...

    • Web Route files are pretty similar to API web files, but they can be named anything.

    Folder Structure

     - app
    - Containers
    - {section-name}
    - {container-name}
    - UI
    - API
    - Routes
    - CreateItem.v1.public.php
    - DeleteItem.v1.public.php
    - CreateItem.v2.public.php
    - DeleteItem.v1.private.php
    - ApproveItem.v1.private.php
    - ...
    - WEB
    - Routes
    - main.php
    - ...

    Code Sample

    Web & API route

    Routes are defined exactly like the way you defined them in Laravel.

    Route::post('hello', [Controller::class, 'sayHello']);

    Protected Route (API)

    Route::get('users', [Controller::class, 'listAllUsers'])
    ->middleware(['auth:api']);

    Protect your Endpoints:

    Checkout the Authorization Page.

    Difference between Public & Private routes files

    Apiato has 2 types of endpoint, Public (External) mainly for third parties clients, and Private (Internal) for your own Apps. This will help to generate separate documentations for each and keep your internal API private.

    - + \ No newline at end of file diff --git a/docs/11.x/main-components/subactions/index.html b/docs/11.x/main-components/subactions/index.html index e59f608ce..a801a4469 100644 --- a/docs/11.x/main-components/subactions/index.html +++ b/docs/11.x/main-components/subactions/index.html @@ -4,13 +4,13 @@ Sub Actions | Apiato - +
    Version: 11.x

    Sub Actions

    Definition & Principles

    Read Porto SAP Documentation (#Sub-Actions).

    Rules

    • All SubActions MUST extend from App\Ship\Parents\Actions\SubAction.

    Folder Structure

     - app
    - Containers
    - {section-name}
    - {container-name}
    - Actions
    - ValidateAddressSubAction.php
    - BuildOrderSubAction.php
    - ...

    Code Sample

    ExampleSubAction

    class ExampleSubAction extends SubAction
    {
    public function run(SomeRequest $request)
    {
    app(SomeTask::class)->run($request);
    }
    }
    note

    Every feature available for Actions, are also available in SubActions.

    - + \ No newline at end of file diff --git a/docs/11.x/main-components/tasks/index.html b/docs/11.x/main-components/tasks/index.html index c9e1107ae..26e6f22a6 100644 --- a/docs/11.x/main-components/tasks/index.html +++ b/docs/11.x/main-components/tasks/index.html @@ -4,13 +4,13 @@ Tasks | Apiato - +
    Version: 11.x

    Tasks

    Definition & Principles

    Read Porto SAP Documentation (#Tasks).

    Rules

    • All Tasks MUST extend from App\Ship\Parents\Tasks\Task.

    Folder Structure

     - app
    - Containers
    - {section-name}
    - {container-name}
    - Tasks
    - ConfirmUserEmailTask.php
    - GenerateEmailConfirmationUrlTask.php
    - SendConfirmationEmailTask.php
    - ValidateConfirmationCodeTask.php
    - SetUserEmailTask.php
    - ...

    Code Sample

    Task

    class FindUserByIdTask extends Task
    {
    private $userRepository;

    public function __construct(UserRepository $userRepository)
    {
    $this->userRepository = $userRepository;
    }

    public function run($id)
    {
    try {
    $user = $this->userRepository->find($id);
    } catch (Exception $e) {
    throw new UserNotFoundException();
    }

    return $user;
    }
    }

    Task usage from an Action

    class ValidateUserEmailByConfirmationCodeAction extends Action
    {
    public function run($userId, $code)
    {
    app(ValidateConfirmationCodeTask::class)->run($userId, $code);
    $user = app(FindUserByIdTask::class)->run($userId);
    app(ConfirmUserEmailTask::class)->run($user);
    }
    }
    - + \ No newline at end of file diff --git a/docs/11.x/main-components/transformers/index.html b/docs/11.x/main-components/transformers/index.html index b38d3b33f..5864ff05f 100644 --- a/docs/11.x/main-components/transformers/index.html +++ b/docs/11.x/main-components/transformers/index.html @@ -4,13 +4,13 @@ Transformers | Apiato - +
    Version: 11.x

    Transformers

    Definition & Principles

    Read Porto SAP Documentation (#Transformers).

    Rules

    • All API responses MUST be formatted via a Transformer.

    • Every Transformer SHOULD extend from App\Ship\Parents\Transformers\Transformer.

    • Each Transformer MUST have a transform() function.

    Folder Structure

     - app
    - Containers
    - {section-name}
    - {container-name}
    - UI
    - API
    - Transformers
    - UserTransformer.php
    - ...

    Code Samples

    Reward Transformer with Country relation

    class ItemTransformer extends Transformer
    {
    protected $availableIncludes = [
    'images',
    ];

    protected $defaultIncludes = [
    'roles',
    ];

    public function transform(Item $item)
    {
    $response = [
    'object' => $item->getResourceKey(),
    'id' => $item->getHashedKey(),
    'name' => $item->name,
    'description' => $item->description,
    'price' => (float)$item->price,
    'weight' => (float)$item->weight,
    'created_at' => $item->created_at,
    'updated_at' => $item->updated_at,
    'readable_created_at' => $item->created_at->diffForHumans(),
    'readable_updated_at' => $item->updated_at->diffForHumans(),
    ];

    // add more or modify data for Admins only
    $response = $this->ifAdmin([
    'real_id' => $item->id,
    'deleted_at' => $item->deleted_at,
    ], $response);

    return $response;
    }

    public function includeImages(Item $item)
    {
    return $this->collection($item->images, new ItemImageTransformer());
    }

    public function includeRoles(User $user)
    {
    return $this->collection($user->roles, new RoleTransformer());
    }
    }

    Usage from Controller (Single Item)

    $user = $this->getUser();

    $this->transform($user, UserTransformer::class);

    // more options are available

    Relationships (include)

    Loading relationships in Transformer (calling other Transformers):

    This can be done in 2 ways:

    1. By the User, he can specify what relations to return in response.

    2. By the Developer, define what relations to include at run time.

    From Front-end

    You can request data with their relationships directly from the API call using include=tags,user but first the Transformer need to have the availableIncludes defined with their functions like this:

    class AccountTransformer extends Transformer
    {
    protected $availableIncludes = [
    'tags',
    'user',
    ];

    public function transform(Account $account)
    {
    return [
    'id' => (int)$account->id,
    'url' => $account->url,
    'username' => $account->username,
    'secret' => $account->secret,
    'note' => $account->note,
    ];
    }

    public function includeTags(Account $account)
    {
    // use collection with `multi` relationship
    return $this->collection($account->tags, new TagTransformer());
    }

    public function includeUser(Account $account)
    {
    // use `item` with single relationship
    return $this->item($account->user, new UserTransformer());
    }
    }

    Now to get the Tags with the response when Accounts are requested pass the ?include=tags parameter with the [GET] request.

    To get Tags with User use the comma separator: ?include=tags,user.

    From Back-end

    From the controller you can dynamically set the DefaultInclude using (setDefaultIncludes) anytime you want.

    return $this->transform($rewards, ProductsTransformer::class)->setDefaultIncludes(['tags']);

    You need to have includeTags function defined on the transformer. Look at the full examples above.

    If you want to include a relation with every response from this transformer you can define the relation directly in the transformer on ($defaultIncludes)

    protected $availableIncludes = [
    'users',
    ];

    protected $defaultIncludes = [
    'tags',
    ];

    // ..

    You need to have includeUser and includeTags functions defined on the transformer. Look at the full examples above.

    Transformer Available helper functions:

    • user(): returns current authenticated user object.

    • ifAdmin($adminResponse, $clientResponse): merges normal client response with the admin extra or modified results, when current authenticated user is Admin. Look at the full examples above.

    • nullableItem($model->something, new SomethingTransformer()): it is a shorthand for

    $model->something ? $this->item($model->something, new SomethingTransformer()) : $this->primitive(null)

    For more information about the Transformers read this.

    - + \ No newline at end of file diff --git a/docs/11.x/main-components/views/index.html b/docs/11.x/main-components/views/index.html index 19073887d..ec6d392da 100644 --- a/docs/11.x/main-components/views/index.html +++ b/docs/11.x/main-components/views/index.html @@ -4,13 +4,13 @@ Views | Apiato - +
    Version: 11.x

    Views

    Definition & Principles

    Read Porto SAP Documentation (#Views).

    Rules

    • Views SHOULD be created inside the Containers, and they will be automatically available for use in the Web Controllers.

    Folder Structure

     - app
    - Containers
    - {section-name}
    - {container-name}
    - UI
    - WEB
    - Views
    - welcome.php
    - profile.php
    - ...

    Code Sample

    Welcome page View

    <!DOCTYPE html>
    <html>
    <head>
    <title>Welcome</title>
    </head>
    <body>
    <div class="container">
    <div class="content">
    <div class="title">Welcome</div>
    </div>
    </div>
    </body>
    </html>

    Usage From Controller

    class Controller extends WebController
    {
    public function sayWelcome()
    {
    return view('just-welcome');
    }
    }

    Namespaces

    • By default, all Views are namespaced as the camelCase of its Section name + @ + camelCase of its Container name.

    For example, a view named welcome-page inside MySection > MyContainer can be accessed like this: view(mySection@myContainer::welcome-page)

    If you try to access it without the namespace view('just-welcome'), it will not find your View.

    note

    View files in Ship folder are exception to this and will be namespaced with the word "ship" instead of section name, e.g. view(ship::welcome-page)

    - + \ No newline at end of file diff --git a/docs/11.x/miscellaneous/tasks-queuing/index.html b/docs/11.x/miscellaneous/tasks-queuing/index.html index 06bdfa65a..536c4f8af 100644 --- a/docs/11.x/miscellaneous/tasks-queuing/index.html +++ b/docs/11.x/miscellaneous/tasks-queuing/index.html @@ -4,7 +4,7 @@ Tasks Queuing | Apiato - + @@ -18,7 +18,7 @@

    The only addition to the Laravel's queues in Apiato, is that by default, apiato detects which queue driver you are planning to use (based on the configs), to create the migration files required, in case type database is used.

    if (Config::get('queue.default') == 'database')
    {
    // do something
    }

    (refer to app/Ship/Migrations/ folder for more details).

    Beanstalkd

    In order to use Beanstalkd as your queue driver, you need to require the "pda/pheanstalk": "^4.0" package first. You can include this in any composer.json file you want.

    Further reading

    More info at Laravel Docs.

    - + \ No newline at end of file diff --git a/docs/11.x/miscellaneous/tasks-scheduling/index.html b/docs/11.x/miscellaneous/tasks-scheduling/index.html index 7b37317b6..d087fca6f 100644 --- a/docs/11.x/miscellaneous/tasks-scheduling/index.html +++ b/docs/11.x/miscellaneous/tasks-scheduling/index.html @@ -4,7 +4,7 @@ Tasks Scheduling | Apiato - + @@ -18,7 +18,7 @@ See the Commands Page.

    Once you have your command ready, go to app/Ship/Kernels/ConsoleKernel.php and start adding the commands you need to schedule inside the schedule function.

    Example:

    protected function schedule(Schedule $schedule)
    {
    $schedule->command('apiato:welcome')->everyMinute();
    $schedule->job(new myJob)->hourly();
    $schedule->exec('touch me.txt')->dailyAt('12:00');
    // ...
    }

    More details here.

    note

    You do not need to register the commands with the $commands property or point to them in the commands() function. Apiato will do that automatically for you.

    Further reading

    More info at Laravel Docs.

    - + \ No newline at end of file diff --git a/docs/11.x/miscellaneous/tests-helpers/index.html b/docs/11.x/miscellaneous/tests-helpers/index.html index 55a318cab..036dc62cd 100644 --- a/docs/11.x/miscellaneous/tests-helpers/index.html +++ b/docs/11.x/miscellaneous/tests-helpers/index.html @@ -4,7 +4,7 @@ Tests Helpers | Apiato - + @@ -24,7 +24,7 @@ testing data.

    1. Go to app/Ship/Seeder/SeedTestingData.php seeder class, and create your live testing data.

    2. Run this command php artisan apiato:seed-test

    Debugging with PsySH

    For better debugging and development, you can open a runtime developer console while executing your test.

    Using PsySH (interactive debugger and REPL "read-eval-print loop" for PHP). The package is required by the Laravel Tinker Package.

    To use it set the breakpoint eval(\Psy\sh()); anywhere you want in any Actions, Controllers, Tasks... and run your test normally.

    - + \ No newline at end of file diff --git a/docs/11.x/optional-components/commands/index.html b/docs/11.x/optional-components/commands/index.html index 56b8b6d4f..de4cc6a10 100644 --- a/docs/11.x/optional-components/commands/index.html +++ b/docs/11.x/optional-components/commands/index.html @@ -4,13 +4,13 @@ Commands | Apiato - +
    Version: 11.x

    Commands

    Definition

    • Commands are a Laravel artisan command. Laravel has its own default commands, and you can create your own as well.
    • Commands provide a way to interact with the Laravel app.
    • A Command can be scheduled by a Task scheduler, like Cron Job or by the Laravel built-in wrapper of the Cron Job "laravel scheduler".
    • Commands could be Closure based or Classes.
    • "dispatch" is the term that is usually used to call a Command.

    Principles

    • Containers MAY or MAY NOT have one or more Commands.

    • Every Command SHOULD call an Action to perform its job, and should not contain any business logic.

    • Ship may contain Application general Commands.

    Rules

    • All Commands MUST extend from App\Ship\Parents\Commands\ConsoleCommand.

    Folder Structure

    - app
    - Containers
    - {section-name}
    - {container-name}
    - UI
    - CLI
    - Commands
    - SayHelloCommand.php
    - ...
    - Ship
    - Commands
    - GeneralCommand.php
    - ...

    Code Samples

    A Simple Command

    class HelloWorldCommand extends ConsoleCommand
    {
    protected $signature = 'hello:world';
    protected $description = 'Hello World!';

    public function handle()
    {
    echo "Hello World :)\n";
    }
    }

    Usage from CLI (Terminal)

    php artisan hello:world

    Schedule Commands Execution

    To Schedule the execution of a Command checkout the Tasks Scheduling page.

    Define Consoles Closure Commands

    To define Console closure commands go to app/Ship/Commands/closures.php.

    Further reading

    More info at Laravel Docs.

    - + \ No newline at end of file diff --git a/docs/11.x/optional-components/configs/index.html b/docs/11.x/optional-components/configs/index.html index 26757d05f..828127c5a 100644 --- a/docs/11.x/optional-components/configs/index.html +++ b/docs/11.x/optional-components/configs/index.html @@ -4,13 +4,13 @@ Configs | Apiato - +
    Version: 11.x

    Configs

    Definition

    Configs are files that contain configurations.

    In each Apiato container, there are two types of config files:

    • the container specific config file (a config file that contains the container specific configurations).
    • the container third party packages config files (a config file that belongs to a third party package, required by the composer file of the container).

    Principles

    • Your custom config files and third party packages config files, should be placed in the Container, unless it's too generic then it can be placed on the Ship Layer.
    • Containers can have as many config files as they need.

    Rules

    • When publishing a third party package config file, move it manually to its container or to the Ship Configs folder in case it is generic.
    • Framework config files (provided by Laravel) lives at the default config directory on the root of the project.
    • You SHOULD NOT add any config file to the root config directory.
    • The container specific config file, MUST be named this way:
      camelCase of its Section name + - + camelCase of its Container name, to prevent conflicts between third party packages and container specific packages.
      For example, config file inside MySection > MyContainer should be named like this: mySection-myContainer.php

    Folder Structure

    - app
    - Containers
    {section-name}
    - {container-name}
    - Configs
    - {section-name}-{container-name}.php
    - package-config-file1.php
    - ...
    - Ship
    - Configs
    - apiato.php
    - ...
    - config
    - app.php
    - ...

    Code Samples

    Example simple Config file

    // app/Containers/{SectionName}/{ContainerName}/Configs/{section-name}-{container-name}.php
    return [

    /*
    |--------------------------------------------------------------------------
    | Default Namespace
    |--------------------------------------------------------------------------
    */
    'namespace' => 'App',

    // some other config params here...

    You can access the respective configuration key like this:

    $value = Config::get('{section-name}-{container-name}.namespace');     // returns 'App'
    $value = config('{section-name}-{container-name}.namespace'); // same, but using laravel helper function
    Further reading

    More info at Laravel Docs.

    - + \ No newline at end of file diff --git a/docs/11.x/optional-components/criterias/index.html b/docs/11.x/optional-components/criterias/index.html index edeb93d7c..25bdbea41 100644 --- a/docs/11.x/optional-components/criterias/index.html +++ b/docs/11.x/optional-components/criterias/index.html @@ -4,13 +4,13 @@ Criterias | Apiato - +
    Version: 11.x

    Criterias

    Definition

    Criterias are classes that hold and apply query condition when retrieving data from the database through a Repository.

    Without using a Criteria class, you can add your query conditions to a Repository or to a Model as scope, but with Criterias, your query conditions can be shared across multiple Models and Repositories. It allows you to define the query condition once and use it anywhere in the App.

    Principles

    • Every Container MAY have its own Criterias. However, shared Criterias SHOULD be created in the Ship layer.

    • A Criteria MUST not contain any extra code, if it needs data, the data SHOULD be passed to it from the Actions or the Task. It SHOULD not call any Task for data.

    Rules

    • All Criterias MUST extend from App\Ship\Parents\Criterias\Criteria.

    • Every Criteria SHOULD have an apply() function.

    Folder Structure

     - app
    - Containers
    - {section-name}
    - {container-name}
    - Data
    - Criterias
    - ColourRedCriteria.php
    - RaceCarsCriteria.php
    - ...
    - Ship
    - Criterias
    - CreatedTodayCriteria.php
    - NotNullCriteria.php
    - ...

    Code Samples

    A Shared Criteria

    class OrderByCreationDateDescendingCriteria extends Criteria
    {
    public function apply($model, PrettusRepositoryInterface $repository)
    {
    return $model->orderBy('created_at', 'desc');
    }
    }

    Usage from Task

    public function run()
    {
    $this->userRepository->pushCriteria(new OrderByCreationDateDescendingCriteria());
    return $this->userRepository->paginate();
    }

    Criteria Accepting Data Input

    class ThisUserCriteria extends Criteria
    {
    private $userId;

    public function __construct($userId)
    {
    $this->userId = $userId;
    }

    public function apply($model, PrettusRepositoryInterface $repository)
    {
    return $model->where('user_id', '=', $this->userId);
    }
    }

    Passing Data from Task to Criteria

    public function run($user)
    {
    $this->accountRepository->pushCriteria(new ThisUserCriteria($user->id));
    return $this->accountRepository->paginate();
    }

    Further reading

    More info at Laravel Docs.

    - + \ No newline at end of file diff --git a/docs/11.x/optional-components/events/index.html b/docs/11.x/optional-components/events/index.html index c96dc2841..97c976662 100644 --- a/docs/11.x/optional-components/events/index.html +++ b/docs/11.x/optional-components/events/index.html @@ -4,13 +4,13 @@ Events | Apiato - +
    Version: 11.x

    Events

    Definition

    • Events provide a simple observer implementation, allowing you to subscribe and listen for various events that occur in your application.
    • Events are classes that can be fired from anywhere in your application.
    • An event class will usually be bound to one, or many Events Listeners Classes or has those Listeners registered to listen to it.
    • "fire" is the term that is usually used to call an Event.

    Principles

    • Events can be fired from Actions and or Tasks. It's preferable to choose one place only. (Tasks are recommended).
    • Events SHOULD be created inside the Containers. However, general Events CAN be created in the Ship layer.

    Rules

    • Event classes CAN be placed inside the Containers in Events folders or on the Ship for the general Events.
    • All Events MUST extend from App\Ship\Parents\Events\Event.

    Folder Structure

    - App
    - Containers
    - {section-name}
    - {container-name}
    - Events
    - SomethingHappenedEvent.php
    - ...
    - Listeners
    - ListenToMusicListener.php
    - ...

    - Ship
    - Events
    - GlobalStateChanged.php
    - SomethingBiiigHappenedEvent.php
    - ...

    Usage

    In Laravel, you can create and register events in multiple way. Read Laravel documentation to learn more about Events.

    Your custom EventServiceProvider needs to be registered in the containers MainServiceProvider as well.

    Broadcasting

    To define Broadcasting channels go to app/Ship/Boardcasts/channels.php.

    Further reading

    More info at Laravel Docs.

    - + \ No newline at end of file diff --git a/docs/11.x/optional-components/factories/index.html b/docs/11.x/optional-components/factories/index.html index a2a837341..f79760dec 100644 --- a/docs/11.x/optional-components/factories/index.html +++ b/docs/11.x/optional-components/factories/index.html @@ -4,13 +4,13 @@ Factories | Apiato - +
    Version: 11.x

    Factories

    Definition

    Factories (are a short name for Model Factories).

    Factories are used to generate some fake data with the help of Faker to be used for testing purposes.

    Factories are mainly used from Tests.

    Principles

    • Factories SHOULD be created in the Containers.

    Rules

    • All Factories MUST extend from App\Ship\Parents\Factories\Factory.

    Folder Structure

     - app
    - Containers
    - {section-name}
    - {container-name}
    - Data
    - Factories
    - UserFactory.php
    - ...

    Code Samples

    A User Model Factory

    class UserFactory extends Factory
    {
    protected $model = User::class;

    public function definition(): array
    {
    static $password;

    return [
    'name' => $this->faker->name,
    'email' => $this->faker->unique()->safeEmail,
    'password' => $password ?: $password = Hash::make('testing-password'),
    'email_verified_at' => now(),
    'remember_token' => Str::random(10),
    'is_admin' => false,
    ];
    }
    }

    Usage from Tests or Anywhere Else

    // creating 4 users
    User::factory()->count(4)->create();
    Further reading

    More info at Laravel Docs.

    - + \ No newline at end of file diff --git a/docs/11.x/optional-components/helpers/index.html b/docs/11.x/optional-components/helpers/index.html index 2fb78293e..49fb45295 100644 --- a/docs/11.x/optional-components/helpers/index.html +++ b/docs/11.x/optional-components/helpers/index.html @@ -4,13 +4,13 @@ Helpers | Apiato - +
    Version: 11.x

    Helpers

    Definition

    • Helpers are global PHP functions that you can call from anywhere in your application.
    • Helper files are simple PHP files that hold functions.

    Principles

    • Helpers SHOULD be created inside the Containers. However, general Helpers CAN be created in the Ship layer.
    • You can create as many helper files as you need, per container.
    • You can implement as many helper functions as you need, per helper file.
    • All Helper files will be autoloaded by the framework.

    Rules

    • Helpers CAN be placed inside the Containers in Helpers folder or on the Ship for the general Helpers.

    Folder Structure

    - App
    - Containers
    - {section-name}
    - {container-name}
    - Helpers
    - helpers.php
    - mix.php
    - ...

    - Ship
    - Helpers
    - helpers.php
    - mix.php
    - ...

    Usage

    if (!function_exists('add')) {
    function add(int $firstNumber, int $secondNumber): int
    {
    return $firstNumber + $secondNumber;
    }
    }
    - + \ No newline at end of file diff --git a/docs/11.x/optional-components/jobs/index.html b/docs/11.x/optional-components/jobs/index.html index 7f43973ee..d0c98f2e2 100644 --- a/docs/11.x/optional-components/jobs/index.html +++ b/docs/11.x/optional-components/jobs/index.html @@ -4,13 +4,13 @@ Jobs | Apiato - +
    Version: 11.x

    Jobs

    Definition

    • Jobs are simple classes that can do one thing or multiple related things.
    • Job is a name given to a class that is usually created to be queued (it's execution is usually deferred for later, after the execution of previous Jobs are completed).
    • Jobs can be scheduled to be executed later by a queuing mechanism (a queue system like beanstalkd).
    • When a Job class is dispatched, it performs its specific job and dies.
    • Laravel's queue worker will process every Job as it's pushed onto the queue.

    Principles

    • A Container MAY have more than one Job.

    Rules

    • All Jobs MUST extend from App\Ship\Parents\Jobs\Job.

    Folder Structure

    - app
    - Containers
    - {section-name}
    - {container-name}
    - Jobs
    - DoSomethingJob.php
    - DoSomethingElseJob.php

    Code Samples

    DemoJob

    class DemoJob extends Job
    {
    private $something;

    public function __construct(array $someData)
    {
    $this->something = $someData;
    }

    public function handle()
    {
    foreach ($this->something as $thing) {
    // do whatever you like
    }
    }
    }

    Check the parent Job class.

    Usage from Action

    // using helper function
    dispatch(new DemoJob($someData));

    // manually
    app(\Illuminate\Contracts\Bus\Dispatcher\Dispatcher::class)->dispatch(New DemoJob($someData));

    Execute Jobs Execution

    For running your Jobs checkout the Tasks Queuing page.

    Further reading

    More info at Laravel Docs.

    - + \ No newline at end of file diff --git a/docs/11.x/optional-components/languages/index.html b/docs/11.x/optional-components/languages/index.html index 98f9e2843..02cae6713 100644 --- a/docs/11.x/optional-components/languages/index.html +++ b/docs/11.x/optional-components/languages/index.html @@ -4,14 +4,14 @@ Languages | Apiato - +
    Version: 11.x

    Languages

    Definition

    Languages are not real Components, they are just files that holds translations.

    Rules

    • Languages CAN be placed inside the Containers. However, the default laravel resources/lang languages files are still loaded and can be used as well.

    • All Translations are namespaced as the camelCase of its Section name + @ + camelCase of its Container name.
      For example, translation key inside a translation file named messages inside MySection > MyContainer can be accessed like this: __(mySection@myContainer::messages.welcome)

    Folder Structure

    - app
    - Containers
    - {section-name}
    - {container-name}
    - Resources
    - Languages
    - en
    - messages.php
    - users.php
    - ar
    - messages.php
    - users.php

    Usage

    Nothing much to show here, here's how you use translated strings:

    __('mySection@myContainer::messages.welcome');
    Further reading

    More info at Localization.

    - + \ No newline at end of file diff --git a/docs/11.x/optional-components/mails/index.html b/docs/11.x/optional-components/mails/index.html index 57d98d8f2..b9f114e47 100644 --- a/docs/11.x/optional-components/mails/index.html +++ b/docs/11.x/optional-components/mails/index.html @@ -4,13 +4,13 @@ Mails | Apiato - +
    Version: 11.x

    Mails

    Definition

    The Mail component allows you to describe an email and send it whenever needed.

    Principles

    • Containers MAY or MAY NOT have one or more Mail.

    • Ship may contain general Mails.

    Rules

    • All Notifications MUST extend from App\Ship\Parents\Mails\Mail.
    • Email Templates must be placed inside the Mail directory in a Templates directory app/Containers/{section}/{container}/Mails/Templates.

    Folder Structure

    - app
    - Containers
    - {section-name}
    - {container-name}
    - Mails
    - UserRegisteredMail.php
    - ...
    - Templates
    - user-registered.blade.php
    - ...
    - Ship
    - Mails
    - SomeMail.php
    - ...
    - Templates
    - some-template.blade.php
    - ...

    Code Samples

    A simple Mail

    class UserRegisteredMail extends Mail implements ShouldQueue
    {
    use Queueable;

    protected $user;

    public function __construct(User $user)
    {
    $this->user = $user;
    }

    public function build()
    {
    return $this->view('appSection@user::user-registered')
    ->to($this->user->email, $this->user->name)
    ->with([
    'name' => $this->user->name,
    ]);
    }
    }

    Usage from an Action

    Notifications can be sent from Actions or Tasks using the Mail Facade.

    Mail::send(new UserRegisteredMail($user));

    Email Templates

    Templates should be placed inside a folder Templates inside the Mail folder.

    To access a Mail template (same like accessing a web view) you must call the camelCase of its Section name + @ + camelCase of its Container name.

    In the example below we're using the user-registered.blade.php template in the AppSection Section > User Container.

    $this->view('appSection@user::user-registered');

    Configure Emails

    Open the .env file and set the from mail and address. This will be used globally whenever the from function is not called in the Mail.

    MAIL_FROM_ADDRESS=test@test.test
    MAIL_FROM_NAME="apiato"

    To use different email address in some classes add ->to($this->email, $this->name) to the build function in your Mail class.

    By default Apiato is configured to use Log Driver MAIL_DRIVER=log, you can change that from the .env file.

    Queueing A Notification

    To queue a notification you should use Illuminate\Bus\Queueable and implement Illuminate\Contracts\Queue\ShouldQueue.

    Further reading

    More info at Laravel Docs.

    - + \ No newline at end of file diff --git a/docs/11.x/optional-components/middlewares/index.html b/docs/11.x/optional-components/middlewares/index.html index f12d3c262..8c6c3e104 100644 --- a/docs/11.x/optional-components/middlewares/index.html +++ b/docs/11.x/optional-components/middlewares/index.html @@ -4,7 +4,7 @@ Middlewares | Apiato - + @@ -12,7 +12,7 @@
    Version: 11.x

    Middlewares

    Definition

    Middleware provide a convenient mechanism for filtering HTTP requests entering your application.

    You can enable and disable Middlewares as you wish.

    Principles

    • There are two types of Middlewares, General (applied on all the Routes by default) and Endpoints Middlewares (applied on some Endpoints).

    • The Middlewares CAN be placed in Ship layer or Container layer depending on its roles.

    Rules

    • If a Middleware is written inside a Container then it MUST be registered inside that Container.

    • To register Middlewares in a Container the container needs to have a MiddlewareServiceProvider, and like all other Container Providers it MUST be registered in the MainServiceProvider of that Container.

    • General Middlewares SHOULD live in the Ship layer app/Ship/Middlewares/* and are registered in the app/Ship/Kernels/HttpKernel.

    • Third Party packages Middleware CAN be registered in Containers or on the Ship layer (wherever they make more sense). For example the jwt.auth middleware "provided by the JWT package" should be registered in the Authentication Container (Containers/AppSection/Authentication/Providers/MiddlewareServiceProvider.php).

    Folder Structure

     - App
    - Containers
    - {section-name}
    - {container-name}
    - Middlewares
    - WebAuthentication.php
    - Ship
    - Middleware
    - Http
    - EncryptCookies.php
    - VerifyCsrfToken.php

    Code Sample

    Middleware Registration Inside the Container Example

    class MiddlewareServiceProvider extends MiddlewareProvider
    {
    protected array $middlewares = [
    // ..
    ];

    protected array $middlewareGroups = [
    'web' => [
    // ..
    ],
    'api' => [
    // ..
    ],
    ];

    protected array $routeMiddleware = [
    // apiato User Authentication middleware for Web Pages
    'guest' => RedirectIfAuthenticated::class
    ];
    }

    Middleware Registration Inside the Ship Layer (HTTP Kernel)

    class HttpKernel extends LaravelHttpKernel
    {
    /**
    * The application's global HTTP middleware stack.
    *
    * These middleware are run during every request to your application.
    *
    * @var array
    */
    protected $middleware = [
    // Laravel middleware's
    // \App\Http\Middleware\TrustHosts::class,
    TrustProxies::class,
    HandleCors::class,
    PreventRequestsDuringMaintenance::class,
    ValidatePostSize::class,
    TrimStrings::class,
    ConvertEmptyStringsToNull::class,
    ];

    /**
    * The application's route middleware groups.
    *
    * @var array
    */
    protected $middlewareGroups = [
    'web' => [
    EncryptCookies::class,
    AddQueuedCookiesToResponse::class,
    StartSession::class,
    // \Illuminate\Session\Middleware\AuthenticateSession::class,
    ShareErrorsFromSession::class,
    VerifyCsrfToken::class,
    SubstituteBindings::class,
    ],

    'api' => [
    // Note: The "throttle" Middleware is registered by the RoutesLoaderTrait in the Core
    SubstituteBindings::class,
    ValidateJsonContent::class,
    ProcessETagHeadersMiddleware::class,
    ProfilerMiddleware::class,
    ],
    ];

    /**
    * The application's route middleware.
    *
    * These middleware may be assigned to groups or used individually.
    *
    * @var array
    */
    protected $routeMiddleware = [
    'auth' => Authenticate::class,
    // 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
    'cache.headers' => SetCacheHeaders::class,
    // Note: The "can" Middleware is registered by MiddlewareServiceProvider in Authorization Container
    // 'can' => \Illuminate\Auth\Middleware\Authorize::class,
    // Note: The "guest" Middleware is registered by MiddlewareServiceProvider in Authentication Container
    // 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
    'password.confirm' => RequirePassword::class,
    'signed' => ValidateSignature::class,
    'throttle' => ThrottleRequests::class,
    'verified' => EnsureEmailIsVerified::class,
    ];

    /**
    * The priority-sorted list of middleware.
    *
    * Forces non-global middleware to always be in the given order.
    *
    * @var string[]
    */
    protected $middlewarePriority = [
    EncryptCookies::class,
    StartSession::class,
    ShareErrorsFromSession::class,
    Authenticate::class,
    ThrottleRequests::class,
    AuthenticateSession::class,
    SubstituteBindings::class,
    Authorize::class,
    ];
    }
    Further reading

    More info at Laravel Docs.

    - + \ No newline at end of file diff --git a/docs/11.x/optional-components/migrations/index.html b/docs/11.x/optional-components/migrations/index.html index d6879beb4..1e73c212a 100644 --- a/docs/11.x/optional-components/migrations/index.html +++ b/docs/11.x/optional-components/migrations/index.html @@ -4,13 +4,13 @@ Migrations | Apiato - +
    Version: 11.x

    Migrations

    Definition

    Migrations (are the short name for Database Migrations).

    Migrations are the version control of your database. They are very useful for generating and documenting the database tables.

    Principles

    • Migrations SHOULD be created inside the Containers folders.

    • Migrations will be autoloaded by the framework.

    Rules

    • No need to publish the DB Migrations. Just run the artisan migrate command and Laravel will read the Migrations from the Containers.

    Folder Structure

       - app
    - Containers
    - {section-name}
    - {container-name}
    - Data
    - Migrations
    - 2200_01_01_000001_create_something_table.php
    - ...

    Code Samples

    User CreateDemoTable Migrations

    class CreateDemoTable extends Migration
    {
    public function up()
    {
    Schema::create('demos', function (Blueprint $table) {
    $table->increments('id');
    // ...
    $table->timestamps();
    $table->softDeletes();
    });
    }

    public function down()
    {
    Schema::drop('demos');
    }
    }

    Further reading

    More info at Laravel Docs.

    - + \ No newline at end of file diff --git a/docs/11.x/optional-components/notifications/index.html b/docs/11.x/optional-components/notifications/index.html index 8fcd207de..f70928f97 100644 --- a/docs/11.x/optional-components/notifications/index.html +++ b/docs/11.x/optional-components/notifications/index.html @@ -4,7 +4,7 @@ Notifications | Apiato - + @@ -12,7 +12,7 @@
    Version: 11.x

    Notifications

    Definition

    Notifications allow you to inform the user about a state changes in your application.

    The Laravel notifications supports sending notifications across a variety of channels (mail, SMS, Slack, Database...).

    When using the Database channel, the notifications will be stored in a database to be displayed in your client interface.

    Principles

    • Containers MAY or MAY NOT have one or more Notification.

    • Ship MAY contain Application general Notifications.

    Rules

    • All Notifications MUST extend from App\Ship\Parents\Notifications\Notification.

    Folder Structure

    - app
    - Containers
    - {select-name}
    - {container-name}
    - Notifications
    - UserRegisteredNotification.php
    - ...
    - Ship
    - Notifications
    - SystemFailureNotification.php
    - ...

    Code Samples

    A Simple Notification

    class BirthdayReminderNotification extends Notification implements ShouldQueue
    {
    use Queueable;

    protected $notificationMessage;

    public function __construct($notificationMessage)
    {
    $this->notificationMessage = $notificationMessage;
    }

    public function toArray($notifiable)
    {
    return [
    'content' => $this->notificationMessage,
    ];
    }

    public function toMail($notifiable)
    {
    // $notifiable is the object you want to notify "e.g. user"
    return (new MailMessage)
    ->subject("Hello World")
    ->line("Hi, $notifiable->name")
    ->line($this->notificationMessage);
    }

    public function toSms($notifiable)
    {
    // ...
    }

    // ...
    }

    Usage from an Action or Task

    Notifications can be sent from Actions or Tasks using the Notification Facade.

    \Notification::send($user, new BirthdayReminderNotification($notificationMessage));

    Alternatively you can use the Illuminate\Notifications\Notifiable trait on the notifiable object "e.g. User" and then call it as follows:

    // call notify, found on the Notifiable trait
    $user->notify(new BirthdayReminderNotification($notificationMessage));

    Select Channels

    To select a notification channel, apiato have the app/Ship/Configs/notification.php config file where you can define the array of supported channels "e.g. SMS, Email, WebPush...", to be used for all your notifications.

    If you want to override the configuration for some notifications classes, or if you prefer to define the channels within each notification class itself, you can override the via function public function via($notifiable) in the notification class and define your channels.

    Checkout laravel notification channels for list of supported integrations.

    Queueing a Notification

    To queue a notification you should use Illuminate\Bus\Queueable and implement Illuminate\Contracts\Queue\ShouldQueue.

    Use DB channel

    Generally you need to generate the notification migration php artisan notifications:table, then run php artisan migrate, however just running the migration command will do the job, since Apiato already adds the _create_notifications_table.php in the default migrations files directory app/Ship/Migrations/.

    Further reading

    More info at Laravel Docs.

    - + \ No newline at end of file diff --git a/docs/11.x/optional-components/providers/index.html b/docs/11.x/optional-components/providers/index.html index 56bed0244..bed54a6cc 100644 --- a/docs/11.x/optional-components/providers/index.html +++ b/docs/11.x/optional-components/providers/index.html @@ -4,7 +4,7 @@ Providers | Apiato - + @@ -14,7 +14,7 @@ In apiato those providers have been renamed and moved to the Ship Layer app/Ship/Parents/Providers/*:

    • AppServiceProvider
    • RouteServiceProvider
    • AuthServiceProvider
    • BroadcastServiceProvider
    • EventsServiceProvider
    note

    You should not touch those providers, instead you have to extend them from a containers providers in order to modify them. Example: the app/Containers/AppSection/Authentication/Providers/AuthProvider.php is extending the AuthServiceProvider to modify it.

    Those providers are not auto registered by default, thus writing any code there will not be available, unless you extend them. Once extended the child Provider should be registered in its Container Main Provider, which makes its parent available.

    This rule does not apply to the RouteServiceProvider since it's required by Apiato, this Provider is registered by the ShipProvider.

    Check How Service Providers are auto-loaded.

    Further reading

    More info at Laravel Docs.

    - + \ No newline at end of file diff --git a/docs/11.x/optional-components/repositories/index.html b/docs/11.x/optional-components/repositories/index.html index c8673e482..c8aa8d0cb 100644 --- a/docs/11.x/optional-components/repositories/index.html +++ b/docs/11.x/optional-components/repositories/index.html @@ -4,13 +4,13 @@ Repositories | Apiato - +
    Version: 11.x

    Repositories

    Definition

    The Repository classes are an implementation of the Repository Design Pattern.

    Their major roles are separating the business logic from the data (or the data access Task).

    Repositories save and retrieves Models to/from the underlying storage mechanism.

    The Repository is used to separate the logic that retrieves the data and maps it to a Model, from the business logic that acts on the Model.

    Principles

    • Every Model SHOULD have a Repository.

    • A Model SHOULD always get accessed through its Repository. (Never accessed directly).

    Rules

    • All Repositories MUST extend from App\Ship\Parents\Repositories\Repository. Extending from this class will give you access to methods like (find, create, update and much more).

    • Repository name should be same as it's model name (model: Foo -> repository: FooRepository).

    • If a Repository belongs to a Model whose name is not equal to its Container name, then the Repository implement model() method like this.

    Folder Structure

     - app
    - Containers
    - {section-name}
    - {container-name}
    - Data
    - Repositories
    - UserRepository.php
    - ...

    Code Samples

    Demo Repository

    class DemoRepository extends Repository
    {
    protected $fieldSearchable = [
    'name' => 'like',
    'email' => '=',
    ];
    }

    Usage

    // paginate the data by 10
    $users = $userRepository->paginate(10);

    // search by 1 field
    $cars = $carRepository->findByField('colour', $colour);

    // searching multiple fields
    $offer = $offerRepository->findWhere([
    'offer_id' => $offer_id,
    'user_id' => $user_id,
    ])->first();

    //....

    Different Model and Container Name

    The model() method must be implemented when the model has different name than the container.

    class DemoRepository extends Repository
    {
    // ...

    public function model(): string
    {
    return Demo::class;
    }
    }

    Other Properties:

    API Query Parameters Property

    To enable query parameters (?search=text,...) in your API you need to set the property $fieldSearchable on the Repository class, to instruct the querying on your model. More details.

        protected $fieldSearchable = [
    'name' => 'like',
    'email' => '=',
    ];

    All other Properties

    Apiato uses the l5-repository package, to provide a lot of powerful features to the repository class.

    Further reading

    To learn more about all the properties you can use, visit the andersao/l5-repository package documentation.

    - + \ No newline at end of file diff --git a/docs/11.x/optional-components/seeders/index.html b/docs/11.x/optional-components/seeders/index.html index 9b0da026e..3b3f192f5 100644 --- a/docs/11.x/optional-components/seeders/index.html +++ b/docs/11.x/optional-components/seeders/index.html @@ -4,13 +4,13 @@ Seeders | Apiato - +
    Version: 11.x

    Seeders

    Definition

    Seeders (are a short name for Database Seeders).

    Seeders are classes made to seed the database with real data, this data usually should exist in the Application after the installation (Example: the default Users Roles and Permissions or the list of Countries).

    Principles

    • Seeders SHOULD be created in the Containers. (If the container is using a package that publishes a Seeder class, this class should be manually placed in the Container that make use of it. Do not rely on the package to place it in its right location).

    Rules

    • Seeders should be in the right directory inside the container to be loaded.

    • To avoid any conflict between containers seeders classes, you SHOULD always prepend the Seeders of each container with the container name. (Example: UserPermissionsSeeder, ItemPermissionsSeeder).

      note

      If 2 seeders classes have the same name but live in different containers, one of them will not be loaded. In these situations you can also prepend the seeder name with the section name

    • If you wish to order the seeding of the classes, you can just append _1, _2 to your classes.

    Folder Structure

     - App
    - Containers
    - {section-name}
    - {container-name}
    - Data
    - Seeders
    - ContainerNameRolesSeeder_1.php
    - ContainerNamePermissionsSeeder_2.php
    - ...

    Code Samples

    Demo Seeder

    class DemoSeeder_1 extends Seeder
    {
    public function run()
    {
    app(CreateRoleTask::class)->run('admin', 'Administrator', 'Administrator Role', 999);
    // ...
    }
    }
    note

    Same Seeder class is allowed to contain seeding for multiple Models.

    Run the Seeders

    After registering the Seeders you can run this command:

    php artisan db:seed

    Migrate & seed at the same time

    php artisan migrate --seed

    Testing Seeder Command

    It's useful sometimes to create a big set of testing data. apiato facilitates this task:

    1. Open app/Ship/Seeders/SeedTestingData.php and write your testing data here.
    2. Run this command any time you want this data available (example at staging servers):
    php artisan apiato:seed-test
    Further reading

    More info at Laravel Docs.

    - + \ No newline at end of file diff --git a/docs/11.x/optional-components/tests/index.html b/docs/11.x/optional-components/tests/index.html index 5e72e99c1..e0b26acb6 100644 --- a/docs/11.x/optional-components/tests/index.html +++ b/docs/11.x/optional-components/tests/index.html @@ -4,13 +4,13 @@ Tests | Apiato - +
    Version: 11.x

    Tests

    Definition

    Tests classes are created to test if the Application classes are working as expected.

    The two most essential Test types for this architecture are the Unit Tests and the Functional Tests. However, Integration and Acceptance Tests can be used as well.

    Principles

    • Containers MAY be covered by all types of Tests.

    • Use Functional Tests to test Container Routes are doing what's expected from them.

    • Use Unit Tests to test Container Actions and Tasks are doing what's expected from them.

    Rules

    • All Container Test classes SHOULD extend from a Container Internal TestCase class {container-name}/Tests/TestCase.php. The container TestCase MUST extend main TestCase on Ship layer App\Ship\Parents\Tests\PhpUnit\TestCase. (Adding functions to the container TestCase allows sharing those functions between all Test classes of the Container).

    Folder Structure

     - app
    - Containers
    - {section-name}
    - {container-name}
    - Tests
    - TestCase.php // the container test case
    - Unit
    - CreateUserTest.php
    - UpdateUserTest.php
    - ...
    - UI
    - API
    - Tests
    - Functional
    - LoginTest.php
    - LogoutTest.php
    - ...
    - WEB
    - Tests
    - Functional
    - LoginTest.php
    - LogoutTest.php
    - ...
    - CLI
    - Tests
    - Functional
    - BackupDataTest.php
    - ...

    Code Sample

    class DeleteUserTest extends TestCase
    {
    protected $endpoint = 'delete@v1/users/{id}';

    protected array $access = [
    'roles' => '',
    'permissions' => 'delete-users',
    ];

    public function testDeleteExistingUser()
    {
    $user = $this->getTestingUser();

    $response = $this->injectId($user->id)->makeCall();

    $response->assertStatus(204);
    }
    }

    See the Tests Helpers Page

    - + \ No newline at end of file diff --git a/docs/11.x/optional-components/values/index.html b/docs/11.x/optional-components/values/index.html index 687293602..34825af6a 100644 --- a/docs/11.x/optional-components/values/index.html +++ b/docs/11.x/optional-components/values/index.html @@ -4,7 +4,7 @@ Values | Apiato - + @@ -12,7 +12,7 @@
    Version: 11.x

    Values

    Definition & Principles

    Values are short names for the known "Value Objects" which are simple Objects, pretty similar to Models in the concept of representing data, but they do not get stored in the DB, thus they don't have ID's. They also do not hold functionality or change any state, they just hold data.

    A Value Object is an immutable object that is defined by its encapsulated attributes. We create Value Object when we need it to represent/serve/manipulate some data (attached as attributes), and we'll kill it later when we don't need it anymore, to recreate it again when needed.

    Rules

    • All Models MUST extend from App\Ship\Parents\Values\Value.

    Folder Structure

    - App
    - Containers
    - {section-name}
    - {container-name}
    - Values
    - Output.php
    - Region.php
    - ...

    Code Sample

    class Location extends Value
    {
    private $x = null;
    private $y = null;
    protected $resourceKey = 'locales';

    public function __construct($x, $y)
    {
    $this->x = $x;
    $this->y = $y;
    }

    public function getCoordinatesAsString()
    {
    return $this->x . ' - ' . $this->y;
    }
    }
    - + \ No newline at end of file diff --git a/docs/11.x/upgrade-guide/index.html b/docs/11.x/upgrade-guide/index.html index a8662a9b2..24c53cc3d 100644 --- a/docs/11.x/upgrade-guide/index.html +++ b/docs/11.x/upgrade-guide/index.html @@ -4,7 +4,7 @@ Upgrade Guide | Apiato - + @@ -23,7 +23,7 @@ and the term New Project (referring to the new freshly installed Apiato 5.0).

    1) Download and install Apiato 5.0. See Application Setup.

    2) Delete the Containers directory app/Containers from the new project.

    3) Move the Containers directory app/Containers from the old project to the new project.

    4) Open this file app/Ship/composer.json in your old project and only copy the required dependencies, from the old project to the same file in the new project.

    5) Again, open the app/Ship/composer.json file in the new project, and remove the following dependencies: guzzlehttp/guzzle, prettus/l5-repository, barryvdh/laravel-cors, spatie/laravel-fractal, vinkla/hashids and johannesschobel/apiato-container-installer.

    6) Move and replace the following directories from the old project to the new project: config, public, resources, database and storage.

    7) Open config/app.php and replace App\Ship\Engine\Providers\PortoServiceProvider::class with Apiato\Core\Providers\ApiatoProvider::class.

    8) Move .gitignore, phpunit.xml and .env files, from the old project to the new project.

    9) Open the .env file on the new project and append this to it API_RATE_LIMIT_ENABLED=true.

    10) Open phpunit.xml file of the new project and delete this line from the file <file>./app/Ship/Engine/Loaders/FactoryMixer/FactoriesLoader.php</file>.

    11) If you had live testing data in your old project inside app/Ship/Seeders/Data/Testing/Seeders/TestingDataSeeder.php file, then copy that file content and past it in the new project inside app/Ship/Seeders/SeedTestingData.php. You will need to rename the class (not the file) from TestingDataSeeder to SeedTestingData, and you will need to update the namespace from namespace App\Ship\Seeders\Data\Testing\Seeders; to namespace App\Ship\Seeders;.

    12) If you ever used the HashIdTrait, you need to search and replace this namespace App\Ship\Engine\Traits\HashIdTrait with this Apiato\Core\Traits\HashIdTrait.

    13) Run composer update. If you got any error at this step, try to solve it or open an Issue.

    14) Move the .git directory from the old project to the new one. Add all changes git add . then commit git commit -m 'upgrade Apiato from 4.1 to 5.0'.

    15) Run your tests vendor/bin/phpunit.

    That's it :)

    How to manually upgrade older versions to 4.1?

    Use the Manual Upgrading Guide below.

    Manual Upgrading Guide

    These commands and examples, are compatible with the Apiato 8.0 upgrade. You can just copy/past.

    1) Checkout a new branch from your stable branch, to perform the upgrade.

    git checkout -b upgrade-apiato

    2) Configure a new remote (upstream) that points to the official Apiato repository.

    git remote add upstream https://github.com/apiato/apiato

    Verify the new upstream repository was added, by listing the current configured remote repositories.

    git remote -vv

    origin git@bitbucket.org:username/my-awesome-api.git (fetch)
    origin git@bitbucket.org:username/my-awesome-api.git (push)
    upstream git@github.com:apiato/apiato.git (fetch)
    upstream git@github.com:apiato/apiato.git (push)

    3) Checkout a new branch to hold the latest Apiato changes. This branch will be merged into your upgrade-apiato branch created above.

    git checkout -b apiato-{version}
    // Example: git checkout -b apiato-8.0

    4) Configure this branch to track an upstream specific branch.

    Replace {upstream-branch-name} with the branch name you want to upgrade to (for example 8.0).

    git fetch upstream {upstream-branch-name}
    // Example: git fetch upstream 8.0

    git branch --set-upstream-to upstream/{upstream-branch-name}
    // Example: git branch --set-upstream-to upstream/8.0

    Verify your local branch is tracking the Apiato specified upstream branch.

    git branch -vv

    apiato 77b4d945 [upstream/{upstream-branch-name}] ...
    master 77d302aa [origin/master] ...

    5) Make this branch identical to the remote upstream branch

    git reset --hard upstream/{upstream-branch-name}
    // Example: git reset --hard upstream/8.0

    Verify this branch now contains the latest changes from the upstream branch.

    git log

    6) Switch back to the upgrade-apiato branch

    git checkout upgrade-apiato

    7) Now lets merge the 2 branches. This step can be done in two ways:

    Option A: Merge all the changes together and solve the conflicts if any. (Recommended)

    You can execute the next command with different different parameters, below are 2 options to pick whatever feels safer to you. Do not execute both of them.

    A1: This will overwrite your changes with the upstream changes. (Try this first and if your tests failed then you can try the second one).

    git merge --allow-unrelated-histories --strategy-option=theirs apiato-{version}
    // Example: git merge --allow-unrelated-histories --strategy-option=theirs apiato-8.0

    A2: This will let you solve all the conflicts manually. (Can be the most secure choice, but it's time consuming as well.)

    git merge --allow-unrelated-histories apiato-{version}
    // Example: git merge --allow-unrelated-histories apiato-8.0

    Option B: Manually cherry pick the commits you likes to have:

    git log {upstream-branch-name}

    (copy each commit ID, one by one)

    git cherry-pick {commit-ID}

    (if you get any conflict solve it and keep moving)

    8) Compare your .env with the new .env-example and update it.

    9) Check everything is working. By running Composer install first then re-running your tests.

    • Read the changelog releases page.
    • You may want to update your custom containers dependencies, simply follow the composer install error outputs and bump each failing dependency. (Hint: visit each package releases page, and use the version which supports the supported version of Laravel).
    • You may need to fix the failing tests.
    composer install  &&  vendor/bin/phpunit

    10) Finally, merge the upgrade-apiato (which contains the upgraded changes) with your stable branch (could be master).

    git checkout master
    git merge upgrade-apiato

    php artisan -V

    Enjoy :)

    - + \ No newline at end of file diff --git a/docs/9.x/contribution-guide/index.html b/docs/9.x/contribution-guide/index.html index 75b88ce28..812ba323c 100644 --- a/docs/9.x/contribution-guide/index.html +++ b/docs/9.x/contribution-guide/index.html @@ -4,7 +4,7 @@ Contribution Guide | Apiato - + @@ -43,7 +43,7 @@ 3 - Make sure you write a complete Changelog, in the release description. 4 - Change the default branch on github to that new branch. 5 - If you updated the documentation and you should! then visit the documentation repository and merge the PR into master.

    - + \ No newline at end of file diff --git a/docs/9.x/core-features/admin-dashboard/index.html b/docs/9.x/core-features/admin-dashboard/index.html index 1a89ea035..e96575a9e 100644 --- a/docs/9.x/core-features/admin-dashboard/index.html +++ b/docs/9.x/core-features/admin-dashboard/index.html @@ -4,13 +4,13 @@ Admin Dashboard | Apiato - +
    Version: 9.x

    Admin Dashboard

    Apiato does not recommend serving HTML pages. Instead, you should build your own Frontend App completely isolated from the Backend code.

    The provided Admin route

    How it works

    Visiting http://admin.apiato.test/dashboard will redirect you to a login page for admins.

    the default credentials are:

    It is up to you to build and customize your admin dashboard however you prefer.

    Change default Admin credentials

    you can change these default values from the seeder class in the Authorization container: app/Containers/Authorization/Data/Seeders/RolesAndPermissionsSeeder.php.

    - + \ No newline at end of file diff --git a/docs/9.x/core-features/api-docs-generator/index.html b/docs/9.x/core-features/api-docs-generator/index.html index 57b846312..5f5afc736 100644 --- a/docs/9.x/core-features/api-docs-generator/index.html +++ b/docs/9.x/core-features/api-docs-generator/index.html @@ -4,7 +4,7 @@ API Docs Generator | Apiato - + @@ -12,7 +12,7 @@
    Version: 9.x

    API Docs Generator

    Every great API needs a great Documentation.

    Apiato make writing and generating documentations very easy with the php artisan apiato:apidoc command.

    Alternatively you can generate a swagger doc from the apidoc comments, to do so run php artisan apiato:swagger command.

    Requirements

    • Install the ApiDocJs tool, the project directory

      • (npm install apidoc)
    • (Recommended) read the Routes page first.

    Usage

    1 - Write a PHP docblock on top of your endpoint like this:

    For more info about the parameters check out ApiDocJs documentation

    <?php

    /**
    * @apiGroup Authentication
    * @apiName UserLogin
    * @api {post} /users/login User Login
    * @apiDescription Description Here....
    * @apiVersion 1.0.0
    * @apiPermission none
    *
    * @apiHeader Accept application/json
    *
    * @apiParam {String} email
    * @apiParam {String} password
    *
    * @apiSuccessExample {json} Success-Response:
    * HTTP/1.1 200 OK
    * {
    * "data": {
    * "id": "owpzanmh",
    * "name": "Super Admin",
    * "email": "admin@admin.com"
    * ...
    * }
    *
    * @apiErrorExample {json} Error-Response:
    * {
    * "message":"401 Credentials Incorrect.",
    * "status_code":401
    * }
    *
    * @apiErrorExample {json} Error-Response:
    * {
    * "message":"Invalid Input.",
    * "errors":{
    * "email":[
    * "The email field is required."
    * ]
    * },
    * "status_code":422
    * }
    */

    $router->post('users/login', [
    'uses' => 'Controller@userLogin',
    ]);

    Note: All the Endpoints DocBlocks MUST be written inside Routes files, otherwise they won't be loaded.

    2 - Run the documentation generator command from the root directory:


    php artisan apiato:apidoc

    3 - Visit this URL's as shown in your terminal:

    NOTE: Every time you change the DocBlock of a Route file you need to run the apiato:apidoc command, to regenerate.

    Generate Swagger/OpenAPI JSON schema from apiDoc

    It's also possible to generate a Swagger/OpenAPI JSON schema from apiDoc with:


    php artisan apiato:swagger

    You can find the JSON schema at http://apiato.test/api/private/documentation/swagger/swagger.json

    Error: ApiDoc not found !!

    If you get an error (apidoc not found),

    1. open the container config file Containers/Documentation/Configs/apidoc.php

    2. edit the executable path to $(npm bin)/apidoc or to however you access the apidoc tool on your machine.

    <?php
    /*
    |--------------------------------------------------------------------------
    | Executable
    |--------------------------------------------------------------------------
    |
    | Specify how you run or access the `apidoc` tool on your machine.
    |
    */

    'executable' => 'apidoc',

    Shared response for faster updating and less outdated responses:

    To prevent duplicating the responses between routes, let's create a shared response for each object.

    Example: _user.v1.public.php will contain all responses (single, multiple...) of the User:

    <?php

    /**
    * @apiDefine UserSuccessSingleResponse
    * @apiSuccessExample {json} Success-Response:
    HTTP/1.1 200 OK
    {
    "data":{
    "object":"User",
    "id":eqwja3vw94kzmxr0,
    },
    "meta":{
    "include":[],
    "custom":[]
    }
    }
    */

    Usage of the shared User response from any endpoint:

    * @apiUse UserSuccessSingleResponse

    To avoid having to generate and update the Single and Multiple responses of the same object (recommended only for private API's) you can use the general shared Multiple Response * @apiUse GeneralSuccessMultipleResponse which you can find and modify it from app/Containers/Documentation/UI/API/Routes/*

    Edit the default generated values in the templates:

    Apiato generates by defaults 2 API documentations, each one has its own apidoc.json file. Both can be modified from the Documentation Containers in Containers/Documentation/ApiDocJs/

    apidoc.json Example file:

    {
    "name": "Apiato",
    "description": "Apiato (Private API) Documentation",
    "title": "Welcome to Apiato",
    "version": "1.0.0",
    "url" : "http://api.apiato.test",
    "template": {
    "withCompare": true,
    "withGenerator": true
    },
    "header": {
    "title": "API Overview",
    "filename": "app/Containers/Documentation/ApiDocJs/private/header.md"
    },
    "footer": {
    "title": "Footer",
    "filename": "app/Containers/Documentation/ApiDocJs/private/header.md"
    },
    "order": [

    ]
    }

    Change the Documentations URL's

    Edit the config file of the Documentation Container Containers/Documentation/Configs/apidoc.php

    <?php

    return [

    /*
    |--------------------------------------------------------------------------
    | Executable
    |--------------------------------------------------------------------------
    |
    | Specify how you run or access the `apidoc` tool on your machine.
    |
    */

    'executable' => 'apidoc',

    /*
    |--------------------------------------------------------------------------
    | API Types
    |--------------------------------------------------------------------------
    |
    | The `types` helps generating multiple documentations, by grouping them
    | under types names. You can add or remove any type. By default
    | `public` and `private` types are set.
    |
    | url: The url to access that generated API documentation.
    |
    | routes: The route file to read when generating this documentation.
    | Every route file will have the following name format:
    | `{endpoint-name}.v{version-number}.{documentation-type}.php`.
    |
    */

    'types' => [

    'public' => [
    'url' => 'api/documentation',
    'routes' => [
    'public',
    ],
    ],

    'private' => [
    'url' => 'api/private/documentation',
    'routes' => [
    'private',
    'public',
    ],
    ],
    ],


    /*
    |--------------------------------------------------------------------------
    | HTML files
    |--------------------------------------------------------------------------
    |
    | Specify where to put the generated HTML files.
    |
    */

    'html_files' => 'public/'


    // ...
    ];

    Edit the Documentation Header

    The header is usually the Overview of your API. It contains Info about authenticating users, making requests, responses, potential errors, rate limiting, pagination, query parameters and anything you want.

    All this information is written in app/Containers/Documentation/ApiDocJs/shared/header.template.md file, and the same file is used as header for both private and public documentations.

    To edit the content just open the markdown file in any markdown editor and edit it.

    You will notice some variables like {{rate-limit}} and {{token-expires}}. Those are replaced when running apidoc:generate with real values from your application configuration files.

    Feel free to extend them to include more info about your API from the app/Containers/Documentation/Actions/ProcessMarkdownTemplatesAction.php class.

    - + \ No newline at end of file diff --git a/docs/9.x/core-features/api-versioning/index.html b/docs/9.x/core-features/api-versioning/index.html index e5b61d4e9..a23703ae5 100644 --- a/docs/9.x/core-features/api-versioning/index.html +++ b/docs/9.x/core-features/api-versioning/index.html @@ -4,13 +4,13 @@ API Versioning | Apiato - +
    Version: 9.x

    API Versioning

    Since Laravel does not support API versioning, apiato provide a very easy way to implement versioning for your API.

    How it works

    Create:

    When creating a new API endpoint, specify the version number in the route file name following this naming format {endpoint-name}.{version-number}.{documentation-name}.php.

    Example:

    • MakeOrder.v1.public.php
    • MakeOrder.v2.public.php
    • ListOrders.v1.private.php

    Use:

    Automatically the endpoint inside that route file will be accessible by adding the version number to the URL.

    Example:

    • http://api.apiato.test/v1/register
    • http://api.apiato.test/v1/orders
    • http://api.apiato.test/v2/stores/123

    Version the API in header instead of URL

    First remove the URL version prefix:

    1. Edit app/Ship/Configs/apiato.php, set prefix to 'enable_version_prefix' => 'false',.
    2. Implement the Header versioning anyway you prefer. (this is not implemented in Apiato yet. Consider a contribution).
    - + \ No newline at end of file diff --git a/docs/9.x/core-features/authentication/index.html b/docs/9.x/core-features/authentication/index.html index c6c12cee1..624224e84 100644 --- a/docs/9.x/core-features/authentication/index.html +++ b/docs/9.x/core-features/authentication/index.html @@ -4,7 +4,7 @@ Authentication | Apiato - + @@ -60,7 +60,7 @@ It will send you an email with a link when you make a request to that link, it will call the /password-reset endpoint.

    Note: For security reason, make sure the reset password URL is set in app/Containers/User/Configs/user-container.php, and given to the client App, to be sent as parameter when calling the /password-forgot.

    Note: You must set up the email to get this function to work, however for testing purposes set the MAIL_DRIVER=log in your .env file in order to the see the email content in the log file laravel.log.

    Social Authentication

    For Social Authentication visit the Social Authentication page.

    - + \ No newline at end of file diff --git a/docs/9.x/core-features/authorization/index.html b/docs/9.x/core-features/authorization/index.html index 72d74e223..10cfa63bd 100644 --- a/docs/9.x/core-features/authorization/index.html +++ b/docs/9.x/core-features/authorization/index.html @@ -4,7 +4,7 @@ Authorization | Apiato - + @@ -15,7 +15,7 @@ User has a permission to read articles, moderator can manage comments and admin can create articles. User has a level 1, moderator level 2 and admin level 3. It means, moderator and administrator has also permission to read articles, but administrator can manage comments as well.

    if ($user->getRoleLevel() > 10) {
    //
    }

    If user has multiple roles, the getRoleLevel() method returns the highest one.

    If you don't need the permissions inheriting feature, simply ignore the optional level parameter when creating roles.

    - + \ No newline at end of file diff --git a/docs/9.x/core-features/code-generator/index.html b/docs/9.x/core-features/code-generator/index.html index a234aa431..6a59d2ebc 100644 --- a/docs/9.x/core-features/code-generator/index.html +++ b/docs/9.x/core-features/code-generator/index.html @@ -4,7 +4,7 @@ Code Generator | Apiato - + @@ -17,7 +17,7 @@ file needs to be the same as in vendor/apiato/core/Generator/Stubs.

    Say, if you like to change the config.stub, simply copy the file to app/Ship/Generators/CustomStubs/config.stub and start adapting it to your needs.

    If you run the respective command (e.g., in this case php artisan apiato:generate:configuration) this would read your specific config.stub file instead the pre-defined one!

    Contributing

    If you would like to add your own generators, please check out the Contribution Guide.

    For AngularJS 2 Users

    Checkout this awesome CRUD Containers generator package for Angular 2.4+.

    - + \ No newline at end of file diff --git a/docs/9.x/core-features/data-caching/index.html b/docs/9.x/core-features/data-caching/index.html index 366440f03..8a7c62e27 100644 --- a/docs/9.x/core-features/data-caching/index.html +++ b/docs/9.x/core-features/data-caching/index.html @@ -4,13 +4,13 @@ Data Caching | Apiato - +
    Version: 9.x

    Data Caching

    Enable / Disable Eloquent Query Caching

    By default caching is disabled.

    To enable it, go to app/Ship/Configs/repository.php config file and set cache > enabled => true, or set it from the .env file using ELOQUENT_QUERY_CACHE.

    More details can be found here.

    Users can skip the query caching and request new data by passing specific parameter to the Endpoint. Checkout the Query parameters page.

    Change different caching settings

    You can use different cache setting for each repository.

    To set cache settings on each repository, first the caching must be enabled, second you need to set some properties on the repository class to override the default values.

    For an example look at the app/Containers/Countries/Data/Repositories/CountryRepository.php class. For more details about all the properties refer to the L5 repository package documentation.

    Note: you don't need to use the CacheableRepository trait or implement the CacheableInterface since they both exist on the Abstract repository class (App\Ship\Parents\Repositories\Repository).

    - + \ No newline at end of file diff --git a/docs/9.x/core-features/default-endpoints/index.html b/docs/9.x/core-features/default-endpoints/index.html index 8f161f98a..cb4043b59 100644 --- a/docs/9.x/core-features/default-endpoints/index.html +++ b/docs/9.x/core-features/default-endpoints/index.html @@ -4,13 +4,13 @@ Default Endpoints | Apiato - +
    Version: 9.x

    Default Endpoints

    Apiato comes shipped with many useful API endpoints for speeding up the development process.

    You can see the endpoints in three ways:

    • In Terminal, by running php artisan route:list.
    • In Browser, by generating the beautiful detailed documentation. See the API Docs Generator page for more details.
    • In Code, by navigating to the Routes folder of each container's UI.

    Screenshot showing the result of route:list:

    - + \ No newline at end of file diff --git a/docs/9.x/core-features/etag/index.html b/docs/9.x/core-features/etag/index.html index f6739f738..409786c06 100644 --- a/docs/9.x/core-features/etag/index.html +++ b/docs/9.x/core-features/etag/index.html @@ -4,7 +4,7 @@ ETag | Apiato - + @@ -12,7 +12,7 @@
    Version: 9.x

    ETag

    ETag Middleware

    Apiato provides an ETag Middleware (app/Ship/Middlewares/Http/ProcessETagHeadersMiddleware.php) that implements the Shallow technique. It can be used to reduce bandwidth on the client side (especially for Mobile devices).

    By default, the feature is disabled. To enable it go to app/Ship/Configs/apiato.php and set use-etag to true. Of course your client should send the If-None-Match HTTP Header (= etag) in his request for this feature to work properly.

    - + \ No newline at end of file diff --git a/docs/9.x/core-features/hash-id/index.html b/docs/9.x/core-features/hash-id/index.html index 5e98d7a7b..c0d68f0c0 100644 --- a/docs/9.x/core-features/hash-id/index.html +++ b/docs/9.x/core-features/hash-id/index.html @@ -4,13 +4,13 @@ Hash ID | Apiato - +
    Version: 9.x

    Hash ID

    Hashing your internal ID's, is a very helpful feature for security reasons (to prevent some hack attacks) and business reasons (to hide the real total records from your competitors).

    Enable Hash ID

    Set the HASH_ID=true in the .env file.

    Also, with the feature make sure to always use the getHashedKey() on any model, whenever you need to return an ID (mainly from transformers) weather hashed ID or not.

    Example:


    'id' => $user->getHashedKey(),

    Note: if the feature is set to false HASH_ID=false the getHashedKey() will return the normal ID.

    Usage

    There are 2 ways an ID's can be passed to your system via the API:

    In URL example: www.apiato.test/items/abcdef.

    In parameters example: [GET] or [POST] www.apiato.test/items?id=abcdef.

    in both cases you will need to inform your API about what's coming form the Request class.

    Checkout the Requests page. After setting the $decode and $urlParameters properties on your Request class, the ID will be automatically decoded for you, to apply validation rules on it or/and use it from your controller ($request->id will return the decoded ID).

    Configuration

    You can change the default length and characters used in the ID from the config file app/Ship/Configs/hashids.phpor in the .env file by editing the HASH_ID_LENGTH value.

    From Apiato Version 7.4.*

    You can set the HASH_ID_KEY in the .env file to any random string. You can generate this from any of the online random string generators, or run head /dev/urandom | tr -dc 'A-Za-z0-9!"#$%&'\''()*+,-./:;<=>?@[\]^_{|}~' | head -c 32 ; echo on the linux commandline. Apiato defaults to the APP_KEY should this not be set.

    The HASH_ID_KEY acts as the salt during hashing of the ID. This should never be changed in production as it renders all previously generated IDs quite difficult to decode and recover.

    Testing

    In your tests you must hash the ID's before making the calls, because if you tell your Request class to decode an ID for you, it will throw an exception when the ID is not encoded.

    for Parameter ID's

    Always use getHashedKey() on your models when you want to get the ID

    Example:

    $data = [
    'roles_ids' => [
    $role1->getHashedKey(),
    $role2->getHashedKey(),
    ],
    'user_id' => $randomUser->getHashedKey(),
    ];
    $response = $this->makeCall($data);

    Or you can do this manually Hashids::encode($id);.

    for URL ID's

    You can use this helper function injectId($id, $skipEncoding = false, $replace = '{id}').

    Example:

    $response = $this->injectId($admin->id)->makeCall();

    More details on the Tests Helpers page.

    Availability

    You can use the Apiato\Core\Traits\HashIdTrait to any model or class, in order to have the encode and decode functions.

    By default, you have access to these functions $this->encode($id) and $this->decode($id) from all your Tests class and Controllers.

    - + \ No newline at end of file diff --git a/docs/9.x/core-features/localization/index.html b/docs/9.x/core-features/localization/index.html index f256ce93d..18b712422 100644 --- a/docs/9.x/core-features/localization/index.html +++ b/docs/9.x/core-features/localization/index.html @@ -4,7 +4,7 @@ Localization | Apiato - + @@ -36,7 +36,7 @@ language in this specific language (e.g., locale_name => Deutsch). Furthermore, the language name is outputted in the applications default name (e.g., configured in app.locale). This would result in default_name => German.

    The same applies to the regions that are defined (e.g., de-DE). Consequently, this results in locale_name => Deutschland and default_name = Germany.

    Tests

    To change the default language in your tests requests. You can set the env language in the phpunit.xml file.

    - + \ No newline at end of file diff --git a/docs/9.x/core-features/pagination/index.html b/docs/9.x/core-features/pagination/index.html index 35ae2dbc1..d1bc2bd7b 100644 --- a/docs/9.x/core-features/pagination/index.html +++ b/docs/9.x/core-features/pagination/index.html @@ -4,7 +4,7 @@ Pagination | Apiato - + @@ -16,7 +16,7 @@ you can manually override the $allowDisablePagination property in your specific Repository class. A requester can then get all data (with no pagination applied) by requesting api.domain.test/endpoint?limit=0. This will return all matching entities.

    Skip the Pagination Limit

    You can allow developers to skip the pagination limit as follows:

    First, you need to enable that feature from the server by setting PAGINATION_SKIP to true (PAGINATION_SKIP=true).

    Second, inform the developers (users) to pass ?limit=0 with the request they wish to get all it's data un-paginated.

    - + \ No newline at end of file diff --git a/docs/9.x/core-features/payments/index.html b/docs/9.x/core-features/payments/index.html index 06ae28eed..47d648754 100644 --- a/docs/9.x/core-features/payments/index.html +++ b/docs/9.x/core-features/payments/index.html @@ -4,7 +4,7 @@ Payments | Apiato - + @@ -38,7 +38,7 @@ need to create your own ChargeWithFooTask. This class, however, needs to implement the PaymentChargerInterface distributed via the Payment container. This interface, in turn, requires you to implement the charge() method.

    This method needs to connect to the FooService, create the payment and return a PaymentTransaction model.

    Finally, you need to register the new service. This can be done in the Payment\Configs\payment-container.php file. For the payment-container.gateways key, add the new entry for your Foo Payment Gateway. This may look like this:

        // ...
    'foo' => [
    'container' => 'Foo',
    'charge_task' => \App\Containers\Foo\Tasks\ChargeWithFooTask::class,
    ],
    // ...

    Basically, this entry points to the charger_task that handles, how to charge a User with the specific Payment Gateway.

    That's all!

    Mocking the real payment call for Testing

    <?php

    // mock the ChargeWithStripeService external API call
    $this->mockIt(ChargeWithStripeService::class)->shouldReceive('charge')->andReturn([
    'payment_method' => 'stripe',
    'description' => $payId
    ]);

    // mock the ChargeWithPaypalService external API call
    $this->mockIt(ChargeWithPaypalService::class)->shouldReceive('charge')->andReturn([
    'payment_method' => 'paypal',
    'description' => $payId
    ]);

    Checkout the Tests Helpers page for about Testing.

    - + \ No newline at end of file diff --git a/docs/9.x/core-features/profiler/index.html b/docs/9.x/core-features/profiler/index.html index 4d712c2cc..539667242 100644 --- a/docs/9.x/core-features/profiler/index.html +++ b/docs/9.x/core-features/profiler/index.html @@ -4,14 +4,14 @@ Profiler | Apiato - +
    Version: 9.x

    Profiler

    Profiling is very important to optimize the performance of your application, and help you better understand what happens when a request is received, as well as it can speed up the debugging process.

    Apiato uses the third-party package laravel-debugbar (which uses the PHP Debug Bar), to collect the profiling data.

    By default, the laravel-debugbar package displays the profiling data in the browser. However, Apiato uses a middleware app/Ship/Middlewares/Http/ProfilerMiddleware.php to append the profiling data to the response.

    Sample Profiler response

    {
    // Actual Response Here...
    "_profiler": {
    "__meta": {
    "id": "X167f293230e3457f1bbd95d9c82aba4a",
    "datetime": "2017-09-22 18:45:27",
    "utime": 1506105927.799299,
    "method": "GET",
    "uri": "/",
    "ip": "172.20.0.1"
    },
    "messages": {
    "count": 0,
    "messages": []
    },
    "time": {
    "start": 1506105922.742068,
    "end": 1506105927.799333,
    "duration": 5.057265043258667,
    "duration_str": "5.06s",
    "measures": [
    {
    "label": "Booting",
    "start": 1506105922.742068,
    "relative_start": 0,
    "end": 1506105923.524004,
    "relative_end": 1506105923.524004,
    "duration": 0.7819359302520752,
    "duration_str": "781.94ms",
    "params": [],
    "collector": null
    },
    {
    "label": "Application",
    "start": 1506105923.535343,
    "relative_start": 0.7932748794555664,
    "end": 1506105927.799336,
    "relative_end": 0.00000286102294921875,
    "duration": 4.26399302482605,
    "duration_str": "4.26s",
    "params": [],
    "collector": null
    }
    ]
    },
    "memory": {
    "peak_usage": 13234248,
    "peak_usage_str": "12.62MB"
    },
    "exceptions": {
    "count": 0,
    "exceptions": []
    },
    "route": {
    "uri": "GET /",
    "middleware": "api, throttle:30,1",
    "domain": "http://api.apiato.test",
    "as": "apis_root_page",
    "controller": "App\\Containers\\Welcome\\UI\\API\\Controllers\\Controller@apiRoot",
    "namespace": "App\\Containers\\Welcome\\UI\\API\\Controllers",
    "prefix": "/",
    "where": [],
    "file": "app/Containers/Welcome/UI/API/Controllers/Controller.php:20-25"
    },
    "queries": {
    "nb_statements": 0,
    "nb_failed_statements": 0,
    "accumulated_duration": 0,
    "accumulated_duration_str": "0μs",
    "statements": []
    },
    "swiftmailer_mails": {
    "count": 0,
    "mails": []
    },
    "logs": {
    "count": 3,
    "messages": [
    {
    "message": "...",
    "message_html": null,
    "is_string": false,
    "label": "error",
    "time": 1506105927.694807
    },
    {
    "message": "...",
    "message_html": null,
    "is_string": false,
    "label": "error",
    "time": 1506105927.694811
    },
    {
    "message": "[2017-09-18 17:38:15] testing.INFO: New User registration. ID = 970ylqvaogmxnbdr | Email = apiato@mail.test. Thank you for signing up.\n</div>\n</body>\n</html>\n \n",
    "message_html": null,
    "is_string": false,
    "label": "info",
    "time": 1506105927.694812
    }
    ]
    },
    "auth": {
    "guards": {
    "web": "array:2 [\n \"name\" => \"Guest\"\n \"user\" => array:1 [\n \"guest\" => true\n ]\n]",
    "api": "array:2 [\n \"name\" => \"Guest\"\n \"user\" => array:1 [\n \"guest\" => true\n ]\n]"
    },
    "names": ""
    },
    "gate": {
    "count": 0,
    "messages": []
    }
    }
    }

    Configuration

    By default, the profiler feature is turned off. To turn it on edit the .env file and set DEBUGBAR_ENABLED=true.

    To control and modify the profiler response, you need to edit this config file app/Ship/Configs/debugbar.php.

    - + \ No newline at end of file diff --git a/docs/9.x/core-features/query-parameters/index.html b/docs/9.x/core-features/query-parameters/index.html index 8e2b3f07e..2dc93269f 100644 --- a/docs/9.x/core-features/query-parameters/index.html +++ b/docs/9.x/core-features/query-parameters/index.html @@ -4,7 +4,7 @@ Query Parameters | Apiato - + @@ -22,7 +22,7 @@ accepts driver as relationship (in the Available Relationships section).

    Nested Includes

    It is also possible to request "nested includes". Extend the example from above. Imagine, that a Driver may also have a relationship to an Address object. You can access this information as well by calling ?include=driver,driver.address.

    Of course, the address include is defined in the respective DriverTransformer that is used here.

    Usage:

    api.domain.test/endpoint?include=relationship

    Where to define the includes:

    Every Transformer can have 2 types of includes $availableIncludes and $defaultIncludes:

        protected $availableIncludes = [
    'products',
    'store',
    'recipients',
    ];

    protected $defaultIncludes = [
    'invoice',
    ];

    $defaultIncludes will not be listed in the response, only the $availableIncludes will be.

    Visit the Transformers page for more details.

    (provided by the Fractal Transformer)

    Caching skipping

    Note: You need to turn the Eloquent Query Caching ON for this feature to work. Checkout the Configuration Page "ELOQUENT_QUERY_CACHE".

    To run a new query and force disabling the cache on certain endpoints, you can use this parameter

    ?skipCache=true

    It's not recommended to keep skipping cache as it has bad impact on the performance.

    (provided by the L5 Repository)

    Configuration

    Most of these parameters are provided by the L5 Repository and configurable from the Ship/Configs/repository.php file. Some of them are built in house, or inherited from other packages such as Fractal.

    See the Query parameters from the User Developer perspective

    1) Generate the Default API documentation

    2) Visit the documentation URL

    More details in the API Docs Generator page.

    More Information

    For more details on these parameters check out these links:

    - + \ No newline at end of file diff --git a/docs/9.x/core-features/rate-limiting/index.html b/docs/9.x/core-features/rate-limiting/index.html index 349b65de2..bea4d7f37 100644 --- a/docs/9.x/core-features/rate-limiting/index.html +++ b/docs/9.x/core-features/rate-limiting/index.html @@ -4,14 +4,14 @@ Rate Limiting | Apiato - +
    Version: 9.x

    Rate Limiting

    apiato uses the default Laravel middleware for rate limiting (throttling).

    All REST API requests are throttled to prevent abuse and ensure stability. The exact number of calls that your application can make per day varies based on the type of request you are making.

    The rate limit window is 1 minute per endpoint, with most individual calls allowing for 30 requests in each window.

    In other words, each user is allowed to make 30 calls per endpoint every 1 minute. (For each unique access token).

    To update these values go to app/Ship/Configs/apiato.php config file, or to the ENV file.

    'throttle' => [
    'enabled' => env('API_RATE_LIMIT_ENABLED', true),
    'attempts' => env('API_RATE_LIMIT_ATTEMPTS', '30'),
    'expires' => env('API_RATE_LIMIT_EXPIRES', '1'),
    ]
    API_RATE_LIMIT_ENABLED=true
    API_RATE_LIMIT_ATTEMPTS=30
    API_RATE_LIMIT_EXPIRES=1

    For how many hits you can preform on an endpoint, you can always check the header:

    X-RateLimit-Limit →30
    X-RateLimit-Remaining →29

    Enable/Disable Rate Limiting:

    The API rate limiting middleware is enabled and applied to all the Container Endpoints by default.

    To disable it set API_RATE_LIMIT_ENABLED to false in the .env file.

    - + \ No newline at end of file diff --git a/docs/9.x/core-features/requests-monitor/index.html b/docs/9.x/core-features/requests-monitor/index.html index 84c788908..ed2144bde 100644 --- a/docs/9.x/core-features/requests-monitor/index.html +++ b/docs/9.x/core-features/requests-monitor/index.html @@ -4,13 +4,13 @@ Requests Monitor | Apiato - +
    Version: 9.x

    Requests Monitor

    Apiato provides a simple and easy way to monitor and log all the HTTP requests coming to your application.

    The request monitor can be very useful when testing and debugging your frontend Apps which consume your API. Especially when the frontend apps (Mobile, Web,...) are built by other developers who are far from you.

    The requests monitor is provided by the Debugger Container, by a RequestsMonitorMiddleware middleware.

    Enable requests logging

    From the .env file set REQUESTS_DEBUG to true.

    Now in order for this to start displaying the results you need to enable the debugging mode in Laravel by setting APP_DEBUG to true in the .env as well.

    Usage

    Simply tail the log file


    tail -f storage/logs/debugger.log

    Result

    Screenshot example:

    Change the default log file

    By default, everything is logged in the debugger.log file, to change the default file go to Debugger/Configs/debugger.php config file and set the file name:

    <?php

    /*

    |--------------------------------------------------------------------------
    | Log File
    |--------------------------------------------------------------------------
    |
    | What to name the log file in the `storage/log` path.
    |
    */

    'log_file' => 'debugger.log',

    This feature is provided by the Debugger Container via its RequestsMonitorMiddleware middleware.

    To see the results go ahead and Tail the default Laravel debug file tail -f storage/logs/laravel.log.

    Note: this will also not run in Testing environments, to enable it you need to manually edit the Middleware.

    - + \ No newline at end of file diff --git a/docs/9.x/core-features/search-query-parameter/index.html b/docs/9.x/core-features/search-query-parameter/index.html index 34969d151..9349a4f5d 100644 --- a/docs/9.x/core-features/search-query-parameter/index.html +++ b/docs/9.x/core-features/search-query-parameter/index.html @@ -4,13 +4,13 @@ Search Query Parameter | Apiato - +
    Version: 9.x

    Search Query Parameter

    Below we'll see how to set up a Search Query Parameter, on a Model:

    1. Add searchable Fields on the Model Repository, all the other steps are normal steps
    <?php

    namespace App\Containers\User\Data\Repositories;

    use App\Containers\User\Contracts\UserRepositoryInterface;
    use App\Ship\Parents\Repositories\Repository;

    class UserRepository extends Repository implements UserRepositoryInterface
    {

    protected $fieldSearchable = [
    'name' => 'like',
    'id' => '=',
    'email' => '=',
    ];

    }
    1. Create basic list and search Task
    <?php

    namespace App\Containers\User\Tasks;

    use App\Containers\User\Contracts\UserRepositoryInterface;
    use App\Port\Action\Abstracts\Action;

    class ListUsersTask extends Action
    {
    private $userRepository;

    public function __construct(UserRepositoryInterface $userRepository)
    {
    $this->userRepository = $userRepository;
    }

    public function run($order = true)
    {
    return $this->userRepository->paginate();
    }
    }

    1. Create basic Action to call that basic Task, and maybe other Tasks later in the future when needed
    <?php

    namespace App\Containers\User\Actions;

    use App\Containers\User\Tasks\ListUsersTask;
    use App\Port\Action\Abstracts\Action;

    class ListAndSearchUsersAction extends Action
    {

    private $listUsersTask;

    public function __construct(ListUsersTask $listUsersTask)
    {
    $this->listUsersTask = $listUsersTask;
    }

    public function run($order = true)
    {
    return $this->listUsersTask->run($order);
    }
    }

    1. Use the Action from a Controller

    <?php

    public function listAllUsers()
    {
    $users = Apiato::call('User@ListAndSearchUsersAction');

    return $this->response->paginator($users, new UserTransformer());
    }

    1. Call it from anywhere as follows: [GET] http://api.apiato.com/users?search=Mahmoud@apiato.com
    - + \ No newline at end of file diff --git a/docs/9.x/core-features/social-authentication/index.html b/docs/9.x/core-features/social-authentication/index.html index 718f62229..cdc19b2a8 100644 --- a/docs/9.x/core-features/social-authentication/index.html +++ b/docs/9.x/core-features/social-authentication/index.html @@ -4,14 +4,14 @@ Social Authentication | Apiato - +
    Version: 9.x

    Social Authentication

    For Social Authentication Apiato uses Socialite.

    Default Supported Auth Provide

    • Facebook
    • Twitter

    How Social Authentication Works

    1. The Client (Mobile or Web) sends a request to the Social Auth Provider (Facebook, Twitter..).
    2. The Social Auth Provider returns a Code (Tokens).
    3. The Client makes a call to the server (our server) and passes the Code (Tokens) retrieved from the Provider.
    4. The Server fetches the user data from the Social Auth Provider using the received Code (Tokens).
    5. The Server create new User from the collected social data and return the Authenticated User (If the user already created then it just returns it).

    Setup Social Authentication

    1) Create an App on the supported Social Auth provider.

    For the callback URL you can use this Apiato web endpoint http://apiato.test/auth/{provider}/callback (replace the provider with any of the supported providers facebook, twitter,..).

    2) Set the Tokens and Secrets in the .env file

        'facebook' => [
    'client_id' => env('AUTH_FACEBOOK_CLIENT_ID'), // App ID
    'client_secret' => env('AUTH_FACEBOOK_CLIENT_SECRET'), // App Secret
    'redirect' => env('AUTH_FACEBOOK_CLIENT_REDIRECT'),
    ],

    'twitter' => [
    'client_id' => env('AUTH_TWITTER_CLIENT_ID'), // Consumer Key (API Key)
    'client_secret' => env('AUTH_TWITTER_CLIENT_SECRET'), // Consumer Secret (API Secret)
    'redirect' => env('AUTH_TWITTER_CLIENT_REDIRECT'),
    ],

    'google' => [
    'client_id' => env('AUTH_GOOGLE_CLIENT_ID'), // Client ID
    'client_secret' => env('AUTH_GOOGLE_CLIENT_SECRET'), // Client secret
    'redirect' => env('AUTH_GOOGLE_CLIENT_REDIRECT'),
    ],

    3) Make a request from your client to get the oauth info. Each Social provider returns different response and keys

    For testing purposes Apiato provides a web endpoint (http://apiato.test/auth/{provider} ) to act as a client.

    Use that endpoint from your browser (replace the provider with any of the supported providers facebook, twitter,..) to get the oauth info.

    Example Twitter Response:

    User {
    tokentoken: "121212121-121212121"
    tokentokenSecret: "34343434343434343343434343"
    tokenid: "777777777"
    tokennickname: "Mahmoud_Zalt"
    tokenname: "Mahmoud Zalt"
    tokenemail: null
    tokenavatar: "http://pbs.twimg.com/profile_images/88888888/PENrcePC_normal.jpg"
    tokenuser:
    token"avatar_original": "http://pbs.twimg.com/profile_images/9999999/PENrcePC.jpg"
    }

    NOTE: This step should be done by your client App, which could be a Web, Mobile or other kind of client Apps.

    4) Use the oauth info to make a call from your server to the Social Provider in order to get the User info.

    Example Getting Twitter User: Twitter requires the oauth_token and oauth_secret, other Providers might only require the oauth_token

    POST /v1/auth/twitter HTTP/1.1
    Host: api.apiato.test
    Content-Type: application/x-www-form-urlencoded
    Accept: application/json

    oauth_token=121212121-121212121&oauth_secret=34343434343434343343434343

    Note: For Facebook only send oauth_token which is named as access_token in facebook response. For more details about parameters checkout the generated documentation or visit app/Containers/Socialauth/UI/API/Routes/AuthenticateAll.v1.private.php

    5) The endpoint above should return the User and his Personal Access Token.

    Example Twitter Response:

    {
    "data": {
    "object": "User",
    "id": "eqwja3vw94kzmxr0",
    "name": "Mahmoud Zalt",
    "email": null,
    "confirmed": false,
    "nickname": null,
    "gender": null,
    "birth": null,
    "social_auth_provider": "twitter",
    "social_id": "42719726",
    "social_avatar": {
    "avatar": "http://pbs.twimg.com/profile_images/1111111111/PENrcePC_normal.jpg",
    "original": null
    },
    "created_at": {
    "date": "2017-10-20 21:45:03.000000",
    "timezone_type": 3,
    "timezone": "UTC"
    },
    "updated_at": {
    "date": "2017-10-20 21:45:03.000000",
    "timezone_type": 3,
    "timezone": "UTC"
    },
    "readable_created_at": "48 minutes ago",
    "readable_updated_at": "48 minutes ago"
    },
    "meta": {
    "include": [
    "roles"
    ],
    "custom": {
    "token_type": "personal",
    "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI..."
    }
    }
    }

    Support new Auth Provider

    1) Pick an Auth Provider from the supported providers by Socialite.

    2) Go to app/Containers/Socialauth/Tasks/FindUserSocialProfileTask.php and support your provider.

    - + \ No newline at end of file diff --git a/docs/9.x/core-features/system-settings/index.html b/docs/9.x/core-features/system-settings/index.html index dcbe295e9..19abc1a77 100644 --- a/docs/9.x/core-features/system-settings/index.html +++ b/docs/9.x/core-features/system-settings/index.html @@ -4,13 +4,13 @@ System Settings | Apiato - +
    Version: 9.x

    System Settings

    At many cases you need to have some dynamic system settings, such as in a referral program, where you give 'gifts' to anyone who refers new users but, those gifts can be changed in the future, so it's better not have them statically created in the code, instead read from the database where an Admin can manage them at any time.

    app/Containers/Settings Container helps to store and retrieve those key values settings. It also seeds the database with the default configurations during the installation.

    Seed the default settings

    Default Settings should be seeded in app/Containers/Settings/Database/Seeders/DefaultSystemSettingsSeeder.php

    Read settings

    <?php
    $value = $this->findSettingsByKeyTask->run('whateverSettingsName');

    You can search for settings by Key as in the example above, or create a class for each settings as follows:

    <?php
    $value = $this->findWhateverSettingsTask->run();
    - + \ No newline at end of file diff --git a/docs/9.x/core-features/useful-commands/index.html b/docs/9.x/core-features/useful-commands/index.html index 9261fb9a6..98743f636 100644 --- a/docs/9.x/core-features/useful-commands/index.html +++ b/docs/9.x/core-features/useful-commands/index.html @@ -4,7 +4,7 @@ Useful Commands | Apiato - + @@ -16,7 +16,7 @@ php artisan apiato:list:tasks

    You can also pass --withfilename flag to see all Tasks with the files names. apiato:list:tasks --withfilename

    List Container Dependencies Command

    Sometimes it is required to show dependencies between containers (e.g., how they are interlinked amongst each others). Apiato provides a command to list all dependencies for one specific container. The command does take the Apiato::call() and $this->call() (with use X) into account.

    If you want to get the dependencies for one container, you can call

    php artisan apiato:list:dependencies app/Containers/{container-name}

    - + \ No newline at end of file diff --git a/docs/9.x/core-features/user-registration/index.html b/docs/9.x/core-features/user-registration/index.html index f630ea462..782262444 100644 --- a/docs/9.x/core-features/user-registration/index.html +++ b/docs/9.x/core-features/user-registration/index.html @@ -4,13 +4,13 @@ User Registration | Apiato - +
    Version: 9.x

    User Registration

    Register users by credentials (email and passwords)

    Call the http://api.apiato.test/v1/register endpoint (you can find its documentation after generating the API Docs.

    Check out the registerUser endpoint in the API Routes files.

    This will register a new User and respond with user object.

    Registration request:

    curl --request POST \
    --url http://api.apiato.test/v1/register \
    --header 'accept: application/json' \
    --header 'content-type: application/x-www-form-urlencoded' \
    --data 'email=apiato%40mail.com1&password=password&name=Mahmoud%20Zalt'

    Registration response:

    {
    "data": {
    "object": "User",
    "id": XbPW7awNkzl83LD6,
    "name": "Mahmoud Zalt",
    "email": "apiato@mail.com",
    "confirmed": null,
    "nickname": "Mega",
    "gender": "male",
    "birth": null,
    "social_auth_provider": null,
    "social_id": null,
    "social_avatar": {
    "avatar": null,
    "original": null
    },
    "created_at": "2021-03-24T15:02:56.000000Z",
    "updated_at": "2021-03-24T15:02:56.000000Z",
    "readable_created_at": "19 seconds ago",
    "readable_updated_at": "19 seconds ago"
    "roles": {
    "data": []
    }
    }
    }

    Note: After registration in order to get the user access token you will have to send another call to http://api.example.com/v1/oauth/token with following fields and values

    username => your_username e.g. admin@admin.com
    password => your_password
    grant_type => password
    client_id => your_client_id
    client_secret => your_client_secret

    For Third-Party Clients you must have client ID and secret first. You can generate them by creating new client in your app using Laravel Passport.

    For First-Party Clients you can use a proxy to add those fields on requests coming from your trusted client. For an example on how to do it look at ProxyLoginForAdminWebClientAction Action in Authentication Container.

    Register users by Social Account

    (Facebook, Twitter, Google..)

    Checkout the Social Authentication Page for how to Sign up with Social Account.

    - + \ No newline at end of file diff --git a/docs/9.x/core-features/validation/index.html b/docs/9.x/core-features/validation/index.html index d2f0ffd96..d9efc3f06 100644 --- a/docs/9.x/core-features/validation/index.html +++ b/docs/9.x/core-features/validation/index.html @@ -4,13 +4,13 @@ Validation | Apiato - +
    Version: 9.x

    Validation

    Apiato uses the powerful Laravel validation system.

    In Apiato, validation must be defined in Request component, since every request might have different rules.

    Validation rules are automatically applied, once injecting the Request in the Controller.

    Requests help validating User data, accessibility, ownership and more can be added if needed.

    Example Request with Validation rules:

    <?php

    namespace App\Containers\User\UI\API\Requests;

    use App\Ship\Parents\Requests\Request;

    class RegisterUserRequest extends Request
    {
    /**
    * @return array
    */
    public function rules()
    {
    return [
    'email' => 'required|email|max:200|unique:users,email',
    'password' => 'required|min:20|max:300',
    'name' => 'required|min:2|max:400',
    ];
    }

    }

    Usage from Controller Example:

        public function registerUser(RegisterUserRequest $request, CreateUserAction $action)
    {
    // if the actions takes the request object, you can pass the entire request instance as parameter
    $user = Apiato::call('User@RegisterUserAction', [
    [
    $request['email'],
    $request['password'],
    $request['name'],
    $request['gender'],
    $request['birth']
    ]
    ]);

    return $this->transform($user, UserTransformer::class);
    }

    Responses

    Validation Error response format:

    Single Field:

    {
    "errors": {
    "email": [
    "The email has already been taken."
    ]
    },
    "status_code": 422,
    "message": "The given data failed to pass validation."
    }

    Multiple Fields:

    {
    "errors": {
    "email": [
    "The email field is required."
    ],
    "password": [
    "The password field is required."
    ]
    },
    "status_code": 422,
    "message": "The given data failed to pass validation."
    }

    More details about requests in the Requests Page.

    - + \ No newline at end of file diff --git a/docs/9.x/faq/index.html b/docs/9.x/faq/index.html index 478b0fd2e..d1c961083 100644 --- a/docs/9.x/faq/index.html +++ b/docs/9.x/faq/index.html @@ -4,7 +4,7 @@ Frequently Asked Questions | Apiato - + @@ -60,7 +60,7 @@ Slack.

    Lastly, if you got your question answered, consider sharing it, if you believe it can help others. You can submit a PR adding the questions and answer here on the FAQ page. Or leave it somewhere on the repository or on Slack. Thanks in advance :)

    - + \ No newline at end of file diff --git a/docs/9.x/getting-started/conventions-and-principles/index.html b/docs/9.x/getting-started/conventions-and-principles/index.html index d8aec8478..1f938ddd4 100644 --- a/docs/9.x/getting-started/conventions-and-principles/index.html +++ b/docs/9.x/getting-started/conventions-and-principles/index.html @@ -4,13 +4,13 @@ Conventions and Principles | Apiato - +
    Version: 9.x

    Conventions and Principles

    HTTP Methods usage in RESTful API's

    • GET (SELECT): retrieve a specific resource from the server, or a listing of resources.
    • POST (CREATE): create a new resource on the server.
    • PUT (UPDATE): update a resource on the server, providing the entire resource.
    • PATCH (UPDATE): update a resource on the server, providing only changed attributes.
    • DELETE (DELETE): remove a resource from the server.

    Naming Conventions for Routes & Actions

    • GetAllResource: to fetch all resources. You can apply ?search query parameter to filter data.
    • FindResourceByID: to search for single resource by its unique identifier.
    • CreateResource: to create a new resource.
    • UpdateResource: to update/edit existing resource.
    • DeleteResource: to delete a resource.

    General guidelines and principles for RESTful URLs

    • A URL identifies a resource.
    • URLs should include nouns, not verbs.
    • Use plural nouns only for consistency (no singular nouns).
    • Use HTTP verbs (GET, POST, PUT, DELETE) to operate on the collections and elements.
    • You should not need to go deeper than resource/identifier/resource.
    • Put the version number at the base of your URL, for example http://apiato.test/v1/path/to/resource.
    • If an input data changes the logic of the endpoint, it should be passed in the URL. If not can go in the header "like Auth Token".
    • Don't use query parameters to alter state.
    • Don't use mixed-case paths if you can help it; lowercase is best.
    • Don't use implementation-specific extensions in your URIs (.php, .py, .pl, etc.)
    • Limit your URI space as much as possible. And keep path segments short.
    • Don't put metadata in the body of a response that should be in a header

    Good URL examples

    • Find a single Car by its unique identifier (ID):
      • GET http://www.api.apiato.test/v1/cars/123
    • Get all Cars:
      • GET http://www.api.apiato.test/v1/cars
    • Find/Search cars by one or more fields:
      • GET http://www.api.apiato.test/v1/cars?search=maker:mercedes
      • GET http://www.api.apiato.test/v1/cars?search=maker:mercedes;color:white
    • Order and Sort query result:
      • GET http://www.api.apiato.test/v1/cars?orderBy=created_at&sortedBy=desc
      • GET http://www.api.apiato.test/v1/cars?search=maker:mercedes&orderBy=created_at&sortedBy=desc
    • Specify optional fields:
      • GET http://www.api.apiato.test/v1/cars?filter=id;name;status
      • GET http://www.api.apiato.test/v1/cars/123?filter=id;name;status
    • Get all Drivers belonging to a Car:
      • GET http://www.api.apiato.test/v1/cars/123/drivers
      • GET http://www.api.apiato.test/v1/cars/123/drivers/123/addresses
    • Include Drivers objects relationship with the car response:
      • GET http://www.api.apiato.test/v1/cars/123?include=drivers
      • GET http://www.api.apiato.test/v1/cars/123?include=drivers,owner
    • Add new Car:
      • POST http://www.api.apiato.test/v1/cars
    • Add new Driver to a Car:
      • POST http://www.api.apiato.test/v1/cars/123/drivers

    General principles for HTTP methods

    • Don't ever use GET to alter state; to prevent Googlebot from corrupting your data. And use GET as much as possible.
    • Don't use PUT unless you are updating an entire resource. And unless you can also legitimately do a GET on the same URI.
    • Don't use POST to retrieve information that is long-lived or that might be reasonable to cache.
    • Don't perform an operation that is not idempotent with PUT.
    • Use GET for things like calculations, unless your input is large, in which case use POST.
    • Use POST in preference to PUT when in doubt.
    • Use POST whenever you have to do something that feels RPC-like.
    • Use PUT for classes of resources that are larger or hierarchical.
    • Use DELETE in preference to POST to remove resources.
    - + \ No newline at end of file diff --git a/docs/9.x/getting-started/installation/index.html b/docs/9.x/getting-started/installation/index.html index 95faaea23..46d709d61 100644 --- a/docs/9.x/getting-started/installation/index.html +++ b/docs/9.x/getting-started/installation/index.html @@ -4,7 +4,7 @@ Installation | Apiato - + @@ -34,7 +34,7 @@ try running this command homestead halt && homestead up --provision.

    A.3) Using anything else

    If you're not into virtualization solutions, you can set up your environment directly on your machine. Check the software's requirements list.

    C) Let's Play

    Now let's see it in action

    1.a. Open your web browser and visit:

    • http://apiato.test You should see an HTML page, with Apiato in the middle.
    • http://admin.apiato.test You should see an HTML Login page.

    1.b. Open your HTTP client and call:

    • http://api.apiato.test/ You should see a JSON response with message: "Welcome to apiato.",
    • http://api.apiato.test/v1 You should see a JSON response with message: "Welcome to apiato (API V1).",

    2) Make some HTTP calls to the API:

    To make the calls you can use Postman, HTTPIE or any other tool you prefer.

    Let's test the (user registration) endpoint http://api.apiato.test/v1/register with cURL:

    curl -X POST -H "Accept: application/json" -H "Cache-Control: no-cache" -F "email=mahmoud@zalt.me" -F "password=so-secret" -F "name=Mahmoud Zalt" "http://api.apiato.test/v1/register"

    You should get response like this:

    Access-Control-Allow-Origin → ...
    Cache-Control → ...
    Connection → keep-alive
    Content-Language → en
    Content-Type → application/json
    Date → Wed, 11 Apr 2000 22:55:88 GMT
    Server → nginx
    Transfer-Encoding → chunked
    Vary → Origin
    X-Powered-By → PHP/7.7.7
    X-RateLimit-Limit → 30
    X-RateLimit-Remaining → 29

    {
    "data": {
    "object": "User",
    "id": 77,
    "name": "Mahmoud Zalt",
    "email": "apiato@mail.com",
    "confirmed": null,
    "nickname": "Mega",
    "gender": "male",
    "birth": null,
    "social_auth_provider": null,
    "social_id": null,
    "social_avatar": {
    "avatar": null,
    "original": null
    },
    "created_at": {
    "date": "2017-04-05 16:17:26.000000",
    "timezone_type": 3,
    "timezone": "UTC"
    },
    "updated_at": {
    "date": "2017-04-05 16:17:26.000000",
    "timezone_type": 3,
    "timezone": "UTC"
    },
    "roles": {
    "data": []
    }
    }
    }
    - + \ No newline at end of file diff --git a/docs/9.x/getting-started/markdown-features/index.html b/docs/9.x/getting-started/markdown-features/index.html index 5cee68210..553352d94 100644 --- a/docs/9.x/getting-started/markdown-features/index.html +++ b/docs/9.x/getting-started/markdown-features/index.html @@ -4,13 +4,13 @@ Markdown Features | Apiato - +
    Version: 9.x

    Markdown Features

    Docusaurus supports the Markdown syntax and has some additional features.

    Front Matter

    Markdown documents can have associated metadata at the top called Front Matter:

    ---
    id: my-doc
    title: My document title
    description: My document description
    sidebar_label: My doc
    ---

    Markdown content

    Regular Markdown links are supported using url paths or relative file paths.

    Let's see how to [Create a page].
    Let's see how to [Create a page].

    Let's see how to [Create a page].

    Markdown images

    Regular Markdown images are supported.

    Add an image at static/img/logo.png and use this Markdown declaration:

    ![Docusaurus logo](/img/logo.png)

    Docusaurus logo

    Code Blocks

    Markdown code blocks are supported with Syntax highlighting.

    ```jsx title="src/components/HelloDocusaurus.js"
    function HelloDocusaurus() {
    return (
    <h1>Hello, Docusaurus!</h1>
    )
    }
    ```
    src/components/HelloDocusaurus.js
    function HelloDocusaurus() {
    return <h1>Hello, Docusaurus!</h1>;
    }

    Admonitions

    Docusaurus has a special syntax to create admonitions and callouts:

    :::tip My tip

    Use this awesome feature option

    :::

    :::danger Take care

    This action is dangerous

    :::
    My tip

    Use this awesome feature option

    Take care

    This action is dangerous

    React components

    Thanks to MDX, you can make your doc more interactive and use React components inside Markdown:

    export const Highlight = ({children, color}) => (
    <span
    style={{
    backgroundColor: color,
    borderRadius: '2px',
    color: 'red',
    padding: '0.2rem',
    }}>
    {children}
    </span>
    );

    <Highlight color="#25c2a0">Docusaurus green</Highlight> and <Highlight color="#1877F2">Facebook blue</Highlight> are my favorite colors.
    Docusaurus green and Facebook blue are my favorite colors.
    - + \ No newline at end of file diff --git a/docs/9.x/getting-started/overview/index.html b/docs/9.x/getting-started/overview/index.html index 6ee8305d7..8beba9617 100644 --- a/docs/9.x/getting-started/overview/index.html +++ b/docs/9.x/getting-started/overview/index.html @@ -4,7 +4,7 @@ Overview | Apiato - + @@ -12,7 +12,7 @@
    Version: 9.x

    Overview

    The basic flow

    When an HTTP request is received, it first hits your predefined Endpoint (each endpoint live in its own Route file).

    Sample Route Endpoint

    <?php
    $router->get('hello', [
    'uses' => 'Controller@sayHello',
    ]);

    After the user makes a request to the endpoint [GET] www.api.apiato.com/v1/hello it calls the defined controller function (sayHello).

    Sample Controller Function

    <?php
    class Controller extends ApiController
    {
    public function sayHello(SayHelloRequest $request)
    {
    $helloMessage = Apiato::call(SayHelloAction::class);

    $this->json([
    $helloMessage
    ]);
    }
    }

    This function takes a Request class SayHelloRequest to automatically checks if the user has the right access to this endpoint. Only if the user has access, it proceeds to the function body.

    Then the function calls an Action (SayHelloAction) to perform the business logic.

    Sample Action

    <?php
    class SayHelloAction extends Action
    {
    public function run()
    {
    return 'Hello World!';
    }
    }

    The Action can do anything then return a result (could be an Object, a String or anything).

    When the Action finishes its job, the controller function gets ready to build a response.

    Json responses can be built using the helper function json ($this->json(['foo' => 'bar']);).

    Sample User Response

    [
    "Hello World!"
    ]
    - + \ No newline at end of file diff --git a/docs/9.x/getting-started/requests/index.html b/docs/9.x/getting-started/requests/index.html index dc5b39a70..5cce443e5 100644 --- a/docs/9.x/getting-started/requests/index.html +++ b/docs/9.x/getting-started/requests/index.html @@ -4,7 +4,7 @@ Requests | Apiato - + @@ -17,7 +17,7 @@ you can force your users to send application/json by setting 'force-accept-header' => true, in app/Ship/Configs/apiato.php or allow them to skip it completely by setting the 'force-accept-header' => false,. By default this flag is set to false.

    Calling Endpoints

    Calling unprotected endpoint example:

    curl -X POST -H "Accept: application/json" -H "Content-Type: application/x-www-form-urlencoded; -F "email=admin@admin.com" -F "password=admin" -F "=" "http://api.domain.test/v2/register"

    Calling protected endpoint (passing Bearer Token) example:

    curl -X GET -H "Accept: application/json" -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..." -H "http://api.domain.test/v1/users"
    - + \ No newline at end of file diff --git a/docs/9.x/getting-started/responses/index.html b/docs/9.x/getting-started/responses/index.html index 5dce97880..d3089b8df 100644 --- a/docs/9.x/getting-started/responses/index.html +++ b/docs/9.x/getting-started/responses/index.html @@ -4,7 +4,7 @@ Responses | Apiato - + @@ -18,7 +18,7 @@ the App\Containers\User\Models\User::class is User.

    For DataArraySerializer.

    By default, the object keyword is used as a resource key for each response, and it's set manually in each transformer, to be automated later.

    Error Responses formats

    Visit each feature, example the Authentication and there you will see how an unauthenticated response looks like, same for Authorization, Validation and so on.

    Building a Responses from the Controller:

    Checkout the Controller response builder helper functions.

    - + \ No newline at end of file diff --git a/docs/9.x/getting-started/software-architectural-patterns/index.html b/docs/9.x/getting-started/software-architectural-patterns/index.html index 8fbbd37b3..6ae7afc81 100644 --- a/docs/9.x/getting-started/software-architectural-patterns/index.html +++ b/docs/9.x/getting-started/software-architectural-patterns/index.html @@ -4,7 +4,7 @@ Software Architectural Patterns | Apiato - + @@ -41,7 +41,7 @@ app/Containers/Application/Data/Migrations/.

    10) Create Seeds

    In Laravel, the Seeds files live in the database/migrations/ folder on the root of the project. In Apiato MVC, the Seeds files can live in that same directory or/and in this container folder app/Containers/Application/Data/Seeders/.

    More Classes

    All other classes types work the same way, you can refer to the documentation for where to place them and what they should extend. For more details you can always get in touch with us on Slack.

    How to use Apiato features

    Apiato features are all provided as Actions & Tasks classes.

    • Each Action class has single function run which does one feature only.
    • Each Task class has single function run which does one job only (a tiny piece of the business logic).

    You can use Actions/Tasks classes anyway you want:

    • Using Apiato Facade with Apiato caller style $user = \Apiato::call('Car@GetDriversAction', [$request->id]);
    • Using Apiato Facade with full class name $user = \Apiato::call(GetDriversAction::class, [$request->id]);
    • Using the helper call function with full class name $user = $this->call(GetDriversAction::class, [$request->id]);
    • Using the helper call function with Apiato caller style $user = $this->call('Car@GetDriversAction', [$request->id]);
    • Without Apiato involvement using plain PHP $user = $action = new GetDriversAction::class; $action->run($request->id);
    • Without Apiato involvement using plain Laravel IoC $user = \App::make(GetDriversAction::class)->run($request->id);

    Be creative, at the end of the day it's a class with a function.

    - + \ No newline at end of file diff --git a/docs/9.x/index.html b/docs/9.x/index.html index e0a246c34..6cae83606 100644 --- a/docs/9.x/index.html +++ b/docs/9.x/index.html @@ -4,13 +4,13 @@ Requirements | Apiato - +
    Version: 9.x

    Requirements

    Requirements

    • GIT
    • PHP >= 7.2 (7.4 is recommended)
    • PHP Extensions:
      • OpenSSL PHP Extension
      • PDO PHP Extension
      • Mbstring PHP Extension
      • Tokenizer PHP Extension
      • BCMath PHP Extension (required when the Hash ID feature is enabled)
      • Intl Extension (required when you use the Localization Container)
    • Composer
    • Node (required for the API Docs generator feature)
    • Web Server (example: Nginx)
    • Cache Engine (example: Redis)
    • Database Engine (example: MySQL)
    • Queues Engine (example: Beanstalkd)
    - + \ No newline at end of file diff --git a/docs/9.x/main-components/actions/index.html b/docs/9.x/main-components/actions/index.html index 5cb0abfef..56729938f 100644 --- a/docs/9.x/main-components/actions/index.html +++ b/docs/9.x/main-components/actions/index.html @@ -4,14 +4,14 @@ Actions | Apiato - +
    Version: 9.x

    Actions

    Definition & Principles

    Read the section in the Porto SAP Documentation (#Actions).

    Rules

    • All Actions MUST extend App\Ship\Parents\Actions\Action.

    Folder Structure

     - app
    - Containers
    - {container-name}
    - Actions
    - CreateUserAction.php
    - DeleteUserAction.php
    - ...

    Code Sample

    Delete User Action:

    <?php

    namespace App\Containers\User\Actions;

    use Apiato\Core\Foundation\Facades\Apiato;
    use App\Containers\User\Models\User;
    use App\Ship\Parents\Actions\Action;

    class CreateAdminAction extends Action
    {

    /**
    * @param string $email
    * @param string $password
    * @param string $name
    * @param bool $isClient
    *
    * @return \App\Containers\User\Models\User
    */
    public function run(string $email, string $password, string $name, bool $isClient = false): User
    {
    $admin = Apiato::call('User@CreateUserByCredentialsTask', [
    $isClient,
    $email,
    $password,
    $name
    ]);

    Apiato::call('Authorization@AssignUserToRoleTask', [$admin, ['admin']]);

    return $admin;
    }
    }

    Note: instead of passing these parameters string $email, string $password, string $name, bool $isClient = false from place to another over and over. Consider using the Transporters classes (simple DTO's "Data Transfer Objects"), for more details read the Transporters Page.

    Injecting each Task in constructor and then using it below through its property is really boring, and the more Tasks you use the worse it gets. So instead you can use the function call to call whichever Task you want and then pass any parameters to it.

    The Action itself was also called using Apiato::call() which triggers the run function in it.

    Refer to the Magical Call page for more info and examples on how to use the call function.

    Same Example using the call function:

    <?php

    namespace App\Containers\User\Actions;

    use App\Containers\User\Tasks\DeleteUserTask;
    use App\Ship\Parents\Actions\Action;

    class DeleteUserAction extends Action
    {

    public function run($userId)
    {
    return Apiato::call(DeleteUserTask::class, [$userId]); // <<<<<
    }

    }

    Example: Calling multiple Tasks:

    <?php

    namespace App\Containers\Email\Actions;

    use App\Containers\Xxx\Tasks\Sample111Task;
    use App\Containers\Xxx\Tasks\Sample222Task;
    use App\Ship\Parents\Actions\Action;

    class DemoAction extends Action
    {

    public function run($xxx, $yyy, $zzz)
    {

    $foo = Apiato::call(Sample111Task::class, [$xxx, $yyy]);

    $bar = Apiato::call(Sample222Task::class, [$zzz]);

    // ...

    }

    }

    Action usage from a Controller:

     <?php
    //...

    public function deleteUser(DeleteUserRequest $request)
    {
    $user = Apiato::call(DeleteUserAction::class, [$request->xxx, $request->yyy]);

    return $this->deleted($user);
    }

    //...

    The same Action MAY be called by multiple Controllers (Web, Api, Cli).

    - + \ No newline at end of file diff --git a/docs/9.x/main-components/controllers/index.html b/docs/9.x/main-components/controllers/index.html index 474c800e3..2952178ac 100644 --- a/docs/9.x/main-components/controllers/index.html +++ b/docs/9.x/main-components/controllers/index.html @@ -4,7 +4,7 @@ Controllers | Apiato - + @@ -13,7 +13,7 @@ This is the most useful function which you will be using in most cases.

    • First required parameter accepts data as object or Collection of objects.
    • Second required parameter is the transformer object
    • Third optional parameter take the includes that should be returned by the response, ($availableIncludes and $defaultIncludes in the transformer class).
    • Fourth optional parameter accepts metadata to be injected in the response.
    // $user is a User Object
    return $this->transform($user, UserTransformer::class);

    // $orders is a Collection of Order Objects
    return $this->transform($orders, OrderTransformer::class, ['products', 'recipients', 'store', 'invoice']);

    withMeta This function allows including metadata in the response.

    $metaData = ['total_credits', 10000];

    return $this->withMeta($metaData)->transform($receipt, ReceiptTransformer::class);

    json This function allows passing array data to be represented as json.

    return $this->json([
    'foo': 'bar'
    ]);

    Other functions

    • accepted
    • deleted
    • noContent
    • // Some functions might not be documented, so refer to the vendor/apiato/core/Traits/ResponseTrait.php and see the public functions.
    - + \ No newline at end of file diff --git a/docs/9.x/main-components/models/index.html b/docs/9.x/main-components/models/index.html index 3746797e9..3a8297ce5 100644 --- a/docs/9.x/main-components/models/index.html +++ b/docs/9.x/main-components/models/index.html @@ -4,13 +4,13 @@ Models | Apiato - +
    Version: 9.x

    Models

    Definition & Principles

    Read from the Porto SAP Documentation (#Models).

    Rules

    • All Models MUST extend from App\Ship\Parents\Models\Model.
    • If the name of a model differs from the Container name you have to set the $container attribute in the repository - more details.

    Folder Structure

     - App
    - Containers
    - {container-name}
    - Models
    - User.php
    - UserId.php
    - ...

    Code Sample

    <?php

    namespace App\Containers\Demo\Models;

    use App\Ship\Parents\Models\Model;

    class Demo extends Model
    {
    protected $table = 'demos';

    protected $fillable = [
    'label',
    'user_id'
    ];

    protected $hidden = [
    'token',
    ];

    protected $casts = [
    'total_credits' => 'float',
    ];

    protected $dates = [
    'created_at',
    'updated_at',
    ];

    public function user()
    {
    return $this->belongsTo(\App\Containes\User\Models\User::class);
    }
    }

    Notice the Demo Model has a relationship with User Model, which lives in another Container.

    Casts

    The casts attribute can be used to parse any of the model's attributes to a specific type. In the code sample below we can cast total_credits to float.

    More information about the applicable cast-types can be found in the laravel eloquent-mutators documentation.

    You can place any dates inside the $dates to parse those automatically.

    - + \ No newline at end of file diff --git a/docs/9.x/main-components/requests/index.html b/docs/9.x/main-components/requests/index.html index 24d48d0cc..f5e33ad5f 100644 --- a/docs/9.x/main-components/requests/index.html +++ b/docs/9.x/main-components/requests/index.html @@ -4,7 +4,7 @@ Requests | Apiato - + @@ -24,7 +24,7 @@ function.

    This helper, in turn, allows to "redefine" keys in the request for subsequent processing. Consider the following example request:

    {
    "data" : {
    "name" : "John Doe"
    }
    }

    Your Task to process this data, however, requests the field data.name as data.username. You can call the helper like this:

    $request->mapInput([
    'data.name' => 'data.username',
    ]);

    The resulting structure would look like this:

    {
    "data" : {
    "username" : "John Doe"
    }
    }

    Storing Data on the Request

    During the Request life-cycle you may want to store some data on the request object and pass it to other SubActions (or Tasks).

    To store some data on the request use:

    $request->keep(['someKey' => $someValue]);

    To retrieve the data back at any time during the request life-cycle use:

    $someValue = $request->retrieve('someKey')

    Unit Testing for Actions (Request)

    Since we're passing Request objects to Actions. When writing unit tests we need to create fake Request just to pass it to the Action with some fake data.

    // creating
    $request = RegisterUserRequest::injectData($data);

    Example One:

    $data = [
    'email' => 'Mahmoud@test.test',
    'name' => 'Mahmoud',
    'password' => 'so-secret',
    ];

    // create request object with some data
    $request = RegisterUserRequest::injectData($data);

    // create instance of the Action
    $action = App::make(RegisterUserAction::class)->run($request);

    // do any kind of assertions..
    $this->assertInstanceOf(User::class, $user);

    // ...

    Example Two (With Authenticated User):

    $data = [
    'store_id' => $this->encode($store->id),
    'items' => $orderItems,
    'recipient' => $receipient,
    ];

    $user = factory(User::class)->create();

    $request = MakeOrderRequest::injectData($data, $user);

    $order = App::make(MakeOrderAction::class)->run($request);

    // ...

    - + \ No newline at end of file diff --git a/docs/9.x/main-components/routes/index.html b/docs/9.x/main-components/routes/index.html index a238eea16..9496894ae 100644 --- a/docs/9.x/main-components/routes/index.html +++ b/docs/9.x/main-components/routes/index.html @@ -4,13 +4,13 @@ Routes | Apiato - +
    Version: 9.x

    Routes

    Definition & Principles

    Read from the Porto SAP Documentation (#Routes).

    Rules

    • API Route files MUST be named according to their API's versions, exposure and functionality. Example CreateOrder.v1.public.php, FulfillOrder.v2.public.php, CancelOrder.v1.private.php...

    • Web Route files are pretty similar to API web files, but they can be named anything.

    Folder Structure

     - app
    - Containers
    - {container-name}
    - UI
    - API
    - Routes
    - CreateItem.v1.public.php
    - DeleteItem.v1.public.php
    - CreateItem.v2.public.php
    - DeleteItem.v1.private.php
    - ApproveItem.v1.private.php
    - ...
    - WEB
    - Routes
    - main.php
    - ...

    Web Routes

    Example: Endpoint to display a Hello View in the browser

    <?php

    $router->get('/hello', [
    'uses' => 'Controller@sayHello',
    ]);

    In all the Web Routes files the $router variable is an instance of the default Laravel Router Illuminate\Routing\Router.

    API Routes

    Example: User Login API Endpoint

    <?php

    $router->post('login', [
    'uses' => 'Controller@loginUser',
    ]);

    Example: Protected List All Users API Endpoint, for an API Routes file

    <?php

    $router->get('users', [
    'uses' => 'Controller@listAllUsers',
    'middleware' => [
    'api.auth',
    ]
    ]);

    Protect your Endpoints:

    Checkout the Authorization Page.

    Difference between Public & Private routes files

    apiato has 2 types of endpoints, Public (External) mainly for third parties clients, and Private (Internal) for your own Apps. This will help to generate separate documentations for each and keep your internal API private.

    - + \ No newline at end of file diff --git a/docs/9.x/main-components/subactions/index.html b/docs/9.x/main-components/subactions/index.html index 1b1b8b1f5..9288dd9c2 100644 --- a/docs/9.x/main-components/subactions/index.html +++ b/docs/9.x/main-components/subactions/index.html @@ -4,13 +4,13 @@ Sub Actions | Apiato - +
    Version: 9.x

    Sub Actions

    Definition & Principles

    Read from the Porto SAP Documentation (#Sub-Actions).

    Rules

    • All SubActions MUST extend from App\Ship\Parents\Actions\SubAction.

    Folder Structure

     - app
    - Containers
    - {container-name}
    - Actions
    - ValidateAddressSubAction.php
    - BuildOrderSubAction.php
    - ...

    Code Sample

    ValidateAddressSubAction User Action:

    <?php

    namespace App\Containers\Shipment\Actions;

    use App\Containers\Recipient\Models\Recipient;
    use App\Containers\Recipient\Tasks\UpdateRecipientTask;
    use App\Containers\Shipment\Tasks\ValidateAddressWithEasyPostTask;
    use App\Containers\Shipment\Tasks\ValidateAddressWithMelissaDataTask;
    use App\Ship\Parents\Actions\SubAction;

    class ValidateAddressSubAction extends SubAction
    {
    public function run(Recipient $recipient)
    {
    $hasValidAddress = true;

    $easyPostResponse = Apiato::call(ValidateAddressWithEasyPostTask::class, [$recipient]);

    ...
    }
    }

    Every feature available for Actions, is also available in SubActions.

    - + \ No newline at end of file diff --git a/docs/9.x/main-components/tasks/index.html b/docs/9.x/main-components/tasks/index.html index 393798365..334ebfc48 100644 --- a/docs/9.x/main-components/tasks/index.html +++ b/docs/9.x/main-components/tasks/index.html @@ -4,13 +4,13 @@ Tasks | Apiato - +
    Version: 9.x

    Tasks

    Definition & Principles

    Read from the Porto SAP Documentation (#Tasks).

    Rules

    • All Tasks MUST extend from App\Ship\Parents\Tasks\Task.

    Folder Structure

     - app
    - Containers
    - {container-name}
    - Tasks
    - ConfirmUserEmailTask.php
    - GenerateEmailConfirmationUrlTask.php
    - SendConfirmationEmailTask.php
    - ValidateConfirmationCodeTask.php
    - SetUserEmailTask.php
    - ...

    Code Sample

    Find User Task by ID:

    <?php

    namespace App\Containers\User\Tasks;

    use App\Containers\User\Contracts\UserRepositoryInterface;
    use App\Ship\Parents\Tasks\Task;
    use Exception;

    class FindUserByIdTask extends Task
    {
    private $userRepository;

    public function __construct(UserRepositoryInterface $userRepository)
    {
    $this->userRepository = $userRepository;
    }

    public function run($id)
    {
    try {
    $user = $this->userRepository->find($id);
    } catch (Exception $e) {
    throw new UserNotFoundException();
    }

    return $user;
    }

    }

    Tasks usage from an Action:

    <?php

    namespace App\Containers\Email\Actions;

    use App\Containers\Email\Tasks\ConfirmUserEmailTask;
    use App\Containers\Email\Tasks\ValidateConfirmationCodeTask;
    use App\Containers\User\Tasks\FindUserByIdTask;
    use App\Ship\Parents\Actions\Action;

    class ValidateUserEmailByConfirmationCodeAction extends Action
    {
    private $validateConfirmationCodeTask;

    private $findUserByIdTask;

    private $confirmUserEmailTask;

    public function __construct(
    ValidateConfirmationCodeTask $validateConfirmationCodeTask,
    FindUserByIdTask $findUserByIdTask,
    ConfirmUserEmailTask $confirmUserEmailTask
    ) {
    $this->validateConfirmationCodeTask = $validateConfirmationCodeTask;
    $this->findUserByIdTask = $findUserByIdTask;
    $this->confirmUserEmailTask = $confirmUserEmailTask;
    }

    public function run($userId, $code)
    {
    $this->validateConfirmationCodeTask->run($userId, $code);
    $user = $this->findUserByIdTask->run($userId);
    $this->confirmUserEmailTask->run($user);
    ...
    }
    }

    - + \ No newline at end of file diff --git a/docs/9.x/main-components/transformers/index.html b/docs/9.x/main-components/transformers/index.html index ece1f462a..290770174 100644 --- a/docs/9.x/main-components/transformers/index.html +++ b/docs/9.x/main-components/transformers/index.html @@ -4,13 +4,13 @@ Transformers | Apiato - +
    Version: 9.x

    Transformers

    Definition & Principles

    Read from the Porto SAP Documentation (#Transformers).

    Rules

    • All API responses MUST be formatted via a Transformer.

    • Every Transformer SHOULD extend from App\Ship\Parents\Transformers\Transformer.

    • Each Transformer MUST have a transform() function.

    Folder Structure

     - app
    - Containers
    - {container-name}
    - UI
    - API
    - Transformers
    - UserTransformer.php
    - ...

    Code Samples

    Reward Transformer with Country relation:

    <?php

    namespace App\Containers\Item\UI\API\Transformers;

    use App\Containers\Item\Models\Item;
    use App\Ship\Parents\Transformers\Transformer;

    class ItemTransformer extends Transformer
    {

    protected $availableIncludes = [
    'images',
    ];

    protected $defaultIncludes = [
    'roles',
    ];

    public function transform(Item $item)
    {
    $response = [
    'object' => 'Item',
    'id' => $item->getHashedKey(),
    'name' => $item->name,
    'description' => $item->description,
    'price' => (float)$item->price,
    'weight' => (float)$item->weight,
    'created_at' => $item->created_at,
    'updated_at' => $item->updated_at,
    ];

    // add more or modify data for Admins only
    $response = $this->ifAdmin([
    'real_id' => $user->id,
    'deleted_at' => $user->deleted_at,
    ], $response);

    return $response;
    }

    public function includeImages(Item $item)
    {
    return $this->collection($item->images, new ItemImageTransformer());
    }

    public function includeRoles(User $user)
    {
    return $this->collection($user->roles, new RoleTransformer());
    }
    }

    Usage from Controller (Single Item)

    <?php

    // getting any Model
    $user = $this->getUser();

    // building the response with the transformer of the Model
    $this->response->item($user, new UserTransformer());

    // in case of collection of data
    $this->response->collection($user, new UserTransformer());

    // in case of Array
    $this->response->array([
    'custom_field' => 'whatever',
    'email' => $user->email,
    ]);

    // more options are available

    Usage from Controller (Multiple Items/Collection)

    <?php

    // getting many Models Paginated
    $rewards = $this->getRewards();

    // building the response with the transformer of the Model
    return $this->response->paginator($rewards, new RewardTransformer());

    Relationships (include)

    Loading relationships in Transformer (calling other Transformers):

    This can be done in 2 ways:

    1. By the User, he can specify what relations to return in response.

    2. By the Developer, define what relations to include at run time.

    From Front-end

    You can request data with their relationships directly from the API call using include=tags,user but first the Transformer need to have the availableIncludes defined with their functions like this:

    <?php

    namespace App\Containers\Account\UI\API\Transformers;

    use App\Ship\Parents\Transformers\Transformer;
    use App\Containers\Account\Models\Account;
    use App\Containers\Tag\Transformers\TagTransformer;
    use App\Containers\User\Transformers\UserTransformer;

    class AccountTransformer extends Transformer
    {
    protected $availableIncludes = [
    'tags',
    'user',
    ];

    public function transform(Account $account)
    {
    return [
    'id' => (int)$account->id,
    'url' => $account->url,
    'username' => $account->username,
    'secret' => $account->secret,
    'note' => $account->note,
    ];
    }

    public function includeTags(Account $account)
    {
    // use collection with `multi` relationship
    return $this->collection($account->tags, new TagTransformer());
    }

    public function includeUser(Account $account)
    {
    // use `item` with single relationship
    return $this->item($account->user, new UserTransformer());
    }

    }

    Now to get the Tags with the response when Accounts are requested pass the ?include=tags parameter with the [GET] request.

    To get Tags with User use the comma separator: ?include=tags,user.

    From Back-end

    From the controller you can dynamically set the DefaultInclude using (setDefaultIncludes) anytime you want.

    <?php

    return $this->response->paginator($rewards, (new ProductsTransformer())->setDefaultIncludes(['tags']));

    You need to have includeTags function defined on the transformer. Look at the full examples above.

    If you want to include a relation with every response from this transformer you can define the relation directly in the transformer on ($defaultIncludes)

    <?php

    protected $availableIncludes = [
    'users',
    ];

    protected $defaultIncludes = [
    'tags',
    ];

    // ..

    You need to have includeUser and includeTags functions defined on the transformer. Look at the full examples above.

    Transformer Available helper functions:

    • user() : returns current authenticated user object.

    • ifAdmin($adminResponse, $clientResponse) : merges normal client response with the admin extra or modified results, when current authenticated user is Admin.

    For more information about the Transformers read this.

    - + \ No newline at end of file diff --git a/docs/9.x/main-components/transporters/index.html b/docs/9.x/main-components/transporters/index.html index 82624d383..37e015eda 100644 --- a/docs/9.x/main-components/transporters/index.html +++ b/docs/9.x/main-components/transporters/index.html @@ -4,7 +4,7 @@ Transporters | Apiato - + @@ -14,7 +14,7 @@ you can call:

    // "simple" access via direct properties
    $name = $transporter->name;

    // complex access via method
    $username = $transporter->getInputByKey('your.nested.username.field');

    Of course, you can also "sanitize" the data, like you would have done in the Request classes by using sanitizeInput(array).

    Finally, if you need to access the original Request object, you can access it via

    $originalRequest = $transporter->request;

    Set Data

    You can set data in many ways

    $dataTransporter = new DataTransporter($request);
    $dataTransporter->bearerToken = $request->bearerToken();

    If the data is defined as required like this on the Transporter:

        protected $schema = [
    'type' => 'object',
    'properties' => [
    'email',
    'password',
    'clientId',
    'clientPassword',
    ],
    'required' => [
    'email',
    'password',
    'clientId',
    'clientPassword',
    ],
    ];

    Then can set data on the Transporter like this:

    $dataTransporter = new ProxyApiLoginTransporter(
    array_merge($request->all(), [
    'clientId' => Config::get('authentication-container.clients.web.admin.id'),
    'clientPassword' => Config::get('authentication-container.clients.web.admin.secret')
    ])
    );

    Set Instance

    Passing Objects does not work!, because the third party package cannot hydrate it. So to pass instances from a place to another on the Transporter object, you can do the following:

    $transporter = new DataTransporter();
    $transporter->setInstance("command_instance", $this);

    Warning: you can set instances, but they do not appear when calling toArray() or other similar functions, since they cannot be hydrated. See below how you can get the instance form the Transporter object.

    Get Instance:

    $console = $data->command_instance;

    Get Data

    To get all data from the Transporter you can call $data->toArray() or $data->toJson()... there are many other functions on the class.

    To get specific data just call the data name, as you would when accessing data from a Request object $data->username.

    - + \ No newline at end of file diff --git a/docs/9.x/main-components/views/index.html b/docs/9.x/main-components/views/index.html index 866def219..a76bc5130 100644 --- a/docs/9.x/main-components/views/index.html +++ b/docs/9.x/main-components/views/index.html @@ -4,13 +4,13 @@ Views | Apiato - +
    Version: 9.x

    Views

    Definition & Principles

    Read from the Porto SAP Documentation (#Views).

    Rules

    • Views SHOULD be created inside the Containers, and they will be automatically available for use in the Web Controllers.

    • All Views are namespaced as the lower case of the Container name.

    Folder Structure

     - app
    - Containers
    - {container-name}
    - UI
    - WEB
    - Views
    - welcome.php
    - profile.php
    - ...

    Code Sample

    Welcome page View

    <!DOCTYPE html>
    <html>
    <head>
    <title>Welcome</title>
    </head>
    <body>
    <div class="container">
    <div class="content">
    <div class="title">Welcome</div>
    </div>
    </div>
    </body>
    </html>

    Example: Usage From Controller

    <?php

    namespace App\Containers\Welcome\UI\WEB\Controllers;

    use App\Ship\Parents\Controllers\WebController;

    class Controller extends WebController
    {
    public function sayWelcome()
    {
    return view('just-welcome');
    }
    }

    Namespaces

    By default, all the Container Views are namespaced to the Container name.

    Example:

    If a Container named Store has View say-hello, you can access the view like this view('store::just-welcome'). If you try to access it without the namespace view('just-welcome'), it will not find your View.

    - + \ No newline at end of file diff --git a/docs/9.x/miscellaneous/container-installer/index.html b/docs/9.x/miscellaneous/container-installer/index.html index 430309547..c0a41c752 100644 --- a/docs/9.x/miscellaneous/container-installer/index.html +++ b/docs/9.x/miscellaneous/container-installer/index.html @@ -4,7 +4,7 @@ Containers Installer | Apiato - + @@ -24,7 +24,7 @@ that allows installing / updating containers.
  • You must provide the key extra.apiato.container.name. This key indicates the name of the folder (e.g., container) when installing the package to the /app/Containers folder. In the shown example, the container would be installed to app/Containers/Foo.
  • - + \ No newline at end of file diff --git a/docs/9.x/miscellaneous/magical-call/index.html b/docs/9.x/miscellaneous/magical-call/index.html index 447dd586d..fff4b6841 100644 --- a/docs/9.x/miscellaneous/magical-call/index.html +++ b/docs/9.x/miscellaneous/magical-call/index.html @@ -4,7 +4,7 @@ Magical Call | Apiato - + @@ -27,7 +27,7 @@ automatically rolled-back from the database. However, respective operations on the file system (e.g., you may also have uploaded a profile picture for this Team already that needs to be removed in this case) need to be performed manually!

    Typically, you may want to use the transactionalCall() on the Controller level!

    Use case example

    <?php

    return Apiato::call('User@ListUsersTask', [], ['ordered']);
    // can be called this way as well Apiato::call(ListUsersTask::class, [], ['ordered']);

    return Apiato::call('User@ListUsersTask', [], ['ordered', 'clients']);

    return Apiato::call('User@ListUsersTask', [], ['admins']);

    return Apiato::call('User@ListUsersTask', [], ['admins', ['roles' => ['manager', 'employee']]]);

    The ListUsersTask class

    <?php

    namespace App\Containers\User\Tasks;

    use App\Containers\User\Data\Criterias\AdminsCriteria;
    use App\Containers\User\Data\Criterias\ClientsCriteria;
    use App\Containers\User\Data\Criterias\RoleCriteria;
    use App\Containers\User\Data\Repositories\UserRepository;
    use App\Ship\Criterias\Eloquent\OrderByCreationDateDescendingCriteria;
    use App\Ship\Parents\Tasks\Task;

    class ListUsersTask extends Task
    {
    private $userRepository;

    public function __construct(UserRepository $userRepository)
    {
    $this->userRepository = $userRepository;
    }

    public function run()
    {
    return $this->userRepository->paginate();
    }

    public function clients()
    {
    $this->userRepository->pushCriteria(new ClientsCriteria());
    }

    public function admins()
    {
    $this->userRepository->pushCriteria(new AdminsCriteria());
    }

    public function ordered()
    {
    $this->userRepository->pushCriteria(new OrderByCreationDateDescendingCriteria());
    }

    public function withRole($roles)
    {
    $this->userRepository->pushCriteria(new RoleCriteria($roles));
    }

    }

    - + \ No newline at end of file diff --git a/docs/9.x/miscellaneous/postman/index.html b/docs/9.x/miscellaneous/postman/index.html index c71cb3651..00b454eae 100644 --- a/docs/9.x/miscellaneous/postman/index.html +++ b/docs/9.x/miscellaneous/postman/index.html @@ -4,7 +4,7 @@ Postman Environment | Apiato - + @@ -15,7 +15,7 @@ access all routes.

    php artisan migrate:refresh --seed
    php artisan passport:client --password
    php artisan apiato:permissions:toRole admin

    Be sure to copy and paste your new client_id and client_secret into the .env file. Like so...

    CLIENT_WEB_ADMIN_ID={CLIENT_ID}
    CLIENT_WEB_ADMIN_SECRET={CLIENT_SECRET}

    CLIENT_MOBILE_ADMIN_ID={CLIENT_ID}
    CLIENT_MOBILE_ADMIN_SECRET={CLIENT_SECRET}

    The above steps will ensure you have a functioning Apiato API environment to explore with Postman.

    Download & Install Postman

    Visit the Postman website and download the application.

    Download Postman

    Add Apiato Environment & Collection to Postman

    Download Apiato Environment & Collection

    Steps

    • Open Postman
    • Click on "import" button top left of Postman application.
    • Click on "Choose files". Select both the Environment and Collection JSON files and click add.
    • Select "Apiato Environment" from the Environment dropdown list on the top right of the Postman Application.

    Using the Postman Apiato API Collection

    The first thing you need to do to use the Apiato endpoints is to log in to your Apiato API.

    • Select the Apiato API Collection in the left menu.
    • Select Authentication folder.
    • Select Login endpoint.
    • Click Send button.

    The response will return a body with the API access token. Normally you would have to manually add this in a header with each request using Authorization: Bearer TOKEN. This however is automatically done for you.

    From this point you can now access all endpoints using the Super Admin role.

    If you would like to test logging into your application with different users then switch to the body tab on the login endpoint and update the credentials.

    {
    "email": "admin@admin.com",
    "password": "admin"
    }
    - + \ No newline at end of file diff --git a/docs/9.x/miscellaneous/tasks-queuing/index.html b/docs/9.x/miscellaneous/tasks-queuing/index.html index ba18c1de8..e79e1f30f 100644 --- a/docs/9.x/miscellaneous/tasks-queuing/index.html +++ b/docs/9.x/miscellaneous/tasks-queuing/index.html @@ -4,7 +4,7 @@ Tasks Queuing | Apiato - + @@ -18,7 +18,7 @@

    More info here (docs).

    The only addition to the Laravel's queues in Apiato, is that by default, apiato detects which queue driver you are planning to use (based on the configs), to create the migration files required, in case type database is used.

    if (Config::get('queue.default') == 'database')
    {
    // do something
    }

    (refer to app/Ship/Migrations/ folder for more details).

    Beanstalkd

    In order to use Beanstalkd as your queue driver, you need to require the "pda/pheanstalk": "^3.1" package first. You can include this in any composer.json file you want.

    - + \ No newline at end of file diff --git a/docs/9.x/miscellaneous/tasks-scheduling/index.html b/docs/9.x/miscellaneous/tasks-scheduling/index.html index 499529425..344e80e4a 100644 --- a/docs/9.x/miscellaneous/tasks-scheduling/index.html +++ b/docs/9.x/miscellaneous/tasks-scheduling/index.html @@ -4,7 +4,7 @@ Tasks Scheduling | Apiato - + @@ -18,7 +18,7 @@ See the Commands Page.

    Once you have your command ready, go to app/Ship/Kernels/ConsoleKernel.php and start adding the commands you need to schedule inside the schedule function.

    Example:

    <?php
    protected function schedule(Schedule $schedule)
    {
    $schedule->command('apiato:welcome')->everyMinute();
    $schedule->job(new myJob)->hourly();
    $schedule->exec('touch me.txt')->dailyAt('12:00');
    // ...
    }

    More details here.

    NOTE: you do not need to register the commands with the $commands property or point to them in the commands() function. Apiato will do that automatically for you.

    - + \ No newline at end of file diff --git a/docs/9.x/miscellaneous/tests-helpers/index.html b/docs/9.x/miscellaneous/tests-helpers/index.html index 99572a52e..961862d3b 100644 --- a/docs/9.x/miscellaneous/tests-helpers/index.html +++ b/docs/9.x/miscellaneous/tests-helpers/index.html @@ -4,7 +4,7 @@ Tests Helpers | Apiato - + @@ -25,7 +25,7 @@ testing data.

    1. Go to Seeder/SeedTestingData.php seeder class, and create your live testing data.

    2. Run this command php artisan apiato:seed-test

    Debugging with PsySH

    For better debugging and development, you can open a runtime developer console while executing your test.

    Using PsySH (interactive debugger and REPL "read-eval-print loop" for PHP). The package is required by the Laravel Tinker Package.

    To use it set the breakpoint eval(\Psy\sh()); anywhere you want in any Actions, Controllers, Tasks... and run your test normally.

    - + \ No newline at end of file diff --git a/docs/9.x/optional-components/commands/index.html b/docs/9.x/optional-components/commands/index.html index 2c5c09b7d..9311f85d2 100644 --- a/docs/9.x/optional-components/commands/index.html +++ b/docs/9.x/optional-components/commands/index.html @@ -4,13 +4,13 @@ Commands | Apiato - +
    Version: 9.x

    Commands

    Definition

    Commands:

    • is a laravel artisan command. Laravel has its own default commands, and you create your own as well.
    • provides a way to interact with the Laravel app.
    • a Command can be scheduled by a Task scheduler, like Cron Job or by the Laravel built-in wrapper of the Cron Job "laravel scheduler".
    • Commands could be Closure based or Classes.
    • "dispatch" is the term that is usually used to call a Command.

    Principles

    • Containers MAY or MAY NOT have one or more Commands.

    • Every Command SHOULD call an Action to perform its job, and should not container any business logic.

    • Ship may contain Application general Commands.

    Rules

    • All Commands MUST extend from App\Ship\Parents\Commands\ConsoleCommand.

    Folder Structure

     - app
    - Containers
    - {container-name}
    - UI
    - CLI
    - Commands
    - SayHelloCommand.php
    - ...
    - Ship
    - Commands
    - GeneralCommand.php
    - ...

    Code Samples

    Example: a simple Command

    <?php

    namespace App\Containers\Welcome\UI\CLI\Commands;

    use App\Ship\Parents\Commands\ConsoleCommand;

    /**
    * Class SayWelcomeCommand
    *
    * @author Mahmoud Zalt <mahmoud@zalt.me>
    */
    class SayWelcomeCommand extends ConsoleCommand
    {

    /**
    * The name and signature of the console command.
    *
    * @var string
    */
    protected $signature = 'apiato:welcome';

    /**
    * The console command description.
    *
    * @var string
    */
    protected $description = 'Just saying Welcome.';

    /**
    * Create a new command instance.
    *
    * @return void
    */
    public function __construct()
    {
    parent::__construct();
    }

    /**
    * Execute the console command.
    *
    * @return void
    */
    public function handle()
    {
    $this->info('Welcome to apiato :)'); // green color
    // $this->line('Welcome to apiato :)'); // normal color
    }
    }

    Usage from CLI (Terminal):

    php artisan apiato:welcome

    Schedule Commands Execution

    To Schedule the execution of a Command checkout the Tasks Scheduling page.

    Define Consoles Routes

    To define Console route go to app/Ship/Commands/Routes.php.

    - + \ No newline at end of file diff --git a/docs/9.x/optional-components/configs/index.html b/docs/9.x/optional-components/configs/index.html index 6451620c3..6ea682ecd 100644 --- a/docs/9.x/optional-components/configs/index.html +++ b/docs/9.x/optional-components/configs/index.html @@ -4,13 +4,13 @@ Configs | Apiato - +
    Version: 9.x

    Configs

    Definition

    Configs are files that container configurations. For more details about them check this doc.

    In each Apiato container, there are two types of config files:

    • the container specific config file (a config file that contains the container specific configurations).
    • the container third party packages config files (a config file that belongs to a third party package, required by the composer file of the container).

    Principles

    • Your custom config files and third party packages config files, should be placed in the Container, unless it's too generic then it can be placed on the Ship Layer.
    • Container can have as many config files as it needs.

    Rules

    • When publishing a third party package config file move it manually to its container or to the Ship Features Config folder in case it is generic.
    • Framework config files (provided by laravel) lives at the default config directory on the root of the project.
    • You SHOULD NOT add any config file to the config directory.
    • The container specific config file, MUST have the same name of the container in lower letters and post-fixed with -container, to prevent conflicts between third party packages and container specific packages.

    Folder Structure

    - app
    - Containers
    - {container-name}
    - Configs
    - {container-name}-container.php
    - package-config-file1.php
    - ...
    - Ship
    - Configs
    - apiato.php
    - ...
    - config
    - app.php
    - ...

    Code Samples

    Example simple Config file

    <?php
    // app/Containers/{ContainerName}/Configs/{container-name}-container.php
    return [

    /*
    |--------------------------------------------------------------------------
    | Default Namespace
    |--------------------------------------------------------------------------
    */
    'namespace' => 'App',

    // some other config params here...

    You can access the respective configuration key like this:

    $value = Config::get('{container-name}-container.namespace');     // returns 'App'
    $value = config('{container-name}-container.namespace'); // same, but using a function

    $defaultValue = Config::get('{container-name}-container.unknown.key', 'defaultvalue'); // returns 'defaultvalue' as this key is not set!
    - + \ No newline at end of file diff --git a/docs/9.x/optional-components/criterias/index.html b/docs/9.x/optional-components/criterias/index.html index e33852db9..3a2276219 100644 --- a/docs/9.x/optional-components/criterias/index.html +++ b/docs/9.x/optional-components/criterias/index.html @@ -4,13 +4,13 @@ Criterias | Apiato - +
    Version: 9.x

    Criterias

    Definition

    Criterias are classes used to hold and apply query condition when retrieving data from the database through a Repository.

    Without using a Criteria class, you can add your query conditions to a Repository or to a Model as scope, but with Criterias, your query conditions can be shared across multiple Models and Repositories. It allows you to define the query condition once and use it anywhere in the App.

    Principles

    • Every Container MAY have its own Criterias. However, shared Criterias SHOULD be created in the Ship layer.

    • A Criteria MUST not contain any extra code, if it needs data, the data SHOULD be passed to it from the Actions or the Task. It SHOULD not run (call) any Task for data.

    Rules

    • All Criterias MUST extend from App\Ship\Parents\Criterias\Criteria.

    • Every Criteria SHOULD have an apply() function.

    • A simple query condition example "where user_id = $id", this can be named "This User Criteria", and used with all Models which have relations with the User Model.

    Folder Structure

     - app
    - Containers
    - {container-name}
    - Data
    - Criterias
    - ColourRedCriteria.php
    - RaceCarsCriteria.php
    - ...
    - Ship
    - Features
    - Criterias
    - Eloquent
    - CreatedTodayCriteria.php
    - NotNullCriteria.php
    - ...

    Code Samples

    Example: a shared Criteria

    <?php

    namespace App\Ship\Features\Criterias\Eloquent;

    use App\Ship\Parents\Criterias\Criteria;
    use Prettus\Repository\Contracts\RepositoryInterface as PrettusRepositoryInterface;

    class OrderByCreationDateDescendingCriteria extends Criteria
    {
    public function apply($model, PrettusRepositoryInterface $repository)
    {
    return $model->orderBy('created_at', 'desc');
    }
    }

    Usage from Task:

    <?php

    public function run()
    {
    $this->userRepository->pushCriteria(new OrderByCreationDateDescendingCriteria);

    $users = $this->userRepository->paginate();

    return $users;
    }

    Example: Criteria accepting data input:

    <?php

    namespace App\Ship\Features\Criterias\Eloquent;

    use App\Ship\Parents\Criterias\Criteria;
    use Prettus\Repository\Contracts\RepositoryInterface as PrettusRepositoryInterface;

    class ThisUserCriteria extends Criteria
    {

    private $userId;

    public function __construct($userId)
    {
    $this->userId = $userId;
    }

    public function apply($model, PrettusRepositoryInterface $repository)
    {
    return $model->where('user_id', '=', $this->userId);
    }
    }

    Example: Passing data from Task to Criteria:

    <?php

    public function run($user)
    {
    $this->accountRepository->pushCriteria(new ThisUserCriteria($user->id));

    $accounts = $this->accountRepository->paginate();

    return $accounts;
    }

    For more information about the Criteria read this.

    - + \ No newline at end of file diff --git a/docs/9.x/optional-components/events/index.html b/docs/9.x/optional-components/events/index.html index cfe8b4795..0b71447fa 100644 --- a/docs/9.x/optional-components/events/index.html +++ b/docs/9.x/optional-components/events/index.html @@ -4,13 +4,13 @@ Events | Apiato - +
    Version: 9.x

    Events

    Definition

    Events:

    • Events provide a simple observer implementation, allowing you to subscribe and listen for various events that occur in your application.
    • Events are classes that can be fired from anywhere in your application.
    • An event class will usually be bound to one or many Events Listeners Classes or has those Listeners registered to listen to it.
    • "fire" is the term that is usually used to call an Event.

    More details here.

    Principles

    • Events can be fired from Actions and or Tasks. It's preferable to choose one place only. (Tasks are recommended).
    • Events SHOULD be created inside the Containers. However, general Events CAN be created in the Port layer.

    Rules

    • Event classes CAN be placed inside the Containers in Events folders or on the Ship for the general Events.
    • All Events MUST extend from App\Ship\Parents\Events\Event.

    Folder Structure

     - App
    - Containers
    - {container-name}
    - Events
    - SomethingHappenedEvent.php
    - ...
    - Listeners
    - ListenToMusicListener.php
    - ...

    - Ship
    - Events
    - GlobalStateChanged.php
    - SomethingBiiigHappenedEvent.php
    - ...

    Enabling Events

    Before you can use events you need to add the EventServiceProvider to the MainServiceProvider of the Ship (if this has not been registered so far). See example below.

    <?php

    namespace App\Containers\Car\Providers;

    class MainServiceProvider extends MainProvider
    {

    /**
    * Container Service Providers.
    *
    * @var array
    */
    public $serviceProviders = [
    EventServiceProvider::class,
    ];

    // ...
    }

    Usage

    In Laravel, you can create and register events in multiple way. Below is an example of an Event that handles itself.

    Event Class Example:

    <?php

    namespace App\Containers\User\Events;

    use App\Containers\User\Models\User;
    use App\Ship\Parents\Events\Event;
    use Illuminate\Broadcasting\PrivateChannel;
    use Illuminate\Contracts\Queue\ShouldQueue;
    use Illuminate\Support\Facades\Log;

    class UserRegisteredEvent extends Event implements ShouldQueue
    {
    protected $user;

    public function __construct(User $user)
    {
    $this->user = $user;
    }

    public function handle()
    {
    Log::info('New User registration. ID = ' . $this->user->getHashedKey() . ' | Email = ' . $this->user->email . '.');

    // ...
    }

    public function broadcastOn()
    {
    return new PrivateChannel('channel-name');
    }
    }

    Note: You will get more benefits creating Events Listeners for each Event.

    To do this you will need to create a custom EventServiceProvider in your container extending App\Ship\Parents\Providers\EventsProvider.

    Your custom EventServiceProvider needs to be registered in the containers MainServiceProvider as well.

    <?php

    namespace App\Containers\Car\Providers;

    use App\Ship\Parents\Providers\MainProvider;

    /**
    * Class MainServiceProvider.
    *
    * The Main Service Provider of this container, it will be automatically registered in the framework.
    */
    class MainServiceProvider extends MainProvider
    {

    /**
    * Container Service Providers.
    *
    * @var array
    */
    public $serviceProviders = [
    EventServiceProvider::class,
    ];

    Dispatch Events

    You can dispatch an Event from anywhere you want (ideally from Actions and Tasks).

    Example: Dispatching the Event class from the example above

    <?php

    // using helper function
    event(New UserEmailChangedEvent($user));

    // manually
    \App::make(\Illuminate\Contracts\Bus\Dispatcher\Dispatcher::class)->dispatch(New UserEmailChangedEvent($user));

    Queueing an Event

    Events can implement Illuminate\Contracts\Queue\ShouldQueue to be queued.

    Handling an Event

    You can handle jobs on dispatching an event.

    To do so, you need to implement one of the following interfaces:

    Apiato\Core\Abstracts\Events\Interfaces\ShouldHandleNow

    Apiato\Core\Abstracts\Events\Interfaces\ShouldHandle

    This will force you to implement the handle method and will make apiato execute the method upon dispatching the event.

    • The ShouldHandleNow Interface will make the event execute the handle method as soon as the event gets dispatched.

    • The ShouldHandle Interface will create an eventjob and execute the handle method async (through laravel jobs).

    namespace App\Containers\Example\Events;


    use Apiato\Core\Abstracts\Events\Interfaces\ShouldHandle;
    use App\Ship\Parents\Events\Event;

    class ExampleEvent extends Event implements ShouldHandle
    {
    /**
    * If ShouldHandle interface is implemented this variable
    * sets the time (in seconds or timestamp) to wait before a job is executed
    *
    * @var \DateTimeInterface|\DateInterval|int|null $jobDelay
    */
    public $jobDelay = 60;

    /**
    * If ShouldHandle interface is implemented this variable
    * sets the name of the queue to push the job on
    *
    * @var string $jobQueue
    */
    public $jobQueue = "example_queue";

    public function handle()
    {
    // Do some handling here
    }

    }

    Broadcasting

    Note: to define Broadcasting route go to app/Ship/Boardcasts/Routes.php.

    - + \ No newline at end of file diff --git a/docs/9.x/optional-components/exceptions/index.html b/docs/9.x/optional-components/exceptions/index.html index 29d82c48a..36ed8587b 100644 --- a/docs/9.x/optional-components/exceptions/index.html +++ b/docs/9.x/optional-components/exceptions/index.html @@ -4,13 +4,13 @@ Exceptions | Apiato - +
    Version: 9.x

    Exceptions

    Definition

    Exceptions are classes the handles errors, and helps developers debug their code in a more efficient way.

    Principles

    • Exceptions can be thrown from anywhere in the application.
    • Exceptions SHOULD be created inside the Containers. However, general Exceptions CAN be created in the Ship layer.

    Rules

    • All Exceptions MUST extend App\Ship\Parents\Exceptions\Exception.
    • Shared (general) Exceptions between all Containers SHOULD be created in the Exceptions Ship folder (app/Ship/Exceptions/*).
    • Every Exception SHOULD have two properties httpStatusCode and message, both properties will be displayed when an error occurs. You can override those values while throwing the error.

    Folder Structure

     - App
    - Containers
    - {container-name}
    - Exceptions
    - AccountFailedException.php
    - ...

    - Ship
    - Exceptions
    - IncorrectIdException.php
    - InternalErrorException.php
    - ...

    Code Samples

    User Exception:

    <?php

    namespace App\Containers\User\Exceptions;

    use App\Ship\Parents\Exceptions\Exception;
    use Symfony\Component\HttpFoundation\Response;

    class AccountFailedException extends Exception
    {
    public $httpStatusCode = Response::HTTP_CONFLICT;

    public $message = 'Failed creating new User.';

    public $code = 4711;
    }

    General Exception:

    <?php

    namespace App\Ship\Exceptions;

    use App\Ship\Parents\Exceptions\Exception;
    use Symfony\Component\HttpFoundation\Response as SymfonyResponse;

    class InternalErrorException extends Exception
    {
    public $httpStatusCode = SymfonyResponse::HTTP_INTERNAL_SERVER_ERROR;

    public $message = 'Something went wrong!';
    }

    General Exception with CustomData:

    <?php

    namespace App\Ship\Exceptions;

    use App\Ship\Parents\Exceptions\Exception;
    use Symfony\Component\HttpFoundation\Response as SymfonyResponse;

    class AwesomeExceptionWithCustomData extends Exception
    {
    public $httpStatusCode = SymfonyResponse::HTTP_INTERNAL_SERVER_ERROR;

    public $message = 'Something went wrong!';

    public $code = 1234;

    /*
    * Everything you add here will be automatically added to the ExceptionFormatter on the top level!
    * You can define any structure you want or maybe include translated messages
    */
    public function addCustomData() {
    return [
    'title' => 'nice',
    'description' => 'one fancy description here',
    'foo' => true,
    'meta' => [
    'bar' => 1234,
    ]
    ];
    }
    }

    Exception usage from anywhere:

    <?php

    throw new AccountFailedException();

    Usage with Log for Debugging:

    <?php

    throw (new AccountFailedException())->debug($e); // debug() accepts string or \Exception instance

    Usage and overriding the default message:

    <?php

    throw new AccountFailedException('I am the message to be displayed for the user');

    Usage and overwriting pre-set CustomData

    <?php

    throw (new AwesomeExceptionWithCustomData())->overrideCustomData(['foo' => 'bar']);

    Application Error Codes

    Apiato provides a convenient way to manage all application error codes in one central place. Therefore, Apiato provides, amongst others, the \App\Ship\Exceptions\Codes\ApplicationErrorCodesTable class, which already holds various information for multiple errors.

    Thereby, one error look like this:

    const BASE_GENERAL_ERROR = [
    'code' => 1001,
    'title' => 'Unknown / Unspecified Error.',
    'description' => 'Something unexpected happened.',
    ];

    Note that the code is used to be sent back to the client. The title and description, however, can be used to automatically generate a documentation regarding all defined error codes and their meaning. Please note that this feature is currently not implemented but will be added later on.

    Linking Exceptions and Error Codes

    In order to link an error code to an Exception, you simply need override the useErrorCode() method of the Exception.

    Consider the following example:

    class InternalErrorException extends Exception
    {

    public $httpStatusCode = SymfonyResponse::HTTP_INTERNAL_SERVER_ERROR;

    public $message = 'Something went wrong!';

    public code = 4711; // this code will be overwritten by the useErrorCode() method!

    public function useErrorCode()
    {
    return ApplicationErrorCodes::BASE_INTERNAL_ERROR;
    }
    }

    Please note that already defined $code values may be overwritten by the useErrorCode() method! Furthermore, this feature is completely optional - you may still use the known public $code = 4711; approach to manually set an error code.

    Defining Own Error Code Tables

    Of course, Apiato allows you to define your own CustomErrorCodesTable. In fact, there already exists such a file where you can define your own error codes. Please note that the ApplicationErrorCodesTable may be adapted by Apiato - the others will not.

    If you like to split the errors in various files, you can easily create a UserErrorCodesTable in respective namespace and define the errors accordingly. However, you need to manually "register" this code table. This can be achieved in the ErrorCodeManager::getCodeTables() method.

    Now you can easily use your UserErrorCodesTable::USER_NOT_VERIFIED error in your Exception class.

    - + \ No newline at end of file diff --git a/docs/9.x/optional-components/factories/index.html b/docs/9.x/optional-components/factories/index.html index 141a95c1c..f1bf10723 100644 --- a/docs/9.x/optional-components/factories/index.html +++ b/docs/9.x/optional-components/factories/index.html @@ -4,13 +4,13 @@ Factories | Apiato - +
    Version: 9.x

    Factories

    Definition

    Factories (are a short name for Models Factories).

    Factories are used to generate some fake data with the help of Faker to be used for testing purposes.

    Factories are mainly used from Tests.

    Principles

    • Factories SHOULD be created in the Containers.

    Rules

    • A Factory is just a plain PHP script. (No classes or namespaces required)

    Folder Structure

     - app
    - Containers
    - {container-name}
    - Data
    - Factories
    - UserFactory.php
    - ...

    Code Samples

    A User Model Factory:

    <?php

    // User
    $factory->define(App\Containers\User\Models\User::class, function (Faker\Generator $faker) {
    return [
    'name' => $faker->name,
    'email' => $faker->email,
    'password' => bcrypt(str_random(10)),
    ];
    });

    // ...

    Usage from Tests or anywhere else:

    <?php

    // creating 4 users
    factory(User::class, 4)->create();

    Usage with relationships:

    <?php

    $countries = Country::all();

    // creating 3 rewards and attaching country relation to them
    $rewards = factory(Reward::class, 3)->make()->each(function ($reward) use ($countries) {
    $reward->save();
    $reward->countries()->attach([$countries->random(1)->id, $countries->random(1)->id]);
    $reward->save();
    });

    Use make instance of create and pass any data any way, then save after establishing the relations.

    Usage while overriding some values:

    <?php

    // creating single Offer and setting a user id
    $offer = factory(Offer::class)->make();
    $offer->user_id = $user->id;
    $offer->save();

    // ANOTHER EXAMPLE:

    // creating multiple Accounts
    factory(Account::class, 3)->make()->each(function ($account) use ($user) {
    $account->user_id = $user->id;
    $account->save();
    });

    For more information about the Models Factories read this.

    - + \ No newline at end of file diff --git a/docs/9.x/optional-components/jobs/index.html b/docs/9.x/optional-components/jobs/index.html index 1bf38e571..e598d1a84 100644 --- a/docs/9.x/optional-components/jobs/index.html +++ b/docs/9.x/optional-components/jobs/index.html @@ -4,13 +4,13 @@ Jobs | Apiato - +
    Version: 9.x

    Jobs

    Definition

    Jobs:

    • are simple classes that can do one thing or multiple related things.
    • Job is a name given to a class that is usually created to be queued (it's execution is usually deferred for later, after the execution of previous Jobs are completed).
    • Jobs can be scheduled to be executed later by a queuing mechanism (queue system like beanstalkd).
    • When a Job class is dispatched, it performs its specific job and dies.
    • Laravel's queue worker will process every Job as it's pushed onto the queue.

    More info here.

    Principles

    • A Container MAY have more than one Job.

    Rules

    • All Jobs MUST extend from App\Ship\Parents\Jobs\Job.

    Folder Structure

     - app
    - Containers
    - {container-name}
    - Jobs
    - DoSomethingJob.php
    - DoSomethingElseJob.php

    Code Samples

    CreateAndValidateAddress with third party Job:

    <?php

    namespace App\Containers\Shipment\Jobs;

    use App\Port\Job\Abstracts\Job;

    class CreateAndValidateAddressJob extends Job
    {
    private $recipients;

    public function __construct(array $recipients)
    {
    $this->recipients = $recipients;
    }

    public function handle()
    {
    foreach ($this->recipients as $recipient) {
    // do whatever you like
    }
    }
    }

    Check the parent Job class.

    Usage from Action:

    <?php

    // using helper function
    dispatch(new CreateAndValidateAddressJob($recipients));

    // manually
    App::make(\Illuminate\Contracts\Bus\Dispatcher\Dispatcher::class)->dispatch(New StatusChangedJob($object));

    Execute Jobs Execution

    For running your Jobs checkout the Tasks Queuing page.

    - + \ No newline at end of file diff --git a/docs/9.x/optional-components/languages/index.html b/docs/9.x/optional-components/languages/index.html index 5f9bef4ce..41a3d02da 100644 --- a/docs/9.x/optional-components/languages/index.html +++ b/docs/9.x/optional-components/languages/index.html @@ -4,13 +4,13 @@ Languages | Apiato - +
    Version: 9.x

    Languages

    Definition

    Languages are not real Components, they are just files that holds translations.

    Rules

    • Languages CAN be placed inside the Containers. However, the default laravel resources/lang languages files are still loaded and can be used as well.

    • All Translations are namespaced as the lower case of the Container name.

    Folder Structure

     - app
    - Containers
    - {container-name}
    - Resources
    - Languages
    - en
    - messages.php
    - users.php
    - ar
    - messages.php
    - users.php

    Usage

    Nothing much to show here, here's how you use translated strings:

    <?php

    __('messages.welcome');

    echo __('messages.welcome');

    dd(__('messages.welcome'));

    For more info about the localization checkout the Localization page.

    - + \ No newline at end of file diff --git a/docs/9.x/optional-components/mails/index.html b/docs/9.x/optional-components/mails/index.html index b180136ca..30965f4b6 100644 --- a/docs/9.x/optional-components/mails/index.html +++ b/docs/9.x/optional-components/mails/index.html @@ -4,13 +4,13 @@ Mails | Apiato - +
    Version: 9.x

    Mails

    Definition

    The Mail component allows you to describe an email and send it whenever needed.

    For more details refer to this link.

    Principles

    • Containers MAY or MAY NOT have one or more Mail.

    • Ship may contain general Mails.

    Rules

    • All Notifications MUST extend from App\Ship\Parents\Mails\Mail.
    • Email Templates must be placed inside the Mail directory in a Templates directory app/Containers/{container}/Mails/Templates.

    Folder Structure

     - app
    - Containers
    - {container-name}
    - Mails
    - UserRegisteredMail.php
    - ...
    - Templates
    - user-registered.blade.php
    - ...
    - Ship
    - Mails
    - SomeMail.php
    - ...
    - Templates
    - some-template.blade.php
    - ...

    Code Samples

    Example: a simple Mail

    <?php

    namespace App\Containers\User\Mails;

    use App\Containers\User\Models\User;
    use Illuminate\Bus\Queueable;
    use App\Ship\Parents\Mails\Mail;
    use Illuminate\Contracts\Queue\ShouldQueue;

    class UserRegisteredMail extends Mail implements ShouldQueue
    {
    use Queueable;

    protected $user;

    public function __construct(User $user)
    {
    $this->user = $user;
    }

    public function build()
    {
    return $this->view('user::user-registered')
    ->to($this->user->email, $this->user->name)
    ->with([
    'name' => $this->user->name,
    ]);
    }
    }

    Usage from an Action:

    Notifications can be sent from Actions or Tasks using the Mail Facade.

    Mail::send(new UserRegisteredMail($user));

    Email Templates

    Templates should be placed inside a folder Templates inside the Mail folder.

    To access a Mail template (same like accessing a web view) you must call the container name then the view name.

    In the example below we're using the user-registered.blade.php template in the User Container.

    $this->view('user::user-registered')

    Configure Emails

    Open the .env file and set the From mail and address, this will be used globally whenever the from function is not called in the Mail.

    MAIL_FROM_ADDRESS=test@test.test
    MAIL_FROM_NAME="apiato"

    To use different email address in some classes add ->to($this->email, $this->name) to the build function in your Mail class.

    By default Apiato is configured to use Log Driver MAIL_DRIVER=log, you can change that from the .env file.

    Queueing A Notification

    To queue a notification you should use Illuminate\Bus\Queueable and implement Illuminate\Contracts\Queue\ShouldQueue.

    - + \ No newline at end of file diff --git a/docs/9.x/optional-components/middlewares/index.html b/docs/9.x/optional-components/middlewares/index.html index 05224768d..61db36b7d 100644 --- a/docs/9.x/optional-components/middlewares/index.html +++ b/docs/9.x/optional-components/middlewares/index.html @@ -4,7 +4,7 @@ Middlewares | Apiato - + @@ -12,7 +12,7 @@
    Version: 9.x

    Middlewares

    Definition

    Middleware provide a convenient mechanism for filtering HTTP requests entering your application. More about them here.

    You can enable and disable Middlewares as you wish.

    Principles

    • There are two types of Middlewares, General (applied on all the Routes by default) and Endpoints Middlewares (applied on some Endpoints).

    • The Middlewares CAN be placed in Ship layer or Container layer depending on its roles.

    Rules

    • If a Middleware is written inside a Container it MUST be registered inside that Container.

    • To register Middlewares in a Container the container needs to have a MiddlewareServiceProvider, and like all other Container Providers it MUST be registered in the MainServiceProvider of that Container.

    • General Middlewares (like some default Laravel Middlewares) SHOULD live in the Ship layer app/Ship/Middlewares/* and are registered in the Ship Main Provider.

    • Third Party packages Middleware CAN be registered in Containers or on the Ship layer (wherever they make more sense). Example: the jwt.auth middleware "provided by the JWT package" is registered in the Authentication Container (Containers/Authentication/Providers/MiddlewareServiceProvider.php).

    Folder Structure

     - App
    - Containers
    - {container-name}
    - Middlewares
    - WebAuthentication.php
    - Ship
    - Middleware
    - Http
    - EncryptCookies.php
    - VerifyCsrfToken.php

    Code Sample

    Middleware Example:

    <?php

    namespace App\Containers\Authentication\Middlewares;

    use App\Ship\Engine\Butlers\Facades\ContainersButler;
    use App\Ship\Parents\Middlewares\Middleware;
    use Closure;
    use Illuminate\Contracts\Auth\Guard;
    use Illuminate\Http\Request;

    /**
    * Class WebAuthentication
    *
    * @author Mahmoud Zalt <mahmoud@zalt.me>
    */
    class WebAuthentication extends Middleware
    {

    protected $auth;

    public function __construct(Guard $auth)
    {
    $this->auth = $auth;
    }

    public function handle(Request $request, Closure $next)
    {
    if ($this->auth->guest()) {
    return response()->view(ContainersButler::getLoginWebPageName(), [
    'errorMessage' => 'Credentials Incorrect.'
    ]);
    }

    return $next($request);
    }
    }

    Middleware registration inside the Container Example:

    <?php

    namespace App\Containers\Authentication\Providers;

    use App\Containers\Authentication\Middlewares\WebAuthentication;
    use App\Ship\Parents\Providers\MiddlewareProvider;
    use Tymon\JWTAuth\Middleware\GetUserFromToken;
    use Tymon\JWTAuth\Middleware\RefreshToken;

    class MiddlewareServiceProvider extends MiddlewareProvider
    {

    protected $middleware = [

    ];

    protected $middlewareGroups = [
    'web' => [

    ],
    'api' => [

    ],
    ];

    protected $routeMiddleware = [
    'jwt.auth' => GetUserFromToken::class,
    'jwt.refresh' => RefreshToken::class,
    'auth:web' => WebAuthentication::class,
    ];

    public function boot()
    {
    $this->loadContainersInternalMiddlewares();
    }

    public function register()
    {

    }
    }

    Middleware registration inside the Ship layer (HTTP Kernel) Example:

    <?php

    namespace App\Ship\Kernels;

    use App\Ship\Middlewares\Http\ProcessETagHeadersMiddleware;
    use App\Ship\Middlewares\Http\ProfilerMiddleware;
    use App\Ship\Middlewares\Http\ValidateJsonContent;
    use Illuminate\Foundation\Http\Kernel as LaravelHttpKernel;

    /**
    * Class HttpKernel
    *
    * A.K.A (app/Http/Kernel.php)
    *
    * @author Mahmoud Zalt <mahmoud@zalt.me>
    */
    class HttpKernel extends LaravelHttpKernel
    {

    /**
    * The application's global HTTP middleware stack.
    *
    * These middleware are run during every request to your application.
    *
    * @var array
    */
    protected $middleware = [
    // Laravel middleware's
    \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
    \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
    \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
    \App\Ship\Middlewares\Http\TrimStrings::class,
    \App\Ship\Middlewares\Http\TrustProxies::class,

    // CORS package middleware
    \Barryvdh\Cors\HandleCors::class,
    ];

    /**
    * The application's route middleware groups.
    *
    * @var array
    */
    protected $middlewareGroups = [
    'web' => [
    \App\Ship\Middlewares\Http\EncryptCookies::class,
    \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
    \Illuminate\Session\Middleware\StartSession::class,
    \Illuminate\View\Middleware\ShareErrorsFromSession::class,
    \App\Ship\Middlewares\Http\VerifyCsrfToken::class,
    \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],

    'api' => [
    ValidateJsonContent::class,
    'bindings',
    ProcessETagHeadersMiddleware::class,
    ProfilerMiddleware::class,
    // The throttle Middleware is registered by the RoutesLoaderTrait in the Core
    ],
    ];

    /**
    * The application's route middleware.
    *
    * These middleware may be assigned to groups or used individually.
    *
    * @var array
    */
    protected $routeMiddleware = [
    'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
    'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
    'can' => \Illuminate\Auth\Middleware\Authorize::class,
    'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
    ];

    }
    - + \ No newline at end of file diff --git a/docs/9.x/optional-components/migrations/index.html b/docs/9.x/optional-components/migrations/index.html index ca943938b..f7943aa7e 100644 --- a/docs/9.x/optional-components/migrations/index.html +++ b/docs/9.x/optional-components/migrations/index.html @@ -4,13 +4,13 @@ Migrations | Apiato - +
    Version: 9.x

    Migrations

    Definition

    Migrations (are the short name for Database Migrations).

    Migrations are the version control of your database. They are very useful for generating and documenting the database tables.

    Principles

    • Migrations SHOULD be created inside the Containers folders.

    • Migrations will be autoloaded in the framework

    Rules

    • No need to publish the DB Migrations. Just run the artisan migrate command and Laravel will read the Migrations from the Containers.

    Folder Structure

     - app
    - Containers
    - User
    - Data
    - Migrations
    - 2200_01_01_000001_create_users_table.php
    - ...

    Code Samples

    User CreateUsersTable Migrations:


    <?php

    use Illuminate\Database\Migrations\Migration;
    use Illuminate\Database\Schema\Blueprint;

    class CreateUsersTable extends Migration
    {
    public function up()
    {
    Schema::create('users', function (Blueprint $table) {
    $table->increments('id');
    $table->string('name');
    $table->string('email')->unique();
    $table->string('password');
    $table->rememberToken();
    $table->timestamps();
    $table->softDeletes();
    });
    }

    public function down()
    {
    Schema::drop('users');
    }
    }

    For more information about the Database Migrations read this.

    - + \ No newline at end of file diff --git a/docs/9.x/optional-components/notifications/index.html b/docs/9.x/optional-components/notifications/index.html index 7492cffb4..2cfdd4c28 100644 --- a/docs/9.x/optional-components/notifications/index.html +++ b/docs/9.x/optional-components/notifications/index.html @@ -4,7 +4,7 @@ Notifications | Apiato - + @@ -12,7 +12,7 @@
    Version: 9.x

    Notifications

    Definition

    Notifications allow you to inform the user about a state changes in your application.

    The Laravel notifications supports sending notifications across a variety channels (mail, SMS, Slack, Database...).

    When using the Database channel the notifications will be stored in a database to be displayed in your client interface.

    For more details refer to this link.

    Principles

    • Containers MAY or MAY NOT have one or more Notification.

    • Ship may contain Application general Notifications.

    Rules

    • All Notifications MUST extend from App\Ship\Parents\Notifications\Notification.

    Folder Structure

     - app
    - Containers
    - {container-name}
    - Notifications
    - UserRegisteredNotification.php
    - ...
    - Ship
    - Notifications
    - SystemFailureNotification.php
    - ...

    Code Samples

    Example: a simple Notification

    <?php

    namespace App\Containers\User\Notifications;

    use App\Containers\User\Models\User;
    use App\Ship\Parents\Notifications\Notification;
    use Illuminate\Bus\Queueable;
    use Illuminate\Contracts\Queue\ShouldQueue;

    class BirthdayReminderNotification extends Notification implements ShouldQueue
    {

    use Queueable;

    protected $notificationMessage;

    public function __construct($notificationMessage)
    {
    $this->notificationMessage = $notificationMessage;
    }

    public function toArray($notifiable)
    {
    return [
    'content' => $this->notificationMessage,
    ];
    }

    public function toMail($notifiable)
    {
    // $notifiable is the object you want to notify "e.g. user"
    return (new MailMessage)
    ->subject("Hello World")
    ->line("Hi, $notifiable->name")
    ->line($this->notificationMessage);
    }

    public function toSms($notifiable)
    {
    // ...
    }

    // ...
    }

    Usage from an Action or Task:

    Notifications can be sent from Actions or Tasks using the Notification Facade.

    \Notification::send($user, new BirthdayReminderNotification($notificationMessage));

    Alternatively you can use the Illuminate\Notifications\Notifiable trait on the notifiable object "e.g. User" and then call it as follows:

    // get any user
    $user = User::firstOrCreate([
    'name' => 'Mahmoud Zalt',
    'email' => 'mail@something.com',
    'phone' => '0096123456789',
    ]);

    // call notify, found on the Notifiable trait
    $user->notify(new BirthdayReminderNotification($notificationMessage));

    Select Channels

    To select a notification channel, apiato have the app/Ship/Configs/notification.php config file where you can define the array of supported channels "e.g. SMS, Email, WebPush...", to be used for all your notifications.

    If you want to override the configuration for some notifications classes, or if you prefer to define the channels within each notification class itself, you can override the via function public function via($notifiable) in the notification class and define your channels.

    Checkout laravel notification channels for list of supported integrations.

    Queueing a Notification

    To queue a notification you should use Illuminate\Bus\Queueable and implement Illuminate\Contracts\Queue\ShouldQueue.

    Use DB channel

    Generally you need to generate the notification migration php artisan notifications:table, then run php artisan migrate, however just running the migration command will do the job, since Apiato already adds the _create_notifications_table.php in the default migrations files directory app/Ship/Migrations/.

    - + \ No newline at end of file diff --git a/docs/9.x/optional-components/providers/index.html b/docs/9.x/optional-components/providers/index.html index f6111aeaa..737239589 100644 --- a/docs/9.x/optional-components/providers/index.html +++ b/docs/9.x/optional-components/providers/index.html @@ -4,7 +4,7 @@ Providers | Apiato - + @@ -17,7 +17,7 @@ In apiato those providers have been renamed and moved to the Ship Layer app/Ship/Parents/Providers/*:

    • AppServiceProvider
    • RouteServiceProvider
    • AuthServiceProvider
    • BroadcastServiceProvider
    • EventsServiceProvider

    VIP Note: you should not touch those providers, instead you have to extend them from a containers providers in order to modify them. Example: the app/Containers/Authentication/Providers/AuthProvider.php is extending the AuthServiceProvider to modify it.

    Those providers are not auto registered by default, thus writing any code there will not be available, unless you extend them. Once extended the child Provider should be registered in its Container Main Provider, which makes its parent available.

    This rule does not apply to the RouteServiceProvider since it's required by Apiato, this Provider is registered by the ApiatoProvider.

    Check How Service Providers are auto-loaded.

    - + \ No newline at end of file diff --git a/docs/9.x/optional-components/repositories/index.html b/docs/9.x/optional-components/repositories/index.html index d49955096..ccce48575 100644 --- a/docs/9.x/optional-components/repositories/index.html +++ b/docs/9.x/optional-components/repositories/index.html @@ -4,13 +4,13 @@ Repositories | Apiato - +
    Version: 9.x

    Repositories

    Definition

    The Repository classes are an implementation of the Repository Design Pattern.

    Their major roles are separating the business logic from the data (or the data access Task).

    Repositories save and retrieves Models to/from the underlying storage mechanism.

    The Repository is used to separate the logic that retrieves the data and maps it to a Model, from the business logic that acts on the Model.

    Principles

    • Every Model SHOULD have a Repository.

    • A Model SHOULD always get accessed through its Repository. (Never direct access to Model).

    Rules

    • All Repositories MUST extend from App\Ship\Parents\Repositories\Repository. Extending from this class will give access to functions like (find, create, update and much more).

    • Repository name should be same like it's model name (model: Foo -> repository: FooRepository).

    • If a Repository belongs to a Model whose name is not equal to its Container name, then the Repository must set the $container property like this: $container='ContainerName'. See an example below

    Folder Structure

     - app
    - Containers
    - {container-name}
    - Data
    - Repositories
    - UserRepository.php
    - ...

    Code Samples

    User Repository:

    <?php

    namespace App\Containers\User\Data\Repositories;

    use App\Containers\User\Contracts\UserRepositoryInterface;
    use App\Containers\User\Models\User;
    use App\Ship\Parents\Repositories\Repository;

    class UserRepository extends Repository implements UserRepositoryInterface
    {
    protected $fieldSearchable = [
    'name' => 'like',
    'email' => '=',
    ];
    }

    Usage:

    <?php

    // paginate the data by 10
    $users = $userRepository->paginate(10);

    // search by 1 field
    $cars = $carRepository->findByField('colour', $colour);

    // searching multiple fields
    $offer = $offerRepository->findWhere([
    'offer_id' => $offer_id,
    'user_id' => $user_id,
    ])->first();

    //....

    Note: If the Repository belongs to Model with a name different from its Container name, the Repository class of that Model must set the property $container and define the Container name.

    Example:

    <?php

    namespace App\Containers\Authorization\Data\Repositories;

    use App\Ship\Parents\Repositories\Repository;

    class RoleRepository extends Repository
    {
    protected $container = 'Authorization'; // the container name. Must be set when the model has different name than the container

    protected $fieldSearchable = [

    ];

    }

    Other Properties:

    API Query Parameters Property

    To enable query parameters (?search=text,...) in your API you need to set the property $fieldSearchable on the Repository class, to instruct the querying on your model.

    Example $fieldSearchable of a Repository:

         <?php

    protected $fieldSearchable = [
    'name' => 'like',
    'email' => '=',
    ];

    Continue reading to find more about those properties and what they do.

    All other Properties

    apiato uses the andersao/l5-repository package, to provide a lot of powerful features to the repository class. such as

    <?php

    // ...

    protected $cacheMinutes = 1440; // 1 day

    protected $cacheOnly = ['all'];

    To learn more about all the properties you can use, visit the andersao/l5-repository package documentation.

    - + \ No newline at end of file diff --git a/docs/9.x/optional-components/seeders/index.html b/docs/9.x/optional-components/seeders/index.html index aaa1d718a..ca3f5d230 100644 --- a/docs/9.x/optional-components/seeders/index.html +++ b/docs/9.x/optional-components/seeders/index.html @@ -4,13 +4,13 @@ Seeders | Apiato - +
    Version: 9.x

    Seeders

    Definition

    Seeders (are a short name for Database Seeders).

    Seeders are classes made to seed the database with real data, this data usually should exist in the Application after the installation (Example: the default Users Roles and Permissions or the list of Countries).

    Principles

    • Seeders SHOULD be created in the Containers. (If the container is using a package that publishes a Seeder class, this class should be manually placed in the Container that make use of it. Do not reply on the package to place it on its right location).

    Rules

    • Seeders should be in the right directory inside the container to be loaded.

    • To avoid any conflict between containers seeders classes, you SHOULD always prepend the Seeders of each container with the container name. (Example: UserPermissionsSeeder, ItemPermissionsSeeder). If 2 seeders classes have the same name but live in different containers, one of them will not be loaded.

    • If you wish to order the seeding of the classes, you can just append _1, _2 to your classes.

    Folder Structure

     - App
    - Containers
    - {container-name}
    - Data
    - Seeders
    - ContainerNameRolesSeeder_1.php
    - ContainerNamePermissionsSeeder_2.php
    - ...

    Code Samples

    Roles Seeder:

    <?php

    namespace App\Containers\Order\Data\Seeders;

    use App\Ship\Parents\Seeders\Seeder;
    use Apiato\Core\Foundation\Facades\Apiato;

    class OrderPermissionsSeeder_1 extends Seeder
    {

    public function run()
    {
    Apiato::call('Authorization@CreatePermissionTask', ['approve-reject-orders']);
    Apiato::call('Authorization@CreatePermissionTask', ['find-orders']);
    Apiato::call('Authorization@CreatePermissionTask', ['list-orders']);
    Apiato::call('Authorization@CreatePermissionTask', ['update-orders']);
    Apiato::call('Authorization@CreatePermissionTask', ['delete-orders']);

    // ...

    }
    }

    Note: Same Seeder class is allowed to contain seeding for multiple Models.

    Run the Seeders

    After registering the Seeders you can run this command:

    php artisan db:seed

    To run specific Seeder class you can specify its class in the parameter as follows:

    php artisan db:seed --class="your\single\seeder\goes-here"

    Migrate & seed at the same time

    php artisan migrate --seed

    For more information about the Database Seeders read this.

    Apiato testing seeder command

    It's useful sometimes to create a big set of testing data. apiato facilitates this task:

    1. Open app/Ship/Seeders/SeedTestingData.php and write your testing data here.
    2. Run this command any time you want this data available (example at staging servers):
    php artisan apiato:seed-test
    - + \ No newline at end of file diff --git a/docs/9.x/optional-components/tests/index.html b/docs/9.x/optional-components/tests/index.html index bde67c7fa..1c4f38e4e 100644 --- a/docs/9.x/optional-components/tests/index.html +++ b/docs/9.x/optional-components/tests/index.html @@ -4,13 +4,13 @@ Tests | Apiato - +
    Version: 9.x

    Tests

    Definition

    Tests classes are created to test the Application classes are working as expected.

    The two most essential Tests types for this architecture are the Unit Tests and the Functional Tests. However, Integration and Acceptance Tests can be used as well.

    Principles

    • Containers MAY be covered by all types of Tests.

    • Use Functional Tests to test Container Routes are doing what's expected from them.

    • Use Unit Tests to test Container Actions and Tasks are doing what's expected from them.

    Rules

    • All Container Tests classes SHOULD extend from a Container Internal TestCase class {container-name}/tests/TestCase.php. The container TestCase MUST extend main TestCase on Ship layer App\Ship\Parents\Tests\PhpUnit\TestCase. (Adding functions to the container TestCase allows sharing those functions between all Test classes of the Container).

    Folder Structure

     - app
    - Containers
    - {container-name}
    - Tests
    - TestCase.php // the container test case
    - Unit
    - CreateUserTest.php
    - UpdateUserTest.php
    - ...
    - UI
    - API
    - Tests
    - Functional
    - LoginTest.php
    - LogoutTest.php
    - ...
    - WEB
    - Tests
    - Functional
    - LoginTest.php
    - LogoutTest.php
    - ...
    - CLI
    - Tests
    - Functional
    - BackupDataTest.php
    - ...

    Code Sample

    <?php

    namespace App\Containers\User\UI\API\Tests\Functional;

    use App\Containers\{container-name}\Tests\TestCase;

    class DeleteUserTest extends TestCase
    {
    protected $endpoint = '/users';

    protected $permissions = [
    'delete-users'
    ];

    public function testDeleteExistingUser_()
    {
    // get a testing user of type admin.
    $user = $this->getLoggedInTestingAdmin();

    // send the HTTP request
    $response = $this->apiCall($this->endpoint, 'delete');

    // assert response status is correct
    $this->assertEquals($response->getStatusCode(), '202');

    // ...
    }

    }

    See the Tests Helpers Page

    - + \ No newline at end of file diff --git a/docs/9.x/optional-components/values/index.html b/docs/9.x/optional-components/values/index.html index 74075a023..13105f773 100644 --- a/docs/9.x/optional-components/values/index.html +++ b/docs/9.x/optional-components/values/index.html @@ -4,7 +4,7 @@ Values | Apiato - + @@ -12,7 +12,7 @@
    Version: 9.x

    Values

    Definition & Principles

    Values are short names for the known "Value Objects" which are simple Objects, pretty similar to Models in the concept of representing data, but they do not get stored in the DB, thus they don't have ID's. They also do not hold functionality or change any state, they just hold data.

    A Value Object is an immutable object that is defined by its encapsulated attributes. We create Value Object when we need it to represent/serve/manipulate some data (attached as attributes), and we'll kill it later when we finish using it, to recreate it again when needed.

    Rules

    • All Models MUST extend from App\Ship\Parents\Values\Value.

    Folder Structure

     - App
    - Containers
    - {container-name}
    - Values
    - Output.php
    - Region.php
    - ...

    Code Sample

    <?php

    use App\Ship\Parents\Values\Value;

    class Location extends Value
    {
    private $x = null;

    private $y = null;

    protected $resourceKey = 'locales';

    public function __construct($x, $y)
    {
    $this->x = $x;
    $this->y = $y;
    }

    public function getCoordinatesAsString()
    {
    return $this->x . ' - ' . $this->y;
    }
    }
    - + \ No newline at end of file diff --git a/docs/9.x/upgrade-guide/index.html b/docs/9.x/upgrade-guide/index.html index 71aa71594..cbd71c462 100644 --- a/docs/9.x/upgrade-guide/index.html +++ b/docs/9.x/upgrade-guide/index.html @@ -4,7 +4,7 @@ Upgrade Guide | Apiato - + @@ -22,7 +22,7 @@ and the term New Project (referring to the new freshly installed Apiato 5.0).

    1) Download and install Apiato 5.0. See Application Setup.

    2) Delete the Containers directory app/Containers from the new project.

    3) Move the Containers directory app/Containers from the old project to the new project.

    4) Open this file app/Ship/composer.json in your old project and only copy the required dependencies, from the old project to the same file in the new project.

    5) Again, open the app/Ship/composer.json file in the new project, and remove the following dependencies: guzzlehttp/guzzle, prettus/l5-repository, barryvdh/laravel-cors, spatie/laravel-fractal, vinkla/hashids and johannesschobel/apiato-container-installer.

    6) Move and replace the following directories from the old project to the new project: config, public, resources, database and storage.

    7) Open config/app.php and replace App\Ship\Engine\Providers\PortoServiceProvider::class with Apiato\Core\Providers\ApiatoProvider::class.

    8) Move .gitignore, phpunit.xml and .env files, from the old project to the new project.

    9) Open the .env file on the new project and append this to it API_RATE_LIMIT_ENABLED=true.

    10) Open phpunit.xml file of the new project and delete this line from the file <file>./app/Ship/Engine/Loaders/FactoryMixer/FactoriesLoader.php</file>.

    11) If you had live testing data in your old project inside app/Ship/Seeders/Data/Testing/Seeders/TestingDataSeeder.php file, then copy that file content and past it in the new project inside app/Ship/Seeders/SeedTestingData.php. You will need to rename the class (not the file) from TestingDataSeeder to SeedTestingData, and you will need to update the namespace from namespace App\Ship\Seeders\Data\Testing\Seeders; to namespace App\Ship\Seeders;.

    12) If you ever used the HashIdTrait, you need to search and replace this namespace App\Ship\Engine\Traits\HashIdTrait with this Apiato\Core\Traits\HashIdTrait.

    13) Run composer update. If you got any error at this step, try to solve it or open an Issue.

    14) Move the .git directory from the old project to the new one. Add all changes git add . then commit git commit -m 'upgrade Apiato from 4.1 to 5.0'.

    15) Run your tests vendor/bin/phpunit.

    That's it :)

    How to manually upgrade older versions to 4.1?

    Use the Manual Upgrading Guide below.

    Manual Upgrading Guide

    These commands and examples, are compatible with the Apiato 8.0 upgrade. You can just copy/past.

    1) Checkout a new branch from your stable branch, to perform the upgrade.

    git checkout -b upgrade-apiato

    2) Configure a new remote (upstream) that points to the official Apiato repository.

    git remote add upstream https://github.com/apiato/apiato

    Verify the new upstream repository was added, by listing the current configured remote repositories.

    git remote -vv

    origin git@bitbucket.org:username/my-awesome-api.git (fetch)
    origin git@bitbucket.org:username/my-awesome-api.git (push)
    upstream git@github.com:apiato/apiato.git (fetch)
    upstream git@github.com:apiato/apiato.git (push)

    3) Checkout a new branch to hold the latest Apiato changes. This branch will be merged into your upgrade-apiato branch created above.

    git checkout -b apiato-{version}
    // Example: git checkout -b apiato-8.0

    4) Configure this branch to track an upstream specific branch.

    Replace {upstream-branch-name} with the branch name you want to upgrade to (for example 8.0).

    git fetch upstream {upstream-branch-name}
    // Example: git fetch upstream 8.0

    git branch --set-upstream-to upstream/{upstream-branch-name}
    // Example: git branch --set-upstream-to upstream/8.0

    Verify your local branch is tracking the Apiato specified upstream branch.

    git branch -vv

    apiato 77b4d945 [upstream/{upstream-branch-name}] ...
    master 77d302aa [origin/master] ...

    5) Make this branch identical to the remote upstream branch

    git reset --hard upstream/{upstream-branch-name}
    // Example: git reset --hard upstream/8.0

    Verify this branch now contains the latest changes from the upstream branch.

    git log

    6) Switch back to the upgrade-apiato branch

    git checkout upgrade-apiato

    7) Now lets merge the 2 branches. This step can be done in two ways:

    Option A: Merge all the changes together and solve the conflicts if any. (Recommended)

    You can execute the next command with different different parameters, below are 2 options to pick whatever feels safer to you. Do not execute both of them.

    A1: This will overwrite your changes with the upstream changes. (Try this first and if your tests failed then you can try the second one).

    git merge --allow-unrelated-histories --strategy-option=theirs apiato-{version}
    // Example: git merge --allow-unrelated-histories --strategy-option=theirs apiato-8.0

    A2: This will let you solve all the conflicts manually. (Can be the most secure choice, but it's time consuming as well.)

    git merge --allow-unrelated-histories apiato-{version}
    // Example: git merge --allow-unrelated-histories apiato-8.0

    Option B: Manually cherry pick the commits you likes to have:

    git log {upstream-branch-name}

    (copy each commit ID, one by one)

    git cherry-pick {commit-ID}

    (if you get any conflict solve it and keep moving)

    8) Compare your .env with the new .env-example and update it.

    9) Check everything is working. By running Composer install first then re-running your tests.

    • Read the changelog releases page.
    • You may want to update your custom containers dependencies, simply follow the composer install error outputs and bump each failing dependency. (Hint: visit each package releases page, and use the version which supports the supported version of Laravel).
    • You may need to fix the failing tests.
    composer install  &&  vendor/bin/phpunit

    10) Finally, merge the upgrade-apiato (which contains the upgraded changes) with your stable branch (could be master).

    git checkout master
    git merge upgrade-apiato

    php artisan -V

    Enjoy :)

    - + \ No newline at end of file diff --git a/docs/architecture-concepts/components/index.html b/docs/architecture-concepts/components/index.html index aa5bf7976..3844b2532 100644 --- a/docs/architecture-concepts/components/index.html +++ b/docs/architecture-concepts/components/index.html @@ -4,7 +4,7 @@ Components | Apiato - + @@ -19,7 +19,7 @@ simplifying future maintenance and facilitating modifications when needed.

    Components in Porto are categorized into two types: Main Components and Optional Components.

    Main Components

    Main Components are essential for the Container's functionality and must be used to achieve its core purpose.

    Optional Components

    Optional Components offer additional functionality that can be incorporated into the Container. Their usage is discretionary, depending on specific requirements.

    tip

    To learn more about how all this fits together, read the Request Lifecycle page.

    - + \ No newline at end of file diff --git a/docs/architecture-concepts/container/index.html b/docs/architecture-concepts/container/index.html index 41666bff3..2ffc3e2f8 100644 --- a/docs/architecture-concepts/container/index.html +++ b/docs/architecture-concepts/container/index.html @@ -4,7 +4,7 @@ Container | Apiato - + @@ -23,7 +23,7 @@ you may use the apiato:generate:container interactive command:

    php artisan apiato:generate:container

    Composer Dependencies

    To manage Composer dependencies, follow these guidelines:

    • All the Composer dependencies for a specific Container should be defined within that Container's composer.json file.
    • Dependencies related to the Ship layer should be placed in the root of the Ship layer, in a composer.json file.
    • Framework core dependencies should be defined in the project's root-level composer.json file.

    In practice, you can choose to place Composer dependencies in any of these composer.json files, and they will perform the same function. The choice of location depends on what is most relevant and convenient for your project.

    Readme

    Each Container has the option to include a readme.md file at its root, which serves to explain the Container's purpose and how to use it.

    To generate new readme files, you may use the apiato:generate:readme interactive command:

    php artisan apiato:generate:readme
    - + \ No newline at end of file diff --git a/docs/architecture-concepts/index.html b/docs/architecture-concepts/index.html index caa12df81..9bc3a9ef0 100644 --- a/docs/architecture-concepts/index.html +++ b/docs/architecture-concepts/index.html @@ -4,7 +4,7 @@ Architecture Concepts | Apiato - + @@ -13,7 +13,7 @@ to structure the application code.

    Investing 30 minutes in reading the Porto Documentation before getting started is highly recommended and can prove to be a valuable use of your time. The document serves as a comprehensive guide and resource for understanding the Apiato project.

    - + \ No newline at end of file diff --git a/docs/architecture-concepts/porto/index.html b/docs/architecture-concepts/porto/index.html index b20c43eb7..9d5cf1ee7 100644 --- a/docs/architecture-concepts/porto/index.html +++ b/docs/architecture-concepts/porto/index.html @@ -4,7 +4,7 @@ Porto | Apiato - + @@ -18,7 +18,7 @@ It can be a specific feature or a wrapper around a RESTful API resource.

    note

    A Container is allowed to depend on other Containers in the same Section.

    Ship

    The Ship layer contains the infrastructure code, which consists of shared code utilized by all Containers.

    Typical Project Structure

    app
    ├── Containers
    │ ├── Section
    │ │ └── Container
    │ │ ├── Actions
    │ │ ├── Configs
    │ │ ├── Data
    │ │ │ ├── Factories
    │ │ │ ├── Migrations
    │ │ │ ├── Repositories
    │ │ │ └── Seeders
    │ │ ├── Mails
    │ │ │ └── Templates
    │ │ ├── Middlewares
    │ │ ├── Models
    │ │ ├── Notifications
    │ │ ├── Providers
    │ │ ├── Tasks
    │ │ ├── Tests
    │ │ ├── Traits
    │ │ └── UI
    │ │ ├── API
    │ │ │ ├── Controllers
    │ │ │ ├── Requests
    │ │ │ ├── Routes
    │ │ │ └── Transformers
    │ │ ├── WEB
    │ │ │ ├── Controllers
    │ │ │ ├── Requests
    │ │ │ ├── Routes
    │ │ │ └── Views
    │ │ └── CLI
    │ │ └── Commands
    │ └── Vendor `// All installed and reusable Containers`
    │ ├── ContainerA
    │ └── ContainerB
    └── Ship `// All shared code between all Containers`
    ├── Broadcasts
    ├── Commands
    ├── Configs
    ├── Contracts
    ├── Criterias
    ├── Events
    ├── Exceptions
    ├── Generators
    ├── Helpers
    ├── Kernels
    ├── Listeners
    ├── Mails
    ├── Middlewares
    ├── Migrations
    ├── Notifications
    ├── Parents
    ├── Providers
    ├── Seeders
    └── Tests

    Default Sections

    Apiato ships with two default Sections:

    • AppSection: contains all the default Containers.
    • Vendor: contains all the installed and reusable Containers.
    tip

    The Vendor section is a special Section within the Containers layer that holds installed and reusable Containers. It serves a similar purpose as the vendor folder located at the root. Any Section is permitted to depend on the Vendor Section, allowing for the utilization of its Containers.

    Read more about the Container Installer to learn how to install Vendor Containers.

    - + \ No newline at end of file diff --git a/docs/architecture-concepts/request-lifecycle/index.html b/docs/architecture-concepts/request-lifecycle/index.html index 66d62cb45..0eb848d22 100644 --- a/docs/architecture-concepts/request-lifecycle/index.html +++ b/docs/architecture-concepts/request-lifecycle/index.html @@ -4,7 +4,7 @@ Request Lifecycle | Apiato - + @@ -16,7 +16,7 @@ The Tasks can be used to execute reusable subsets of the business logic, with each Task responsible for a single portion of the main Action. The View or Transformer is used to build the response that is sent back to the User.

    Request Lifecycle Diagram

    Legend:

    • Solid Line: Mandatory dependency (always used)
    • Doted Line: Optional dependency (not always used)
    • Red Solid Border: Response generation
    • Green Dashed Border: Optional component (not always used)
    - + \ No newline at end of file diff --git a/docs/components/index.html b/docs/components/index.html index 527ec6bbe..f485c245b 100644 --- a/docs/components/index.html +++ b/docs/components/index.html @@ -4,7 +4,7 @@ Components | Apiato - + @@ -13,7 +13,7 @@ "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.

    info

    To learn more about Apiato components, check the Components section.

    - + \ No newline at end of file diff --git a/docs/components/main-components/actions/index.html b/docs/components/main-components/actions/index.html index a53339ea4..d3bba8804 100644 --- a/docs/components/main-components/actions/index.html +++ b/docs/components/main-components/actions/index.html @@ -4,7 +4,7 @@ Actions | Apiato - + @@ -20,7 +20,7 @@ However, it's important to note that not all operations may be automatically rolled back. For example, file system operations, such as uploading an image, are typically not covered by the database transaction and would need to be handled manually.

    - + \ No newline at end of file diff --git a/docs/components/main-components/controllers/index.html b/docs/components/main-components/controllers/index.html index fed935917..663730709 100644 --- a/docs/components/main-components/controllers/index.html +++ b/docs/components/main-components/controllers/index.html @@ -4,7 +4,7 @@ Controllers | Apiato - + @@ -16,7 +16,7 @@ and it MUST be used in conjunction with the transform method. This is different from the meta parameter in the transform method. This metadata will be returned directly under the meta key.

    You can use this method in conjunction with the meta parameter in the transform method.

    $metaData = ['foo' => 999, 'bar'];

    $this->withMeta($metaData)->transform($sample, SampleTransformer::class, meta: ['foo' => 'bar', 'baz' => 1]);

    // Response
    {
    "data": {},
    "meta": {
    "foo": 999,
    "0": "bar",
    "include": [...],
    "custom": {
    "foo": "bar",
    "baz": 1
    },
    "pagination": {}
    }
    }

    json

    This method allows you to pass an array of data that will be represented as JSON.

    $this->json($data)

    created

    This method allows you to return a response with a 201 status code.

    $this->created($data)

    deleted

    This method allows you to return a response with a 202 status code.

    $this->deleted($deletedModel)

    // Response
    {
    "message": "Model (1) Deleted Successfully."
    }

    accepted

    This method allows you to return a response with a 202 status code.

    $this->accepted($data)

    noContent

    This method allows you to return a response with a 204 status code.

    $this->noContent()
    - + \ No newline at end of file diff --git a/docs/components/main-components/exceptions/index.html b/docs/components/main-components/exceptions/index.html index b9b058397..a5cc29647 100644 --- a/docs/components/main-components/exceptions/index.html +++ b/docs/components/main-components/exceptions/index.html @@ -4,7 +4,7 @@ Exceptions | Apiato - + @@ -13,7 +13,7 @@ Translation strings are automatically translated if the translations are found. To handle localization, you can use the Localization Container.

    // Example 1
    throw (new AccountFailedException())->withErrors(['email' => 'appSection@user::exceptions.email-taken']);
    // Example 2
    throw (new AccountFailedException())->withErrors(['email' => 'appSection@user::exceptions.email-taken', 'Another not translated message']);

    Response:

    {
    "message": "The exception error message.",
    "errors": {
    "email": [
    "The email has already been taken.",
    "Another not translated message"
    ]
    }
    }

    debug

    The debug method is used for logging error messages during debugging and development. The debug method accepts string or \Exception instance

    throw (new AccountFailedException())->debug($e);
    - + \ No newline at end of file diff --git a/docs/components/main-components/index.html b/docs/components/main-components/index.html index d3c8ea008..6aafe6103 100644 --- a/docs/components/main-components/index.html +++ b/docs/components/main-components/index.html @@ -4,13 +4,13 @@ Main Components | Apiato - + - + \ No newline at end of file diff --git a/docs/components/main-components/models/index.html b/docs/components/main-components/models/index.html index 07ae1eb3c..44f551a75 100644 --- a/docs/components/main-components/models/index.html +++ b/docs/components/main-components/models/index.html @@ -4,7 +4,7 @@ Models | Apiato - + @@ -16,7 +16,7 @@ it is essential to incorporate the ModelTrait trait into your model. By doing so, your model will benefit from various functionalities provided by the trait, such as hash ids and other features necessary for proper integration with the framework.

    use Apiato\Core\Traits\ModelTrait;

    class Demo
    {
    use ModelTrait;
    ...
    }
    - + \ No newline at end of file diff --git a/docs/components/main-components/requests/index.html b/docs/components/main-components/requests/index.html index dab099320..e49b343d3 100644 --- a/docs/components/main-components/requests/index.html +++ b/docs/components/main-components/requests/index.html @@ -4,7 +4,7 @@ Requests | Apiato - + @@ -18,7 +18,7 @@ the validation rules defined in RegisterUserRequest will be automatically applied before the __invoke method is executed. If the validation fails, an appropriate error response will be generated.

    Request Properties

    Apiato introduces new properties to the Request Class that enhance its functionality.

    access

    The $access property allows you to define Roles and Permissions that can access a specific endpoint. -It's used by the hasAccess method to check if a user has the required Roles and Permissions to use that endpoint.

    class DemoRequest extends ParentRequest
    {
    protected array $access = [
    'permissions' => 'delete-users',
    'roles' => 'manager'
    ];

    public function authorize(): bool
    {
    return $this->check([
    'hasAccess',
    ]);
    }
    }

    You can also use the array notation or pipe to define multiple Roles and Permissions.

    class DemoRequest extends ParentRequest
    {
    protected $access = [
    'permissions' => ['delete-users', 'another-permissions'],
    'roles' => 'manager|admin',
    ];

    // ...
    }
    tip

    If there's no need to set any roles or permissions, +It's used by the hasAccess method to check if a user has the required Roles and Permissions to use that endpoint.

    class DemoRequest extends ParentRequest
    {
    protected array $access = [
    'permissions' => 'delete-users',
    'roles' => 'manager'
    ];

    public function authorize(): bool
    {
    return $this->hasAccess();
    }
    }

    You can also use the array notation or pipe to define multiple Roles and Permissions.

    class DemoRequest extends ParentRequest
    {
    protected $access = [
    'permissions' => ['delete-users', 'another-permissions'],
    'roles' => 'manager|admin',
    ];

    // ...
    }
    tip

    If there's no need to set any roles or permissions, you can simply set the $permissions or $roles property to an empty string '', an empty array [], or null.

    decode

    The $decode property is used to handle the decoding of Hashed IDs from the incoming Request.

    When you enable the Hash ID feature, your application can receive Hashed IDs from users. These Hashed IDs need to be decoded before they can be effectively validated. Apiato facilitates this process @@ -27,11 +27,7 @@ will not function correctly unless you decode the ID before passing it to the validation process.

    urlParameters

    The $urlParameters property simplifies the process of applying validation rules to URL parameters.

    By default, Laravel doesn't provide validation for URL parameters (/stores/999/items). However, by using the $urlParameters property, you can enable validation for these parameters. By specifying the desired URL parameters within this property, -you not only enable validation but also gain direct access to these parameters from the Request object.

    // URL: /stores/{id}/items
    // GET /stores/999/items
    class DemoRequest extends ParentRequest
    {
    protected array $urlParameters = [
    'id',
    ];

    public function rules(): array
    {
    return [
    'id' => 'integer', // url parameter
    ];
    }
    }

    Helper Methods

    check

    The check method is used to authorize the user to access the endpoint. -It accepts an array of methods names that will be called to check if the user has access or not. -Each of those methods must return a boolean. -Take a look at the following example:

    use App\Ship\Parents\Requests\Request as ParentRequest;

    class DemoRequest extends ParentRequest
    {
    use IsAuthorTrait;

    // ...

    public function authorize(): bool
    {
    return $this->check([
    'hasAccess|isOwner',
    'isKing',
    ]);
    }
    }

    Here we are passing the the hasAccess, isOwner and isKing methods to the check method. -Then the check method follows the following rules and checks if the user has access or not:

    • The separator | between the methods indicates an OR operation.
    • The default operation between all methods in the array is AND.

    So in the above example, the call to the check method will be translated to:

    return ($this->hasAccess() || $this->isOwner()) && $this->isKing();

    And if the result of this operation is true then the user will be authorized to access the endpoint.

    note

    hasAccess

    The hasAccess method assesses a user's access rights based on the Request's $access property. +you not only enable validation but also gain direct access to these parameters from the Request object.

    // URL: /stores/{id}/items
    // GET /stores/999/items
    class DemoRequest extends ParentRequest
    {
    protected array $urlParameters = [
    'id',
    ];

    public function rules(): array
    {
    return [
    'id' => 'integer', // url parameter
    ];
    }
    }

    Helper Methods

    hasAccess

    The hasAccess method assesses a user's access rights based on the Request's $access property. If the user has any of the specified Roles or Permissions, the method will return true otherwise it will return false.

    sanitizeInput

    The sanitizeInput method is employed to cleanse request data before its utilization within the application.

    Particularly useful for PATCH requests, where you may want @@ -53,12 +49,7 @@ when you wish to provide data directly to the request instead of sending it through the request body.

    $request = RegisterUserRequest::injectData($data);

    withUrlParameters

    The withUrlParameters method enables you to inject URL parameters into the request. This is especially useful when you need to include properties in the request that are not part of the request body but are required for the request to be processed. -This method is often used in conjunction with the injectData method.

    $request = RegisterUserRequest::injectData($data)
    ->withUrlParameters(['id' => 123]);

    Custom Authorize Methods

    The recommended approach for adding custom authorization functions is by using a Trait, -which can be included in your Request classes.

    For instance, -let's -create an IsAuthorTrait Trait with a single method -named isAuthor to determine if the current user holds the role of an author.

    trait IsAuthorTrait
    {
    public function isAuthor(): bool
    {
    return $this->user()->hasRole('author');
    }
    }

    Subsequently, you can apply the IsAuthorTrait Trait to a Request class, -allowing the utilization of the isAuthor function within the authorization process.

    use App\Ship\Parents\Requests\Request as ParentRequest;

    class DemoRequest extends ParentRequest
    {
    use IsAuthorTrait;

    // ...

    public function authorize(): bool
    {
    return $this->check([
    'isAuthor',
    ]);
    }
    }

    Bypass Authorization

    To grant certain Roles access to all endpoints within the system without the need +This method is often used in conjunction with the injectData method.

    $request = RegisterUserRequest::injectData($data)
    ->withUrlParameters(['id' => 123]);

    Bypassing Authorization

    To grant certain Roles access to all endpoints within the system without the need to define the role in each Request object, you can follow this approach. This is particularly beneficial when you want to provide unrestricted access to users with the admin role. @@ -72,8 +63,8 @@ (Wikipedia)

    Apiato offers support for Etag through the Apiato\Core\Middlewares\HttpProcessETagHeadersMiddleware middleware, which employs the Shallow technique. This middleware can be particularly valuable in reducing bandwidth usage for clients, especially on mobile devices.

    Please note that this feature is disabled by default. To enable it, follow these steps:

    1. Navigate to the app/Ship/Configs/apiato.php configuration file.
    2. Inside the configuration file, locate the use-etag configuration parameter.
    3. Set the use-etag parameter to true.

    Keep in mind that for this feature to function correctly, the client must include the If-None-Match HTTP header, -which corresponds to the ETag value, in their request.

    - +which corresponds to the ETag value, in their request.

    + \ No newline at end of file diff --git a/docs/components/main-components/routes/index.html b/docs/components/main-components/routes/index.html index 95f6ea2c9..c9b1025f6 100644 --- a/docs/components/main-components/routes/index.html +++ b/docs/components/main-components/routes/index.html @@ -4,7 +4,7 @@ Routes | Apiato - + @@ -18,7 +18,7 @@ Maintaining this distinction enables the generation of separate documentations for each type, ensuring that your internal API remains private and secure. This feature can be configured through the Documentation Generator package.

    Public Routes:

    • Accessible to third parties.
    • May or may not require authentication.

    Private Routes:

    • Accessible only to your own apps.
    • May or may not require authentication.
    - + \ No newline at end of file diff --git a/docs/components/main-components/subactions/index.html b/docs/components/main-components/subactions/index.html index fccbe22d1..53528fd58 100644 --- a/docs/components/main-components/subactions/index.html +++ b/docs/components/main-components/subactions/index.html @@ -4,7 +4,7 @@ Sub Actions | Apiato - + @@ -13,7 +13,7 @@ They enable Actions to share a sequence of Tasks, thus promoting reusability. Similar to how Tasks enable Actions to share specific functionalities, SubActions serve to share a predefined set of Tasks.

    All the features and capabilities available for regular Actions are also applicable to SubActions.

    To generate new SubActions you may use the apiato:generate:subaction interactive command:

    php artisan apiato:generate:subaction

    Definition & Principles

    Read Porto SAP Documentation (#Sub-Actions).

    Rules

    • All SubActions:
      • MUST be placed in the app/Containers/{Section}/{Container}/Actions directory.
      • MUST extend the App\Ship\Parents\Actions\SubAction class.
        • The parent extension SHOULD be aliased as ParentSubAction.

    Folder Structure

    app
    └── Containers
    └── Section
    └── Container
    └── Actions
    ├── ValidateAddressSubAction.php
    ├── BuildOrderSubAction.php
    └── ...

    Code Example

    use ...
    use App\Ship\Parents\Actions\SubAction as ParentSubAction;

    class DemoSubAction extends ParentSubAction
    {
    public function __construct(
    private readonly DemoTask $demoTask
    ) {
    }

    public function run(array $data)
    {
    return $this->demoTask->run($data);
    }
    }
    - + \ No newline at end of file diff --git a/docs/components/main-components/tasks/index.html b/docs/components/main-components/tasks/index.html index 0d6be6857..289eb3b69 100644 --- a/docs/components/main-components/tasks/index.html +++ b/docs/components/main-components/tasks/index.html @@ -4,7 +4,7 @@ Tasks | Apiato - + @@ -15,7 +15,7 @@ in encapsulating functionalities that are utilized by multiple Actions spanning various Containers within your application.

    To generate new tasks you may use the apiato:generate:task interactive command:

    php artisan apiato:generate:task

    Additionally, to retrieve a list of the existing tasks in your Apiato application, use the apiato:list:tasks command.

    php artisan apiato:list:tasks

    Definition & Principles

    Read Porto SAP Documentation (#Tasks).

    Rules

    • All Tasks:
      • MUST be placed in the app/Containers/{Section}/{Container}/Tasks directory.
      • MUST extend the App\Ship\Parents\Tasks\Task class.
        • The parent extension SHOULD be aliased as ParentTask.

    Folder Structure

    app
    └── Containers
    └── Section
    └── Container
    └── Tasks
    ├── CreateResourceTask.php
    ├── DeleteResourceTask.php
    └── ...

    Code Example

    use ...
    use App\Ship\Parents\Tasks\Task as ParentTask;

    class DemoTask extends ParentTask
    {
    public function run(int $a, int $b): int
    {
    return $a + $b;
    }
    }
    - + \ No newline at end of file diff --git a/docs/components/main-components/transformers/index.html b/docs/components/main-components/transformers/index.html index 8609393ef..d0d821a28 100644 --- a/docs/components/main-components/transformers/index.html +++ b/docs/components/main-components/transformers/index.html @@ -4,7 +4,7 @@ Transformers | Apiato - + @@ -54,7 +54,7 @@ This serializer is not to everyone’s tastes, because it adds a data namespace to the output. A very basic response of the DataArraySerializer will look like this:

    {
    "data": {
    "object": "User",
    "id": "XbPW7awNkzl83LD6",
    "name": "Mohammad Alavi"
    }
    }

    The DataArraySerializer is handy because it allows space for meta data (like pagination, or totals) in both Items and Collections.

    {
    "data": [ ... ],
    "meta": {
    "include": [
    "xxx",
    "yyy"
    ],
    "custom": [],
    "pagination": {
    "total": 999,
    "count": 999,
    "per_page": 999,
    "current_page": 999,
    "total_pages": 999,
    "links": {
    "next": "http://api.apiato.test/v1/accounts?page=999"
    }
    }
    }
    }
    Further Reading

    For more detailed information, please refer to Fractal and Laravel Fractal Wrapper documentations.

    - + \ No newline at end of file diff --git a/docs/components/main-components/views/index.html b/docs/components/main-components/views/index.html index b13da1081..542aff45f 100644 --- a/docs/components/main-components/views/index.html +++ b/docs/components/main-components/views/index.html @@ -4,7 +4,7 @@ Views | Apiato - + @@ -17,7 +17,7 @@ such as view('welcome-page'), will result in the view not being found.

    An exception to this namespace convention is for view files located in the app/Ship/Views and app/Ship/Mails/Templates directories. These views will be namespaced using the word ship instead of the Section and Container names.

    For example, you would access such a view like this: view(ship::welcome-page).

    - + \ No newline at end of file diff --git a/docs/components/optional-components/commands/index.html b/docs/components/optional-components/commands/index.html index e8aa34db4..ef243e541 100644 --- a/docs/components/optional-components/commands/index.html +++ b/docs/components/optional-components/commands/index.html @@ -4,7 +4,7 @@ Commands | Apiato - + @@ -12,7 +12,7 @@
    Version: 12.x

    Commands

    Apiato commands are just Laravel Commands, and they function in the exact same way as Laravel commands. However, they come with additional rules and conventions specific to Apiato.

    Rules

    • All container-specific Commands MUST be placed in the app/Containers/{Section}/{Container}/UI/CLI/Commands directory.
    • All general Commands MUST be placed in the app/Ship/Commands directory.
    • All Commands:
      • MUST extend the App\Ship\Parents\Commands\ConsoleCommand class.
        • The parent extension SHOULD be aliased as ConsoleCommand.
      • SHOULD call an Action to perform its job, and SHOULD NOT contain any business logic.

    Folder Structure

    app
    ├── Containers
    │ └── Section
    │ └── Container
    │ └── UI
    │ └── CLI
    │ └── Commands
    │ ├── FirstCommand.php
    │ ├── SecondCommand.php
    │ └── ...
    └── Ship
    └── Commands
    ├── FirstCommand.php
    ├── SecondCommand.php
    └── ...

    Code Example

    Commands are defined exactly as you would define them in Laravel.

    Closure Commands

    You can define your console closure commands in app/Ship/Commands/closures.php.

    - + \ No newline at end of file diff --git a/docs/components/optional-components/configs/index.html b/docs/components/optional-components/configs/index.html index a07ec5818..51f84103b 100644 --- a/docs/components/optional-components/configs/index.html +++ b/docs/components/optional-components/configs/index.html @@ -4,7 +4,7 @@ Configs | Apiato - + @@ -17,7 +17,7 @@ camelCase representation of the container's Section name, succeeded by -, and then the camelCase representation of the Container name.

    For instance, if you have a container named "MyContainer" within the "MySection" section, the configuration file would be named mySection-myContainer.php.

    - + \ No newline at end of file diff --git a/docs/components/optional-components/events/index.html b/docs/components/optional-components/events/index.html index e3f3204e3..69bdabbad 100644 --- a/docs/components/optional-components/events/index.html +++ b/docs/components/optional-components/events/index.html @@ -4,7 +4,7 @@ Events | Apiato - + @@ -17,7 +17,7 @@ you may use the apiato:generate:provider interactive command:

    php artisan apiato:generate:provider

    Remember to also register the EventServiceProvider in the container's MainServiceProvider:

    use ...
    use App\Ship\Parents\Providers\MainServiceProvider as ParentMainServiceProvider;

    class MainServiceProvider extends ParentMainServiceProvider
    {
    protected array $serviceProviders = [
    // ... Other service providers
    EventServiceProvider::class,
    ];
    }

    In The Ship

    Registering events and listeners in the Ship can be done by adding them to the listen array in the App\Ship\Providers\EventServiceProvider class.

    Events & Listeners Registration Flow

    If you are manually registering events and listeners and wish to understand the registration process, here is a breakdown of the registration flow.

    Consider the following folder structure:

    app
    ├── Containers
    │ └── Section
    │ └── Container
    │ ├── Events
    │ │ ├── DemoEvent.php ────►─┐
    │ │ └── ...
    │ ├── Listeners │
    │ │ ├── DemoListener.php ─►─┤
    │ │ └── ...
    │ └── Providers ▼
    │ ├── EventServiceProvider.php ─────────►─────────┐
    │ ├── MainServiceProvider.php ◄───registered─in─◄─┘
    │ └── ...
    └── Ship
    ├── Events
    │ ├── ShipDemoEvent.php ──►─┐
    │ └── ...
    ├── Listeners │
    │ ├── ShipDemoListener.php ►┤
    │ └── ...
    └── Providers ▼
    ├── EventServiceProvider.php ─────────►─────────┐
    ├── ShipProvider.php ◄───registered─in─◄─┘
    └── ...

    The following diagram illustrates the registration flow of events and listeners in the above folder structure:

    - + \ No newline at end of file diff --git a/docs/components/optional-components/factories/index.html b/docs/components/optional-components/factories/index.html index 7e8b10fc2..b9cce19a8 100644 --- a/docs/components/optional-components/factories/index.html +++ b/docs/components/optional-components/factories/index.html @@ -4,7 +4,7 @@ Factories | Apiato - + @@ -22,7 +22,7 @@ if your model does not extend the App\Ship\Parents\Models\Model or the App\Ship\Parents\Models\UserModel class, it is essential to include the ModelTrait trait in your model. By doing so, Apiato will be able to locate the appropriate factory and use it for the model when needed.

    use Apiato\Core\Traits\ModelTrait;

    class Demo
    {
    use ModelTrait;
    ...
    }
    - + \ No newline at end of file diff --git a/docs/components/optional-components/helpers/index.html b/docs/components/optional-components/helpers/index.html index 0c9eeae83..98cd28013 100644 --- a/docs/components/optional-components/helpers/index.html +++ b/docs/components/optional-components/helpers/index.html @@ -4,13 +4,13 @@ Helpers | Apiato - +
    Version: 12.x

    Helpers

    You have the option to create your own global "helper" PHP functions in designated directories, and Apiato will automatically autoload them for you.

    Deprecation Notice

    Please be aware that Container Helpers are deprecated and will be removed in the next major release.

    Rules

    • You MAY create as many helper files as you need per container.
    • All container-specific helpers MUST be placed in the app/Containers/{Section}/{Container}/Helpers directory.
    • All general helpers MUST be placed in the app/Ship/Helpers directory.

    Folder Structure

    app
    ├── Containers
    │ └── Section
    │ └── Container
    │ └── Helpers
    │ ├── helpers.php
    │ ├── mix.php
    │ └── ...
    └── Ship
    └── Helpers
    ├── another-helper.php
    ├── and-another.php
    └── ...

    Code Example

    if (!function_exists('add')) {
    function add(int $firstNumber, int $secondNumber): int
    {
    return $firstNumber + $secondNumber;
    }
    }
    - + \ No newline at end of file diff --git a/docs/components/optional-components/index.html b/docs/components/optional-components/index.html index deefd270e..87372221f 100644 --- a/docs/components/optional-components/index.html +++ b/docs/components/optional-components/index.html @@ -4,7 +4,7 @@ Optional Components | Apiato - + @@ -14,7 +14,7 @@ Their usage is discretionary, depending on specific requirements.

    Most of these components are just Laravel components, and they function in the exact same way as Laravel components. However, they come with additional rules and conventions specific to Apiato.

    info

    To learn more about Apiato components, check the Components section.

    - + \ No newline at end of file diff --git a/docs/components/optional-components/jobs/index.html b/docs/components/optional-components/jobs/index.html index 5e40ff95c..d5a5c9a2b 100644 --- a/docs/components/optional-components/jobs/index.html +++ b/docs/components/optional-components/jobs/index.html @@ -4,7 +4,7 @@ Jobs | Apiato - + @@ -12,7 +12,7 @@
    Version: 12.x

    Jobs

    Apiato jobs are just Laravel Jobs, and they function in the exact same way as Laravel jobs. However, they come with additional rules and conventions specific to Apiato.

    To generate new jobs you may use the apiato:generate:job interactive command:

    php artisan apiato:generate:job

    Rules

    • All container-specific Jobs MUST be placed in the app/Containers/{Section}/{Container}/Jobs directory.
    • All general Jobs MUST be placed in the app/Ship/Jobs directory.
    • All Jobs MUST extend the App\Ship\Parents\Jobs\Job class.
      • The parent extension SHOULD be aliased as ParentJob.

    Folder Structure

    app
    ├── Containers
    │ └── Section
    │ └── Container
    │ └── Jobs
    │ ├── FooJob.php
    │ ├── BarJob.php
    │ └── ...
    └── Ship
    └── Jobs
    ├── BazJob.php
    └── ...

    Code Example

    Jobs are defined exactly as you would define them in Laravel.

    - + \ No newline at end of file diff --git a/docs/components/optional-components/mail/index.html b/docs/components/optional-components/mail/index.html index add973e98..e870d5b25 100644 --- a/docs/components/optional-components/mail/index.html +++ b/docs/components/optional-components/mail/index.html @@ -4,7 +4,7 @@ Mail | Apiato - + @@ -18,7 +18,7 @@ such as view('welcome'), will result in the template not being found.

    An exception to this namespace convention is for template files located in the app/Ship/Mails/Templates directory. These templates will be namespaced using the word ship instead of the Section and Container names.

    For example, you would access such a template like this: view(ship::welcome).

    - + \ No newline at end of file diff --git a/docs/components/optional-components/middlewares/index.html b/docs/components/optional-components/middlewares/index.html index eb95fff1b..ff7a69a30 100644 --- a/docs/components/optional-components/middlewares/index.html +++ b/docs/components/optional-components/middlewares/index.html @@ -4,7 +4,7 @@ Middlewares | Apiato - + @@ -19,7 +19,7 @@ you may use the apiato:generate:provider interactive command:

    php artisan apiato:generate:provider

    Remember to also register the MiddlewareServiceProvider in the container's MainServiceProvider:

    use ...
    use App\Ship\Parents\Providers\MainServiceProvider as ParentMainServiceProvider;

    class MainServiceProvider extends ParentMainServiceProvider
    {
    protected array $serviceProviders = [
    // ... Other service providers
    MiddlewareServiceProvider::class,
    ];
    }

    General Middlewares

    General middlewares must be registered in the App\Ship\Kernels\HttpKernel class.

    Third Party Middlewares

    When dealing with third-party packages that require middleware registration in the App\Ship\Kernels\HttpKernel class, you should follow these guidelines:

    • Specific Container Usage: If the package is used within a particular container, register its middleware in that container App\Containers\{Section}\{Container}\Providers\MiddlewareServiceProvider class.

    • Framework-wide Usage: If the package is generic and used throughout the entire application, you can register its middleware in the App\Ship\Kernels\HttpKernel class.

    Middleware Registration Flow

    If you want to understand the middleware registration process, here is a breakdown of the registration flow.

    Consider the following folder structure:

    app
    ├── Containers
    │ └── Section
    │ └── Container
    │ ├── Middlewares
    │ │ ├── DemoMiddleware.php ─►─┐
    │ │ └── ...
    │ └── Providers ▼
    │ ├── MiddlewareServiceProvider.php ─────►─────┐
    │ ├── MainServiceProvider.php ◄─registered─in─◄┘
    │ └── ...
    └── Ship
    ├── Kernels
    │ ├── HttpKernel.php ◄─registered─in─◄┐
    │ └── ...
    └── Middlewares │
    ├── AnotherMiddleware.php ─────►────┘
    └── ...

    The following diagram illustrates the registration flow of middlewares in the above folder structure:

    - + \ No newline at end of file diff --git a/docs/components/optional-components/migrations/index.html b/docs/components/optional-components/migrations/index.html index 6d44e04b7..a70d2a6b3 100644 --- a/docs/components/optional-components/migrations/index.html +++ b/docs/components/optional-components/migrations/index.html @@ -4,7 +4,7 @@ Migrations | Apiato - + @@ -12,7 +12,7 @@
    Version: 12.x

    Migrations

    Apiato migrations are just Laravel Migrations, and they function in the exact same way as Laravel migrations. However, they come with additional rules and conventions specific to Apiato.

    To generate new migrations you may use the apiato:generate:migration interactive command:

    php artisan apiato:generate:migration

    Rules

    • All container-specific Migrations MUST be placed in the app/Containers/{Section}/{Container}/Data/Migrations directory.
    • All general Migrations MUST be placed in the app/Ship/Migrations directory.

    Folder Structure

    app
    ├── Containers
    │ └── Section
    │ └── Container
    │ └── Data
    │ └── Migrations
    │ ├── 0000_01_01_000001_create_things_table.php
    │ └── ...
    └── Ship
    └── Migrations
    ├── 0000_02_02_000002_create_another_things_table.php
    └── ...

    Code Example

    Migrations are defined exactly as you would define them in Laravel.

    - + \ No newline at end of file diff --git a/docs/components/optional-components/notifications/index.html b/docs/components/optional-components/notifications/index.html index b511f9f18..c19ada4bf 100644 --- a/docs/components/optional-components/notifications/index.html +++ b/docs/components/optional-components/notifications/index.html @@ -4,7 +4,7 @@ Notifications | Apiato - + @@ -24,7 +24,7 @@ As a result, you don't need to manually generate the migration file. You can directly run the migrations using the php artisan migrate command, and the notifications table will be created for you.

    - + \ No newline at end of file diff --git a/docs/components/optional-components/policies/index.html b/docs/components/optional-components/policies/index.html index a5a43867e..7d49f6c8b 100644 --- a/docs/components/optional-components/policies/index.html +++ b/docs/components/optional-components/policies/index.html @@ -4,7 +4,7 @@ Policies | Apiato - + @@ -21,7 +21,7 @@ Here is a breakdown of the registration flow.

    Consider the following folder structure:

    app
    └── Containers
    └── Section
    └── Container
    ├── Policies
    │ ├── DemoPolicy.php ─►─┐
    │ └── ...
    └── Providers ▼
    ├── AuthServiceProvider.php ─────────►───────┐
    ├── MainServiceProvider.php ◄─registered─in─◄┘
    └── ...

    The following diagram illustrates the registration flow of policies in the above folder structure:

    Helper Methods

    Available since Core v8.7.0

    All models are equipped with the owns and isOwnedBy methods, made available through the Apiato\Core\Traits\CanOwnTrait trait. These methods offer a convenient way to determine if a model is owned by another model or if a model owns another model.

    These methods support all types of relationships, as demonstrated below:

    // Check if a user owns a post
    $user->owns($post);

    // Check if a post is owned by a user
    $post->isOwnedBy($user);
    - + \ No newline at end of file diff --git a/docs/components/optional-components/repository/criterias/index.html b/docs/components/optional-components/repository/criterias/index.html index 1a4434a2b..f58bf8d48 100644 --- a/docs/components/optional-components/repository/criterias/index.html +++ b/docs/components/optional-components/repository/criterias/index.html @@ -4,7 +4,7 @@ Criterias | Apiato - + @@ -23,7 +23,7 @@ This approach offers the flexibility to create query conditions once and apply them consistently anywhere in your application, enhancing code reusability and maintainability.

    Rules

    • All container-specific Criterias MUST be placed in the app/Containers/{Section}/{Container}/Data/Criterias directory.
    • All general Criterias MUST be placed in the app/Ship/Criterias directory.
    • All Criterias MUST extend the App\Ship\Parents\Criterias\Criteria class.
      • The parent extension SHOULD be aliased as ParentCriteria.
    • Every Criteria MUST have an apply method.
    • A Criteria MUST not contain any extra code. If it needs data, the data MUST be passed to it.

    Folder Structure

    app
    ├── Containers
    │ └── Section
    │ └── Container
    │ └── Data
    │ └── Criterias
    │ ├── ColourRedCriteria.php
    │ ├── RaceCarsCriteria.php
    │ └── ...
    └── Ship
    └── Criterias
    ├── CreatedTodayCriteria.php
    ├── NotNullCriteria.php
    └── ...

    Code Example

    use App\Ship\Parents\Criterias\Criteria as ParentCriteria;
    use Prettus\Repository\Contracts\RepositoryInterface as PrettusRepositoryInterface;

    class IsNullCriteria extends ParentCriteria
    {
    public function __construct(
    private readonly string $field
    ) {
    }

    public function apply($model, PrettusRepositoryInterface $repository)
    {
    return $model->whereNull($this->field);
    }
    }

    Applying Criteria

    A Criteria can be applied to a Repository by using the pushCriteria method.

    public function __construct(
    protected readonly UserRepository $repository
    ) {
    }

    The pushCriteria method accepts an instance of a Criteria class or a string with the Criteria class name.

    public function run()
    {
    $this->repository->pushCriteria(new IsNullCriteria('email'));
    return $this->repository->paginate();
    }

    You can also apply multiple Criterias to a Repository by using the pushCriteria method multiple times.

    public function run()
    {
    $this->repository->pushCriteria(new IsNullCriteria('email'));
    $this->repository->pushCriteria(OrderByNameCriteria::class);
    return $this->repository->paginate();
    }
    - + \ No newline at end of file diff --git a/docs/components/optional-components/repository/repositories/index.html b/docs/components/optional-components/repository/repositories/index.html index e53e85a51..28d8e22e7 100644 --- a/docs/components/optional-components/repository/repositories/index.html +++ b/docs/components/optional-components/repository/repositories/index.html @@ -4,7 +4,7 @@ Repositories | Apiato - + @@ -58,7 +58,7 @@ findById
    getById
    findMany

    pushCriteriaWith

    This method is a wrapper around the pushCriteria method. It accepts data to be passed to the criteria class and allows for easier testing.

    findById

    This method is a wrapper around the find method. But it returns null if the record is not found.

    getById

    This method is a wrapper around the find method. But it throws a NotFoundException if the record is not found.

    findMany

    This method is a wrapper around the find method. But it returns an empty collection if no records are found.

    - + \ No newline at end of file diff --git a/docs/components/optional-components/seeders/index.html b/docs/components/optional-components/seeders/index.html index d6fb669e2..6134567b4 100644 --- a/docs/components/optional-components/seeders/index.html +++ b/docs/components/optional-components/seeders/index.html @@ -4,7 +4,7 @@ Seeders | Apiato - + @@ -25,7 +25,7 @@ You can locate this seeder class at app/Ship/Seeders/SeedDeploymentData.php. Similar to the testing seeder, the deployment seeder is not automatically loaded by Apiato. You can call this seeder and populate your database with production data by executing the following command:

    php artisan apiato:seed-deployment
    - + \ No newline at end of file diff --git a/docs/components/optional-components/service-providers/index.html b/docs/components/optional-components/service-providers/index.html index 18dcbdf7d..e9772e5b1 100644 --- a/docs/components/optional-components/service-providers/index.html +++ b/docs/components/optional-components/service-providers/index.html @@ -4,7 +4,7 @@ Service Providers | Apiato - + @@ -36,7 +36,7 @@ which makes it available.

    note

    Do note that the App\Ship\Parents\Providers\RouteServiceProvider is a unique case. Because it's required by Apiato, it is registered by the App\Ship\Prviders\ShipProvider and is loaded automatically.

    Service Providers Registration Flow

    If you want to understand the service provider registration process, here is a breakdown of the registration flow.

    Consider the following folder structure:

    app
    ├── Containers
    │ └── Section
    │ ├── ContainerA
    │ │ └── Providers
    │ │ ├── CustomServiceProvider.php ─────────►────────┐
    │ │ ├── EventServiceProvider.php ─────────►────────┤
    │ │ ├── MainServiceProvider.php ◄──registered─in─◄─┘
    │ │ └── ...
    │ └── ContainerB
    │ └── Providers
    │ ├── AnotherCustomServiceProvider.php ────────►────────┐
    │ ├── EventServiceProvider.php ────────►────────┤
    │ ├── MainServiceProvider.php ◄──registered─in─◄─┤
    │ ├── MiddlewareServiceProvider.php ────────►────────┘
    │ └── ...
    └── Ship
    └── Providers
    ├── CustomGeneralServiceProvider.php ────────►────────┐
    ├── RouteServiceProvider.php ────────►────────┤
    ├── ShipProvider.php ◄──registered─in─◄─┘
    └── ...

    The following diagram illustrates the registration flow of service providers in the above folder structure:

    - + \ No newline at end of file diff --git a/docs/components/optional-components/tests/index.html b/docs/components/optional-components/tests/index.html index a95b12702..cd6679a41 100644 --- a/docs/components/optional-components/tests/index.html +++ b/docs/components/optional-components/tests/index.html @@ -4,7 +4,7 @@ Tests | Apiato - + @@ -118,7 +118,7 @@ Within this class, you can define the logic and data generation process for your testing data.

    Once you have defined your testing data, you can run the following command in your terminal:

    php artisan apiato:seed-test

    This command triggers the seeding process specifically for testing data, populating your application with the generated data.

    - + \ No newline at end of file diff --git a/docs/components/optional-components/values/index.html b/docs/components/optional-components/values/index.html index 0071e081f..20b599b49 100644 --- a/docs/components/optional-components/values/index.html +++ b/docs/components/optional-components/values/index.html @@ -4,7 +4,7 @@ Values | Apiato - + @@ -15,7 +15,7 @@ Additionally, they do not possess functionality or modify any state; their sole purpose is to hold data.

    Value Objects are particularly well-suited for use with Laravel attributes casting, which allows us to cast a Value Object to a specific type, enabling seamless integration with Eloquent models and database operations.

    To generate new values you may use the apiato:generate:value interactive command:

    php artisan apiato:generate:value

    Rules

    • All container-specific Values MUST be placed in the app/Containers/{section}/{container}/Values directory.
    • All general Values MUST be placed in the app/Ship/Values directory.
    • All Values MUST extend the App\Ship\Parents\Values\Value class.
      • The parent extension SHOULD be aliased as ParentValue.

    Folder Structure

    app
    └── Containers
    └── Section
    └── Container
    └── Values
    ├── Output.php
    ├── Region.php
    └── ...

    Code Example

    class Location extends Value
    {
    public function __construct(
    public float $latitude,
    public float $longitude,
    ) {
    }
    }
    - + \ No newline at end of file diff --git a/docs/consulting/index.html b/docs/consulting/index.html index 989d22aa2..4402e21f4 100644 --- a/docs/consulting/index.html +++ b/docs/consulting/index.html @@ -4,13 +4,13 @@ Consulting | Apiato - +
    - + \ No newline at end of file diff --git a/docs/framework-features/api-versioning/index.html b/docs/framework-features/api-versioning/index.html index 401787008..4b22484bb 100644 --- a/docs/framework-features/api-versioning/index.html +++ b/docs/framework-features/api-versioning/index.html @@ -4,13 +4,13 @@ API Versioning | Apiato - +
    - + \ No newline at end of file diff --git a/docs/framework-features/code-generator/index.html b/docs/framework-features/code-generator/index.html index 6205f576d..32f9cdc38 100644 --- a/docs/framework-features/code-generator/index.html +++ b/docs/framework-features/code-generator/index.html @@ -4,7 +4,7 @@ Code Generator | Apiato - + @@ -22,7 +22,7 @@ However, it is crucial to adhere to one essential rule:

    • The name of the file and the folder structure in app/Ship/Generators/CustomStubs MUST exactly match those in vendor/apiato/core/Generator/Stubs.

    To illustrate the process, let's assume you want to customize the creation of an action. Follow these steps:

    1. Locate the action.stub file in vendor/apiato/core/Generator/Stubs/actions.
    2. Copy the action.stub file and paste it into the app/Ship/Generators/CustomStubs/actions directory.
    3. Make the desired changes to the copied action.stub file according to your requirements.

    By completing these steps, whenever you run the php artisan apiato:generate:action command, your customized stub file will be employed instead of the default one, applying your modifications to the generated action files.

    - + \ No newline at end of file diff --git a/docs/framework-features/etag/index.html b/docs/framework-features/etag/index.html index ec814d686..cf795969e 100644 --- a/docs/framework-features/etag/index.html +++ b/docs/framework-features/etag/index.html @@ -4,7 +4,7 @@ Etag | Apiato - + @@ -12,7 +12,7 @@
    Version: 12.x

    Etag

    The ETag or entity tag is part of HTTP, the protocol for the World Wide Web. It is one of several mechanisms that HTTP provides for Web cache validation, which allows a client to make conditional requests.

    You can read more about this feature in the Requests documentation.

    - + \ No newline at end of file diff --git a/docs/framework-features/index.html b/docs/framework-features/index.html index f60193905..bd599d0cb 100644 --- a/docs/framework-features/index.html +++ b/docs/framework-features/index.html @@ -4,13 +4,13 @@ Framework Features | Apiato - + - + \ No newline at end of file diff --git a/docs/framework-features/profiler/index.html b/docs/framework-features/profiler/index.html index 015149c5e..2fca73ca1 100644 --- a/docs/framework-features/profiler/index.html +++ b/docs/framework-features/profiler/index.html @@ -4,7 +4,7 @@ Profiler | Apiato - + @@ -17,7 +17,7 @@ Apiato employs the Apiato\Core\Middlewares\Http\ProfilerMiddleware to include the profiling data in the response.

    The profiler feature is initially disabled by default. To enable it, you should edit the .env file and set DEBUGBAR_ENABLED=true.

    To customize and manage the profiler response, you'll need to make adjustments in the configuration file located at app/Ship/Configs/debugbar.php.

    The following is an example of the profiler response:

    {
    "_profiler": {
    "__meta": {
    "id": "X167f293230e3457f1bbd95d9c82aba4a",
    "datetime": "2017-09-22 18:45:27",
    "utime": 1506105927.799299,
    "method": "GET",
    "uri": "/",
    "ip": "172.20.0.1"
    },
    "messages": {
    "count": 0,
    "messages": []
    },
    "time": {
    "start": 1506105922.742068,
    "end": 1506105927.799333,
    "duration": 5.057265043258667,
    "duration_str": "5.06s",
    "measures": [
    {
    "label": "Booting",
    "start": 1506105922.742068,
    "relative_start": 0,
    "end": 1506105923.524004,
    "relative_end": 1506105923.524004,
    "duration": 0.7819359302520752,
    "duration_str": "781.94ms",
    "params": [],
    "collector": null
    },
    {
    "label": "Application",
    "start": 1506105923.535343,
    "relative_start": 0.7932748794555664,
    "end": 1506105927.799336,
    "relative_end": 0.00000286102294921875,
    "duration": 4.26399302482605,
    "duration_str": "4.26s",
    "params": [],
    "collector": null
    }
    ]
    },
    "memory": {
    "peak_usage": 13234248,
    "peak_usage_str": "12.62MB"
    },
    "exceptions": {
    "count": 0,
    "exceptions": []
    },
    "route": {
    "uri": "GET /",
    "middleware": "api, throttle:30,1",
    "domain": "http://api.apiato.test",
    "as": "apis_root_page",
    "controller": "App\\Containers\\Welcome\\UI\\API\\Controllers\\Controller@apiRoot",
    "namespace": "App\\Containers\\Welcome\\UI\\API\\Controllers",
    "prefix": "/",
    "where": [],
    "file": "app/Containers/Welcome/UI/API/Controllers/Controller.php:20-25"
    },
    "queries": {
    "nb_statements": 0,
    "nb_failed_statements": 0,
    "accumulated_duration": 0,
    "accumulated_duration_str": "0μs",
    "statements": []
    },
    "logs": {
    "count": 3,
    "messages": [
    {
    "message": "...",
    "message_html": null,
    "is_string": false,
    "label": "error",
    "time": 1506105927.694807
    },
    {
    "message": "...",
    "message_html": null,
    "is_string": false,
    "label": "error",
    "time": 1506105927.694811
    },
    {
    "message": "[2017-09-18 17:38:15] testing.INFO: New User registration. ID = 970ylqvaogmxnbdr | Email = apiato@mail.test. Thank you for signing up.\n</div>\n</body>\n</html>\n \n",
    "message_html": null,
    "is_string": false,
    "label": "info",
    "time": 1506105927.694812
    }
    ]
    },
    "auth": {
    "guards": {
    "web": "array:2 [\n \"name\" => \"Guest\"\n \"user\" => array:1 [\n \"guest\" => true\n ]\n]",
    "api": "array:2 [\n \"name\" => \"Guest\"\n \"user\" => array:1 [\n \"guest\" => true\n ]\n]"
    },
    "names": ""
    },
    "gate": {
    "count": 0,
    "messages": []
    }
    }
    }
    - + \ No newline at end of file diff --git a/docs/framework-features/rate-limiting/index.html b/docs/framework-features/rate-limiting/index.html index e68fd0fe5..bf5bfbac3 100644 --- a/docs/framework-features/rate-limiting/index.html +++ b/docs/framework-features/rate-limiting/index.html @@ -4,7 +4,7 @@ Rate Limiting | Apiato - + @@ -17,7 +17,7 @@ You can disable it on specific endpoints or globally.

    On a specific endpoint

    Rate limiting can be disabled by removing the api middleware from an endpoint using withoutMiddleware('throttle:api') method. Read More.

    Globally

    To disable rate limiting completely, set GLOBAL_API_RATE_LIMIT_ENABLED to false in the .env file.

    - + \ No newline at end of file diff --git a/docs/framework-features/rbac/index.html b/docs/framework-features/rbac/index.html index 84c423f2d..f7d327f6f 100644 --- a/docs/framework-features/rbac/index.html +++ b/docs/framework-features/rbac/index.html @@ -4,7 +4,7 @@ Role Based Access Control | Apiato - + @@ -12,7 +12,7 @@
    - + \ No newline at end of file diff --git a/docs/getting-started/best-practices/index.html b/docs/getting-started/best-practices/index.html index 10f782a9b..974e52fee 100644 --- a/docs/getting-started/best-practices/index.html +++ b/docs/getting-started/best-practices/index.html @@ -4,7 +4,7 @@ Best Practices | Apiato - + @@ -45,7 +45,7 @@ This gives maintainers of the API enough information to understand the problem that’s occurred. We don’t want errors to bring down our system, so we can leave them unhandled, which means that the API consumer has to handle them.

    Common error HTTP status codes include:

    • 200 OK Server successfully returned the requested data.
    • 201 CREATED Server successfully created or modified the requested resource.
    • 204 NO CONTENT Server successfully deleted the requested resource.
    • 400 INVALID REQUEST The request was invalid or cannot be served. The exact error should be explained in the error payload. E.g. „The JSON is not valid“.
    • 401 UNAUTHORIZED The request requires an user authentication.
    • 403 FORBIDDEN The server understood the request, but is refusing it or the access is not allowed.
    • 404 NOT FOUND There is no resource behind the URI.
    • 422 Unprocessable Entity Should be used if the server cannot process the enitity, e.g. if an image cannot be formatted or mandatory fields are missing in the payload.
    • 500 INTERNAL SERVER ERROR Internal Server Error.
    • 502 BAD GATEWAY Server received an invalid response from the upstream server while trying to fulfill the request.
    • 503 SERVICE UNAVAILABLE Service unavailable.

    Naming Conventions For Routes & Actions

    • ListResources: to fetch all resources.
    • FindResourceByID: to search for a single resource by its unique identifier.
    • CreateResource: to create a new resource.
    • UpdateResource: to update/edit existing resource.
    • DeleteResource: to delete a resource.
    - + \ No newline at end of file diff --git a/docs/getting-started/customized-laravel-components/index.html b/docs/getting-started/customized-laravel-components/index.html index 160f24988..381f8b081 100644 --- a/docs/getting-started/customized-laravel-components/index.html +++ b/docs/getting-started/customized-laravel-components/index.html @@ -4,14 +4,14 @@ Customized Laravel Components | Apiato - +
    Version: 12.x

    Customized Laravel Components

    Apiato provides a refined organization for Laravel default class locations. Here, you can find the default Laravel components and their corresponding locations within Apiato.

    Kernels

    • Http Kernel is moved from app/Http to app/Ship/Kernels and renamed to HttpKernel.

    • Console Kernel is moved from app/Console to app/Ship/Kernels and renamed to ConsoleKernel.

    Middlewares

    • Middlewares are moved from app/Http/Middleware to app/Ship/Middlewares.

    Handler

    • Exception Handler is moved from app/Exceptions to app/Ship/Exceptions/Handlers and renamed to ExceptionsHandler.

    Providers

    • For information about the new locations of Providers, please refer to this link.

    Routes

    Web and API

    Apiato introduces a new approach to route organization and does not use the default routes/web.php and routes/api.php files. Therefore, you won't find these files in Apiato. To learn more, please visit this link.

    Channels

    • The channels.php file has been relocated from routes to app/Ship/Broadcasts.

    Console

    • The console.php file has been moved from routes to app/Ship/Commands and renamed to closures.php.
    - + \ No newline at end of file diff --git a/docs/getting-started/installation/index.html b/docs/getting-started/installation/index.html index 239f331aa..39b1834fb 100644 --- a/docs/getting-started/installation/index.html +++ b/docs/getting-started/installation/index.html @@ -4,7 +4,7 @@ Installation | Apiato - + @@ -42,7 +42,7 @@ assuming you are using the default Subdomain and API Version Prefix configuration, you should be able to access the following URLs and see the following results:

    Web (Browser)

    API (HTTP Client)

    Next Steps

    Now that you have created your Apiato project, you may be wondering what to learn next. If you're looking for a place to start, you should check out the following resources:

    - + \ No newline at end of file diff --git a/docs/next/architecture-concepts/components/index.html b/docs/next/architecture-concepts/components/index.html index b6e3b8f57..4615e354a 100644 --- a/docs/next/architecture-concepts/components/index.html +++ b/docs/next/architecture-concepts/components/index.html @@ -4,7 +4,7 @@ Components | Apiato - + @@ -19,7 +19,7 @@ simplifying future maintenance and facilitating modifications when needed.

    Components in Porto are categorized into two types: Main Components and Optional Components.

    Main Components

    Main Components are essential for the Container's functionality and must be used to achieve its core purpose.

    Optional Components

    Optional Components offer additional functionality that can be incorporated into the Container. Their usage is discretionary, depending on specific requirements.

    tip

    To learn more about how all this fits together, read the Request Lifecycle page.

    - + \ No newline at end of file diff --git a/docs/next/architecture-concepts/container/index.html b/docs/next/architecture-concepts/container/index.html index 758158401..cc302282a 100644 --- a/docs/next/architecture-concepts/container/index.html +++ b/docs/next/architecture-concepts/container/index.html @@ -4,7 +4,7 @@ Container | Apiato - + @@ -23,7 +23,7 @@ you may use the apiato:generate:container interactive command:

    php artisan apiato:generate:container

    Composer Dependencies

    To manage Composer dependencies, follow these guidelines:

    • All the Composer dependencies for a specific Container should be defined within that Container's composer.json file.
    • Dependencies related to the Ship layer should be placed in the root of the Ship layer, in a composer.json file.
    • Framework core dependencies should be defined in the project's root-level composer.json file.

    In practice, you can choose to place Composer dependencies in any of these composer.json files, and they will perform the same function. The choice of location depends on what is most relevant and convenient for your project.

    Readme

    Each Container has the option to include a readme.md file at its root, which serves to explain the Container's purpose and how to use it.

    To generate new readme files, you may use the apiato:generate:readme interactive command:

    php artisan apiato:generate:readme
    - + \ No newline at end of file diff --git a/docs/next/architecture-concepts/index.html b/docs/next/architecture-concepts/index.html index 0b1570d16..c9a193eee 100644 --- a/docs/next/architecture-concepts/index.html +++ b/docs/next/architecture-concepts/index.html @@ -4,7 +4,7 @@ Architecture Concepts | Apiato - + @@ -13,7 +13,7 @@ to structure the application code.

    Investing 30 minutes in reading the Porto Documentation before getting started is highly recommended and can prove to be a valuable use of your time. The document serves as a comprehensive guide and resource for understanding the Apiato project.

    - + \ No newline at end of file diff --git a/docs/next/architecture-concepts/porto/index.html b/docs/next/architecture-concepts/porto/index.html index 18d2427fe..cdf96b967 100644 --- a/docs/next/architecture-concepts/porto/index.html +++ b/docs/next/architecture-concepts/porto/index.html @@ -4,7 +4,7 @@ Porto | Apiato - + @@ -18,7 +18,7 @@ It can be a specific feature or a wrapper around a RESTful API resource.

    note

    A Container is allowed to depend on other Containers in the same Section.

    Ship

    The Ship layer contains the infrastructure code, which consists of shared code utilized by all Containers.

    Typical Project Structure

    app
    ├── Containers
    │ ├── Section
    │ │ └── Container
    │ │ ├── Actions
    │ │ ├── Configs
    │ │ ├── Data
    │ │ │ ├── Factories
    │ │ │ ├── Migrations
    │ │ │ ├── Repositories
    │ │ │ └── Seeders
    │ │ ├── Mails
    │ │ │ └── Templates
    │ │ ├── Middlewares
    │ │ ├── Models
    │ │ ├── Notifications
    │ │ ├── Providers
    │ │ ├── Tasks
    │ │ ├── Tests
    │ │ ├── Traits
    │ │ └── UI
    │ │ ├── API
    │ │ │ ├── Controllers
    │ │ │ ├── Requests
    │ │ │ ├── Routes
    │ │ │ └── Transformers
    │ │ ├── WEB
    │ │ │ ├── Controllers
    │ │ │ ├── Requests
    │ │ │ ├── Routes
    │ │ │ └── Views
    │ │ └── CLI
    │ │ └── Commands
    │ └── Vendor `// All installed and reusable Containers`
    │ ├── ContainerA
    │ └── ContainerB
    └── Ship `// All shared code between all Containers`
    ├── Broadcasts
    ├── Commands
    ├── Configs
    ├── Contracts
    ├── Criterias
    ├── Events
    ├── Exceptions
    ├── Generators
    ├── Helpers
    ├── Kernels
    ├── Listeners
    ├── Mails
    ├── Middlewares
    ├── Migrations
    ├── Notifications
    ├── Parents
    ├── Providers
    ├── Seeders
    └── Tests

    Default Sections

    Apiato ships with two default Sections:

    • AppSection: contains all the default Containers.
    • Vendor: contains all the installed and reusable Containers.
    tip

    The Vendor section is a special Section within the Containers layer that holds installed and reusable Containers. It serves a similar purpose as the vendor folder located at the root. Any Section is permitted to depend on the Vendor Section, allowing for the utilization of its Containers.

    Read more about the Container Installer to learn how to install Vendor Containers.

    - + \ No newline at end of file diff --git a/docs/next/architecture-concepts/request-lifecycle/index.html b/docs/next/architecture-concepts/request-lifecycle/index.html index 6517de80a..24d8bcd92 100644 --- a/docs/next/architecture-concepts/request-lifecycle/index.html +++ b/docs/next/architecture-concepts/request-lifecycle/index.html @@ -4,7 +4,7 @@ Request Lifecycle | Apiato - + @@ -16,7 +16,7 @@ The Tasks can be used to execute reusable subsets of the business logic, with each Task responsible for a single portion of the main Action. The View or Transformer is used to build the response that is sent back to the User.

    Request Lifecycle Diagram

    Legend:

    • Solid Line: Mandatory dependency (always used)
    • Doted Line: Optional dependency (not always used)
    • Red Solid Border: Response generation
    • Green Dashed Border: Optional component (not always used)
    - + \ No newline at end of file diff --git a/docs/next/components/index.html b/docs/next/components/index.html index e4b744e44..d3662ba70 100644 --- a/docs/next/components/index.html +++ b/docs/next/components/index.html @@ -4,7 +4,7 @@ Components | Apiato - + @@ -13,7 +13,7 @@ "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.

    info

    To learn more about Apiato components, check the Components section.

    - + \ No newline at end of file diff --git a/docs/next/components/main-components/actions/index.html b/docs/next/components/main-components/actions/index.html index 509fcd73f..78a2a6732 100644 --- a/docs/next/components/main-components/actions/index.html +++ b/docs/next/components/main-components/actions/index.html @@ -4,7 +4,7 @@ Actions | Apiato - + @@ -20,7 +20,7 @@ However, it's important to note that not all operations may be automatically rolled back. For example, file system operations, such as uploading an image, are typically not covered by the database transaction and would need to be handled manually.

    - + \ No newline at end of file diff --git a/docs/next/components/main-components/controllers/index.html b/docs/next/components/main-components/controllers/index.html index 06d5c20de..40e7059b4 100644 --- a/docs/next/components/main-components/controllers/index.html +++ b/docs/next/components/main-components/controllers/index.html @@ -4,7 +4,7 @@ Controllers | Apiato - + @@ -16,7 +16,7 @@ and it MUST be used in conjunction with the transform method. This is different from the meta parameter in the transform method. This metadata will be returned directly under the meta key.

    You can use this method in conjunction with the meta parameter in the transform method.

    $metaData = ['foo' => 999, 'bar'];

    $this->withMeta($metaData)->transform($sample, SampleTransformer::class, meta: ['foo' => 'bar', 'baz' => 1]);

    // Response
    {
    "data": {},
    "meta": {
    "foo": 999,
    "0": "bar",
    "include": [...],
    "custom": {
    "foo": "bar",
    "baz": 1
    },
    "pagination": {}
    }
    }

    json

    This method allows you to pass an array of data that will be represented as JSON.

    $this->json($data)

    created

    This method allows you to return a response with a 201 status code.

    $this->created($data)

    deleted

    This method allows you to return a response with a 202 status code.

    $this->deleted($deletedModel)

    // Response
    {
    "message": "Model (1) Deleted Successfully."
    }

    accepted

    This method allows you to return a response with a 202 status code.

    $this->accepted($data)

    noContent

    This method allows you to return a response with a 204 status code.

    $this->noContent()
    - + \ No newline at end of file diff --git a/docs/next/components/main-components/exceptions/index.html b/docs/next/components/main-components/exceptions/index.html index 69e5a505b..d5dd53990 100644 --- a/docs/next/components/main-components/exceptions/index.html +++ b/docs/next/components/main-components/exceptions/index.html @@ -4,7 +4,7 @@ Exceptions | Apiato - + @@ -13,7 +13,7 @@ Translation strings are automatically translated if the translations are found. To handle localization, you can use the Localization Container.

    // Example 1
    throw (new AccountFailedException())->withErrors(['email' => 'appSection@user::exceptions.email-taken']);
    // Example 2
    throw (new AccountFailedException())->withErrors(['email' => 'appSection@user::exceptions.email-taken', 'Another not translated message']);

    Response:

    {
    "message": "The exception error message.",
    "errors": {
    "email": [
    "The email has already been taken.",
    "Another not translated message"
    ]
    }
    }

    debug

    The debug method is used for logging error messages during debugging and development. The debug method accepts string or \Exception instance

    throw (new AccountFailedException())->debug($e);
    - + \ No newline at end of file diff --git a/docs/next/components/main-components/index.html b/docs/next/components/main-components/index.html index f6e5e87f4..46cd21ff8 100644 --- a/docs/next/components/main-components/index.html +++ b/docs/next/components/main-components/index.html @@ -4,13 +4,13 @@ Main Components | Apiato - +
    - + \ No newline at end of file diff --git a/docs/next/components/main-components/models/index.html b/docs/next/components/main-components/models/index.html index 31409bfc6..602e5aa32 100644 --- a/docs/next/components/main-components/models/index.html +++ b/docs/next/components/main-components/models/index.html @@ -4,7 +4,7 @@ Models | Apiato - + @@ -16,7 +16,7 @@ it is essential to incorporate the ModelTrait trait into your model. By doing so, your model will benefit from various functionalities provided by the trait, such as hash ids and other features necessary for proper integration with the framework.

    use Apiato\Core\Traits\ModelTrait;

    class Demo
    {
    use ModelTrait;
    ...
    }
    - + \ No newline at end of file diff --git a/docs/next/components/main-components/requests/index.html b/docs/next/components/main-components/requests/index.html index 5a57a9bf1..b67daef2d 100644 --- a/docs/next/components/main-components/requests/index.html +++ b/docs/next/components/main-components/requests/index.html @@ -4,7 +4,7 @@ Requests | Apiato - + @@ -18,7 +18,7 @@ the validation rules defined in RegisterUserRequest will be automatically applied before the __invoke method is executed. If the validation fails, an appropriate error response will be generated.

    Request Properties

    Apiato introduces new properties to the Request Class that enhance its functionality.

    access

    The $access property allows you to define Roles and Permissions that can access a specific endpoint. -It's used by the hasAccess method to check if a user has the required Roles and Permissions to use that endpoint.

    class DemoRequest extends ParentRequest
    {
    protected array $access = [
    'permissions' => 'delete-users',
    'roles' => 'manager'
    ];

    public function authorize(): bool
    {
    return $this->check([
    'hasAccess',
    ]);
    }
    }

    You can also use the array notation or pipe to define multiple Roles and Permissions.

    class DemoRequest extends ParentRequest
    {
    protected $access = [
    'permissions' => ['delete-users', 'another-permissions'],
    'roles' => 'manager|admin',
    ];

    // ...
    }
    tip

    If there's no need to set any roles or permissions, +It's used by the hasAccess method to check if a user has the required Roles and Permissions to use that endpoint.

    class DemoRequest extends ParentRequest
    {
    protected array $access = [
    'permissions' => 'delete-users',
    'roles' => 'manager'
    ];

    public function authorize(): bool
    {
    return $this->hasAccess();
    }
    }

    You can also use the array notation or pipe to define multiple Roles and Permissions.

    class DemoRequest extends ParentRequest
    {
    protected $access = [
    'permissions' => ['delete-users', 'another-permissions'],
    'roles' => 'manager|admin',
    ];

    // ...
    }
    tip

    If there's no need to set any roles or permissions, you can simply set the $permissions or $roles property to an empty string '', an empty array [], or null.

    decode

    The $decode property is used to handle the decoding of Hashed IDs from the incoming Request.

    When you enable the Hash ID feature, your application can receive Hashed IDs from users. These Hashed IDs need to be decoded before they can be effectively validated. Apiato facilitates this process @@ -27,11 +27,7 @@ will not function correctly unless you decode the ID before passing it to the validation process.

    urlParameters

    The $urlParameters property simplifies the process of applying validation rules to URL parameters.

    By default, Laravel doesn't provide validation for URL parameters (/stores/999/items). However, by using the $urlParameters property, you can enable validation for these parameters. By specifying the desired URL parameters within this property, -you not only enable validation but also gain direct access to these parameters from the Request object.

    // URL: /stores/{id}/items
    // GET /stores/999/items
    class DemoRequest extends ParentRequest
    {
    protected array $urlParameters = [
    'id',
    ];

    public function rules(): array
    {
    return [
    'id' => 'integer', // url parameter
    ];
    }
    }

    Helper Methods

    check

    The check method is used to authorize the user to access the endpoint. -It accepts an array of methods names that will be called to check if the user has access or not. -Each of those methods must return a boolean. -Take a look at the following example:

    use App\Ship\Parents\Requests\Request as ParentRequest;

    class DemoRequest extends ParentRequest
    {
    use IsAuthorTrait;

    // ...

    public function authorize(): bool
    {
    return $this->check([
    'hasAccess|isOwner',
    'isKing',
    ]);
    }
    }

    Here we are passing the the hasAccess, isOwner and isKing methods to the check method. -Then the check method follows the following rules and checks if the user has access or not:

    • The separator | between the methods indicates an OR operation.
    • The default operation between all methods in the array is AND.

    So in the above example, the call to the check method will be translated to:

    return ($this->hasAccess() || $this->isOwner()) && $this->isKing();

    And if the result of this operation is true then the user will be authorized to access the endpoint.

    note

    hasAccess

    The hasAccess method assesses a user's access rights based on the Request's $access property. +you not only enable validation but also gain direct access to these parameters from the Request object.

    // URL: /stores/{id}/items
    // GET /stores/999/items
    class DemoRequest extends ParentRequest
    {
    protected array $urlParameters = [
    'id',
    ];

    public function rules(): array
    {
    return [
    'id' => 'integer', // url parameter
    ];
    }
    }

    Helper Methods

    hasAccess

    The hasAccess method assesses a user's access rights based on the Request's $access property. If the user has any of the specified Roles or Permissions, the method will return true otherwise it will return false.

    sanitizeInput

    The sanitizeInput method is employed to cleanse request data before its utilization within the application.

    Particularly useful for PATCH requests, where you may want @@ -53,12 +49,7 @@ when you wish to provide data directly to the request instead of sending it through the request body.

    $request = RegisterUserRequest::injectData($data);

    withUrlParameters

    The withUrlParameters method enables you to inject URL parameters into the request. This is especially useful when you need to include properties in the request that are not part of the request body but are required for the request to be processed. -This method is often used in conjunction with the injectData method.

    $request = RegisterUserRequest::injectData($data)
    ->withUrlParameters(['id' => 123]);

    Custom Authorize Methods

    The recommended approach for adding custom authorization functions is by using a Trait, -which can be included in your Request classes.

    For instance, -let's -create an IsAuthorTrait Trait with a single method -named isAuthor to determine if the current user holds the role of an author.

    trait IsAuthorTrait
    {
    public function isAuthor(): bool
    {
    return $this->user()->hasRole('author');
    }
    }

    Subsequently, you can apply the IsAuthorTrait Trait to a Request class, -allowing the utilization of the isAuthor function within the authorization process.

    use App\Ship\Parents\Requests\Request as ParentRequest;

    class DemoRequest extends ParentRequest
    {
    use IsAuthorTrait;

    // ...

    public function authorize(): bool
    {
    return $this->check([
    'isAuthor',
    ]);
    }
    }

    Bypass Authorization

    To grant certain Roles access to all endpoints within the system without the need +This method is often used in conjunction with the injectData method.

    $request = RegisterUserRequest::injectData($data)
    ->withUrlParameters(['id' => 123]);

    Bypassing Authorization

    To grant certain Roles access to all endpoints within the system without the need to define the role in each Request object, you can follow this approach. This is particularly beneficial when you want to provide unrestricted access to users with the admin role. @@ -72,8 +63,8 @@ (Wikipedia)

    Apiato offers support for Etag through the Apiato\Core\Middlewares\HttpProcessETagHeadersMiddleware middleware, which employs the Shallow technique. This middleware can be particularly valuable in reducing bandwidth usage for clients, especially on mobile devices.

    Please note that this feature is disabled by default. To enable it, follow these steps:

    1. Navigate to the app/Ship/Configs/apiato.php configuration file.
    2. Inside the configuration file, locate the use-etag configuration parameter.
    3. Set the use-etag parameter to true.

    Keep in mind that for this feature to function correctly, the client must include the If-None-Match HTTP header, -which corresponds to the ETag value, in their request.

    - +which corresponds to the ETag value, in their request.

    + \ No newline at end of file diff --git a/docs/next/components/main-components/routes/index.html b/docs/next/components/main-components/routes/index.html index 6df5d9b93..3a40d9673 100644 --- a/docs/next/components/main-components/routes/index.html +++ b/docs/next/components/main-components/routes/index.html @@ -4,7 +4,7 @@ Routes | Apiato - + @@ -18,7 +18,7 @@ Maintaining this distinction enables the generation of separate documentations for each type, ensuring that your internal API remains private and secure. This feature can be configured through the Documentation Generator package.

    Public Routes:

    • Accessible to third parties.
    • May or may not require authentication.

    Private Routes:

    • Accessible only to your own apps.
    • May or may not require authentication.
    - + \ No newline at end of file diff --git a/docs/next/components/main-components/subactions/index.html b/docs/next/components/main-components/subactions/index.html index 93ccdd7b4..39086288e 100644 --- a/docs/next/components/main-components/subactions/index.html +++ b/docs/next/components/main-components/subactions/index.html @@ -4,7 +4,7 @@ Sub Actions | Apiato - + @@ -13,7 +13,7 @@ They enable Actions to share a sequence of Tasks, thus promoting reusability. Similar to how Tasks enable Actions to share specific functionalities, SubActions serve to share a predefined set of Tasks.

    All the features and capabilities available for regular Actions are also applicable to SubActions.

    To generate new SubActions you may use the apiato:generate:subaction interactive command:

    php artisan apiato:generate:subaction

    Definition & Principles

    Read Porto SAP Documentation (#Sub-Actions).

    Rules

    • All SubActions:
      • MUST be placed in the app/Containers/{Section}/{Container}/Actions directory.
      • MUST extend the App\Ship\Parents\Actions\SubAction class.
        • The parent extension SHOULD be aliased as ParentSubAction.

    Folder Structure

    app
    └── Containers
    └── Section
    └── Container
    └── Actions
    ├── ValidateAddressSubAction.php
    ├── BuildOrderSubAction.php
    └── ...

    Code Example

    use ...
    use App\Ship\Parents\Actions\SubAction as ParentSubAction;

    class DemoSubAction extends ParentSubAction
    {
    public function __construct(
    private readonly DemoTask $demoTask
    ) {
    }

    public function run(array $data)
    {
    return $this->demoTask->run($data);
    }
    }
    - + \ No newline at end of file diff --git a/docs/next/components/main-components/tasks/index.html b/docs/next/components/main-components/tasks/index.html index aa5039a68..fa4899ee2 100644 --- a/docs/next/components/main-components/tasks/index.html +++ b/docs/next/components/main-components/tasks/index.html @@ -4,7 +4,7 @@ Tasks | Apiato - + @@ -15,7 +15,7 @@ in encapsulating functionalities that are utilized by multiple Actions spanning various Containers within your application.

    To generate new tasks you may use the apiato:generate:task interactive command:

    php artisan apiato:generate:task

    Additionally, to retrieve a list of the existing tasks in your Apiato application, use the apiato:list:tasks command.

    php artisan apiato:list:tasks

    Definition & Principles

    Read Porto SAP Documentation (#Tasks).

    Rules

    • All Tasks:
      • MUST be placed in the app/Containers/{Section}/{Container}/Tasks directory.
      • MUST extend the App\Ship\Parents\Tasks\Task class.
        • The parent extension SHOULD be aliased as ParentTask.

    Folder Structure

    app
    └── Containers
    └── Section
    └── Container
    └── Tasks
    ├── CreateResourceTask.php
    ├── DeleteResourceTask.php
    └── ...

    Code Example

    use ...
    use App\Ship\Parents\Tasks\Task as ParentTask;

    class DemoTask extends ParentTask
    {
    public function run(int $a, int $b): int
    {
    return $a + $b;
    }
    }
    - + \ No newline at end of file diff --git a/docs/next/components/main-components/transformers/index.html b/docs/next/components/main-components/transformers/index.html index ed149919b..18308eba0 100644 --- a/docs/next/components/main-components/transformers/index.html +++ b/docs/next/components/main-components/transformers/index.html @@ -4,7 +4,7 @@ Transformers | Apiato - + @@ -54,7 +54,7 @@ This serializer is not to everyone’s tastes, because it adds a data namespace to the output. A very basic response of the DataArraySerializer will look like this:

    {
    "data": {
    "object": "User",
    "id": "XbPW7awNkzl83LD6",
    "name": "Mohammad Alavi"
    }
    }

    The DataArraySerializer is handy because it allows space for meta data (like pagination, or totals) in both Items and Collections.

    {
    "data": [ ... ],
    "meta": {
    "include": [
    "xxx",
    "yyy"
    ],
    "custom": [],
    "pagination": {
    "total": 999,
    "count": 999,
    "per_page": 999,
    "current_page": 999,
    "total_pages": 999,
    "links": {
    "next": "http://api.apiato.test/v1/accounts?page=999"
    }
    }
    }
    }
    Further Reading

    For more detailed information, please refer to Fractal and Laravel Fractal Wrapper documentations.

    - + \ No newline at end of file diff --git a/docs/next/components/main-components/views/index.html b/docs/next/components/main-components/views/index.html index 4461db7e8..8ab0c2166 100644 --- a/docs/next/components/main-components/views/index.html +++ b/docs/next/components/main-components/views/index.html @@ -4,7 +4,7 @@ Views | Apiato - + @@ -17,7 +17,7 @@ such as view('welcome-page'), will result in the view not being found.

    An exception to this namespace convention is for view files located in the app/Ship/Views and app/Ship/Mails/Templates directories. These views will be namespaced using the word ship instead of the Section and Container names.

    For example, you would access such a view like this: view(ship::welcome-page).

    - + \ No newline at end of file diff --git a/docs/next/components/optional-components/commands/index.html b/docs/next/components/optional-components/commands/index.html index 247367e7a..31a2917af 100644 --- a/docs/next/components/optional-components/commands/index.html +++ b/docs/next/components/optional-components/commands/index.html @@ -4,7 +4,7 @@ Commands | Apiato - + @@ -12,7 +12,7 @@
    Version: Next 🚧

    Commands

    Apiato commands are just Laravel Commands, and they function in the exact same way as Laravel commands. However, they come with additional rules and conventions specific to Apiato.

    Rules

    • All container-specific Commands MUST be placed in the app/Containers/{Section}/{Container}/UI/CLI/Commands directory.
    • All general Commands MUST be placed in the app/Ship/Commands directory.
    • All Commands:
      • MUST extend the App\Ship\Parents\Commands\ConsoleCommand class.
        • The parent extension SHOULD be aliased as ConsoleCommand.
      • SHOULD call an Action to perform its job, and SHOULD NOT contain any business logic.

    Folder Structure

    app
    ├── Containers
    │ └── Section
    │ └── Container
    │ └── UI
    │ └── CLI
    │ └── Commands
    │ ├── FirstCommand.php
    │ ├── SecondCommand.php
    │ └── ...
    └── Ship
    └── Commands
    ├── FirstCommand.php
    ├── SecondCommand.php
    └── ...

    Code Example

    Commands are defined exactly as you would define them in Laravel.

    Closure Commands

    You can define your console closure commands in app/Ship/Commands/closures.php.

    - + \ No newline at end of file diff --git a/docs/next/components/optional-components/configs/index.html b/docs/next/components/optional-components/configs/index.html index 10c42e01b..00d118422 100644 --- a/docs/next/components/optional-components/configs/index.html +++ b/docs/next/components/optional-components/configs/index.html @@ -4,7 +4,7 @@ Configs | Apiato - + @@ -17,7 +17,7 @@ camelCase representation of the container's Section name, succeeded by -, and then the camelCase representation of the Container name.

    For instance, if you have a container named "MyContainer" within the "MySection" section, the configuration file would be named mySection-myContainer.php.

    - + \ No newline at end of file diff --git a/docs/next/components/optional-components/events/index.html b/docs/next/components/optional-components/events/index.html index 73c29a104..caa47006e 100644 --- a/docs/next/components/optional-components/events/index.html +++ b/docs/next/components/optional-components/events/index.html @@ -4,7 +4,7 @@ Events | Apiato - + @@ -17,7 +17,7 @@ you may use the apiato:generate:provider interactive command:

    php artisan apiato:generate:provider

    Remember to also register the EventServiceProvider in the container's MainServiceProvider:

    use ...
    use App\Ship\Parents\Providers\MainServiceProvider as ParentMainServiceProvider;

    class MainServiceProvider extends ParentMainServiceProvider
    {
    protected array $serviceProviders = [
    // ... Other service providers
    EventServiceProvider::class,
    ];
    }

    In The Ship

    Registering events and listeners in the Ship can be done by adding them to the listen array in the App\Ship\Providers\EventServiceProvider class.

    Events & Listeners Registration Flow

    If you are manually registering events and listeners and wish to understand the registration process, here is a breakdown of the registration flow.

    Consider the following folder structure:

    app
    ├── Containers
    │ └── Section
    │ └── Container
    │ ├── Events
    │ │ ├── DemoEvent.php ────►─┐
    │ │ └── ...
    │ ├── Listeners │
    │ │ ├── DemoListener.php ─►─┤
    │ │ └── ...
    │ └── Providers ▼
    │ ├── EventServiceProvider.php ─────────►─────────┐
    │ ├── MainServiceProvider.php ◄───registered─in─◄─┘
    │ └── ...
    └── Ship
    ├── Events
    │ ├── ShipDemoEvent.php ──►─┐
    │ └── ...
    ├── Listeners │
    │ ├── ShipDemoListener.php ►┤
    │ └── ...
    └── Providers ▼
    ├── EventServiceProvider.php ─────────►─────────┐
    ├── ShipProvider.php ◄───registered─in─◄─┘
    └── ...

    The following diagram illustrates the registration flow of events and listeners in the above folder structure:

    - + \ No newline at end of file diff --git a/docs/next/components/optional-components/factories/index.html b/docs/next/components/optional-components/factories/index.html index d8c904044..702a6cf95 100644 --- a/docs/next/components/optional-components/factories/index.html +++ b/docs/next/components/optional-components/factories/index.html @@ -4,7 +4,7 @@ Factories | Apiato - + @@ -22,7 +22,7 @@ if your model does not extend the App\Ship\Parents\Models\Model or the App\Ship\Parents\Models\UserModel class, it is essential to include the ModelTrait trait in your model. By doing so, Apiato will be able to locate the appropriate factory and use it for the model when needed.

    use Apiato\Core\Traits\ModelTrait;

    class Demo
    {
    use ModelTrait;
    ...
    }
    - + \ No newline at end of file diff --git a/docs/next/components/optional-components/helpers/index.html b/docs/next/components/optional-components/helpers/index.html index d7b8d26e9..9513c37e2 100644 --- a/docs/next/components/optional-components/helpers/index.html +++ b/docs/next/components/optional-components/helpers/index.html @@ -4,13 +4,13 @@ Helpers | Apiato - +
    Version: Next 🚧

    Helpers

    You have the option to create your own global "helper" PHP functions in designated directories, and Apiato will automatically autoload them for you.

    Deprecation Notice

    Please be aware that Container Helpers are deprecated and will be removed in the next major release.

    Rules

    • You MAY create as many helper files as you need per container.
    • All container-specific helpers MUST be placed in the app/Containers/{Section}/{Container}/Helpers directory.
    • All general helpers MUST be placed in the app/Ship/Helpers directory.

    Folder Structure

    app
    ├── Containers
    │ └── Section
    │ └── Container
    │ └── Helpers
    │ ├── helpers.php
    │ ├── mix.php
    │ └── ...
    └── Ship
    └── Helpers
    ├── another-helper.php
    ├── and-another.php
    └── ...

    Code Example

    if (!function_exists('add')) {
    function add(int $firstNumber, int $secondNumber): int
    {
    return $firstNumber + $secondNumber;
    }
    }
    - + \ No newline at end of file diff --git a/docs/next/components/optional-components/index.html b/docs/next/components/optional-components/index.html index 2d7acf13d..e533279a8 100644 --- a/docs/next/components/optional-components/index.html +++ b/docs/next/components/optional-components/index.html @@ -4,7 +4,7 @@ Optional Components | Apiato - + @@ -14,7 +14,7 @@ Their usage is discretionary, depending on specific requirements.

    Most of these components are just Laravel components, and they function in the exact same way as Laravel components. However, they come with additional rules and conventions specific to Apiato.

    info

    To learn more about Apiato components, check the Components section.

    - + \ No newline at end of file diff --git a/docs/next/components/optional-components/jobs/index.html b/docs/next/components/optional-components/jobs/index.html index 6f758e2af..18a369e8e 100644 --- a/docs/next/components/optional-components/jobs/index.html +++ b/docs/next/components/optional-components/jobs/index.html @@ -4,7 +4,7 @@ Jobs | Apiato - + @@ -12,7 +12,7 @@
    Version: Next 🚧

    Jobs

    Apiato jobs are just Laravel Jobs, and they function in the exact same way as Laravel jobs. However, they come with additional rules and conventions specific to Apiato.

    To generate new jobs you may use the apiato:generate:job interactive command:

    php artisan apiato:generate:job

    Rules

    • All container-specific Jobs MUST be placed in the app/Containers/{Section}/{Container}/Jobs directory.
    • All general Jobs MUST be placed in the app/Ship/Jobs directory.
    • All Jobs MUST extend the App\Ship\Parents\Jobs\Job class.
      • The parent extension SHOULD be aliased as ParentJob.

    Folder Structure

    app
    ├── Containers
    │ └── Section
    │ └── Container
    │ └── Jobs
    │ ├── FooJob.php
    │ ├── BarJob.php
    │ └── ...
    └── Ship
    └── Jobs
    ├── BazJob.php
    └── ...

    Code Example

    Jobs are defined exactly as you would define them in Laravel.

    - + \ No newline at end of file diff --git a/docs/next/components/optional-components/mail/index.html b/docs/next/components/optional-components/mail/index.html index 91994daf8..69f5f096e 100644 --- a/docs/next/components/optional-components/mail/index.html +++ b/docs/next/components/optional-components/mail/index.html @@ -4,7 +4,7 @@ Mail | Apiato - + @@ -18,7 +18,7 @@ such as view('welcome'), will result in the template not being found.

    An exception to this namespace convention is for template files located in the app/Ship/Mails/Templates directory. These templates will be namespaced using the word ship instead of the Section and Container names.

    For example, you would access such a template like this: view(ship::welcome).

    - + \ No newline at end of file diff --git a/docs/next/components/optional-components/middlewares/index.html b/docs/next/components/optional-components/middlewares/index.html index 2849357ca..404e4c73f 100644 --- a/docs/next/components/optional-components/middlewares/index.html +++ b/docs/next/components/optional-components/middlewares/index.html @@ -4,7 +4,7 @@ Middlewares | Apiato - + @@ -19,7 +19,7 @@ you may use the apiato:generate:provider interactive command:

    php artisan apiato:generate:provider

    Remember to also register the MiddlewareServiceProvider in the container's MainServiceProvider:

    use ...
    use App\Ship\Parents\Providers\MainServiceProvider as ParentMainServiceProvider;

    class MainServiceProvider extends ParentMainServiceProvider
    {
    protected array $serviceProviders = [
    // ... Other service providers
    MiddlewareServiceProvider::class,
    ];
    }

    General Middlewares

    General middlewares must be registered in the App\Ship\Kernels\HttpKernel class.

    Third Party Middlewares

    When dealing with third-party packages that require middleware registration in the App\Ship\Kernels\HttpKernel class, you should follow these guidelines:

    • Specific Container Usage: If the package is used within a particular container, register its middleware in that container App\Containers\{Section}\{Container}\Providers\MiddlewareServiceProvider class.

    • Framework-wide Usage: If the package is generic and used throughout the entire application, you can register its middleware in the App\Ship\Kernels\HttpKernel class.

    Middleware Registration Flow

    If you want to understand the middleware registration process, here is a breakdown of the registration flow.

    Consider the following folder structure:

    app
    ├── Containers
    │ └── Section
    │ └── Container
    │ ├── Middlewares
    │ │ ├── DemoMiddleware.php ─►─┐
    │ │ └── ...
    │ └── Providers ▼
    │ ├── MiddlewareServiceProvider.php ─────►─────┐
    │ ├── MainServiceProvider.php ◄─registered─in─◄┘
    │ └── ...
    └── Ship
    ├── Kernels
    │ ├── HttpKernel.php ◄─registered─in─◄┐
    │ └── ...
    └── Middlewares │
    ├── AnotherMiddleware.php ─────►────┘
    └── ...

    The following diagram illustrates the registration flow of middlewares in the above folder structure:

    - + \ No newline at end of file diff --git a/docs/next/components/optional-components/migrations/index.html b/docs/next/components/optional-components/migrations/index.html index da1b1854c..76927a44e 100644 --- a/docs/next/components/optional-components/migrations/index.html +++ b/docs/next/components/optional-components/migrations/index.html @@ -4,7 +4,7 @@ Migrations | Apiato - + @@ -12,7 +12,7 @@
    Version: Next 🚧

    Migrations

    Apiato migrations are just Laravel Migrations, and they function in the exact same way as Laravel migrations. However, they come with additional rules and conventions specific to Apiato.

    To generate new migrations you may use the apiato:generate:migration interactive command:

    php artisan apiato:generate:migration

    Rules

    • All container-specific Migrations MUST be placed in the app/Containers/{Section}/{Container}/Data/Migrations directory.
    • All general Migrations MUST be placed in the app/Ship/Migrations directory.

    Folder Structure

    app
    ├── Containers
    │ └── Section
    │ └── Container
    │ └── Data
    │ └── Migrations
    │ ├── 0000_01_01_000001_create_things_table.php
    │ └── ...
    └── Ship
    └── Migrations
    ├── 0000_02_02_000002_create_another_things_table.php
    └── ...

    Code Example

    Migrations are defined exactly as you would define them in Laravel.

    - + \ No newline at end of file diff --git a/docs/next/components/optional-components/notifications/index.html b/docs/next/components/optional-components/notifications/index.html index 1e1c3f271..77b802230 100644 --- a/docs/next/components/optional-components/notifications/index.html +++ b/docs/next/components/optional-components/notifications/index.html @@ -4,7 +4,7 @@ Notifications | Apiato - + @@ -24,7 +24,7 @@ As a result, you don't need to manually generate the migration file. You can directly run the migrations using the php artisan migrate command, and the notifications table will be created for you.

    - + \ No newline at end of file diff --git a/docs/next/components/optional-components/policies/index.html b/docs/next/components/optional-components/policies/index.html index d7326072e..1b27ff9f3 100644 --- a/docs/next/components/optional-components/policies/index.html +++ b/docs/next/components/optional-components/policies/index.html @@ -4,7 +4,7 @@ Policies | Apiato - + @@ -21,7 +21,7 @@ Here is a breakdown of the registration flow.

    Consider the following folder structure:

    app
    └── Containers
    └── Section
    └── Container
    ├── Policies
    │ ├── DemoPolicy.php ─►─┐
    │ └── ...
    └── Providers ▼
    ├── AuthServiceProvider.php ─────────►───────┐
    ├── MainServiceProvider.php ◄─registered─in─◄┘
    └── ...

    The following diagram illustrates the registration flow of policies in the above folder structure:

    Helper Methods

    Available since Core v8.7.0

    All models are equipped with the owns and isOwnedBy methods, made available through the Apiato\Core\Traits\CanOwnTrait trait. These methods offer a convenient way to determine if a model is owned by another model or if a model owns another model.

    These methods support all types of relationships, as demonstrated below:

    // Check if a user owns a post
    $user->owns($post);

    // Check if a post is owned by a user
    $post->isOwnedBy($user);
    - + \ No newline at end of file diff --git a/docs/next/components/optional-components/repository/criterias/index.html b/docs/next/components/optional-components/repository/criterias/index.html index db95c191e..997614d72 100644 --- a/docs/next/components/optional-components/repository/criterias/index.html +++ b/docs/next/components/optional-components/repository/criterias/index.html @@ -4,7 +4,7 @@ Criterias | Apiato - + @@ -23,7 +23,7 @@ This approach offers the flexibility to create query conditions once and apply them consistently anywhere in your application, enhancing code reusability and maintainability.

    Rules

    • All container-specific Criterias MUST be placed in the app/Containers/{Section}/{Container}/Data/Criterias directory.
    • All general Criterias MUST be placed in the app/Ship/Criterias directory.
    • All Criterias MUST extend the App\Ship\Parents\Criterias\Criteria class.
      • The parent extension SHOULD be aliased as ParentCriteria.
    • Every Criteria MUST have an apply method.
    • A Criteria MUST not contain any extra code. If it needs data, the data MUST be passed to it.

    Folder Structure

    app
    ├── Containers
    │ └── Section
    │ └── Container
    │ └── Data
    │ └── Criterias
    │ ├── ColourRedCriteria.php
    │ ├── RaceCarsCriteria.php
    │ └── ...
    └── Ship
    └── Criterias
    ├── CreatedTodayCriteria.php
    ├── NotNullCriteria.php
    └── ...

    Code Example

    use App\Ship\Parents\Criterias\Criteria as ParentCriteria;
    use Prettus\Repository\Contracts\RepositoryInterface as PrettusRepositoryInterface;

    class IsNullCriteria extends ParentCriteria
    {
    public function __construct(
    private readonly string $field
    ) {
    }

    public function apply($model, PrettusRepositoryInterface $repository)
    {
    return $model->whereNull($this->field);
    }
    }

    Applying Criteria

    A Criteria can be applied to a Repository by using the pushCriteria method.

    public function __construct(
    protected readonly UserRepository $repository
    ) {
    }

    The pushCriteria method accepts an instance of a Criteria class or a string with the Criteria class name.

    public function run()
    {
    $this->repository->pushCriteria(new IsNullCriteria('email'));
    return $this->repository->paginate();
    }

    You can also apply multiple Criterias to a Repository by using the pushCriteria method multiple times.

    public function run()
    {
    $this->repository->pushCriteria(new IsNullCriteria('email'));
    $this->repository->pushCriteria(OrderByNameCriteria::class);
    return $this->repository->paginate();
    }
    - + \ No newline at end of file diff --git a/docs/next/components/optional-components/repository/repositories/index.html b/docs/next/components/optional-components/repository/repositories/index.html index a798fb5d3..e116689ec 100644 --- a/docs/next/components/optional-components/repository/repositories/index.html +++ b/docs/next/components/optional-components/repository/repositories/index.html @@ -4,7 +4,7 @@ Repositories | Apiato - + @@ -58,7 +58,7 @@ findById
    getById
    findMany

    pushCriteriaWith

    This method is a wrapper around the pushCriteria method. It accepts data to be passed to the criteria class and allows for easier testing.

    findById

    This method is a wrapper around the find method. But it returns null if the record is not found.

    getById

    This method is a wrapper around the find method. But it throws a NotFoundException if the record is not found.

    findMany

    This method is a wrapper around the find method. But it returns an empty collection if no records are found.

    - + \ No newline at end of file diff --git a/docs/next/components/optional-components/seeders/index.html b/docs/next/components/optional-components/seeders/index.html index 466b8d180..b90cae008 100644 --- a/docs/next/components/optional-components/seeders/index.html +++ b/docs/next/components/optional-components/seeders/index.html @@ -4,7 +4,7 @@ Seeders | Apiato - + @@ -25,7 +25,7 @@ You can locate this seeder class at app/Ship/Seeders/SeedDeploymentData.php. Similar to the testing seeder, the deployment seeder is not automatically loaded by Apiato. You can call this seeder and populate your database with production data by executing the following command:

    php artisan apiato:seed-deployment
    - + \ No newline at end of file diff --git a/docs/next/components/optional-components/service-providers/index.html b/docs/next/components/optional-components/service-providers/index.html index d54a491db..86caec81b 100644 --- a/docs/next/components/optional-components/service-providers/index.html +++ b/docs/next/components/optional-components/service-providers/index.html @@ -4,7 +4,7 @@ Service Providers | Apiato - + @@ -36,7 +36,7 @@ which makes it available.

    note

    Do note that the App\Ship\Parents\Providers\RouteServiceProvider is a unique case. Because it's required by Apiato, it is registered by the App\Ship\Prviders\ShipProvider and is loaded automatically.

    Service Providers Registration Flow

    If you want to understand the service provider registration process, here is a breakdown of the registration flow.

    Consider the following folder structure:

    app
    ├── Containers
    │ └── Section
    │ ├── ContainerA
    │ │ └── Providers
    │ │ ├── CustomServiceProvider.php ─────────►────────┐
    │ │ ├── EventServiceProvider.php ─────────►────────┤
    │ │ ├── MainServiceProvider.php ◄──registered─in─◄─┘
    │ │ └── ...
    │ └── ContainerB
    │ └── Providers
    │ ├── AnotherCustomServiceProvider.php ────────►────────┐
    │ ├── EventServiceProvider.php ────────►────────┤
    │ ├── MainServiceProvider.php ◄──registered─in─◄─┤
    │ ├── MiddlewareServiceProvider.php ────────►────────┘
    │ └── ...
    └── Ship
    └── Providers
    ├── CustomGeneralServiceProvider.php ────────►────────┐
    ├── RouteServiceProvider.php ────────►────────┤
    ├── ShipProvider.php ◄──registered─in─◄─┘
    └── ...

    The following diagram illustrates the registration flow of service providers in the above folder structure:

    - + \ No newline at end of file diff --git a/docs/next/components/optional-components/tests/index.html b/docs/next/components/optional-components/tests/index.html index 1e156c280..caadc1ecc 100644 --- a/docs/next/components/optional-components/tests/index.html +++ b/docs/next/components/optional-components/tests/index.html @@ -4,7 +4,7 @@ Tests | Apiato - + @@ -118,7 +118,7 @@ Within this class, you can define the logic and data generation process for your testing data.

    Once you have defined your testing data, you can run the following command in your terminal:

    php artisan apiato:seed-test

    This command triggers the seeding process specifically for testing data, populating your application with the generated data.

    - + \ No newline at end of file diff --git a/docs/next/components/optional-components/values/index.html b/docs/next/components/optional-components/values/index.html index fee530cbd..6c096f5fa 100644 --- a/docs/next/components/optional-components/values/index.html +++ b/docs/next/components/optional-components/values/index.html @@ -4,7 +4,7 @@ Values | Apiato - + @@ -15,7 +15,7 @@ Additionally, they do not possess functionality or modify any state; their sole purpose is to hold data.

    Value Objects are particularly well-suited for use with Laravel attributes casting, which allows us to cast a Value Object to a specific type, enabling seamless integration with Eloquent models and database operations.

    To generate new values you may use the apiato:generate:value interactive command:

    php artisan apiato:generate:value

    Rules

    • All container-specific Values MUST be placed in the app/Containers/{section}/{container}/Values directory.
    • All general Values MUST be placed in the app/Ship/Values directory.
    • All Values MUST extend the App\Ship\Parents\Values\Value class.
      • The parent extension SHOULD be aliased as ParentValue.

    Folder Structure

    app
    └── Containers
    └── Section
    └── Container
    └── Values
    ├── Output.php
    ├── Region.php
    └── ...

    Code Example

    class Location extends Value
    {
    public function __construct(
    public float $latitude,
    public float $longitude,
    ) {
    }
    }
    - + \ No newline at end of file diff --git a/docs/next/consulting/index.html b/docs/next/consulting/index.html index 11027100f..f884a22b0 100644 --- a/docs/next/consulting/index.html +++ b/docs/next/consulting/index.html @@ -4,13 +4,13 @@ Consulting | Apiato - +
    - + \ No newline at end of file diff --git a/docs/next/framework-features/api-versioning/index.html b/docs/next/framework-features/api-versioning/index.html index f15f7f8fd..f29c0dd87 100644 --- a/docs/next/framework-features/api-versioning/index.html +++ b/docs/next/framework-features/api-versioning/index.html @@ -4,13 +4,13 @@ API Versioning | Apiato - +
    - + \ No newline at end of file diff --git a/docs/next/framework-features/code-generator/index.html b/docs/next/framework-features/code-generator/index.html index 54e95ea5b..b521c16f0 100644 --- a/docs/next/framework-features/code-generator/index.html +++ b/docs/next/framework-features/code-generator/index.html @@ -4,7 +4,7 @@ Code Generator | Apiato - + @@ -22,7 +22,7 @@ However, it is crucial to adhere to one essential rule:

    • The name of the file and the folder structure in app/Ship/Generators/CustomStubs MUST exactly match those in vendor/apiato/core/Generator/Stubs.

    To illustrate the process, let's assume you want to customize the creation of an action. Follow these steps:

    1. Locate the action.stub file in vendor/apiato/core/Generator/Stubs/actions.
    2. Copy the action.stub file and paste it into the app/Ship/Generators/CustomStubs/actions directory.
    3. Make the desired changes to the copied action.stub file according to your requirements.

    By completing these steps, whenever you run the php artisan apiato:generate:action command, your customized stub file will be employed instead of the default one, applying your modifications to the generated action files.

    - + \ No newline at end of file diff --git a/docs/next/framework-features/etag/index.html b/docs/next/framework-features/etag/index.html index f54107afc..51cac8707 100644 --- a/docs/next/framework-features/etag/index.html +++ b/docs/next/framework-features/etag/index.html @@ -4,7 +4,7 @@ Etag | Apiato - + @@ -12,7 +12,7 @@
    Version: Next 🚧

    Etag

    The ETag or entity tag is part of HTTP, the protocol for the World Wide Web. It is one of several mechanisms that HTTP provides for Web cache validation, which allows a client to make conditional requests.

    You can read more about this feature in the Requests documentation.

    - + \ No newline at end of file diff --git a/docs/next/framework-features/index.html b/docs/next/framework-features/index.html index 00ab2f31c..5438ecb39 100644 --- a/docs/next/framework-features/index.html +++ b/docs/next/framework-features/index.html @@ -4,13 +4,13 @@ Framework Features | Apiato - + - + \ No newline at end of file diff --git a/docs/next/framework-features/profiler/index.html b/docs/next/framework-features/profiler/index.html index cc8194d21..baed22439 100644 --- a/docs/next/framework-features/profiler/index.html +++ b/docs/next/framework-features/profiler/index.html @@ -4,7 +4,7 @@ Profiler | Apiato - + @@ -17,7 +17,7 @@ Apiato employs the Apiato\Core\Middlewares\Http\ProfilerMiddleware to include the profiling data in the response.

    The profiler feature is initially disabled by default. To enable it, you should edit the .env file and set DEBUGBAR_ENABLED=true.

    To customize and manage the profiler response, you'll need to make adjustments in the configuration file located at app/Ship/Configs/debugbar.php.

    The following is an example of the profiler response:

    {
    "_profiler": {
    "__meta": {
    "id": "X167f293230e3457f1bbd95d9c82aba4a",
    "datetime": "2017-09-22 18:45:27",
    "utime": 1506105927.799299,
    "method": "GET",
    "uri": "/",
    "ip": "172.20.0.1"
    },
    "messages": {
    "count": 0,
    "messages": []
    },
    "time": {
    "start": 1506105922.742068,
    "end": 1506105927.799333,
    "duration": 5.057265043258667,
    "duration_str": "5.06s",
    "measures": [
    {
    "label": "Booting",
    "start": 1506105922.742068,
    "relative_start": 0,
    "end": 1506105923.524004,
    "relative_end": 1506105923.524004,
    "duration": 0.7819359302520752,
    "duration_str": "781.94ms",
    "params": [],
    "collector": null
    },
    {
    "label": "Application",
    "start": 1506105923.535343,
    "relative_start": 0.7932748794555664,
    "end": 1506105927.799336,
    "relative_end": 0.00000286102294921875,
    "duration": 4.26399302482605,
    "duration_str": "4.26s",
    "params": [],
    "collector": null
    }
    ]
    },
    "memory": {
    "peak_usage": 13234248,
    "peak_usage_str": "12.62MB"
    },
    "exceptions": {
    "count": 0,
    "exceptions": []
    },
    "route": {
    "uri": "GET /",
    "middleware": "api, throttle:30,1",
    "domain": "http://api.apiato.test",
    "as": "apis_root_page",
    "controller": "App\\Containers\\Welcome\\UI\\API\\Controllers\\Controller@apiRoot",
    "namespace": "App\\Containers\\Welcome\\UI\\API\\Controllers",
    "prefix": "/",
    "where": [],
    "file": "app/Containers/Welcome/UI/API/Controllers/Controller.php:20-25"
    },
    "queries": {
    "nb_statements": 0,
    "nb_failed_statements": 0,
    "accumulated_duration": 0,
    "accumulated_duration_str": "0μs",
    "statements": []
    },
    "logs": {
    "count": 3,
    "messages": [
    {
    "message": "...",
    "message_html": null,
    "is_string": false,
    "label": "error",
    "time": 1506105927.694807
    },
    {
    "message": "...",
    "message_html": null,
    "is_string": false,
    "label": "error",
    "time": 1506105927.694811
    },
    {
    "message": "[2017-09-18 17:38:15] testing.INFO: New User registration. ID = 970ylqvaogmxnbdr | Email = apiato@mail.test. Thank you for signing up.\n</div>\n</body>\n</html>\n \n",
    "message_html": null,
    "is_string": false,
    "label": "info",
    "time": 1506105927.694812
    }
    ]
    },
    "auth": {
    "guards": {
    "web": "array:2 [\n \"name\" => \"Guest\"\n \"user\" => array:1 [\n \"guest\" => true\n ]\n]",
    "api": "array:2 [\n \"name\" => \"Guest\"\n \"user\" => array:1 [\n \"guest\" => true\n ]\n]"
    },
    "names": ""
    },
    "gate": {
    "count": 0,
    "messages": []
    }
    }
    }
    - + \ No newline at end of file diff --git a/docs/next/framework-features/rate-limiting/index.html b/docs/next/framework-features/rate-limiting/index.html index 6239d7a7e..70e7cfea8 100644 --- a/docs/next/framework-features/rate-limiting/index.html +++ b/docs/next/framework-features/rate-limiting/index.html @@ -4,7 +4,7 @@ Rate Limiting | Apiato - + @@ -17,7 +17,7 @@ You can disable it on specific endpoints or globally.

    On a specific endpoint

    Rate limiting can be disabled by removing the api middleware from an endpoint using withoutMiddleware('throttle:api') method. Read More.

    Globally

    To disable rate limiting completely, set GLOBAL_API_RATE_LIMIT_ENABLED to false in the .env file.

    - + \ No newline at end of file diff --git a/docs/next/framework-features/rbac/index.html b/docs/next/framework-features/rbac/index.html index e8a84e212..09750d858 100644 --- a/docs/next/framework-features/rbac/index.html +++ b/docs/next/framework-features/rbac/index.html @@ -4,7 +4,7 @@ Role Based Access Control | Apiato - + @@ -12,7 +12,7 @@
    - + \ No newline at end of file diff --git a/docs/next/getting-started/best-practices/index.html b/docs/next/getting-started/best-practices/index.html index 526bbc8c1..8776c1c42 100644 --- a/docs/next/getting-started/best-practices/index.html +++ b/docs/next/getting-started/best-practices/index.html @@ -4,7 +4,7 @@ Best Practices | Apiato - + @@ -45,7 +45,7 @@ This gives maintainers of the API enough information to understand the problem that’s occurred. We don’t want errors to bring down our system, so we can leave them unhandled, which means that the API consumer has to handle them.

    Common error HTTP status codes include:

    • 200 OK Server successfully returned the requested data.
    • 201 CREATED Server successfully created or modified the requested resource.
    • 204 NO CONTENT Server successfully deleted the requested resource.
    • 400 INVALID REQUEST The request was invalid or cannot be served. The exact error should be explained in the error payload. E.g. „The JSON is not valid“.
    • 401 UNAUTHORIZED The request requires an user authentication.
    • 403 FORBIDDEN The server understood the request, but is refusing it or the access is not allowed.
    • 404 NOT FOUND There is no resource behind the URI.
    • 422 Unprocessable Entity Should be used if the server cannot process the enitity, e.g. if an image cannot be formatted or mandatory fields are missing in the payload.
    • 500 INTERNAL SERVER ERROR Internal Server Error.
    • 502 BAD GATEWAY Server received an invalid response from the upstream server while trying to fulfill the request.
    • 503 SERVICE UNAVAILABLE Service unavailable.

    Naming Conventions For Routes & Actions

    • ListResources: to fetch all resources.
    • FindResourceByID: to search for a single resource by its unique identifier.
    • CreateResource: to create a new resource.
    • UpdateResource: to update/edit existing resource.
    • DeleteResource: to delete a resource.
    - + \ No newline at end of file diff --git a/docs/next/getting-started/customized-laravel-components/index.html b/docs/next/getting-started/customized-laravel-components/index.html index ccc005877..781766f58 100644 --- a/docs/next/getting-started/customized-laravel-components/index.html +++ b/docs/next/getting-started/customized-laravel-components/index.html @@ -4,14 +4,14 @@ Customized Laravel Components | Apiato - +
    Version: Next 🚧

    Customized Laravel Components

    Apiato provides a refined organization for Laravel default class locations. Here, you can find the default Laravel components and their corresponding locations within Apiato.

    Kernels

    • Http Kernel is moved from app/Http to app/Ship/Kernels and renamed to HttpKernel.

    • Console Kernel is moved from app/Console to app/Ship/Kernels and renamed to ConsoleKernel.

    Middlewares

    • Middlewares are moved from app/Http/Middleware to app/Ship/Middlewares.

    Handler

    • Exception Handler is moved from app/Exceptions to app/Ship/Exceptions/Handlers and renamed to ExceptionsHandler.

    Providers

    • For information about the new locations of Providers, please refer to this link.

    Routes

    Web and API

    Apiato introduces a new approach to route organization and does not use the default routes/web.php and routes/api.php files. Therefore, you won't find these files in Apiato. To learn more, please visit this link.

    Channels

    • The channels.php file has been relocated from routes to app/Ship/Broadcasts.

    Console

    • The console.php file has been moved from routes to app/Ship/Commands and renamed to closures.php.
    - + \ No newline at end of file diff --git a/docs/next/getting-started/installation/index.html b/docs/next/getting-started/installation/index.html index f03b215ba..84ac16874 100644 --- a/docs/next/getting-started/installation/index.html +++ b/docs/next/getting-started/installation/index.html @@ -4,7 +4,7 @@ Installation | Apiato - + @@ -42,7 +42,7 @@ assuming you are using the default Subdomain and API Version Prefix configuration, you should be able to access the following URLs and see the following results:

    Web (Browser)

    API (HTTP Client)

    Next Steps

    Now that you have created your Apiato project, you may be wondering what to learn next. If you're looking for a place to start, you should check out the following resources:

    - + \ No newline at end of file diff --git a/docs/next/pacakges/documentation/index.html b/docs/next/pacakges/documentation/index.html index f09d9b33f..576736de2 100644 --- a/docs/next/pacakges/documentation/index.html +++ b/docs/next/pacakges/documentation/index.html @@ -4,7 +4,7 @@ Documentation | Apiato - + @@ -15,7 +15,7 @@ access-private-docs-permission values in documentation config. By default, users need access-private-docs permission to access private docs.

    Edit Default Generated Values in Templates

    Apiato by defaults generates 2 API documentations, each one has its own apidoc.json file. Both can be modified from the Documentation Container in app/Containers/Vendor/Documentation/ApiDocJs and need Source code modification.

    Edit the Documentation Header

    The header is usually the Overview of your API. It contains Info about authenticating users, making requests, responses, potential errors, rate limiting, pagination, query parameters and anything you want.

    All this information is written in app/Containers/Vendor/Documentation/ApiDocJs/shared/header.template.en.md file, and the same file is used as header for both private and public documentations.

    To edit its content you need to modify its source code and open the markdown file in any markdown editor and edit it.

    You will notice some variables like {{rate-limit}} and {{token-expires}}. Those are replaced when running apiato:apidoc with real values from your application configuration files.

    Feel free to extend them to include more info about your API from the app/Containers/Vendor/Documentation/Tasks/RenderTemplatesTask.php class.

    Localization for Documentation Header

    Default, the documentation title is in English en localization.

    See which locales are supported by going in app/Containers/Vendor/Documentation/ApiDocJs/shared

    There will be some header.template.{locale}.md files in the folder.

    You can change the language by adding APIDOC_LOCALE=ru to the .env file.

    If you didn't find a file with your locale, you can create it. You need to modify its source code and create new file like header.template.cn.md

    - + \ No newline at end of file diff --git a/docs/next/pacakges/index.html b/docs/next/pacakges/index.html index 4e19f65dd..330444a71 100644 --- a/docs/next/pacakges/index.html +++ b/docs/next/pacakges/index.html @@ -4,7 +4,7 @@ Overview | Apiato - + @@ -21,7 +21,7 @@ that allows installing/updating containers.
  • You must provide the key extra.apiato.container.name. This key indicates the name of the folder (e.g., container) when installing the package to the app/Containers/Vendor directory. In the shown example, the container would be installed to app/Containers/Vendor/Foo.
  • - + \ No newline at end of file diff --git a/docs/next/pacakges/localization/index.html b/docs/next/pacakges/localization/index.html index 0b2483034..7e42653f3 100644 --- a/docs/next/pacakges/localization/index.html +++ b/docs/next/pacakges/localization/index.html @@ -4,7 +4,7 @@ Localization | Apiato - + @@ -41,7 +41,7 @@ language in this specific language (e.g., locale_name => Deutsch). Furthermore, the language name is outputted in the applications default name (e.g., configured in app.locale). This would result in default_name => German.

    The same applies to the regions that are defined (e.g., de-DE). Consequently, this results in locale_name => Deutschland and default_name = Germany.

    Tests

    To change the default language in your tests requests. You can set the env language in the phpunit.xml file.

    - + \ No newline at end of file diff --git a/docs/next/pacakges/social-authentication/index.html b/docs/next/pacakges/social-authentication/index.html index 435830271..d96fee61e 100644 --- a/docs/next/pacakges/social-authentication/index.html +++ b/docs/next/pacakges/social-authentication/index.html @@ -4,7 +4,7 @@ Social Authentication | Apiato - + @@ -19,7 +19,7 @@ to get the oauth info and user data respectively.

    Social Authentication Container Customization

    You can customize this container by publishing its config and modifying its values

    php artisan vendor:publish

    Config file will be copied to app/Ship/Configs/vendor-socialAuth.php

    Support new Auth Provider

    1. Publish the configs
    2. Create your new auth provider by implementing the App\Containers\Vendor\SocialAuth\Contracts\SocialAuthProvider contract.
      To get an idea about how to implement your own provider you can check out supported providers here app/Containers/Vendor/SocialAuth/SocialAuthProviders.
    3. Add your new provider to providers array in the vendor-socialAuth config.
        'providers' => [
    ...
    'something' => Location\Of\Your\Provider\SomthingSocialAuthProvider::class,
    ],

    Changing default used Repository, Transformer & DB user table name

    This container depends on Apiato default user repository, transformer & database user table name. If you changed those defaults you can update and provide them in the configs.

    - + \ No newline at end of file diff --git a/docs/next/prologue/contribution-guide/index.html b/docs/next/prologue/contribution-guide/index.html index 8ba2db093..568233607 100644 --- a/docs/next/prologue/contribution-guide/index.html +++ b/docs/next/prologue/contribution-guide/index.html @@ -4,7 +4,7 @@ Contribution Guide | Apiato - + @@ -37,7 +37,7 @@ after pull requests are merged. This allows us to focus on the content of the contribution and not the code style.

    Code of Conduct

    The Apiato code of conduct is derived from the Ruby code of conduct. Any violations of the code of conduct may be reported to Mohammad Alavi (mohammad.alavi1990@gmail.com):

    • Participants will be tolerant of opposing views.
    • Participants must ensure that their language and actions are free of personal attacks and disparaging personal remarks.
    • When interpreting the words and actions of others, participants should always assume good intentions.
    • Behavior that can be reasonably considered harassment will not be tolerated.
    - + \ No newline at end of file diff --git a/docs/next/prologue/release-notes/index.html b/docs/next/prologue/release-notes/index.html index 56156f3d4..41810c6d9 100644 --- a/docs/next/prologue/release-notes/index.html +++ b/docs/next/prologue/release-notes/index.html @@ -4,7 +4,7 @@ Release Notes | Apiato - + @@ -17,7 +17,7 @@ since major releases of Apiato do include breaking changes. However, we strive to always ensure you may update to a new major release in one day or less.

    Support Policy

    For all Apiato releases, bug fixes are provided for 18 months and security fixes are provided for 2 years.

    VersionPHP (*)ReleaseBug Fixes UntilSecurity Fixes Until
    107.3 - 8.1April 25th, 2021October 25th, 2022April 25th, 2023
    118.0 - 8.2April 27th, 2022October 27th, 2023April 27th, 2024
    128.1 - 8.2June 4th, 2023December 4th, 2024June 4th, 2025
    138.2Q1 2024August 5th, 2025February 3rd, 2026

    (*) Supported PHP versions

    Apiato 12

    Full Changelog: https://github.com/apiato/apiato/compare/v11.3.2...v12.0.0

    PHP 8.1

    Apiato 12.x requires a minimum PHP version of 8.1.

    Breaking Changes

    • Upgraded to Laravel v10 (All Laravel files (e.g. configs, .env, etc...) are now synced with the latest Laravel changes)
    • Updated Composer dependencies to their latest version
    • Laravel Passport route registration & customization has changed. Passport routes are now reside in a dedicated route file (Instead of registering them in the provider).
    • Middleware $routeMiddleware field is renamed to $middlewareAliases
    • Trimmed down the TestCase by removing some useless traits including:
    TestsMockHelperTrait
    TestsResponseHelperTrait
    • encode() method return value has changed -> In case of unencodable value (e.g. null), now returns null instead of ''
    • decode() method return value has changed -> In case of undecodable value (e.g. null), now returns null instead of []
    • StateKeeperTrait is removed from Request

    None Breaking Changes

    • Everything is refactored to use constructor injection instead of directly using the Service Container like so app(CreateUserByCredentialsTask::class)->run()
    • Added more tests and refactored the rest
    • Switched to invokable controllers
    \\ from
    Route::get('profile', [GetAuthenticatedUserController::class, 'getAuthenticatedUser']);
    \\ to
    Route::get('profile', GetAuthenticatedUserController::class);
    • All rotues are moved into the private documentation. e.g. RefreshProxyForWebClient.v1.public.php -> RefreshProxyForWebClient.v1.private.php
    • Added some getter methods to the Request including:
    withUrlParameters()
    getAccessArray()
    getDecodeArray()
    getUrlParametersArray()
    • Added a TestAssertionHelperTrait to the TestCase which provides some usefull assertions

    Bug Fixes

    • withMeta() method on ResponseTrait now correctly includes added meta data
    • Calling invokable controllers from routes #174
    • Exception when try to generate an WEB CRUD Controller from generator #171
    • PHP 8.1 warning on passing null to explode #176
    - + \ No newline at end of file diff --git a/docs/next/prologue/upgrade-guide/index.html b/docs/next/prologue/upgrade-guide/index.html index d33d4943c..f378a6c02 100644 --- a/docs/next/prologue/upgrade-guide/index.html +++ b/docs/next/prologue/upgrade-guide/index.html @@ -4,13 +4,13 @@ Upgrade Guide | Apiato - +
    Version: Next 🚧

    Upgrade Guide

    Upgrade from 11.0 to 12.0

    WORK IN PROGRESS

    - + \ No newline at end of file diff --git a/docs/next/security/authentication/index.html b/docs/next/security/authentication/index.html index ae3feabb0..aa8e2170e 100644 --- a/docs/next/security/authentication/index.html +++ b/docs/next/security/authentication/index.html @@ -4,7 +4,7 @@ Authentication | Apiato - + @@ -67,7 +67,7 @@ you can navigate to the app/Ship/Providers/RouteServiceProvider.php file and update the LOGIN constant.

    Passing The Access Token

    When calling routes that are protected by Passport, your application's API consumers should specify their access token as a Bearer token in the Authorization header of their request. For example, when using the Guzzle HTTP library:

    use Illuminate\Support\Facades\Http;

    $response = Http::withHeaders([
    'Accept' => 'application/json',
    'Authorization' => 'Bearer '.$accessToken,
    ])->get('http://api.apiato.test/v1/users');

    return $response->json();

    Configuration

    Most of the configuration is done in the app/Ships/Configs/apiato.php file.

    Social Authentication

    For Social Authentication visit the Social Authentication page.

    - + \ No newline at end of file diff --git a/docs/next/security/authorization/index.html b/docs/next/security/authorization/index.html index 9b81c87d4..fb83a3828 100644 --- a/docs/next/security/authorization/index.html +++ b/docs/next/security/authorization/index.html @@ -4,7 +4,7 @@ Authorization | Apiato - + @@ -18,8 +18,8 @@ By verifying permissions and roles at the request level, you ensure that unauthorized users are denied access before any further processing takes place.

    Default Roles & Permissions

    Apiato comes with some default Roles and Permissions. You can find them in app/Containers/AppSection/Authorization/Data/Seeders. -You can use them as a starting point, or delete them and create your own.

    Code Example

    Protecting the delete user endpoint with delete-users permission:

    use App\Ship\Parents\Requests\Request as ParentRequest;

    class DeleteUserRequest extends ParentRequest
    {
    protected array $access = [
    'permissions' => 'delete-users',
    'roles' => '',
    ];

    public function authorize(): bool
    {
    return $this->check([
    'hasAccess',
    ]);
    }
    }

    Authorization failed JSON response:

    {
    "message": "This action is unauthorized.",
    "errors": []
    }
    - +You can use them as a starting point, or delete them and create your own.

    Code Example

    Protecting the delete user endpoint with delete-users permission:

    use App\Ship\Parents\Requests\Request as ParentRequest;

    class DeleteUserRequest extends ParentRequest
    {
    protected array $access = [
    'permissions' => 'delete-users',
    'roles' => '',
    ];

    public function authorize(): bool
    {
    return $this->hasAccess();
    }
    }

    Authorization failed JSON response:

    {
    "message": "This action is unauthorized.",
    "errors": []
    }
    + \ No newline at end of file diff --git a/docs/next/security/email-varification/index.html b/docs/next/security/email-varification/index.html index 5bd4811b1..de496e257 100644 --- a/docs/next/security/email-varification/index.html +++ b/docs/next/security/email-varification/index.html @@ -4,7 +4,7 @@ Email Verification | Apiato - + @@ -22,7 +22,7 @@ when using a load balancer, set the protected $proxies = '*' in the app/Ship/Middlewares/TrustProxies.php or customize it according to your needs.

    - + \ No newline at end of file diff --git a/docs/next/security/hash-id/index.html b/docs/next/security/hash-id/index.html index d0a38fb35..9335f92d0 100644 --- a/docs/next/security/hash-id/index.html +++ b/docs/next/security/hash-id/index.html @@ -4,7 +4,7 @@ Hash ID | Apiato - + @@ -20,7 +20,7 @@ You can set the HASH_ID_KEY in the .env file to any random string. Apiato defaults to the APP_KEY should this not be set.

    danger

    The HASH_ID_KEY acts as the salt during hashing of the ID. This should never be changed in production as it renders all previously generated IDs impossible to decode.

    Route Model Binding

    Laravel Route Model Binding feature is supported out of the box and Apiato will automatically decode the ID for you.

    - + \ No newline at end of file diff --git a/docs/next/security/password-reset/index.html b/docs/next/security/password-reset/index.html index 4260cddc6..3f8fadc48 100644 --- a/docs/next/security/password-reset/index.html +++ b/docs/next/security/password-reset/index.html @@ -4,7 +4,7 @@ Password Reset | Apiato - + @@ -15,7 +15,7 @@ in the allowed-reset-password-urls array within the appSection-authentication configuration.

    Routing

    To request a password reset link, call the /password/forgot endpoint with the user's email address.

    Resetting The Password

    To reset the user's password, call the /password/reset endpoint with the user's email address, new password, and password reset token.

    Process Flow

    1. Add your web app's password reset page URL, for example, https://myapp.com/password/reset, to the allowed-reset-password-urls array within the appSection-authentication configuration.

    2. Call the /password/forgot endpoint with a reset URL of your choice, which should correspond to one of the URLs in the allowed-reset-password-urls array. This endpoint will send the user an email containing a link like this:
      https://myapp.com/password/resetd?email=mohammad.alavi1990@gmail.com&token=51f8d80182f3785648c9b9dc7162719d158fc418b3cca86c14963638ec83d663

    3. When the user clicks on that link, they will be directed to your front-end app's password reset page. From there, you can collect the user's new password and make a call to the /password/reset endpoint with all the required fields to complete the password reset.

    - + \ No newline at end of file diff --git a/docs/next/security/registration/index.html b/docs/next/security/registration/index.html index 674aa99a9..b88935462 100644 --- a/docs/next/security/registration/index.html +++ b/docs/next/security/registration/index.html @@ -4,13 +4,13 @@ Registration | Apiato - +
    Version: Next 🚧

    Registration

    Apiato supports two default user registration methods:

    1. Register by Credentials
    2. Register by Social Account

    You can also extend these methods or add new ones to customize your registration process.

    Register by Credentials

    To register a new user, send a POST request to the /register endpoint.

    api.apiato.test/v1/register
    tip

    Don't forget to add Accept: application/json header to your request.

    The /register endpoint expects a string email address and a string password field.

    {
    "email": "gandalg@the.grey",
    "password": "password"
    }

    You should receive a response similar to the following:

    {
    "data": {
    "object": "User",
    "id": "XbPW7awNkzl83LD6",
    "name": null,
    "email": "john@doe.com",
    "email_verified_at": null,
    "gender": null,
    "birth": null
    },
    "meta": {
    "include": [
    "roles",
    "permissions"
    ],
    "custom": []
    }
    }

    Register by Social Account

    (Facebook, Twitter, Google, etc...)

    Checkout the Social Authentication documentation.

    - + \ No newline at end of file diff --git a/docs/next/tags/action/index.html b/docs/next/tags/action/index.html index e00029c9b..1cb85341b 100644 --- a/docs/next/tags/action/index.html +++ b/docs/next/tags/action/index.html @@ -4,13 +4,13 @@ 9 docs tagged with "action" | Apiato - +

    9 docs tagged with "action"

    View All Tags

    Actions

    Actions serve as the embodiment of the application's Use Cases,

    Commands

    Apiato commands are just Laravel Commands,

    Controllers

    Controllers are tasked with two primary responsibilities:

    Criterias

    To prevent overlap with the L5 Repository documentation, this page

    Events

    Apiato events are just Laravel Events,

    Repositories

    Apiato provides a powerful repository pattern implementation based on the L5 Repository package.

    Requests

    Requests components are a way to interact with the current HTTP request

    Sub Actions

    SubActions are designed to eliminate code duplication within Actions.

    Tasks

    Tasks are specialized classes that hold shared business logic,

    - + \ No newline at end of file diff --git a/docs/next/tags/api-versioning/index.html b/docs/next/tags/api-versioning/index.html index 1409352da..fd313cfa4 100644 --- a/docs/next/tags/api-versioning/index.html +++ b/docs/next/tags/api-versioning/index.html @@ -4,13 +4,13 @@ One doc tagged with "api-versioning" | Apiato - +

    One doc tagged with "api-versioning"

    View All Tags

    API Versioning

    Apiato provides a streamlined approach to implementing API versioning within your application.

    - + \ No newline at end of file diff --git a/docs/next/tags/architecture/index.html b/docs/next/tags/architecture/index.html index 05022df5e..f7f7c3821 100644 --- a/docs/next/tags/architecture/index.html +++ b/docs/next/tags/architecture/index.html @@ -4,13 +4,13 @@ 5 docs tagged with "architecture" | Apiato - +

    5 docs tagged with "architecture"

    View All Tags

    Container

    Containers are at the core of Apiato.

    Porto

    Porto is a modern software architectural pattern that offers developers a comprehensive set of guidelines,

    Request Lifecycle

    When using any tool in the "real world", you feel more confident if you understand how that tool works.

    - + \ No newline at end of file diff --git a/docs/next/tags/authorization/index.html b/docs/next/tags/authorization/index.html index e9995306f..0302aecaa 100644 --- a/docs/next/tags/authorization/index.html +++ b/docs/next/tags/authorization/index.html @@ -4,13 +4,13 @@ 2 docs tagged with "authorization" | Apiato - +

    2 docs tagged with "authorization"

    View All Tags

    Authorization

    Apiato provides a Role-Based Access Control (RBAC) through its Authorization Container.

    Policies

    Apiato policies are just Laravel Policies,

    - + \ No newline at end of file diff --git a/docs/next/tags/code-generator/index.html b/docs/next/tags/code-generator/index.html index 67cffcb92..e2d9d3f78 100644 --- a/docs/next/tags/code-generator/index.html +++ b/docs/next/tags/code-generator/index.html @@ -4,13 +4,13 @@ One doc tagged with "code-generator" | Apiato - +

    One doc tagged with "code-generator"

    View All Tags

    Code Generator

    Apiato comes with a powerful code generator that can help you to generate all the boilerplate code for your containers.

    - + \ No newline at end of file diff --git a/docs/next/tags/command/index.html b/docs/next/tags/command/index.html index e4a928342..a6af540f9 100644 --- a/docs/next/tags/command/index.html +++ b/docs/next/tags/command/index.html @@ -4,13 +4,13 @@ One doc tagged with "command" | Apiato - +

    One doc tagged with "command"

    View All Tags

    Commands

    Apiato commands are just Laravel Commands,

    - + \ No newline at end of file diff --git a/docs/next/tags/component/index.html b/docs/next/tags/component/index.html index a54ca0580..8c3a0ffdc 100644 --- a/docs/next/tags/component/index.html +++ b/docs/next/tags/component/index.html @@ -4,13 +4,13 @@ 29 docs tagged with "component" | Apiato - +

    29 docs tagged with "component"

    View All Tags

    Actions

    Actions serve as the embodiment of the application's Use Cases,

    Authorization

    Apiato provides a Role-Based Access Control (RBAC) through its Authorization Container.

    Commands

    Apiato commands are just Laravel Commands,

    Configs

    Apiato configs are just Laravel configs, and they function in the exact same way as Laravel configs.

    Controllers

    Controllers are tasked with two primary responsibilities:

    Criterias

    To prevent overlap with the L5 Repository documentation, this page

    Events

    Apiato events are just Laravel Events,

    Exceptions

    Exceptions are used to handle errors and exceptions in the application.

    Factories

    Apiato factories are just Laravel Factories,

    Helpers

    You have the option to create your own global "helper" PHP functions in designated directories, and Apiato will automatically autoload them for you.

    Jobs

    Apiato jobs are just Laravel Jobs,

    Mail

    Apiato mails are just Laravel Mails,

    Middlewares

    Apiato middlewares are just Laravel Middlewares,

    Migrations

    Apiato migrations are just Laravel Migrations,

    Models

    Models are responsible for representing the data of the application

    Notifications

    Apiato notifications are just Laravel Notifications,

    Policies

    Apiato policies are just Laravel Policies,

    Repositories

    Apiato provides a powerful repository pattern implementation based on the L5 Repository package.

    Requests

    Requests components are a way to interact with the current HTTP request

    Routes

    Routes are responsible for mapping incoming HTTP requests to their corresponding controller functions.

    Seeders

    Apiato seeders are just Laravel Seeders,

    Sub Actions

    SubActions are designed to eliminate code duplication within Actions.

    Tasks

    Tasks are specialized classes that hold shared business logic,

    Tests

    Apiato is built with testing in mind.

    Values

    Value Objects are short names for known "Value Objects",

    Views

    Views offer a convenient mechanism for organizing HTML content in separate files.

    - + \ No newline at end of file diff --git a/docs/next/tags/config/index.html b/docs/next/tags/config/index.html index af53b20fa..f59131315 100644 --- a/docs/next/tags/config/index.html +++ b/docs/next/tags/config/index.html @@ -4,13 +4,13 @@ One doc tagged with "config" | Apiato - +

    One doc tagged with "config"

    View All Tags

    Configs

    Apiato configs are just Laravel configs, and they function in the exact same way as Laravel configs.

    - + \ No newline at end of file diff --git a/docs/next/tags/container/index.html b/docs/next/tags/container/index.html index 94040b6fb..a346004ba 100644 --- a/docs/next/tags/container/index.html +++ b/docs/next/tags/container/index.html @@ -4,13 +4,13 @@ One doc tagged with "container" | Apiato - +

    One doc tagged with "container"

    View All Tags

    Container

    Containers are at the core of Apiato.

    - + \ No newline at end of file diff --git a/docs/next/tags/controller/index.html b/docs/next/tags/controller/index.html index 6b65769ec..6e9dd1452 100644 --- a/docs/next/tags/controller/index.html +++ b/docs/next/tags/controller/index.html @@ -4,13 +4,13 @@ 8 docs tagged with "controller" | Apiato - +

    8 docs tagged with "controller"

    View All Tags

    Actions

    Actions serve as the embodiment of the application's Use Cases,

    Controllers

    Controllers are tasked with two primary responsibilities:

    Middlewares

    Apiato middlewares are just Laravel Middlewares,

    Requests

    Requests components are a way to interact with the current HTTP request

    Routes

    Routes are responsible for mapping incoming HTTP requests to their corresponding controller functions.

    Sub Actions

    SubActions are designed to eliminate code duplication within Actions.

    Views

    Views offer a convenient mechanism for organizing HTML content in separate files.

    - + \ No newline at end of file diff --git a/docs/next/tags/criteria/index.html b/docs/next/tags/criteria/index.html index 20044f210..4c4a4f862 100644 --- a/docs/next/tags/criteria/index.html +++ b/docs/next/tags/criteria/index.html @@ -4,13 +4,13 @@ 2 docs tagged with "criteria" | Apiato - +

    2 docs tagged with "criteria"

    View All Tags

    Criterias

    To prevent overlap with the L5 Repository documentation, this page

    Repositories

    Apiato provides a powerful repository pattern implementation based on the L5 Repository package.

    - + \ No newline at end of file diff --git a/docs/next/tags/etag/index.html b/docs/next/tags/etag/index.html index 9cbb335e0..b7c3f6ee1 100644 --- a/docs/next/tags/etag/index.html +++ b/docs/next/tags/etag/index.html @@ -4,13 +4,13 @@ One doc tagged with "etag" | Apiato - +

    One doc tagged with "etag"

    View All Tags

    Etag

    The ETag or entity tag is part of HTTP, the protocol for the World Wide Web.

    - + \ No newline at end of file diff --git a/docs/next/tags/event/index.html b/docs/next/tags/event/index.html index ea3defa51..551caae68 100644 --- a/docs/next/tags/event/index.html +++ b/docs/next/tags/event/index.html @@ -4,13 +4,13 @@ 2 docs tagged with "event" | Apiato - +

    2 docs tagged with "event"

    View All Tags

    Events

    Apiato events are just Laravel Events,

    - + \ No newline at end of file diff --git a/docs/next/tags/exception/index.html b/docs/next/tags/exception/index.html index 13732200f..4580d7cfd 100644 --- a/docs/next/tags/exception/index.html +++ b/docs/next/tags/exception/index.html @@ -4,13 +4,13 @@ One doc tagged with "exception" | Apiato - +

    One doc tagged with "exception"

    View All Tags

    Exceptions

    Exceptions are used to handle errors and exceptions in the application.

    - + \ No newline at end of file diff --git a/docs/next/tags/factory/index.html b/docs/next/tags/factory/index.html index 68b834f67..35904755e 100644 --- a/docs/next/tags/factory/index.html +++ b/docs/next/tags/factory/index.html @@ -4,13 +4,13 @@ One doc tagged with "factory" | Apiato - +

    One doc tagged with "factory"

    View All Tags

    Factories

    Apiato factories are just Laravel Factories,

    - + \ No newline at end of file diff --git a/docs/next/tags/framework-feature/index.html b/docs/next/tags/framework-feature/index.html index cccbc0da5..7ab31c6d3 100644 --- a/docs/next/tags/framework-feature/index.html +++ b/docs/next/tags/framework-feature/index.html @@ -4,13 +4,13 @@ 6 docs tagged with "framework-feature" | Apiato - +

    6 docs tagged with "framework-feature"

    View All Tags

    API Versioning

    Apiato provides a streamlined approach to implementing API versioning within your application.

    Code Generator

    Apiato comes with a powerful code generator that can help you to generate all the boilerplate code for your containers.

    Etag

    The ETag or entity tag is part of HTTP, the protocol for the World Wide Web.

    Profiler

    Profiling is a crucial aspect of optimizing your application's performance

    - + \ No newline at end of file diff --git a/docs/next/tags/helper/index.html b/docs/next/tags/helper/index.html index 9e8f4971b..f5233fc24 100644 --- a/docs/next/tags/helper/index.html +++ b/docs/next/tags/helper/index.html @@ -4,13 +4,13 @@ One doc tagged with "helper" | Apiato - +

    One doc tagged with "helper"

    View All Tags

    Helpers

    You have the option to create your own global "helper" PHP functions in designated directories, and Apiato will automatically autoload them for you.

    - + \ No newline at end of file diff --git a/docs/next/tags/index.html b/docs/next/tags/index.html index b1a667263..1a344a142 100644 --- a/docs/next/tags/index.html +++ b/docs/next/tags/index.html @@ -4,13 +4,13 @@ Tags | Apiato - + - + \ No newline at end of file diff --git a/docs/next/tags/job/index.html b/docs/next/tags/job/index.html index 4ce8e5116..544221768 100644 --- a/docs/next/tags/job/index.html +++ b/docs/next/tags/job/index.html @@ -4,13 +4,13 @@ One doc tagged with "job" | Apiato - +

    One doc tagged with "job"

    View All Tags

    Jobs

    Apiato jobs are just Laravel Jobs,

    - + \ No newline at end of file diff --git a/docs/next/tags/lifecycle/index.html b/docs/next/tags/lifecycle/index.html index 529af6bb6..4658504d0 100644 --- a/docs/next/tags/lifecycle/index.html +++ b/docs/next/tags/lifecycle/index.html @@ -4,13 +4,13 @@ One doc tagged with "lifecycle" | Apiato - +

    One doc tagged with "lifecycle"

    View All Tags

    Request Lifecycle

    When using any tool in the "real world", you feel more confident if you understand how that tool works.

    - + \ No newline at end of file diff --git a/docs/next/tags/listener/index.html b/docs/next/tags/listener/index.html index d233b3caa..7f1d97150 100644 --- a/docs/next/tags/listener/index.html +++ b/docs/next/tags/listener/index.html @@ -4,13 +4,13 @@ One doc tagged with "listener" | Apiato - +

    One doc tagged with "listener"

    View All Tags

    Events

    Apiato events are just Laravel Events,

    - + \ No newline at end of file diff --git a/docs/next/tags/mail/index.html b/docs/next/tags/mail/index.html index 3bff2e0fd..44b972cf5 100644 --- a/docs/next/tags/mail/index.html +++ b/docs/next/tags/mail/index.html @@ -4,13 +4,13 @@ One doc tagged with "mail" | Apiato - +

    One doc tagged with "mail"

    View All Tags

    Mail

    Apiato mails are just Laravel Mails,

    - + \ No newline at end of file diff --git a/docs/next/tags/main-component/index.html b/docs/next/tags/main-component/index.html index 30e3ab1cc..dd092f2bb 100644 --- a/docs/next/tags/main-component/index.html +++ b/docs/next/tags/main-component/index.html @@ -4,13 +4,13 @@ 10 docs tagged with "main-component" | Apiato - +

    10 docs tagged with "main-component"

    View All Tags

    Actions

    Actions serve as the embodiment of the application's Use Cases,

    Controllers

    Controllers are tasked with two primary responsibilities:

    Exceptions

    Exceptions are used to handle errors and exceptions in the application.

    Models

    Models are responsible for representing the data of the application

    Requests

    Requests components are a way to interact with the current HTTP request

    Routes

    Routes are responsible for mapping incoming HTTP requests to their corresponding controller functions.

    Sub Actions

    SubActions are designed to eliminate code duplication within Actions.

    Tasks

    Tasks are specialized classes that hold shared business logic,

    Views

    Views offer a convenient mechanism for organizing HTML content in separate files.

    - + \ No newline at end of file diff --git a/docs/next/tags/middleware/index.html b/docs/next/tags/middleware/index.html index 2f8bfeda9..a29e26882 100644 --- a/docs/next/tags/middleware/index.html +++ b/docs/next/tags/middleware/index.html @@ -4,13 +4,13 @@ 2 docs tagged with "middleware" | Apiato - +

    2 docs tagged with "middleware"

    View All Tags

    Middlewares

    Apiato middlewares are just Laravel Middlewares,

    - + \ No newline at end of file diff --git a/docs/next/tags/migration/index.html b/docs/next/tags/migration/index.html index 0f9fedbb4..23c6a55ee 100644 --- a/docs/next/tags/migration/index.html +++ b/docs/next/tags/migration/index.html @@ -4,13 +4,13 @@ 2 docs tagged with "migration" | Apiato - +

    2 docs tagged with "migration"

    View All Tags

    Migrations

    Apiato migrations are just Laravel Migrations,

    Seeders

    Apiato seeders are just Laravel Seeders,

    - + \ No newline at end of file diff --git a/docs/next/tags/model/index.html b/docs/next/tags/model/index.html index afece1941..62b2928a9 100644 --- a/docs/next/tags/model/index.html +++ b/docs/next/tags/model/index.html @@ -4,13 +4,13 @@ 4 docs tagged with "model" | Apiato - +

    4 docs tagged with "model"

    View All Tags

    Factories

    Apiato factories are just Laravel Factories,

    Models

    Models are responsible for representing the data of the application

    Values

    Value Objects are short names for known "Value Objects",

    - + \ No newline at end of file diff --git a/docs/next/tags/notification/index.html b/docs/next/tags/notification/index.html index 99b594a5a..fa4dedae3 100644 --- a/docs/next/tags/notification/index.html +++ b/docs/next/tags/notification/index.html @@ -4,13 +4,13 @@ One doc tagged with "notification" | Apiato - +

    One doc tagged with "notification"

    View All Tags

    Notifications

    Apiato notifications are just Laravel Notifications,

    - + \ No newline at end of file diff --git a/docs/next/tags/optional-component/index.html b/docs/next/tags/optional-component/index.html index 4c4a3b354..cd169f853 100644 --- a/docs/next/tags/optional-component/index.html +++ b/docs/next/tags/optional-component/index.html @@ -4,13 +4,13 @@ 18 docs tagged with "optional-component" | Apiato - +

    18 docs tagged with "optional-component"

    View All Tags

    Authorization

    Apiato provides a Role-Based Access Control (RBAC) through its Authorization Container.

    Commands

    Apiato commands are just Laravel Commands,

    Configs

    Apiato configs are just Laravel configs, and they function in the exact same way as Laravel configs.

    Criterias

    To prevent overlap with the L5 Repository documentation, this page

    Events

    Apiato events are just Laravel Events,

    Factories

    Apiato factories are just Laravel Factories,

    Helpers

    You have the option to create your own global "helper" PHP functions in designated directories, and Apiato will automatically autoload them for you.

    Jobs

    Apiato jobs are just Laravel Jobs,

    Mail

    Apiato mails are just Laravel Mails,

    Middlewares

    Apiato middlewares are just Laravel Middlewares,

    Migrations

    Apiato migrations are just Laravel Migrations,

    Notifications

    Apiato notifications are just Laravel Notifications,

    Policies

    Apiato policies are just Laravel Policies,

    Repositories

    Apiato provides a powerful repository pattern implementation based on the L5 Repository package.

    Seeders

    Apiato seeders are just Laravel Seeders,

    Tests

    Apiato is built with testing in mind.

    Values

    Value Objects are short names for known "Value Objects",

    - + \ No newline at end of file diff --git a/docs/next/tags/policy/index.html b/docs/next/tags/policy/index.html index 3143370c0..728de5cd3 100644 --- a/docs/next/tags/policy/index.html +++ b/docs/next/tags/policy/index.html @@ -4,13 +4,13 @@ 2 docs tagged with "policy" | Apiato - +

    2 docs tagged with "policy"

    View All Tags

    Authorization

    Apiato provides a Role-Based Access Control (RBAC) through its Authorization Container.

    Policies

    Apiato policies are just Laravel Policies,

    - + \ No newline at end of file diff --git a/docs/next/tags/porto/index.html b/docs/next/tags/porto/index.html index 9052becbc..7671c9f6a 100644 --- a/docs/next/tags/porto/index.html +++ b/docs/next/tags/porto/index.html @@ -4,13 +4,13 @@ 5 docs tagged with "porto" | Apiato - +

    5 docs tagged with "porto"

    View All Tags

    Container

    Containers are at the core of Apiato.

    Porto

    Porto is a modern software architectural pattern that offers developers a comprehensive set of guidelines,

    Request Lifecycle

    When using any tool in the "real world", you feel more confident if you understand how that tool works.

    - + \ No newline at end of file diff --git a/docs/next/tags/profiler/index.html b/docs/next/tags/profiler/index.html index 528135623..7708eca35 100644 --- a/docs/next/tags/profiler/index.html +++ b/docs/next/tags/profiler/index.html @@ -4,13 +4,13 @@ One doc tagged with "profiler" | Apiato - +

    One doc tagged with "profiler"

    View All Tags

    Profiler

    Profiling is a crucial aspect of optimizing your application's performance

    - + \ No newline at end of file diff --git a/docs/next/tags/queue/index.html b/docs/next/tags/queue/index.html index be2ca38b5..5c3008163 100644 --- a/docs/next/tags/queue/index.html +++ b/docs/next/tags/queue/index.html @@ -4,13 +4,13 @@ 3 docs tagged with "queue" | Apiato - +

    3 docs tagged with "queue"

    View All Tags

    Jobs

    Apiato jobs are just Laravel Jobs,

    Mail

    Apiato mails are just Laravel Mails,

    Notifications

    Apiato notifications are just Laravel Notifications,

    - + \ No newline at end of file diff --git a/docs/next/tags/rate-limiting/index.html b/docs/next/tags/rate-limiting/index.html index d8cf011bf..e6cfb8bff 100644 --- a/docs/next/tags/rate-limiting/index.html +++ b/docs/next/tags/rate-limiting/index.html @@ -4,13 +4,13 @@ One doc tagged with "rate-limiting" | Apiato - +

    One doc tagged with "rate-limiting"

    View All Tags
    - + \ No newline at end of file diff --git a/docs/next/tags/repository/index.html b/docs/next/tags/repository/index.html index 5e3b30f94..35ea5d8cb 100644 --- a/docs/next/tags/repository/index.html +++ b/docs/next/tags/repository/index.html @@ -4,13 +4,13 @@ 3 docs tagged with "repository" | Apiato - +

    3 docs tagged with "repository"

    View All Tags

    Criterias

    To prevent overlap with the L5 Repository documentation, this page

    Models

    Models are responsible for representing the data of the application

    Repositories

    Apiato provides a powerful repository pattern implementation based on the L5 Repository package.

    - + \ No newline at end of file diff --git a/docs/next/tags/request/index.html b/docs/next/tags/request/index.html index 5d1333756..b5ead3629 100644 --- a/docs/next/tags/request/index.html +++ b/docs/next/tags/request/index.html @@ -4,13 +4,13 @@ 8 docs tagged with "request" | Apiato - +

    8 docs tagged with "request"

    View All Tags

    Actions

    Actions serve as the embodiment of the application's Use Cases,

    Authorization

    Apiato provides a Role-Based Access Control (RBAC) through its Authorization Container.

    Controllers

    Controllers are tasked with two primary responsibilities:

    Middlewares

    Apiato middlewares are just Laravel Middlewares,

    Policies

    Apiato policies are just Laravel Policies,

    Requests

    Requests components are a way to interact with the current HTTP request

    Routes

    Routes are responsible for mapping incoming HTTP requests to their corresponding controller functions.

    Sub Actions

    SubActions are designed to eliminate code duplication within Actions.

    - + \ No newline at end of file diff --git a/docs/next/tags/response/index.html b/docs/next/tags/response/index.html index 59d6a5760..fc475ab17 100644 --- a/docs/next/tags/response/index.html +++ b/docs/next/tags/response/index.html @@ -4,13 +4,13 @@ One doc tagged with "response" | Apiato - +

    One doc tagged with "response"

    View All Tags
    - + \ No newline at end of file diff --git a/docs/next/tags/role-based-access-control/index.html b/docs/next/tags/role-based-access-control/index.html index 40feb704f..67ff3eab0 100644 --- a/docs/next/tags/role-based-access-control/index.html +++ b/docs/next/tags/role-based-access-control/index.html @@ -4,13 +4,13 @@ One doc tagged with "role-based-access-control" | Apiato - +

    One doc tagged with "role-based-access-control"

    View All Tags
    - + \ No newline at end of file diff --git a/docs/next/tags/route/index.html b/docs/next/tags/route/index.html index 3b39b2f8a..d9e2dccad 100644 --- a/docs/next/tags/route/index.html +++ b/docs/next/tags/route/index.html @@ -4,13 +4,13 @@ 4 docs tagged with "route" | Apiato - +

    4 docs tagged with "route"

    View All Tags

    Controllers

    Controllers are tasked with two primary responsibilities:

    Middlewares

    Apiato middlewares are just Laravel Middlewares,

    Requests

    Requests components are a way to interact with the current HTTP request

    Routes

    Routes are responsible for mapping incoming HTTP requests to their corresponding controller functions.

    - + \ No newline at end of file diff --git a/docs/next/tags/seeder/index.html b/docs/next/tags/seeder/index.html index 8547efea4..8b84fcdac 100644 --- a/docs/next/tags/seeder/index.html +++ b/docs/next/tags/seeder/index.html @@ -4,13 +4,13 @@ 3 docs tagged with "seeder" | Apiato - +

    3 docs tagged with "seeder"

    View All Tags

    Factories

    Apiato factories are just Laravel Factories,

    Migrations

    Apiato migrations are just Laravel Migrations,

    Seeders

    Apiato seeders are just Laravel Seeders,

    - + \ No newline at end of file diff --git a/docs/next/tags/service-provider/index.html b/docs/next/tags/service-provider/index.html index c16282cdc..74c1a340e 100644 --- a/docs/next/tags/service-provider/index.html +++ b/docs/next/tags/service-provider/index.html @@ -4,13 +4,13 @@ 3 docs tagged with "service-provider" | Apiato - +

    3 docs tagged with "service-provider"

    View All Tags

    Events

    Apiato events are just Laravel Events,

    Middlewares

    Apiato middlewares are just Laravel Middlewares,

    - + \ No newline at end of file diff --git a/docs/next/tags/sub-action/index.html b/docs/next/tags/sub-action/index.html index 8147fec5b..5ec9cbfb1 100644 --- a/docs/next/tags/sub-action/index.html +++ b/docs/next/tags/sub-action/index.html @@ -4,13 +4,13 @@ 2 docs tagged with "sub-action" | Apiato - +

    2 docs tagged with "sub-action"

    View All Tags

    Sub Actions

    SubActions are designed to eliminate code duplication within Actions.

    Tasks

    Tasks are specialized classes that hold shared business logic,

    - + \ No newline at end of file diff --git a/docs/next/tags/task/index.html b/docs/next/tags/task/index.html index 858584320..99ec47b54 100644 --- a/docs/next/tags/task/index.html +++ b/docs/next/tags/task/index.html @@ -4,13 +4,13 @@ 4 docs tagged with "task" | Apiato - +

    4 docs tagged with "task"

    View All Tags

    Criterias

    To prevent overlap with the L5 Repository documentation, this page

    Events

    Apiato events are just Laravel Events,

    Repositories

    Apiato provides a powerful repository pattern implementation based on the L5 Repository package.

    Tasks

    Tasks are specialized classes that hold shared business logic,

    - + \ No newline at end of file diff --git a/docs/next/tags/test/index.html b/docs/next/tags/test/index.html index 66750dd7f..63bec181e 100644 --- a/docs/next/tags/test/index.html +++ b/docs/next/tags/test/index.html @@ -4,13 +4,13 @@ One doc tagged with "test" | Apiato - +

    One doc tagged with "test"

    View All Tags

    Tests

    Apiato is built with testing in mind.

    - + \ No newline at end of file diff --git a/docs/next/tags/testing/index.html b/docs/next/tags/testing/index.html index 86b6a5f92..390bbe065 100644 --- a/docs/next/tags/testing/index.html +++ b/docs/next/tags/testing/index.html @@ -4,13 +4,13 @@ One doc tagged with "testing" | Apiato - +

    One doc tagged with "testing"

    View All Tags

    Factories

    Apiato factories are just Laravel Factories,

    - + \ No newline at end of file diff --git a/docs/next/tags/transformer/index.html b/docs/next/tags/transformer/index.html index 187b29b4d..1b35a9811 100644 --- a/docs/next/tags/transformer/index.html +++ b/docs/next/tags/transformer/index.html @@ -4,13 +4,13 @@ 2 docs tagged with "transformer" | Apiato - +

    2 docs tagged with "transformer"

    View All Tags

    Controllers

    Controllers are tasked with two primary responsibilities:

    - + \ No newline at end of file diff --git a/docs/next/tags/value/index.html b/docs/next/tags/value/index.html index 98d1d7cd4..07ba9c9b7 100644 --- a/docs/next/tags/value/index.html +++ b/docs/next/tags/value/index.html @@ -4,13 +4,13 @@ One doc tagged with "value" | Apiato - +

    One doc tagged with "value"

    View All Tags

    Values

    Value Objects are short names for known "Value Objects",

    - + \ No newline at end of file diff --git a/docs/next/tags/view/index.html b/docs/next/tags/view/index.html index 1cb850ae8..3d4350057 100644 --- a/docs/next/tags/view/index.html +++ b/docs/next/tags/view/index.html @@ -4,13 +4,13 @@ One doc tagged with "view" | Apiato - +

    One doc tagged with "view"

    View All Tags

    Controllers

    Controllers are tasked with two primary responsibilities:

    - + \ No newline at end of file diff --git a/docs/pacakges/documentation/index.html b/docs/pacakges/documentation/index.html index e16fa58f6..83d4cf19f 100644 --- a/docs/pacakges/documentation/index.html +++ b/docs/pacakges/documentation/index.html @@ -4,7 +4,7 @@ Documentation | Apiato - + @@ -15,7 +15,7 @@ access-private-docs-permission values in documentation config. By default, users need access-private-docs permission to access private docs.

    Edit Default Generated Values in Templates

    Apiato by defaults generates 2 API documentations, each one has its own apidoc.json file. Both can be modified from the Documentation Container in app/Containers/Vendor/Documentation/ApiDocJs and need Source code modification.

    Edit the Documentation Header

    The header is usually the Overview of your API. It contains Info about authenticating users, making requests, responses, potential errors, rate limiting, pagination, query parameters and anything you want.

    All this information is written in app/Containers/Vendor/Documentation/ApiDocJs/shared/header.template.en.md file, and the same file is used as header for both private and public documentations.

    To edit its content you need to modify its source code and open the markdown file in any markdown editor and edit it.

    You will notice some variables like {{rate-limit}} and {{token-expires}}. Those are replaced when running apiato:apidoc with real values from your application configuration files.

    Feel free to extend them to include more info about your API from the app/Containers/Vendor/Documentation/Tasks/RenderTemplatesTask.php class.

    Localization for Documentation Header

    Default, the documentation title is in English en localization.

    See which locales are supported by going in app/Containers/Vendor/Documentation/ApiDocJs/shared

    There will be some header.template.{locale}.md files in the folder.

    You can change the language by adding APIDOC_LOCALE=ru to the .env file.

    If you didn't find a file with your locale, you can create it. You need to modify its source code and create new file like header.template.cn.md

    - + \ No newline at end of file diff --git a/docs/pacakges/index.html b/docs/pacakges/index.html index d7c4f2bc9..29c488b5f 100644 --- a/docs/pacakges/index.html +++ b/docs/pacakges/index.html @@ -4,7 +4,7 @@ Overview | Apiato - + @@ -21,7 +21,7 @@ that allows installing/updating containers.
  • You must provide the key extra.apiato.container.name. This key indicates the name of the folder (e.g., container) when installing the package to the app/Containers/Vendor directory. In the shown example, the container would be installed to app/Containers/Vendor/Foo.
  • - + \ No newline at end of file diff --git a/docs/pacakges/localization/index.html b/docs/pacakges/localization/index.html index 5ce52deae..f0c608719 100644 --- a/docs/pacakges/localization/index.html +++ b/docs/pacakges/localization/index.html @@ -4,7 +4,7 @@ Localization | Apiato - + @@ -41,7 +41,7 @@ language in this specific language (e.g., locale_name => Deutsch). Furthermore, the language name is outputted in the applications default name (e.g., configured in app.locale). This would result in default_name => German.

    The same applies to the regions that are defined (e.g., de-DE). Consequently, this results in locale_name => Deutschland and default_name = Germany.

    Tests

    To change the default language in your tests requests. You can set the env language in the phpunit.xml file.

    - + \ No newline at end of file diff --git a/docs/pacakges/social-authentication/index.html b/docs/pacakges/social-authentication/index.html index 3fc19b948..fd65b26dc 100644 --- a/docs/pacakges/social-authentication/index.html +++ b/docs/pacakges/social-authentication/index.html @@ -4,7 +4,7 @@ Social Authentication | Apiato - + @@ -19,7 +19,7 @@ to get the oauth info and user data respectively.

    Social Authentication Container Customization

    You can customize this container by publishing its config and modifying its values

    php artisan vendor:publish

    Config file will be copied to app/Ship/Configs/vendor-socialAuth.php

    Support new Auth Provider

    1. Publish the configs
    2. Create your new auth provider by implementing the App\Containers\Vendor\SocialAuth\Contracts\SocialAuthProvider contract.
      To get an idea about how to implement your own provider you can check out supported providers here app/Containers/Vendor/SocialAuth/SocialAuthProviders.
    3. Add your new provider to providers array in the vendor-socialAuth config.
        'providers' => [
    ...
    'something' => Location\Of\Your\Provider\SomthingSocialAuthProvider::class,
    ],

    Changing default used Repository, Transformer & DB user table name

    This container depends on Apiato default user repository, transformer & database user table name. If you changed those defaults you can update and provide them in the configs.

    - + \ No newline at end of file diff --git a/docs/prologue/contribution-guide/index.html b/docs/prologue/contribution-guide/index.html index 3ede6bbac..34b227222 100644 --- a/docs/prologue/contribution-guide/index.html +++ b/docs/prologue/contribution-guide/index.html @@ -4,7 +4,7 @@ Contribution Guide | Apiato - + @@ -37,7 +37,7 @@ after pull requests are merged. This allows us to focus on the content of the contribution and not the code style.

    Code of Conduct

    The Apiato code of conduct is derived from the Ruby code of conduct. Any violations of the code of conduct may be reported to Mohammad Alavi (mohammad.alavi1990@gmail.com):

    • Participants will be tolerant of opposing views.
    • Participants must ensure that their language and actions are free of personal attacks and disparaging personal remarks.
    • When interpreting the words and actions of others, participants should always assume good intentions.
    • Behavior that can be reasonably considered harassment will not be tolerated.
    - + \ No newline at end of file diff --git a/docs/prologue/release-notes/index.html b/docs/prologue/release-notes/index.html index 052fcc833..df236b3df 100644 --- a/docs/prologue/release-notes/index.html +++ b/docs/prologue/release-notes/index.html @@ -4,7 +4,7 @@ Release Notes | Apiato - + @@ -17,7 +17,7 @@ since major releases of Apiato do include breaking changes. However, we strive to always ensure you may update to a new major release in one day or less.

    Support Policy

    For all Apiato releases, bug fixes are provided for 18 months and security fixes are provided for 2 years.

    VersionPHP (*)ReleaseBug Fixes UntilSecurity Fixes Until
    107.3 - 8.1April 25th, 2021October 25th, 2022April 25th, 2023
    118.0 - 8.2April 27th, 2022October 27th, 2023April 27th, 2024
    128.1 - 8.2June 4th, 2023December 4th, 2024June 4th, 2025
    138.2Q1 2024August 5th, 2025February 3rd, 2026

    (*) Supported PHP versions

    Apiato 12

    Full Changelog: https://github.com/apiato/apiato/compare/v11.3.2...v12.0.0

    PHP 8.1

    Apiato 12.x requires a minimum PHP version of 8.1.

    Breaking Changes

    • Upgraded to Laravel v10 (All Laravel files (e.g. configs, .env, etc...) are now synced with the latest Laravel changes)
    • Updated Composer dependencies to their latest version
    • Laravel Passport route registration & customization has changed. Passport routes are now reside in a dedicated route file (Instead of registering them in the provider).
    • Middleware $routeMiddleware field is renamed to $middlewareAliases
    • Trimmed down the TestCase by removing some useless traits including:
    TestsMockHelperTrait
    TestsResponseHelperTrait
    • encode() method return value has changed -> In case of unencodable value (e.g. null), now returns null instead of ''
    • decode() method return value has changed -> In case of undecodable value (e.g. null), now returns null instead of []
    • StateKeeperTrait is removed from Request

    None Breaking Changes

    • Everything is refactored to use constructor injection instead of directly using the Service Container like so app(CreateUserByCredentialsTask::class)->run()
    • Added more tests and refactored the rest
    • Switched to invokable controllers
    \\ from
    Route::get('profile', [GetAuthenticatedUserController::class, 'getAuthenticatedUser']);
    \\ to
    Route::get('profile', GetAuthenticatedUserController::class);
    • All rotues are moved into the private documentation. e.g. RefreshProxyForWebClient.v1.public.php -> RefreshProxyForWebClient.v1.private.php
    • Added some getter methods to the Request including:
    withUrlParameters()
    getAccessArray()
    getDecodeArray()
    getUrlParametersArray()
    • Added a TestAssertionHelperTrait to the TestCase which provides some usefull assertions

    Bug Fixes

    • withMeta() method on ResponseTrait now correctly includes added meta data
    • Calling invokable controllers from routes #174
    • Exception when try to generate an WEB CRUD Controller from generator #171
    • PHP 8.1 warning on passing null to explode #176
    - + \ No newline at end of file diff --git a/docs/prologue/upgrade-guide/index.html b/docs/prologue/upgrade-guide/index.html index d68be6f7b..fa74f41b9 100644 --- a/docs/prologue/upgrade-guide/index.html +++ b/docs/prologue/upgrade-guide/index.html @@ -4,13 +4,13 @@ Upgrade Guide | Apiato - +
    - + \ No newline at end of file diff --git a/docs/security/authentication/index.html b/docs/security/authentication/index.html index 7015fd0c3..41c759923 100644 --- a/docs/security/authentication/index.html +++ b/docs/security/authentication/index.html @@ -4,7 +4,7 @@ Authentication | Apiato - + @@ -67,7 +67,7 @@ you can navigate to the app/Ship/Providers/RouteServiceProvider.php file and update the LOGIN constant.

    Passing The Access Token

    When calling routes that are protected by Passport, your application's API consumers should specify their access token as a Bearer token in the Authorization header of their request. For example, when using the Guzzle HTTP library:

    use Illuminate\Support\Facades\Http;

    $response = Http::withHeaders([
    'Accept' => 'application/json',
    'Authorization' => 'Bearer '.$accessToken,
    ])->get('http://api.apiato.test/v1/users');

    return $response->json();

    Configuration

    Most of the configuration is done in the app/Ships/Configs/apiato.php file.

    Social Authentication

    For Social Authentication visit the Social Authentication page.

    - + \ No newline at end of file diff --git a/docs/security/authorization/index.html b/docs/security/authorization/index.html index 0bfaf39f1..718553167 100644 --- a/docs/security/authorization/index.html +++ b/docs/security/authorization/index.html @@ -4,7 +4,7 @@ Authorization | Apiato - + @@ -18,8 +18,8 @@ By verifying permissions and roles at the request level, you ensure that unauthorized users are denied access before any further processing takes place.

    Default Roles & Permissions

    Apiato comes with some default Roles and Permissions. You can find them in app/Containers/AppSection/Authorization/Data/Seeders. -You can use them as a starting point, or delete them and create your own.

    Code Example

    Protecting the delete user endpoint with delete-users permission:

    use App\Ship\Parents\Requests\Request as ParentRequest;

    class DeleteUserRequest extends ParentRequest
    {
    protected array $access = [
    'permissions' => 'delete-users',
    'roles' => '',
    ];

    public function authorize(): bool
    {
    return $this->check([
    'hasAccess',
    ]);
    }
    }

    Authorization failed JSON response:

    {
    "message": "This action is unauthorized.",
    "errors": []
    }
    - +You can use them as a starting point, or delete them and create your own.

    Code Example

    Protecting the delete user endpoint with delete-users permission:

    use App\Ship\Parents\Requests\Request as ParentRequest;

    class DeleteUserRequest extends ParentRequest
    {
    protected array $access = [
    'permissions' => 'delete-users',
    'roles' => '',
    ];

    public function authorize(): bool
    {
    return $this->hasAccess();
    }
    }

    Authorization failed JSON response:

    {
    "message": "This action is unauthorized.",
    "errors": []
    }
    + \ No newline at end of file diff --git a/docs/security/email-varification/index.html b/docs/security/email-varification/index.html index d9ff8e5de..f1d24c593 100644 --- a/docs/security/email-varification/index.html +++ b/docs/security/email-varification/index.html @@ -4,7 +4,7 @@ Email Verification | Apiato - + @@ -22,7 +22,7 @@ when using a load balancer, set the protected $proxies = '*' in the app/Ship/Middlewares/TrustProxies.php or customize it according to your needs.

    - + \ No newline at end of file diff --git a/docs/security/hash-id/index.html b/docs/security/hash-id/index.html index e7fa302d6..2020cfeee 100644 --- a/docs/security/hash-id/index.html +++ b/docs/security/hash-id/index.html @@ -4,7 +4,7 @@ Hash ID | Apiato - + @@ -20,7 +20,7 @@ You can set the HASH_ID_KEY in the .env file to any random string. Apiato defaults to the APP_KEY should this not be set.

    danger

    The HASH_ID_KEY acts as the salt during hashing of the ID. This should never be changed in production as it renders all previously generated IDs impossible to decode.

    Route Model Binding

    Laravel Route Model Binding feature is supported out of the box and Apiato will automatically decode the ID for you.

    - + \ No newline at end of file diff --git a/docs/security/password-reset/index.html b/docs/security/password-reset/index.html index 61e16092b..84c8cbe0f 100644 --- a/docs/security/password-reset/index.html +++ b/docs/security/password-reset/index.html @@ -4,7 +4,7 @@ Password Reset | Apiato - + @@ -15,7 +15,7 @@ in the allowed-reset-password-urls array within the appSection-authentication configuration.

    Routing

    To request a password reset link, call the /password/forgot endpoint with the user's email address.

    Resetting The Password

    To reset the user's password, call the /password/reset endpoint with the user's email address, new password, and password reset token.

    Process Flow

    1. Add your web app's password reset page URL, for example, https://myapp.com/password/reset, to the allowed-reset-password-urls array within the appSection-authentication configuration.

    2. Call the /password/forgot endpoint with a reset URL of your choice, which should correspond to one of the URLs in the allowed-reset-password-urls array. This endpoint will send the user an email containing a link like this:
      https://myapp.com/password/resetd?email=mohammad.alavi1990@gmail.com&token=51f8d80182f3785648c9b9dc7162719d158fc418b3cca86c14963638ec83d663

    3. When the user clicks on that link, they will be directed to your front-end app's password reset page. From there, you can collect the user's new password and make a call to the /password/reset endpoint with all the required fields to complete the password reset.

    - + \ No newline at end of file diff --git a/docs/security/registration/index.html b/docs/security/registration/index.html index d00e5d7d4..b4de71c73 100644 --- a/docs/security/registration/index.html +++ b/docs/security/registration/index.html @@ -4,13 +4,13 @@ Registration | Apiato - +
    Version: 12.x

    Registration

    Apiato supports two default user registration methods:

    1. Register by Credentials
    2. Register by Social Account

    You can also extend these methods or add new ones to customize your registration process.

    Register by Credentials

    To register a new user, send a POST request to the /register endpoint.

    api.apiato.test/v1/register
    tip

    Don't forget to add Accept: application/json header to your request.

    The /register endpoint expects a string email address and a string password field.

    {
    "email": "gandalg@the.grey",
    "password": "password"
    }

    You should receive a response similar to the following:

    {
    "data": {
    "object": "User",
    "id": "XbPW7awNkzl83LD6",
    "name": null,
    "email": "john@doe.com",
    "email_verified_at": null,
    "gender": null,
    "birth": null
    },
    "meta": {
    "include": [
    "roles",
    "permissions"
    ],
    "custom": []
    }
    }

    Register by Social Account

    (Facebook, Twitter, Google, etc...)

    Checkout the Social Authentication documentation.

    - + \ No newline at end of file diff --git a/docs/tags/action/index.html b/docs/tags/action/index.html index 513c8e6ce..9b56673e1 100644 --- a/docs/tags/action/index.html +++ b/docs/tags/action/index.html @@ -4,13 +4,13 @@ 9 docs tagged with "action" | Apiato - +

    9 docs tagged with "action"

    View All Tags

    Actions

    Actions serve as the embodiment of the application's Use Cases,

    Commands

    Apiato commands are just Laravel Commands,

    Controllers

    Controllers are tasked with two primary responsibilities:

    Criterias

    To prevent overlap with the L5 Repository documentation, this page

    Events

    Apiato events are just Laravel Events,

    Repositories

    Apiato provides a powerful repository pattern implementation based on the L5 Repository package.

    Requests

    Requests components are a way to interact with the current HTTP request

    Sub Actions

    SubActions are designed to eliminate code duplication within Actions.

    Tasks

    Tasks are specialized classes that hold shared business logic,

    - + \ No newline at end of file diff --git a/docs/tags/api-versioning/index.html b/docs/tags/api-versioning/index.html index a0eaf3086..d4e0c8842 100644 --- a/docs/tags/api-versioning/index.html +++ b/docs/tags/api-versioning/index.html @@ -4,13 +4,13 @@ One doc tagged with "api-versioning" | Apiato - +

    One doc tagged with "api-versioning"

    View All Tags

    API Versioning

    Apiato provides a streamlined approach to implementing API versioning within your application.

    - + \ No newline at end of file diff --git a/docs/tags/architecture/index.html b/docs/tags/architecture/index.html index 64f863724..afddee71c 100644 --- a/docs/tags/architecture/index.html +++ b/docs/tags/architecture/index.html @@ -4,13 +4,13 @@ 5 docs tagged with "architecture" | Apiato - +

    5 docs tagged with "architecture"

    View All Tags

    Container

    Containers are at the core of Apiato.

    Porto

    Porto is a modern software architectural pattern that offers developers a comprehensive set of guidelines,

    Request Lifecycle

    When using any tool in the "real world", you feel more confident if you understand how that tool works.

    - + \ No newline at end of file diff --git a/docs/tags/authorization/index.html b/docs/tags/authorization/index.html index 4cb597748..b757a0961 100644 --- a/docs/tags/authorization/index.html +++ b/docs/tags/authorization/index.html @@ -4,13 +4,13 @@ 2 docs tagged with "authorization" | Apiato - +

    2 docs tagged with "authorization"

    View All Tags

    Authorization

    Apiato provides a Role-Based Access Control (RBAC) through its Authorization Container.

    Policies

    Apiato policies are just Laravel Policies,

    - + \ No newline at end of file diff --git a/docs/tags/code-generator/index.html b/docs/tags/code-generator/index.html index ac1c1cf04..b3d1c30f5 100644 --- a/docs/tags/code-generator/index.html +++ b/docs/tags/code-generator/index.html @@ -4,13 +4,13 @@ One doc tagged with "code-generator" | Apiato - +

    One doc tagged with "code-generator"

    View All Tags

    Code Generator

    Apiato comes with a powerful code generator that can help you to generate all the boilerplate code for your containers.

    - + \ No newline at end of file diff --git a/docs/tags/command/index.html b/docs/tags/command/index.html index c1958c134..5214e2e3c 100644 --- a/docs/tags/command/index.html +++ b/docs/tags/command/index.html @@ -4,13 +4,13 @@ One doc tagged with "command" | Apiato - +

    One doc tagged with "command"

    View All Tags

    Commands

    Apiato commands are just Laravel Commands,

    - + \ No newline at end of file diff --git a/docs/tags/component/index.html b/docs/tags/component/index.html index a58d0de68..31248c923 100644 --- a/docs/tags/component/index.html +++ b/docs/tags/component/index.html @@ -4,13 +4,13 @@ 29 docs tagged with "component" | Apiato - +

    29 docs tagged with "component"

    View All Tags

    Actions

    Actions serve as the embodiment of the application's Use Cases,

    Authorization

    Apiato provides a Role-Based Access Control (RBAC) through its Authorization Container.

    Commands

    Apiato commands are just Laravel Commands,

    Configs

    Apiato configs are just Laravel configs, and they function in the exact same way as Laravel configs.

    Controllers

    Controllers are tasked with two primary responsibilities:

    Criterias

    To prevent overlap with the L5 Repository documentation, this page

    Events

    Apiato events are just Laravel Events,

    Exceptions

    Exceptions are used to handle errors and exceptions in the application.

    Factories

    Apiato factories are just Laravel Factories,

    Helpers

    You have the option to create your own global "helper" PHP functions in designated directories, and Apiato will automatically autoload them for you.

    Jobs

    Apiato jobs are just Laravel Jobs,

    Mail

    Apiato mails are just Laravel Mails,

    Middlewares

    Apiato middlewares are just Laravel Middlewares,

    Migrations

    Apiato migrations are just Laravel Migrations,

    Models

    Models are responsible for representing the data of the application

    Notifications

    Apiato notifications are just Laravel Notifications,

    Policies

    Apiato policies are just Laravel Policies,

    Repositories

    Apiato provides a powerful repository pattern implementation based on the L5 Repository package.

    Requests

    Requests components are a way to interact with the current HTTP request

    Routes

    Routes are responsible for mapping incoming HTTP requests to their corresponding controller functions.

    Seeders

    Apiato seeders are just Laravel Seeders,

    Sub Actions

    SubActions are designed to eliminate code duplication within Actions.

    Tasks

    Tasks are specialized classes that hold shared business logic,

    Tests

    Apiato is built with testing in mind.

    Values

    Value Objects are short names for known "Value Objects",

    Views

    Views offer a convenient mechanism for organizing HTML content in separate files.

    - + \ No newline at end of file diff --git a/docs/tags/config/index.html b/docs/tags/config/index.html index 89db8ec36..3b776b630 100644 --- a/docs/tags/config/index.html +++ b/docs/tags/config/index.html @@ -4,13 +4,13 @@ One doc tagged with "config" | Apiato - +

    One doc tagged with "config"

    View All Tags

    Configs

    Apiato configs are just Laravel configs, and they function in the exact same way as Laravel configs.

    - + \ No newline at end of file diff --git a/docs/tags/container/index.html b/docs/tags/container/index.html index 26216581f..69fe44b52 100644 --- a/docs/tags/container/index.html +++ b/docs/tags/container/index.html @@ -4,13 +4,13 @@ One doc tagged with "container" | Apiato - +

    One doc tagged with "container"

    View All Tags

    Container

    Containers are at the core of Apiato.

    - + \ No newline at end of file diff --git a/docs/tags/controller/index.html b/docs/tags/controller/index.html index 1d4d6f436..bded26cb0 100644 --- a/docs/tags/controller/index.html +++ b/docs/tags/controller/index.html @@ -4,13 +4,13 @@ 8 docs tagged with "controller" | Apiato - +

    8 docs tagged with "controller"

    View All Tags

    Actions

    Actions serve as the embodiment of the application's Use Cases,

    Controllers

    Controllers are tasked with two primary responsibilities:

    Middlewares

    Apiato middlewares are just Laravel Middlewares,

    Requests

    Requests components are a way to interact with the current HTTP request

    Routes

    Routes are responsible for mapping incoming HTTP requests to their corresponding controller functions.

    Sub Actions

    SubActions are designed to eliminate code duplication within Actions.

    Views

    Views offer a convenient mechanism for organizing HTML content in separate files.

    - + \ No newline at end of file diff --git a/docs/tags/criteria/index.html b/docs/tags/criteria/index.html index bb175227d..d93fa4688 100644 --- a/docs/tags/criteria/index.html +++ b/docs/tags/criteria/index.html @@ -4,13 +4,13 @@ 2 docs tagged with "criteria" | Apiato - +

    2 docs tagged with "criteria"

    View All Tags

    Criterias

    To prevent overlap with the L5 Repository documentation, this page

    Repositories

    Apiato provides a powerful repository pattern implementation based on the L5 Repository package.

    - + \ No newline at end of file diff --git a/docs/tags/etag/index.html b/docs/tags/etag/index.html index a74523037..ec4b7aec2 100644 --- a/docs/tags/etag/index.html +++ b/docs/tags/etag/index.html @@ -4,13 +4,13 @@ One doc tagged with "etag" | Apiato - +

    One doc tagged with "etag"

    View All Tags

    Etag

    The ETag or entity tag is part of HTTP, the protocol for the World Wide Web.

    - + \ No newline at end of file diff --git a/docs/tags/event/index.html b/docs/tags/event/index.html index 73cf7bf98..af7e70705 100644 --- a/docs/tags/event/index.html +++ b/docs/tags/event/index.html @@ -4,13 +4,13 @@ 2 docs tagged with "event" | Apiato - +

    2 docs tagged with "event"

    View All Tags

    Events

    Apiato events are just Laravel Events,

    - + \ No newline at end of file diff --git a/docs/tags/exception/index.html b/docs/tags/exception/index.html index 3c173bb20..1e77de203 100644 --- a/docs/tags/exception/index.html +++ b/docs/tags/exception/index.html @@ -4,13 +4,13 @@ One doc tagged with "exception" | Apiato - +

    One doc tagged with "exception"

    View All Tags

    Exceptions

    Exceptions are used to handle errors and exceptions in the application.

    - + \ No newline at end of file diff --git a/docs/tags/factory/index.html b/docs/tags/factory/index.html index 812766df3..7dc5d6064 100644 --- a/docs/tags/factory/index.html +++ b/docs/tags/factory/index.html @@ -4,13 +4,13 @@ One doc tagged with "factory" | Apiato - +

    One doc tagged with "factory"

    View All Tags

    Factories

    Apiato factories are just Laravel Factories,

    - + \ No newline at end of file diff --git a/docs/tags/framework-feature/index.html b/docs/tags/framework-feature/index.html index 39de08837..24943665a 100644 --- a/docs/tags/framework-feature/index.html +++ b/docs/tags/framework-feature/index.html @@ -4,13 +4,13 @@ 6 docs tagged with "framework-feature" | Apiato - +

    6 docs tagged with "framework-feature"

    View All Tags

    API Versioning

    Apiato provides a streamlined approach to implementing API versioning within your application.

    Code Generator

    Apiato comes with a powerful code generator that can help you to generate all the boilerplate code for your containers.

    Etag

    The ETag or entity tag is part of HTTP, the protocol for the World Wide Web.

    Profiler

    Profiling is a crucial aspect of optimizing your application's performance

    - + \ No newline at end of file diff --git a/docs/tags/helper/index.html b/docs/tags/helper/index.html index 90d291417..94ed32476 100644 --- a/docs/tags/helper/index.html +++ b/docs/tags/helper/index.html @@ -4,13 +4,13 @@ One doc tagged with "helper" | Apiato - +

    One doc tagged with "helper"

    View All Tags

    Helpers

    You have the option to create your own global "helper" PHP functions in designated directories, and Apiato will automatically autoload them for you.

    - + \ No newline at end of file diff --git a/docs/tags/index.html b/docs/tags/index.html index eee51c70d..b156d3371 100644 --- a/docs/tags/index.html +++ b/docs/tags/index.html @@ -4,13 +4,13 @@ Tags | Apiato - + - + \ No newline at end of file diff --git a/docs/tags/job/index.html b/docs/tags/job/index.html index c17f730e5..3fc313d62 100644 --- a/docs/tags/job/index.html +++ b/docs/tags/job/index.html @@ -4,13 +4,13 @@ One doc tagged with "job" | Apiato - +

    One doc tagged with "job"

    View All Tags

    Jobs

    Apiato jobs are just Laravel Jobs,

    - + \ No newline at end of file diff --git a/docs/tags/lifecycle/index.html b/docs/tags/lifecycle/index.html index b1718efe5..6ec37fca1 100644 --- a/docs/tags/lifecycle/index.html +++ b/docs/tags/lifecycle/index.html @@ -4,13 +4,13 @@ One doc tagged with "lifecycle" | Apiato - +

    One doc tagged with "lifecycle"

    View All Tags

    Request Lifecycle

    When using any tool in the "real world", you feel more confident if you understand how that tool works.

    - + \ No newline at end of file diff --git a/docs/tags/listener/index.html b/docs/tags/listener/index.html index e793b490d..09b19735f 100644 --- a/docs/tags/listener/index.html +++ b/docs/tags/listener/index.html @@ -4,13 +4,13 @@ One doc tagged with "listener" | Apiato - +

    One doc tagged with "listener"

    View All Tags

    Events

    Apiato events are just Laravel Events,

    - + \ No newline at end of file diff --git a/docs/tags/mail/index.html b/docs/tags/mail/index.html index 48e32bdef..c919d2518 100644 --- a/docs/tags/mail/index.html +++ b/docs/tags/mail/index.html @@ -4,13 +4,13 @@ One doc tagged with "mail" | Apiato - +

    One doc tagged with "mail"

    View All Tags

    Mail

    Apiato mails are just Laravel Mails,

    - + \ No newline at end of file diff --git a/docs/tags/main-component/index.html b/docs/tags/main-component/index.html index c6bdc9965..4fd048012 100644 --- a/docs/tags/main-component/index.html +++ b/docs/tags/main-component/index.html @@ -4,13 +4,13 @@ 10 docs tagged with "main-component" | Apiato - +

    10 docs tagged with "main-component"

    View All Tags

    Actions

    Actions serve as the embodiment of the application's Use Cases,

    Controllers

    Controllers are tasked with two primary responsibilities:

    Exceptions

    Exceptions are used to handle errors and exceptions in the application.

    Models

    Models are responsible for representing the data of the application

    Requests

    Requests components are a way to interact with the current HTTP request

    Routes

    Routes are responsible for mapping incoming HTTP requests to their corresponding controller functions.

    Sub Actions

    SubActions are designed to eliminate code duplication within Actions.

    Tasks

    Tasks are specialized classes that hold shared business logic,

    Views

    Views offer a convenient mechanism for organizing HTML content in separate files.

    - + \ No newline at end of file diff --git a/docs/tags/middleware/index.html b/docs/tags/middleware/index.html index 38dd3b229..a16fa7f1d 100644 --- a/docs/tags/middleware/index.html +++ b/docs/tags/middleware/index.html @@ -4,13 +4,13 @@ 2 docs tagged with "middleware" | Apiato - +

    2 docs tagged with "middleware"

    View All Tags

    Middlewares

    Apiato middlewares are just Laravel Middlewares,

    - + \ No newline at end of file diff --git a/docs/tags/migration/index.html b/docs/tags/migration/index.html index b1bb627aa..df82d2438 100644 --- a/docs/tags/migration/index.html +++ b/docs/tags/migration/index.html @@ -4,13 +4,13 @@ 2 docs tagged with "migration" | Apiato - +

    2 docs tagged with "migration"

    View All Tags

    Migrations

    Apiato migrations are just Laravel Migrations,

    Seeders

    Apiato seeders are just Laravel Seeders,

    - + \ No newline at end of file diff --git a/docs/tags/model/index.html b/docs/tags/model/index.html index 68dedc723..238b38457 100644 --- a/docs/tags/model/index.html +++ b/docs/tags/model/index.html @@ -4,13 +4,13 @@ 4 docs tagged with "model" | Apiato - +

    4 docs tagged with "model"

    View All Tags

    Factories

    Apiato factories are just Laravel Factories,

    Models

    Models are responsible for representing the data of the application

    Values

    Value Objects are short names for known "Value Objects",

    - + \ No newline at end of file diff --git a/docs/tags/notification/index.html b/docs/tags/notification/index.html index a2556f7dd..c6943969f 100644 --- a/docs/tags/notification/index.html +++ b/docs/tags/notification/index.html @@ -4,13 +4,13 @@ One doc tagged with "notification" | Apiato - +

    One doc tagged with "notification"

    View All Tags

    Notifications

    Apiato notifications are just Laravel Notifications,

    - + \ No newline at end of file diff --git a/docs/tags/optional-component/index.html b/docs/tags/optional-component/index.html index 6dc7f0598..980beb393 100644 --- a/docs/tags/optional-component/index.html +++ b/docs/tags/optional-component/index.html @@ -4,13 +4,13 @@ 18 docs tagged with "optional-component" | Apiato - +

    18 docs tagged with "optional-component"

    View All Tags

    Authorization

    Apiato provides a Role-Based Access Control (RBAC) through its Authorization Container.

    Commands

    Apiato commands are just Laravel Commands,

    Configs

    Apiato configs are just Laravel configs, and they function in the exact same way as Laravel configs.

    Criterias

    To prevent overlap with the L5 Repository documentation, this page

    Events

    Apiato events are just Laravel Events,

    Factories

    Apiato factories are just Laravel Factories,

    Helpers

    You have the option to create your own global "helper" PHP functions in designated directories, and Apiato will automatically autoload them for you.

    Jobs

    Apiato jobs are just Laravel Jobs,

    Mail

    Apiato mails are just Laravel Mails,

    Middlewares

    Apiato middlewares are just Laravel Middlewares,

    Migrations

    Apiato migrations are just Laravel Migrations,

    Notifications

    Apiato notifications are just Laravel Notifications,

    Policies

    Apiato policies are just Laravel Policies,

    Repositories

    Apiato provides a powerful repository pattern implementation based on the L5 Repository package.

    Seeders

    Apiato seeders are just Laravel Seeders,

    Tests

    Apiato is built with testing in mind.

    Values

    Value Objects are short names for known "Value Objects",

    - + \ No newline at end of file diff --git a/docs/tags/policy/index.html b/docs/tags/policy/index.html index b05bd8b42..0d33bbd06 100644 --- a/docs/tags/policy/index.html +++ b/docs/tags/policy/index.html @@ -4,13 +4,13 @@ 2 docs tagged with "policy" | Apiato - +

    2 docs tagged with "policy"

    View All Tags

    Authorization

    Apiato provides a Role-Based Access Control (RBAC) through its Authorization Container.

    Policies

    Apiato policies are just Laravel Policies,

    - + \ No newline at end of file diff --git a/docs/tags/porto/index.html b/docs/tags/porto/index.html index 3af5554e8..369cfb894 100644 --- a/docs/tags/porto/index.html +++ b/docs/tags/porto/index.html @@ -4,13 +4,13 @@ 5 docs tagged with "porto" | Apiato - +

    5 docs tagged with "porto"

    View All Tags

    Container

    Containers are at the core of Apiato.

    Porto

    Porto is a modern software architectural pattern that offers developers a comprehensive set of guidelines,

    Request Lifecycle

    When using any tool in the "real world", you feel more confident if you understand how that tool works.

    - + \ No newline at end of file diff --git a/docs/tags/profiler/index.html b/docs/tags/profiler/index.html index 873752733..8eb412c5a 100644 --- a/docs/tags/profiler/index.html +++ b/docs/tags/profiler/index.html @@ -4,13 +4,13 @@ One doc tagged with "profiler" | Apiato - +

    One doc tagged with "profiler"

    View All Tags

    Profiler

    Profiling is a crucial aspect of optimizing your application's performance

    - + \ No newline at end of file diff --git a/docs/tags/queue/index.html b/docs/tags/queue/index.html index e310f0fc1..467ce2a26 100644 --- a/docs/tags/queue/index.html +++ b/docs/tags/queue/index.html @@ -4,13 +4,13 @@ 3 docs tagged with "queue" | Apiato - +

    3 docs tagged with "queue"

    View All Tags

    Jobs

    Apiato jobs are just Laravel Jobs,

    Mail

    Apiato mails are just Laravel Mails,

    Notifications

    Apiato notifications are just Laravel Notifications,

    - + \ No newline at end of file diff --git a/docs/tags/rate-limiting/index.html b/docs/tags/rate-limiting/index.html index 3e1272e16..b57afce76 100644 --- a/docs/tags/rate-limiting/index.html +++ b/docs/tags/rate-limiting/index.html @@ -4,13 +4,13 @@ One doc tagged with "rate-limiting" | Apiato - +

    One doc tagged with "rate-limiting"

    View All Tags
    - + \ No newline at end of file diff --git a/docs/tags/repository/index.html b/docs/tags/repository/index.html index 9583f9fd1..7cdec6a72 100644 --- a/docs/tags/repository/index.html +++ b/docs/tags/repository/index.html @@ -4,13 +4,13 @@ 3 docs tagged with "repository" | Apiato - +

    3 docs tagged with "repository"

    View All Tags

    Criterias

    To prevent overlap with the L5 Repository documentation, this page

    Models

    Models are responsible for representing the data of the application

    Repositories

    Apiato provides a powerful repository pattern implementation based on the L5 Repository package.

    - + \ No newline at end of file diff --git a/docs/tags/request/index.html b/docs/tags/request/index.html index d618031da..159817497 100644 --- a/docs/tags/request/index.html +++ b/docs/tags/request/index.html @@ -4,13 +4,13 @@ 8 docs tagged with "request" | Apiato - +

    8 docs tagged with "request"

    View All Tags

    Actions

    Actions serve as the embodiment of the application's Use Cases,

    Authorization

    Apiato provides a Role-Based Access Control (RBAC) through its Authorization Container.

    Controllers

    Controllers are tasked with two primary responsibilities:

    Middlewares

    Apiato middlewares are just Laravel Middlewares,

    Policies

    Apiato policies are just Laravel Policies,

    Requests

    Requests components are a way to interact with the current HTTP request

    Routes

    Routes are responsible for mapping incoming HTTP requests to their corresponding controller functions.

    Sub Actions

    SubActions are designed to eliminate code duplication within Actions.

    - + \ No newline at end of file diff --git a/docs/tags/response/index.html b/docs/tags/response/index.html index 1488563ec..5d71570a7 100644 --- a/docs/tags/response/index.html +++ b/docs/tags/response/index.html @@ -4,13 +4,13 @@ One doc tagged with "response" | Apiato - +

    One doc tagged with "response"

    View All Tags
    - + \ No newline at end of file diff --git a/docs/tags/role-based-access-control/index.html b/docs/tags/role-based-access-control/index.html index 17411acb2..853940aad 100644 --- a/docs/tags/role-based-access-control/index.html +++ b/docs/tags/role-based-access-control/index.html @@ -4,13 +4,13 @@ One doc tagged with "role-based-access-control" | Apiato - +

    One doc tagged with "role-based-access-control"

    View All Tags
    - + \ No newline at end of file diff --git a/docs/tags/route/index.html b/docs/tags/route/index.html index 6eb52ad79..949f8a2b6 100644 --- a/docs/tags/route/index.html +++ b/docs/tags/route/index.html @@ -4,13 +4,13 @@ 4 docs tagged with "route" | Apiato - +

    4 docs tagged with "route"

    View All Tags

    Controllers

    Controllers are tasked with two primary responsibilities:

    Middlewares

    Apiato middlewares are just Laravel Middlewares,

    Requests

    Requests components are a way to interact with the current HTTP request

    Routes

    Routes are responsible for mapping incoming HTTP requests to their corresponding controller functions.

    - + \ No newline at end of file diff --git a/docs/tags/seeder/index.html b/docs/tags/seeder/index.html index 0dd9b9a92..a526843f3 100644 --- a/docs/tags/seeder/index.html +++ b/docs/tags/seeder/index.html @@ -4,13 +4,13 @@ 3 docs tagged with "seeder" | Apiato - +

    3 docs tagged with "seeder"

    View All Tags

    Factories

    Apiato factories are just Laravel Factories,

    Migrations

    Apiato migrations are just Laravel Migrations,

    Seeders

    Apiato seeders are just Laravel Seeders,

    - + \ No newline at end of file diff --git a/docs/tags/service-provider/index.html b/docs/tags/service-provider/index.html index 99a6f65e0..85c46ed39 100644 --- a/docs/tags/service-provider/index.html +++ b/docs/tags/service-provider/index.html @@ -4,13 +4,13 @@ 3 docs tagged with "service-provider" | Apiato - +

    3 docs tagged with "service-provider"

    View All Tags

    Events

    Apiato events are just Laravel Events,

    Middlewares

    Apiato middlewares are just Laravel Middlewares,

    - + \ No newline at end of file diff --git a/docs/tags/sub-action/index.html b/docs/tags/sub-action/index.html index bd0ae9943..77f0cbb70 100644 --- a/docs/tags/sub-action/index.html +++ b/docs/tags/sub-action/index.html @@ -4,13 +4,13 @@ 2 docs tagged with "sub-action" | Apiato - +

    2 docs tagged with "sub-action"

    View All Tags

    Sub Actions

    SubActions are designed to eliminate code duplication within Actions.

    Tasks

    Tasks are specialized classes that hold shared business logic,

    - + \ No newline at end of file diff --git a/docs/tags/task/index.html b/docs/tags/task/index.html index 502acd6b1..2571d44bc 100644 --- a/docs/tags/task/index.html +++ b/docs/tags/task/index.html @@ -4,13 +4,13 @@ 4 docs tagged with "task" | Apiato - +

    4 docs tagged with "task"

    View All Tags

    Criterias

    To prevent overlap with the L5 Repository documentation, this page

    Events

    Apiato events are just Laravel Events,

    Repositories

    Apiato provides a powerful repository pattern implementation based on the L5 Repository package.

    Tasks

    Tasks are specialized classes that hold shared business logic,

    - + \ No newline at end of file diff --git a/docs/tags/test/index.html b/docs/tags/test/index.html index 90ed4e16e..870a88b76 100644 --- a/docs/tags/test/index.html +++ b/docs/tags/test/index.html @@ -4,13 +4,13 @@ One doc tagged with "test" | Apiato - +

    One doc tagged with "test"

    View All Tags

    Tests

    Apiato is built with testing in mind.

    - + \ No newline at end of file diff --git a/docs/tags/testing/index.html b/docs/tags/testing/index.html index 11aa8cea7..dd0eeb8a7 100644 --- a/docs/tags/testing/index.html +++ b/docs/tags/testing/index.html @@ -4,13 +4,13 @@ One doc tagged with "testing" | Apiato - +

    One doc tagged with "testing"

    View All Tags

    Factories

    Apiato factories are just Laravel Factories,

    - + \ No newline at end of file diff --git a/docs/tags/transformer/index.html b/docs/tags/transformer/index.html index 1c2602f90..d831f638c 100644 --- a/docs/tags/transformer/index.html +++ b/docs/tags/transformer/index.html @@ -4,13 +4,13 @@ 2 docs tagged with "transformer" | Apiato - +

    2 docs tagged with "transformer"

    View All Tags

    Controllers

    Controllers are tasked with two primary responsibilities:

    - + \ No newline at end of file diff --git a/docs/tags/value/index.html b/docs/tags/value/index.html index 0948c6edb..0ae64aa76 100644 --- a/docs/tags/value/index.html +++ b/docs/tags/value/index.html @@ -4,13 +4,13 @@ One doc tagged with "value" | Apiato - +

    One doc tagged with "value"

    View All Tags

    Values

    Value Objects are short names for known "Value Objects",

    - + \ No newline at end of file diff --git a/docs/tags/view/index.html b/docs/tags/view/index.html index ec003ba92..fa68d7cc7 100644 --- a/docs/tags/view/index.html +++ b/docs/tags/view/index.html @@ -4,13 +4,13 @@ One doc tagged with "view" | Apiato - +

    One doc tagged with "view"

    View All Tags

    Controllers

    Controllers are tasked with two primary responsibilities:

    - + \ No newline at end of file diff --git a/index.html b/index.html index 49bcbc7ba..1e9dcfc1f 100644 --- a/index.html +++ b/index.html @@ -4,13 +4,13 @@ Apiato - +

    Unearth Scale, Ignite Speed

    Conquer Complexity

    Apiato stands as a scalable and enterprise-grade framework layered atop Laravel, addressing a crucial gap in the development of expansive applications: the Architecture.

    Features

    Detailed Documentation, with Examples, Definitions, Principles and Guidelines.

    Powerful Code Generator

    API Documentations Generator
    (using APIDocJS)

    API Versioning

    OAuth2.0 Authentication
    (using Laravel Passport)

    Hash ID Support

    Role-Based Access Control (RBAC)
    (using Laravel Permission)

    Query Parameters
    (orderBy, sortedBy, etc...)

    Pagination, Limit & Offset

    Data Caching

    Shallow ETag Support

    Performance Profiler

    Localization

    Social Authentication
    (using Laravel Socialite)

    Useful Tests Helpers

    Multiple Response Payload Formats
    (JSON, Data Array & Pure Data)

    - + \ No newline at end of file