diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml new file mode 100644 index 000000000..dc659ea97 --- /dev/null +++ b/.github/workflows/docs.yaml @@ -0,0 +1,75 @@ +name: Split Docs + +on: + push: + paths: + - 'docs/**' + branches: + - main + workflow_dispatch: + +jobs: + translate: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 8.1 + tools: phpize + extensions: swoole, redis + ini-values: extension=opencc + + - name: Install OpenCC + run: | + sudo apt-get install libopencc-dev -y + + - name: Build opencc4php + run: | + git clone https://github.com/nauxliu/opencc4php.git --depth 1 + cd opencc4php + phpize + ./configure + make + sudo make install + php --ri opencc + cd .. + rm -rf opencc4php + + - name: Setup Dependencies + run: composer install + + - name: Start Translate + run: php bin/doc-translate + + - name: Commit Updated + uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: Update docs and translate + split: + needs: translate + name: Split on branch ${{ github.ref }} + if: github.repository == 'friendsofhyperf/components' + runs-on: ubuntu-22.04 + env: + SSH_PRIVATE_KEY: ${{ secrets.SPLIT_PRIVATE_KEY }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Setup Private Key + run: | + mkdir -p ~/.ssh + echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa + chmod 600 ~/.ssh/id_rsa + echo "StrictHostKeyChecking no" >> ~/.ssh/config + - name: Split And Push + run: | + git config pull.rebase true + git config --global user.email "huangdijia@gmail.com" + git config --global user.name "Deeka Wong" + ./bin/split-docs.sh \ No newline at end of file diff --git a/.github/workflows/split-docs.yaml b/.github/workflows/split-docs.yaml deleted file mode 100644 index 84cc8ad0f..000000000 --- a/.github/workflows/split-docs.yaml +++ /dev/null @@ -1,34 +0,0 @@ -name: Split Docs - -on: - push: - paths: - - 'docs/**' - branches: - - main - workflow_dispatch: - -jobs: - split: - name: Split on branch ${{ github.ref }} - if: github.repository == 'friendsofhyperf/components' - runs-on: ubuntu-22.04 - env: - SSH_PRIVATE_KEY: ${{ secrets.SPLIT_PRIVATE_KEY }} - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Setup Private Key - run: | - mkdir -p ~/.ssh - echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa - echo "StrictHostKeyChecking no" >> ~/.ssh/config - - name: Split And Push - run: | - git config pull.rebase true - git config --global user.email "huangdijia@gmail.com" - git config --global user.name "Deeka Wong" - ./bin/split-docs.sh \ No newline at end of file diff --git a/bin/doc-translate b/bin/doc-translate new file mode 100755 index 000000000..a3acc52d8 --- /dev/null +++ b/bin/doc-translate @@ -0,0 +1,63 @@ +#!/usr/bin/env php + [ + 'targetDir' => BASE_PATH . '/docs/zh-tw/', + 'rule' => 's2twp.json', + ], + 'zh-hk' => [ + 'targetDir' => BASE_PATH . '/docs/zh-hk/', + 'rule' => 's2hk.json', + ], + // 'en' => [ + // 'targetDir' => BASE_PATH . '/docs/en/', + // 'rule' => 's2en.json', + // ], +]; + +$finder = new Finder(); +$finder->files()->in(BASE_PATH . '/docs/zh-cn'); + +foreach ($config as $key => $item) { + $od = opencc_open($item['rule']); + foreach ($finder as $fileInfo) { + $targetDir = $item['targetDir']; + $targetPath = $targetDir . $fileInfo->getRelativePath(); + $isCreateDir = false; + if (! is_dir($targetPath)) { + mkdir($targetPath, 0777, true); + chmod($targetPath, 0777); + $isCreateDir = true; + } + if (! is_writable($targetPath)) { + echo sprintf('Target path %s is not writable.' . PHP_EOL, $targetPath); + } + if ($fileInfo->getExtension() === 'md') { + $translated = opencc_convert($fileInfo->getContents(), $od); + $translated = str_replace('](zh-cn/', '](' . $key . '/', $translated); + $translated = str_replace('](/zh-cn/', '](/' . $key . '/', $translated); + $translated = str_replace('](./zh-cn/', '](./' . $key . '/', $translated); + $targetTranslatedPath = $targetDir . $fileInfo->getRelativePathname(); + @file_put_contents($targetTranslatedPath, $translated); + } else { + $targetTranslatedPath = $targetDir . $fileInfo->getRelativePathname(); + @copy($fileInfo->getRealPath(), $targetTranslatedPath); + } + } + opencc_close($od); +} diff --git a/bin/generate-repository-doc.sh b/bin/generate-repository-doc.sh old mode 100644 new mode 100755 diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index ffacd0640..2b5e5047c 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -1,10 +1,21 @@ import { defineConfig } from 'vitepress' + import enGetConfig from "./src/en/config"; import enGetNavs from "./src/en/nav"; import enGetSidebar from "./src/en/sidebars"; -import zhGetConfig from "./src/zh/config"; -import zhGetNavs from "./src/zh/nav"; -import zhGetSidebar from "./src/zh/sidebars"; + +import cnGetConfig from "./src/zh-cn/config"; +import cnGetNavs from "./src/zh-cn/nav"; +import cnGetSidebar from "./src/zh-cn/sidebars"; + +import hkGetConfig from "./src/zh-hk/config"; +import hkGetNavs from "./src/zh-hk/nav"; +import hkGetSidebar from "./src/zh-hk/sidebars"; + +import twGetConfig from "./src/zh-tw/config"; +import twGetNavs from "./src/zh-tw/nav"; +import twGetSidebar from "./src/zh-tw/sidebars"; + import taskLists from 'markdown-it-task-lists' // https://vitepress.dev/reference/site-config @@ -40,9 +51,37 @@ export default defineConfig({ ], locales:{ root:{ - label:"中文", + label:"简体中文", lang:"zh", - ...zhGetConfig, + ...cnGetConfig, + }, + "zh-hk":{ + label:"繁體中文(港)", + lang:"zh-hk", + link:"/zh-hk/index", + ...hkGetConfig, + themeConfig:{ + logo: '/logo.svg', + nav: hkGetNavs, + sidebar:hkGetSidebar, + outline:{ + level:[2 ,4], + }, + } + }, + "zh-tw":{ + label:"繁體中文(臺)", + lang:"zh-tw", + link:"/zh-tw/index", + ...twGetConfig, + themeConfig:{ + logo: '/logo.svg', + nav: twGetNavs, + sidebar:twGetSidebar, + outline:{ + level:[2 ,4], + }, + } }, en:{ label:"English", @@ -128,9 +167,9 @@ export default defineConfig({ i18nRouting:false, // https://vitepress.dev/reference/default-theme-config - nav: zhGetNavs, + nav: cnGetNavs, - sidebar: zhGetSidebar, + sidebar: cnGetSidebar, socialLinks: [ { icon: 'github', link: 'https://github.com/friendsofhyperf/components' }, diff --git a/docs/.vitepress/src/zh/config.ts b/docs/.vitepress/src/zh-cn/config.ts similarity index 100% rename from docs/.vitepress/src/zh/config.ts rename to docs/.vitepress/src/zh-cn/config.ts diff --git a/docs/.vitepress/src/zh/nav.ts b/docs/.vitepress/src/zh-cn/nav.ts similarity index 65% rename from docs/.vitepress/src/zh/nav.ts rename to docs/.vitepress/src/zh-cn/nav.ts index 226c075ca..e9418f67f 100644 --- a/docs/.vitepress/src/zh/nav.ts +++ b/docs/.vitepress/src/zh-cn/nav.ts @@ -1,9 +1,8 @@ import {DefaultTheme} from "vitepress"; const nav:DefaultTheme.NavItem[] = [ - // { text: '入门', link: '/zh_CN/guide/' }, - { text: '组件', link: '/zh_CN/components/' }, - { text: 'FAQ', link: '/zh_CN/faq/index' }, + { text: '组件', link: '/zh-cn/components/' }, + { text: 'FAQ', link: '/zh-cn/faq/index' }, { text: '更多', items:[ { text: 'Hyperf', link: 'https://hyperf.wiki/' }, { text: 'MineAdmin', link: 'https://www.mineadmin.com/' } diff --git a/docs/.vitepress/src/zh/sidebars.ts b/docs/.vitepress/src/zh-cn/sidebars.ts similarity index 53% rename from docs/.vitepress/src/zh/sidebars.ts rename to docs/.vitepress/src/zh-cn/sidebars.ts index e3a4f38bb..57b2fe3da 100644 --- a/docs/.vitepress/src/zh/sidebars.ts +++ b/docs/.vitepress/src/zh-cn/sidebars.ts @@ -1,238 +1,218 @@ import {DefaultTheme} from "vitepress"; const sidebar:DefaultTheme.Sidebar = { - // '/zh_CN/guide/': [ - // { - // text: '介绍', - // items: [ - // { - // text: '关于 FriendsOfHyperf', - // link: '/zh_CN/guide/introduce/about', - // }, - // ] - // }, - // { - // text: '快速开始', - // items: [ - // { - // text: "支持的组件列表", - // link: "/zh_CN/guide/start/components" - // } - // ] - // } - // ], - '/zh_CN/faq/':[{ + '/zh-cn/faq/':[{ text: '常见问题', items: [ { text: '关于 FriendsOfHyperf', - link: '/zh_CN/faq/about' + link: '/zh-cn/faq/about' }, { text: '如何使用', - link: '/zh_CN/faq/how-to-use' + link: '/zh-cn/faq/how-to-use' } ] }], - '/zh_CN/components/':[ + '/zh-cn/components/':[ { text: '组件', items: [ { text: 'Amqp Job', - link: '/zh_CN/components/amqp-job.md' + link: '/zh-cn/components/amqp-job.md' }, { text: 'Cache', - link: '/zh_CN/components/cache.md' + link: '/zh-cn/components/cache.md' }, { text: 'Command Signals', - link: '/zh_CN/components/command-signals.md' + link: '/zh-cn/components/command-signals.md' }, { text: 'Command Validation', - link: '/zh_CN/components/command-validation.md' + link: '/zh-cn/components/command-validation.md' }, { text: 'Compoships', - link: '/zh_CN/components/compoships.md' + link: '/zh-cn/components/compoships.md' }, { text: 'Confd', - link: '/zh_CN/components/confd.md' + link: '/zh-cn/components/confd.md' }, { text: 'Config Consul', - link: '/zh_CN/components/config-consul.md' + link: '/zh-cn/components/config-consul.md' }, { text: 'Console Spinner', - link: '/zh_CN/components/console-spinner.md' + link: '/zh-cn/components/console-spinner.md' }, { text: 'Di Plus', - link: '/zh_CN/components/di-plus.md' + link: '/zh-cn/components/di-plus.md' }, { text: 'Elasticsearch', - link: '/zh_CN/components/elasticsearch.md' + link: '/zh-cn/components/elasticsearch.md' }, { text: 'Encryption', - link: '/zh_CN/components/encryption.md' + link: '/zh-cn/components/encryption.md' }, { text: 'Exception Event', - link: '/zh_CN/components/exception-event.md' + link: '/zh-cn/components/exception-event.md' }, { text: 'Facade', - link: '/zh_CN/components/facade.md' + link: '/zh-cn/components/facade.md' }, { text: 'Fast Paginate', - link: '/zh_CN/components/fast-paginate.md' + link: '/zh-cn/components/fast-paginate.md' }, { text: 'Grpc Validation', - link: '/zh_CN/components/grpc-validation.md' + link: '/zh-cn/components/grpc-validation.md' }, { text: 'Helpers', - link: '/zh_CN/components/helpers.md' + link: '/zh-cn/components/helpers.md' }, { text: 'Http Client', - link: '/zh_CN/components/http-client.md' + link: '/zh-cn/components/http-client.md' }, { text: 'Http Logger', - link: '/zh_CN/components/http-logger.md' + link: '/zh-cn/components/http-logger.md' }, { text: 'Ide Helper', - link: '/zh_CN/components/ide-helper.md' + link: '/zh-cn/components/ide-helper.md' }, { text: 'Ipc Broadcaster', - link: '/zh_CN/components/ipc-broadcaster.md' + link: '/zh-cn/components/ipc-broadcaster.md' }, { text: 'Lock', - link: '/zh_CN/components/lock.md' + link: '/zh-cn/components/lock.md' }, { text: 'Macros', - link: '/zh_CN/components/macros.md' + link: '/zh-cn/components/macros.md' }, { text: 'Mail', - link: '/zh_CN/components/mail.md' + link: '/zh-cn/components/mail.md' }, { text: 'Middleware Plus', - link: '/zh_CN/components/middleware-plus.md' + link: '/zh-cn/components/middleware-plus.md' }, { text: 'Model Factory', - link: '/zh_CN/components/model-factory.md' + link: '/zh-cn/components/model-factory.md' }, { text: 'Model Hashids', - link: '/zh_CN/components/model-hashids.md' + link: '/zh-cn/components/model-hashids.md' }, { text: 'Model Morph Addon', - link: '/zh_CN/components/model-morph-addon.md' + link: '/zh-cn/components/model-morph-addon.md' }, { text: 'Model Observer', - link: '/zh_CN/components/model-observer.md' + link: '/zh-cn/components/model-observer.md' }, { text: 'Model Scope', - link: '/zh_CN/components/model-scope.md' + link: '/zh-cn/components/model-scope.md' }, { text: 'Monolog Hook', - link: '/zh_CN/components/monolog-hook.md' + link: '/zh-cn/components/monolog-hook.md' }, { text: 'Mysql Grammar Addon', - link: '/zh_CN/components/mysql-grammar-addon.md' + link: '/zh-cn/components/mysql-grammar-addon.md' }, { text: 'Notification', - link: '/zh_CN/components/notification.md' + link: '/zh-cn/components/notification.md' }, { text: 'Notification Easysms', - link: '/zh_CN/components/notification-easysms.md' + link: '/zh-cn/components/notification-easysms.md' }, { text: 'Notification Mail', - link: '/zh_CN/components/notification-mail.md' + link: '/zh-cn/components/notification-mail.md' }, { text: 'OpenAI Client', - link: '/zh_CN/components/openai-client.md' + link: '/zh-cn/components/openai-client.md' }, { text: 'Pest Plugin Hyperf', - link: '/zh_CN/components/pest-plugin-hyperf.md' + link: '/zh-cn/components/pest-plugin-hyperf.md' }, { text: 'Pretty Console', - link: '/zh_CN/components/pretty-console.md' + link: '/zh-cn/components/pretty-console.md' }, { text: 'Purifier', - link: '/zh_CN/components/purifier.md' + link: '/zh-cn/components/purifier.md' }, { text: 'Recaptcha', - link: '/zh_CN/components/recaptcha.md' + link: '/zh-cn/components/recaptcha.md' }, { text: 'Redis Subscriber', - link: '/zh_CN/components/redis-subscriber.md' + link: '/zh-cn/components/redis-subscriber.md' }, { text: 'Sentry', - link: '/zh_CN/components/sentry.md' + link: '/zh-cn/components/sentry.md' }, { text: 'Support', - link: '/zh_CN/components/support.md' + link: '/zh-cn/components/support.md' }, { text: 'Tcp Sender', - link: '/zh_CN/components/tcp-sender.md' + link: '/zh-cn/components/tcp-sender.md' }, { text: 'Telescope', - link: '/zh_CN/components/telescope.md' + link: '/zh-cn/components/telescope.md' }, { text: 'Telescope Elasticsearch Driver', - link: '/zh_CN/components/telescope-elasticsearch.md' + link: '/zh-cn/components/telescope-elasticsearch.md' }, { text: 'Tinker', - link: '/zh_CN/components/tinker.md' + link: '/zh-cn/components/tinker.md' }, { text: 'Trigger', - link: '/zh_CN/components/trigger.md' + link: '/zh-cn/components/trigger.md' }, { text: 'Validated DTO', - link: '/zh_CN/components/validated-dto.md' + link: '/zh-cn/components/validated-dto.md' }, { text: 'Web Tinker', - link: '/zh_CN/components/web-tinker.md' + link: '/zh-cn/components/web-tinker.md' } ] } diff --git a/docs/.vitepress/src/zh-hk/config.ts b/docs/.vitepress/src/zh-hk/config.ts new file mode 100644 index 000000000..e909c4b63 --- /dev/null +++ b/docs/.vitepress/src/zh-hk/config.ts @@ -0,0 +1,5 @@ +export default { + title: "FriendsOfHyperf", + description: "🚀 最受歡迎的 Hyperf 組件。", + tagline: "Hyperf 組件", +} \ No newline at end of file diff --git a/docs/.vitepress/src/zh-hk/nav.ts b/docs/.vitepress/src/zh-hk/nav.ts new file mode 100644 index 000000000..74e46f14d --- /dev/null +++ b/docs/.vitepress/src/zh-hk/nav.ts @@ -0,0 +1,13 @@ +import {DefaultTheme} from "vitepress"; + +const nav:DefaultTheme.NavItem[] = [ + { text: '組件', link: '/zh-hk/components/' }, + { text: 'FAQ', link: '/zh-hk/faq/index' }, + { text: '更多', items:[ + { text: 'Hyperf', link: 'https://hyperf.wiki/' }, + { text: 'MineAdmin', link: 'https://www.mineadmin.com/' } + ] + } +] + +export default nav diff --git a/docs/.vitepress/src/zh-hk/sidebars.ts b/docs/.vitepress/src/zh-hk/sidebars.ts new file mode 100644 index 000000000..3980add48 --- /dev/null +++ b/docs/.vitepress/src/zh-hk/sidebars.ts @@ -0,0 +1,222 @@ +import {DefaultTheme} from "vitepress"; + +const sidebar:DefaultTheme.Sidebar = { + '/zh-hk/faq/':[{ + text: '常見問題', + items: [ + { + text: '關於 FriendsOfHyperf', + link: '/zh-hk/faq/about' + }, + { + text: '如何使用', + link: '/zh-hk/faq/how-to-use' + } + ] + }], + '/zh-hk/components/':[ + { + text: '組件', + items: [ + { + text: 'Amqp Job', + link: '/zh-hk/components/amqp-job.md' + }, + { + text: 'Cache', + link: '/zh-hk/components/cache.md' + }, + { + text: 'Command Signals', + link: '/zh-hk/components/command-signals.md' + }, + { + text: 'Command Validation', + link: '/zh-hk/components/command-validation.md' + }, + { + text: 'Compoships', + link: '/zh-hk/components/compoships.md' + }, + { + text: 'Confd', + link: '/zh-hk/components/confd.md' + }, + { + text: 'Config Consul', + link: '/zh-hk/components/config-consul.md' + }, + { + text: 'Console Spinner', + link: '/zh-hk/components/console-spinner.md' + }, + { + text: 'Di Plus', + link: '/zh-hk/components/di-plus.md' + }, + { + text: 'Elasticsearch', + link: '/zh-hk/components/elasticsearch.md' + }, + { + text: 'Encryption', + link: '/zh-hk/components/encryption.md' + }, + { + text: 'Exception Event', + link: '/zh-hk/components/exception-event.md' + }, + { + text: 'Facade', + link: '/zh-hk/components/facade.md' + }, + { + text: 'Fast Paginate', + link: '/zh-hk/components/fast-paginate.md' + }, + { + text: 'Grpc Validation', + link: '/zh-hk/components/grpc-validation.md' + }, + { + text: 'Helpers', + link: '/zh-hk/components/helpers.md' + }, + { + text: 'Http Client', + link: '/zh-hk/components/http-client.md' + }, + { + text: 'Http Logger', + link: '/zh-hk/components/http-logger.md' + }, + { + text: 'Ide Helper', + link: '/zh-hk/components/ide-helper.md' + }, + { + text: 'Ipc Broadcaster', + link: '/zh-hk/components/ipc-broadcaster.md' + }, + { + text: 'Lock', + link: '/zh-hk/components/lock.md' + }, + { + text: 'Macros', + link: '/zh-hk/components/macros.md' + }, + { + text: 'Mail', + link: '/zh-hk/components/mail.md' + }, + { + text: 'Middleware Plus', + link: '/zh-hk/components/middleware-plus.md' + }, + { + text: 'Model Factory', + link: '/zh-hk/components/model-factory.md' + }, + { + text: 'Model Hashids', + link: '/zh-hk/components/model-hashids.md' + }, + { + text: 'Model Morph Addon', + link: '/zh-hk/components/model-morph-addon.md' + }, + { + text: 'Model Observer', + link: '/zh-hk/components/model-observer.md' + }, + { + text: 'Model Scope', + link: '/zh-hk/components/model-scope.md' + }, + { + text: 'Monolog Hook', + link: '/zh-hk/components/monolog-hook.md' + }, + { + text: 'Mysql Grammar Addon', + link: '/zh-hk/components/mysql-grammar-addon.md' + }, + { + text: 'Notification', + link: '/zh-hk/components/notification.md' + }, + { + text: 'Notification Easysms', + link: '/zh-hk/components/notification-easysms.md' + }, + { + text: 'Notification Mail', + link: '/zh-hk/components/notification-mail.md' + }, + { + text: 'OpenAI Client', + link: '/zh-hk/components/openai-client.md' + }, + { + text: 'Pest Plugin Hyperf', + link: '/zh-hk/components/pest-plugin-hyperf.md' + }, + { + text: 'Pretty Console', + link: '/zh-hk/components/pretty-console.md' + }, + { + text: 'Purifier', + link: '/zh-hk/components/purifier.md' + }, + { + text: 'Recaptcha', + link: '/zh-hk/components/recaptcha.md' + }, + { + text: 'Redis Subscriber', + link: '/zh-hk/components/redis-subscriber.md' + }, + { + text: 'Sentry', + link: '/zh-hk/components/sentry.md' + }, + { + text: 'Support', + link: '/zh-hk/components/support.md' + }, + { + text: 'Tcp Sender', + link: '/zh-hk/components/tcp-sender.md' + }, + { + text: 'Telescope', + link: '/zh-hk/components/telescope.md' + }, + { + text: 'Telescope Elasticsearch Driver', + link: '/zh-hk/components/telescope-elasticsearch.md' + }, + { + text: 'Tinker', + link: '/zh-hk/components/tinker.md' + }, + { + text: 'Trigger', + link: '/zh-hk/components/trigger.md' + }, + { + text: 'Validated DTO', + link: '/zh-hk/components/validated-dto.md' + }, + { + text: 'Web Tinker', + link: '/zh-hk/components/web-tinker.md' + } + ] + } + ] +} + +export default sidebar \ No newline at end of file diff --git a/docs/.vitepress/src/zh-tw/config.ts b/docs/.vitepress/src/zh-tw/config.ts new file mode 100644 index 000000000..42df245de --- /dev/null +++ b/docs/.vitepress/src/zh-tw/config.ts @@ -0,0 +1,5 @@ +export default { + title: "FriendsOfHyperf", + description: "🚀 最受歡迎的 Hyperf 元件。", + tagline: "Hyperf 元件", +} \ No newline at end of file diff --git a/docs/.vitepress/src/zh-tw/nav.ts b/docs/.vitepress/src/zh-tw/nav.ts new file mode 100644 index 000000000..8dacebad2 --- /dev/null +++ b/docs/.vitepress/src/zh-tw/nav.ts @@ -0,0 +1,13 @@ +import {DefaultTheme} from "vitepress"; + +const nav:DefaultTheme.NavItem[] = [ + { text: '元件', link: '/zh-tw/components/' }, + { text: 'FAQ', link: '/zh-tw/faq/index' }, + { text: '更多', items:[ + { text: 'Hyperf', link: 'https://hyperf.wiki/' }, + { text: 'MineAdmin', link: 'https://www.mineadmin.com/' } + ] + } +] + +export default nav diff --git a/docs/.vitepress/src/zh-tw/sidebars.ts b/docs/.vitepress/src/zh-tw/sidebars.ts new file mode 100644 index 000000000..1b1f71b7e --- /dev/null +++ b/docs/.vitepress/src/zh-tw/sidebars.ts @@ -0,0 +1,222 @@ +import {DefaultTheme} from "vitepress"; + +const sidebar:DefaultTheme.Sidebar = { + '/zh-tw/faq/':[{ + text: '常見問題', + items: [ + { + text: '關於 FriendsOfHyperf', + link: '/zh-tw/faq/about' + }, + { + text: '如何使用', + link: '/zh-tw/faq/how-to-use' + } + ] + }], + '/zh-tw/components/':[ + { + text: '元件', + items: [ + { + text: 'Amqp Job', + link: '/zh-tw/components/amqp-job.md' + }, + { + text: 'Cache', + link: '/zh-tw/components/cache.md' + }, + { + text: 'Command Signals', + link: '/zh-tw/components/command-signals.md' + }, + { + text: 'Command Validation', + link: '/zh-tw/components/command-validation.md' + }, + { + text: 'Compoships', + link: '/zh-tw/components/compoships.md' + }, + { + text: 'Confd', + link: '/zh-tw/components/confd.md' + }, + { + text: 'Config Consul', + link: '/zh-tw/components/config-consul.md' + }, + { + text: 'Console Spinner', + link: '/zh-tw/components/console-spinner.md' + }, + { + text: 'Di Plus', + link: '/zh-tw/components/di-plus.md' + }, + { + text: 'Elasticsearch', + link: '/zh-tw/components/elasticsearch.md' + }, + { + text: 'Encryption', + link: '/zh-tw/components/encryption.md' + }, + { + text: 'Exception Event', + link: '/zh-tw/components/exception-event.md' + }, + { + text: 'Facade', + link: '/zh-tw/components/facade.md' + }, + { + text: 'Fast Paginate', + link: '/zh-tw/components/fast-paginate.md' + }, + { + text: 'Grpc Validation', + link: '/zh-tw/components/grpc-validation.md' + }, + { + text: 'Helpers', + link: '/zh-tw/components/helpers.md' + }, + { + text: 'Http Client', + link: '/zh-tw/components/http-client.md' + }, + { + text: 'Http Logger', + link: '/zh-tw/components/http-logger.md' + }, + { + text: 'Ide Helper', + link: '/zh-tw/components/ide-helper.md' + }, + { + text: 'Ipc Broadcaster', + link: '/zh-tw/components/ipc-broadcaster.md' + }, + { + text: 'Lock', + link: '/zh-tw/components/lock.md' + }, + { + text: 'Macros', + link: '/zh-tw/components/macros.md' + }, + { + text: 'Mail', + link: '/zh-tw/components/mail.md' + }, + { + text: 'Middleware Plus', + link: '/zh-tw/components/middleware-plus.md' + }, + { + text: 'Model Factory', + link: '/zh-tw/components/model-factory.md' + }, + { + text: 'Model Hashids', + link: '/zh-tw/components/model-hashids.md' + }, + { + text: 'Model Morph Addon', + link: '/zh-tw/components/model-morph-addon.md' + }, + { + text: 'Model Observer', + link: '/zh-tw/components/model-observer.md' + }, + { + text: 'Model Scope', + link: '/zh-tw/components/model-scope.md' + }, + { + text: 'Monolog Hook', + link: '/zh-tw/components/monolog-hook.md' + }, + { + text: 'Mysql Grammar Addon', + link: '/zh-tw/components/mysql-grammar-addon.md' + }, + { + text: 'Notification', + link: '/zh-tw/components/notification.md' + }, + { + text: 'Notification Easysms', + link: '/zh-tw/components/notification-easysms.md' + }, + { + text: 'Notification Mail', + link: '/zh-tw/components/notification-mail.md' + }, + { + text: 'OpenAI Client', + link: '/zh-tw/components/openai-client.md' + }, + { + text: 'Pest Plugin Hyperf', + link: '/zh-tw/components/pest-plugin-hyperf.md' + }, + { + text: 'Pretty Console', + link: '/zh-tw/components/pretty-console.md' + }, + { + text: 'Purifier', + link: '/zh-tw/components/purifier.md' + }, + { + text: 'Recaptcha', + link: '/zh-tw/components/recaptcha.md' + }, + { + text: 'Redis Subscriber', + link: '/zh-tw/components/redis-subscriber.md' + }, + { + text: 'Sentry', + link: '/zh-tw/components/sentry.md' + }, + { + text: 'Support', + link: '/zh-tw/components/support.md' + }, + { + text: 'Tcp Sender', + link: '/zh-tw/components/tcp-sender.md' + }, + { + text: 'Telescope', + link: '/zh-tw/components/telescope.md' + }, + { + text: 'Telescope Elasticsearch Driver', + link: '/zh-tw/components/telescope-elasticsearch.md' + }, + { + text: 'Tinker', + link: '/zh-tw/components/tinker.md' + }, + { + text: 'Trigger', + link: '/zh-tw/components/trigger.md' + }, + { + text: 'Validated DTO', + link: '/zh-tw/components/validated-dto.md' + }, + { + text: 'Web Tinker', + link: '/zh-tw/components/web-tinker.md' + } + ] + } + ] +} + +export default sidebar \ No newline at end of file diff --git a/docs/en/index.md b/docs/en/index.md index 14680fbc0..41a6a883d 100644 --- a/docs/en/index.md +++ b/docs/en/index.md @@ -29,7 +29,7 @@ features: link: /en/components/tinker/ - title: Web Tinker details: Tinker in your browser. - link: /zh_CN/components/web-tinker/ + link: /zh-cn/components/web-tinker/ - title: Encryption details: Encryption provides simple and convenient encryption and decryption functionality. link: /en/components/encryption/ diff --git a/docs/index.md b/docs/index.md index 4a3db5a37..5153f8c7a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -9,10 +9,10 @@ hero: actions: # - theme: brand # text: 入门 - # link: /zh_CN/guide/ + # link: /zh-cn/guide/ - theme: brand text: 组件 - link: /zh_CN/components/ + link: /zh-cn/components/ - theme: alt text: GitHub link: "https://github.com/friendsofhyperf/components" @@ -20,33 +20,33 @@ hero: features: - title: Sentry details: 这是 Sentry 的 Hyperf SDK。 - link: /zh_CN/components/sentry/ + link: /zh-cn/components/sentry/ - title: Telecope details: Telescope 是 Hyperf 框架的优雅调试助手。 - link: /zh_CN/components/telescope/ + link: /zh-cn/components/telescope/ - title: Tinker details: Tinker 是 Hyperf 框架的强大 REPL。 - link: /zh_CN/components/tinker/ + link: /zh-cn/components/tinker/ - title: Web Tinker details: 在浏览器中使用 Tinker。 - link: /zh_CN/components/web-tinker/ + link: /zh-cn/components/web-tinker/ - title: Encryption details: Encryption 提供了一个简单、方便的加密和解密功能。 - link: /zh_CN/components/encryption/ + link: /zh-cn/components/encryption/ - title: Cache details: Cache 提供了一个富有表现力的统一缓存 API。 - link: /zh_CN/components/cache/ + link: /zh-cn/components/cache/ - title: HttpClient details: HttpClient 提供了一个简单、方便的 HTTP 客户端。 - link: /zh_CN/components/http-client/ + link: /zh-cn/components/http-client/ - title: Validated DTO details: Validated DTO 提供了一个简单、方便的数据验证功能。 - link: /zh_CN/components/validated-dto/ + link: /zh-cn/components/validated-dto/ - title: Lock details: Lock 提供了一个简单、方便的分布式锁功能。 - link: /zh_CN/components/lock/ + link: /zh-cn/components/lock/ - title: More details: 更多组件… - link: /zh_CN/components/ + link: /zh-cn/components/ --- diff --git a/docs/zh_CN/components/amqp-job.md b/docs/zh-cn/components/amqp-job.md similarity index 100% rename from docs/zh_CN/components/amqp-job.md rename to docs/zh-cn/components/amqp-job.md diff --git a/docs/zh_CN/components/cache.md b/docs/zh-cn/components/cache.md similarity index 100% rename from docs/zh_CN/components/cache.md rename to docs/zh-cn/components/cache.md diff --git a/docs/zh_CN/components/command-signals.md b/docs/zh-cn/components/command-signals.md similarity index 100% rename from docs/zh_CN/components/command-signals.md rename to docs/zh-cn/components/command-signals.md diff --git a/docs/zh_CN/components/command-validation.md b/docs/zh-cn/components/command-validation.md similarity index 100% rename from docs/zh_CN/components/command-validation.md rename to docs/zh-cn/components/command-validation.md diff --git a/docs/zh_CN/components/compoships.md b/docs/zh-cn/components/compoships.md similarity index 100% rename from docs/zh_CN/components/compoships.md rename to docs/zh-cn/components/compoships.md diff --git a/docs/zh_CN/components/confd.md b/docs/zh-cn/components/confd.md similarity index 100% rename from docs/zh_CN/components/confd.md rename to docs/zh-cn/components/confd.md diff --git a/docs/zh_CN/components/config-consul.md b/docs/zh-cn/components/config-consul.md similarity index 100% rename from docs/zh_CN/components/config-consul.md rename to docs/zh-cn/components/config-consul.md diff --git a/docs/zh_CN/components/console-spinner.md b/docs/zh-cn/components/console-spinner.md similarity index 100% rename from docs/zh_CN/components/console-spinner.md rename to docs/zh-cn/components/console-spinner.md diff --git a/docs/zh_CN/components/di-plus.md b/docs/zh-cn/components/di-plus.md similarity index 100% rename from docs/zh_CN/components/di-plus.md rename to docs/zh-cn/components/di-plus.md diff --git a/docs/zh_CN/components/elasticsearch.md b/docs/zh-cn/components/elasticsearch.md similarity index 100% rename from docs/zh_CN/components/elasticsearch.md rename to docs/zh-cn/components/elasticsearch.md diff --git a/docs/zh_CN/components/encryption.md b/docs/zh-cn/components/encryption.md similarity index 100% rename from docs/zh_CN/components/encryption.md rename to docs/zh-cn/components/encryption.md diff --git a/docs/zh_CN/components/exception-event.md b/docs/zh-cn/components/exception-event.md similarity index 100% rename from docs/zh_CN/components/exception-event.md rename to docs/zh-cn/components/exception-event.md diff --git a/docs/zh_CN/components/facade.md b/docs/zh-cn/components/facade.md similarity index 100% rename from docs/zh_CN/components/facade.md rename to docs/zh-cn/components/facade.md diff --git a/docs/zh_CN/components/fast-paginate.md b/docs/zh-cn/components/fast-paginate.md similarity index 100% rename from docs/zh_CN/components/fast-paginate.md rename to docs/zh-cn/components/fast-paginate.md diff --git a/docs/zh_CN/components/grpc-validation.md b/docs/zh-cn/components/grpc-validation.md similarity index 100% rename from docs/zh_CN/components/grpc-validation.md rename to docs/zh-cn/components/grpc-validation.md diff --git a/docs/zh_CN/components/helpers.md b/docs/zh-cn/components/helpers.md similarity index 100% rename from docs/zh_CN/components/helpers.md rename to docs/zh-cn/components/helpers.md diff --git a/docs/zh_CN/components/http-client.md b/docs/zh-cn/components/http-client.md similarity index 100% rename from docs/zh_CN/components/http-client.md rename to docs/zh-cn/components/http-client.md diff --git a/docs/zh_CN/components/http-logger.md b/docs/zh-cn/components/http-logger.md similarity index 100% rename from docs/zh_CN/components/http-logger.md rename to docs/zh-cn/components/http-logger.md diff --git a/docs/zh_CN/components/ide-helper.md b/docs/zh-cn/components/ide-helper.md similarity index 100% rename from docs/zh_CN/components/ide-helper.md rename to docs/zh-cn/components/ide-helper.md diff --git a/docs/zh_CN/components/index.md b/docs/zh-cn/components/index.md similarity index 100% rename from docs/zh_CN/components/index.md rename to docs/zh-cn/components/index.md diff --git a/docs/zh_CN/components/ipc-broadcaster.md b/docs/zh-cn/components/ipc-broadcaster.md similarity index 100% rename from docs/zh_CN/components/ipc-broadcaster.md rename to docs/zh-cn/components/ipc-broadcaster.md diff --git a/docs/zh_CN/components/lock.md b/docs/zh-cn/components/lock.md similarity index 100% rename from docs/zh_CN/components/lock.md rename to docs/zh-cn/components/lock.md diff --git a/docs/zh_CN/components/macros.md b/docs/zh-cn/components/macros.md similarity index 100% rename from docs/zh_CN/components/macros.md rename to docs/zh-cn/components/macros.md diff --git a/docs/zh_CN/components/mail.md b/docs/zh-cn/components/mail.md similarity index 100% rename from docs/zh_CN/components/mail.md rename to docs/zh-cn/components/mail.md diff --git a/docs/zh_CN/components/middleware-plus.md b/docs/zh-cn/components/middleware-plus.md similarity index 100% rename from docs/zh_CN/components/middleware-plus.md rename to docs/zh-cn/components/middleware-plus.md diff --git a/docs/zh_CN/components/model-factory.md b/docs/zh-cn/components/model-factory.md similarity index 100% rename from docs/zh_CN/components/model-factory.md rename to docs/zh-cn/components/model-factory.md diff --git a/docs/zh_CN/components/model-hashids.md b/docs/zh-cn/components/model-hashids.md similarity index 100% rename from docs/zh_CN/components/model-hashids.md rename to docs/zh-cn/components/model-hashids.md diff --git a/docs/zh_CN/components/model-morph-addon.md b/docs/zh-cn/components/model-morph-addon.md similarity index 100% rename from docs/zh_CN/components/model-morph-addon.md rename to docs/zh-cn/components/model-morph-addon.md diff --git a/docs/zh_CN/components/model-observer.md b/docs/zh-cn/components/model-observer.md similarity index 100% rename from docs/zh_CN/components/model-observer.md rename to docs/zh-cn/components/model-observer.md diff --git a/docs/zh_CN/components/model-scope.md b/docs/zh-cn/components/model-scope.md similarity index 100% rename from docs/zh_CN/components/model-scope.md rename to docs/zh-cn/components/model-scope.md diff --git a/docs/zh_CN/components/monolog-hook.md b/docs/zh-cn/components/monolog-hook.md similarity index 100% rename from docs/zh_CN/components/monolog-hook.md rename to docs/zh-cn/components/monolog-hook.md diff --git a/docs/zh_CN/components/mysql-grammar-addon.md b/docs/zh-cn/components/mysql-grammar-addon.md similarity index 100% rename from docs/zh_CN/components/mysql-grammar-addon.md rename to docs/zh-cn/components/mysql-grammar-addon.md diff --git a/docs/zh_CN/components/notification-easysms.md b/docs/zh-cn/components/notification-easysms.md similarity index 100% rename from docs/zh_CN/components/notification-easysms.md rename to docs/zh-cn/components/notification-easysms.md diff --git a/docs/zh_CN/components/notification-mail.md b/docs/zh-cn/components/notification-mail.md similarity index 100% rename from docs/zh_CN/components/notification-mail.md rename to docs/zh-cn/components/notification-mail.md diff --git a/docs/zh_CN/components/notification.md b/docs/zh-cn/components/notification.md similarity index 100% rename from docs/zh_CN/components/notification.md rename to docs/zh-cn/components/notification.md diff --git a/docs/zh_CN/components/openai-client.md b/docs/zh-cn/components/openai-client.md similarity index 100% rename from docs/zh_CN/components/openai-client.md rename to docs/zh-cn/components/openai-client.md diff --git a/docs/zh_CN/components/pest-plugin-hyperf.md b/docs/zh-cn/components/pest-plugin-hyperf.md similarity index 100% rename from docs/zh_CN/components/pest-plugin-hyperf.md rename to docs/zh-cn/components/pest-plugin-hyperf.md diff --git a/docs/zh_CN/components/pretty-console.md b/docs/zh-cn/components/pretty-console.md similarity index 100% rename from docs/zh_CN/components/pretty-console.md rename to docs/zh-cn/components/pretty-console.md diff --git a/docs/zh_CN/components/purifier.md b/docs/zh-cn/components/purifier.md similarity index 100% rename from docs/zh_CN/components/purifier.md rename to docs/zh-cn/components/purifier.md diff --git a/docs/zh_CN/components/recaptcha.md b/docs/zh-cn/components/recaptcha.md similarity index 100% rename from docs/zh_CN/components/recaptcha.md rename to docs/zh-cn/components/recaptcha.md diff --git a/docs/zh_CN/components/redis-subscriber.md b/docs/zh-cn/components/redis-subscriber.md similarity index 100% rename from docs/zh_CN/components/redis-subscriber.md rename to docs/zh-cn/components/redis-subscriber.md diff --git a/docs/zh_CN/components/sentry.md b/docs/zh-cn/components/sentry.md similarity index 100% rename from docs/zh_CN/components/sentry.md rename to docs/zh-cn/components/sentry.md diff --git a/docs/zh_CN/components/support.md b/docs/zh-cn/components/support.md similarity index 100% rename from docs/zh_CN/components/support.md rename to docs/zh-cn/components/support.md diff --git a/docs/zh_CN/components/tcp-sender.md b/docs/zh-cn/components/tcp-sender.md similarity index 100% rename from docs/zh_CN/components/tcp-sender.md rename to docs/zh-cn/components/tcp-sender.md diff --git a/docs/zh_CN/components/telescope-elasticsearch.md b/docs/zh-cn/components/telescope-elasticsearch.md similarity index 100% rename from docs/zh_CN/components/telescope-elasticsearch.md rename to docs/zh-cn/components/telescope-elasticsearch.md diff --git a/docs/zh_CN/components/telescope.md b/docs/zh-cn/components/telescope.md similarity index 100% rename from docs/zh_CN/components/telescope.md rename to docs/zh-cn/components/telescope.md diff --git a/docs/zh_CN/components/tinker.md b/docs/zh-cn/components/tinker.md similarity index 100% rename from docs/zh_CN/components/tinker.md rename to docs/zh-cn/components/tinker.md diff --git a/docs/zh_CN/components/trigger.md b/docs/zh-cn/components/trigger.md similarity index 100% rename from docs/zh_CN/components/trigger.md rename to docs/zh-cn/components/trigger.md diff --git a/docs/zh_CN/components/validated-dto.md b/docs/zh-cn/components/validated-dto.md similarity index 100% rename from docs/zh_CN/components/validated-dto.md rename to docs/zh-cn/components/validated-dto.md diff --git a/docs/zh_CN/components/web-tinker.md b/docs/zh-cn/components/web-tinker.md similarity index 100% rename from docs/zh_CN/components/web-tinker.md rename to docs/zh-cn/components/web-tinker.md diff --git a/docs/zh_CN/faq/index.md b/docs/zh-cn/faq/index.md similarity index 100% rename from docs/zh_CN/faq/index.md rename to docs/zh-cn/faq/index.md diff --git a/docs/zh_CN/guide/index.md b/docs/zh-cn/guide/index.md similarity index 100% rename from docs/zh_CN/guide/index.md rename to docs/zh-cn/guide/index.md diff --git a/docs/zh_CN/guide/introduce/about.md b/docs/zh-cn/guide/introduce/about.md similarity index 100% rename from docs/zh_CN/guide/introduce/about.md rename to docs/zh-cn/guide/introduce/about.md diff --git a/docs/zh_CN/guide/start/components.md b/docs/zh-cn/guide/start/components.md similarity index 100% rename from docs/zh_CN/guide/start/components.md rename to docs/zh-cn/guide/start/components.md diff --git a/docs/zh-hk/components/amqp-job.md b/docs/zh-hk/components/amqp-job.md new file mode 100644 index 000000000..28a67d6ef --- /dev/null +++ b/docs/zh-hk/components/amqp-job.md @@ -0,0 +1,64 @@ +# Amqp Job + +## 簡介 + +`friendsofhyperf/amqp-job` 是一個基於 `hyperf/amqp` 組件實現的異步任務組件,支持將任務分發到 AMQP 服務,然後通過消費者消費任務。 +封裝了 `hyperf/amqp` 組件,提供了更加便捷的任務分發和消費方式。 + +## 安裝 + +```shell +composer require friendsofhyperf/amqp-job +``` + +## 用法 + +### 分發任務 + +```php +use FriendsOfHyperf\AmqpJob\Job; +use FriendsOfHyperf\AmqpJob\Annotations\AmqpJob; + +use function FriendsOfHyperf\AmqpJob\dispatch; + +#[AmqpJob( + exchange: 'hyperf.exchange', + routingKey: 'hyperf.routing.key', + pool: 'default', + queue: 'hyperf.queue', +)] +class FooJob extends Job +{ + public function handle() + { + var_dump('foo'); + } +} + +dispatch(new FooJob()); + +``` + +### 註冊消費者[可選] + +```php + +namespace App\Amqp\Consumer; + +use FriendsOfHyperf\AmqpJob\JobConsumer; +use Hyperf\Amqp\Annotation\Consumer; + +#[Consumer( + exchange: 'hyperf.exchange', + routingKey: 'hyperf.routing.key', + queue: 'hyperf.queue', + name: 'MyConsumer', + nums: 4 + +)] +class MyConsumer extends JobConsumer +{ + // +} + +``` diff --git a/docs/zh-hk/components/cache.md b/docs/zh-hk/components/cache.md new file mode 100644 index 000000000..1883edfdb --- /dev/null +++ b/docs/zh-hk/components/cache.md @@ -0,0 +1,65 @@ +# Cache + +## 簡介 + +`friendsofhyperf/cache` 是一個基於 `hyperf/cache` 的組件。 提供更多簡潔性的擴展方法 + +## 安裝 + +```shell +composer require friendsofhyperf/cache +``` + +## 用法 + +### 註解 + +```php +namespace App\Controller; + +use FriendsOfHyperf\Cache\Contract\CacheInterface; +use Hyperf\Di\Annotation\Inject; + +class IndexController +{ + + #[Inject] + private CacheInterface $cache; + + public function index() + { + return $this->cache->remember($key, $ttl=60, function() { + // return sth + }); + } +} +``` + +### 門面 + +```php +use FriendsOfHyperf\Cache\Facade\Cache; + +Cache::remember($key, $ttl=60, function() { + // return sth +}); +``` + +### 切換驅動 + +```php +use FriendsOfHyperf\Cache\Facade\Cache; +use FriendsOfHyperf\Cache\CacheManager; + +Cache::store('co')->remember($key, $ttl=60, function() { + // return sth +}); + +di(CacheManager::class)->store('co')->remember($key, $ttl=60, function() { + // return sth +}); +``` + +## 參考 + +Likes [Laravel-Cache](https://laravel.com/docs/8.x/cache) diff --git a/docs/zh-hk/components/command-signals.md b/docs/zh-hk/components/command-signals.md new file mode 100644 index 000000000..d57c4ea2c --- /dev/null +++ b/docs/zh-hk/components/command-signals.md @@ -0,0 +1,65 @@ +# Command Signals + +The signals component for Hyperf Command. + +## 安裝 + +```shell +composer require friendsofhyperf/command-signals +``` + +## 使用 + +```php +namespace App\Command; + +use FriendsOfHyperf\CommandSignals\Traits\InteractsWithSignals; +use Hyperf\Command\Annotation\Command; +use Hyperf\Command\Command as HyperfCommand; +use Psr\Container\ContainerInterface; + +#[Command] +class FooCommand extends HyperfCommand +{ + use InteractsWithSignals; + + public function __construct(protected ContainerInterface $container) + { + parent::__construct('foo'); + } + + public function configure() + { + parent::configure(); + $this->setDescription('Hyperf Demo Command'); + } + + public function handle() + { + $this->trap([SIGINT, SIGTERM], function ($signo) { + $this->warn(sprintf('Received signal %d, exiting...', $signo)); + }); + + sleep(10); + + $this->info('Bye!'); + } +} +``` + +## 運行 + +- `Ctrl + C` + +```shell +$ hyperf foo +^CReceived signal 2, exiting... +``` + +- `killall php` + +```shell +$ hyperf foo +Received signal 15, exiting... +[1] 51936 terminated php bin/hyperf.php foo +``` diff --git a/docs/zh-hk/components/command-validation.md b/docs/zh-hk/components/command-validation.md new file mode 100644 index 000000000..6cb4652c1 --- /dev/null +++ b/docs/zh-hk/components/command-validation.md @@ -0,0 +1,53 @@ +# Command Validation + +The command validation component for Hyperf. + +## 安裝 + +```shell +composer require friendsofhyperf/command-validation +``` + +## 使用 + +```php +info(sprintf('Hello %s.', $this->input->getArgument('name'))); + } + + protected function rules(): array + { + return [ + 'name' => 'required', + ]; + } + + protected function messages(): array + { + return [ + 'name.required' => 'The name is required.', + ]; + } +} +``` diff --git a/docs/zh-hk/components/compoships.md b/docs/zh-hk/components/compoships.md new file mode 100644 index 000000000..ba5cd5838 --- /dev/null +++ b/docs/zh-hk/components/compoships.md @@ -0,0 +1,126 @@ +# Compoships + +**Compoships** 提供了在 Hyperf 的 Model ORM 中基於兩個(或更多)列指定關係的能力。當處理第三方或預先存在的模式/數據庫時,通常會出現需要在 Eloquent 關係的定義中匹配多個列的情況。 + +## 問題 + +Eloquent 不支持複合鍵。因此,無法通過匹配多個列來定義從一個模型到另一個模型的關係。嘗試使用 `where` 子句(如下例所示)在預加載關係時不起作用,因為在處理關係時 **$this->team_id** 為 null。 + +```php +namespace App; + +use Hyperf\Database\Model\Model; + +class User extends Model +{ + public function tasks() + { + //WON'T WORK WITH EAGER LOADING!!! + return $this->hasMany(Task::class)->where('team_id', $this->team_id); + } +} +``` + +## 安裝 + +推薦通過 [Composer](http://getcomposer.org/) 安裝 **Compoships** 組件。 + +```shell +composer require friendsofhyperf/compoships +``` + +## 使用 + +### 使用 `FriendsOfHyperf\Compoships\Database\Eloquent\Model` 類 + +只需讓您的模型類派生自 `FriendsOfHyperf\Compoships\Database\Eloquent\Model` 基類。`FriendsOfHyperf\Compoships\Database\Eloquent\Model` 擴展了 `Eloquent` 基類,而不改變其核心功能。 + +### 使用 `FriendsOfHyperf\Compoships\Compoships` trait + +如果由於某些原因您無法從 `FriendsOfHyperf\Compoships\Database\Eloquent\Model` 派生您的模型,您可以利用 `FriendsOfHyperf\Compoships\Compoships` trait。只需在您的模型中使用該 trait。 + +**注意:** 要從模型 *A* 到另一個模型 *B* 定義多列關係,**兩個模型都必須擴展 `FriendsOfHyperf\Compoships\Database\Eloquent\Model` 或使用 `FriendsOfHyperf\Compoships\Compoships` trait** + +### 用法 + +... 現在我們可以通過匹配兩個或更多列(通過傳遞列數組而不是字符串)來定義從模型 *A* 到另一個模型 *B* 的關係。 + +```php +namespace App; + +use Hyperf\Database\Model\Model; + +class A extends Model +{ + use \FriendsOfHyperf\Compoships\Compoships; + + public function b() + { + return $this->hasMany('B', ['foreignKey1', 'foreignKey2'], ['localKey1', 'localKey2']); + } +} +``` + +我們可以使用相同的語法來定義關係的反向關係: + +```php +namespace App; + +use Hyperf\Database\Model\Model; + +class B extends Model +{ + use \FriendsOfHyperf\Compoships\Compoships; + + public function a() + { + return $this->belongsTo('A', ['foreignKey1', 'foreignKey2'], ['ownerKey1', 'ownerKey2']); + } +} +``` + +### 例子 + +作為一個例子,假設我們有一個帶有分類的任務列表,由多個用户團隊管理,其中: + +- 一個任務屬於一個分類 +- 一個任務被分配給一個團隊 +- 一個團隊有很多用户 +- 一個用户屬於一個團隊 +- 一個用户負責一個分類的任務 + +負責特定任務的用户是當前負責團隊內該分類的用户。 + +```php +namespace App; + +use Hyperf\Database\Model\Model; + +class User extends Model +{ + use \FriendsOfHyperf\Compoships\Compoships; + + public function tasks() + { + return $this->hasMany(Task::class, ['team_id', 'category_id'], ['team_id', 'category_id']); + } +} +``` + +同樣的語法可以定義關係的反向關係: + +```php +namespace App; + +use Hyperf\Database\Model\Model; + +class Task extends Model +{ + use \FriendsOfHyperf\Compoships\Compoships; + + public function user() + { + return $this->belongsTo(User::class, ['team_id', 'category_id'], ['team_id', 'category_id']); + } +} +``` diff --git a/docs/zh-hk/components/confd.md b/docs/zh-hk/components/confd.md new file mode 100644 index 000000000..490bc14b1 --- /dev/null +++ b/docs/zh-hk/components/confd.md @@ -0,0 +1,60 @@ +# Confd + +The confd component for Hyperf. + +## 安裝 + +```shell +composer require friendsofhyperf/confd +composer require friendsofhyperf/etcd +# or +composer require friendsofhyperf/nacos +``` + +## 命令 + +從 `etcd/nacos` 獲取配置並更新 `.env`。 + +```shell +php bin/hyperf.php confd:env +``` + +## 定義監聽器 + +```php +logger->warning('[confd] ConfdChanged'); + // do something + } +} +``` + +## 支持 + +- [x] Etcd +- [x] Nacos +- [ ] Consul diff --git a/docs/zh-hk/components/config-consul.md b/docs/zh-hk/components/config-consul.md new file mode 100644 index 000000000..711ca4a71 --- /dev/null +++ b/docs/zh-hk/components/config-consul.md @@ -0,0 +1,36 @@ +# Config Consul + +The consul config component for Hyperf. + +## 安裝 + +```shell +composer require friendsofhyperf/config-consul +``` + +## 配置 + +```php +// config/autoload/config_center.php + +return [ + 'drivers' => [ + 'consul' => [ + 'driver' => FriendsOfHyperf\ConfigConsul\ConsulDriver::class, + 'packer' => Hyperf\Codec\Packer\JsonPacker::class, + 'client' => [ + 'uri' => env('CONSUL_URI'), + 'token' => env('CONSUL_TOKEN'), + ], + 'namespaces' => [ + '/application', + ], + 'mapping' => [ + // consul key => config key + '/application/test' => 'test', + ], + 'interval' => 5, + ], + ], +]; +``` diff --git a/docs/zh-hk/components/console-spinner.md b/docs/zh-hk/components/console-spinner.md new file mode 100644 index 000000000..fa7634655 --- /dev/null +++ b/docs/zh-hk/components/console-spinner.md @@ -0,0 +1,46 @@ +# Console Spinner + +The progress bar component For Hyperf. + +## 安裝 + +```shell +composer require friendsofhyperf/console-spinner +``` + +## 發佈配置 + +```shell +php bin/hyperf.php vendor:publish friendsofhyperf/console-spinner +``` + +## 使用 + +```php +class FooCommand extends Command +{ + use Spinnerable; + + public function handle() + { + $spinner = $this->spinner($users->count()); + $spinner->setMessage('Loading...'); + $spinner->start(); + + foreach ($users as $user) { + // Do your stuff... + $spinner->advance(); + } + + $spinner->finish(); + } +} +``` + +`$spinner` 兼容 Symfony ProgressBar,因此您可以運行此類的任何方法。或者,您也可以通過提供一個可迭代對象來使用 `withSpinner` 方法。 + +```php +$this->withSpinner(User::all(), function($user) { + // Do your stuff with $user +}, 'Loading...'); +``` diff --git a/docs/zh-hk/components/di-plus.md b/docs/zh-hk/components/di-plus.md new file mode 100644 index 000000000..755e0510b --- /dev/null +++ b/docs/zh-hk/components/di-plus.md @@ -0,0 +1,60 @@ +# DI Plus + +The di plus component for Hyperf. + +## 安裝 + +```shell +composer require friendsofhyperf/di-plus +``` + +## 使用 + +```php + App\BarAtFoo1Factory::class, + 'App\Bar@App\Foo2' => App\BarAtFoo2Factory::class, +]; +``` + +```php +create()->build(); + // ... + } +} +``` diff --git a/docs/zh-hk/components/encryption.md b/docs/zh-hk/components/encryption.md new file mode 100644 index 000000000..661db5932 --- /dev/null +++ b/docs/zh-hk/components/encryption.md @@ -0,0 +1,22 @@ +# Encryption + +The encryption component for Hyperf. + +## 安裝 + +```shell +composer require friendsofhyperf/encryption +``` + +## 發佈配置 + +```shell +php bin/hyperf.php vendor:publish friendsofhyperf/encryption +``` + +## 使用 + +```shell +$encryptString = encrypt($string); +$decryptString = decrypt($encryptString); +``` diff --git a/docs/zh-hk/components/exception-event.md b/docs/zh-hk/components/exception-event.md new file mode 100644 index 000000000..896be46f1 --- /dev/null +++ b/docs/zh-hk/components/exception-event.md @@ -0,0 +1,39 @@ +# Exception Event + +## 安裝 + +```shell +composer require friendsofhyperf/exception-event +``` + +## 使用 + +### 定義監聽器 + +```php +throwable; + $message = sprintf('Exception: %s in %s:%s', $exception->getMessage(), $exception->getFile(), $exception->getLine()); + $event->getLogger()->error($message); + } +} +``` diff --git a/docs/zh-hk/components/facade.md b/docs/zh-hk/components/facade.md new file mode 100644 index 000000000..35212c455 --- /dev/null +++ b/docs/zh-hk/components/facade.md @@ -0,0 +1,28 @@ +# Facades + +## 安裝 + +```shell +composer require friendsofhyperf/facade +``` + +## 支持組件 + +- [x] AMQP +- [x] App (DI alias) +- [x] AsyncQueue +- [x] Cache +- [x] Config +- [x] Cookie +- [x] DI +- [x] Event +- [x] Filesystem/File +- [x] Kafka +- [x] Log +- [x] Redis +- [x] Request +- [x] Response +- [x] Session +- [x] Translator +- [x] Validator +- [x] View diff --git a/docs/zh-hk/components/fast-paginate.md b/docs/zh-hk/components/fast-paginate.md new file mode 100644 index 000000000..63915369b --- /dev/null +++ b/docs/zh-hk/components/fast-paginate.md @@ -0,0 +1,48 @@ +# Fast Paginate + +> Forked from [hammerstonedev/fast-paginate](https://github.com/hammerstonedev/fast-paginate) + +## 關於 + +這是一個用於 Hyperf 的快速 `limit`/`offset` 分頁宏。它可以替代標準的 `paginate` 方法。 + +這個包使用了一種類似於“延遲連接”的 SQL 方法來實現這種加速。延遲連接是一種在應用 `offset` 和 `limit` 之後才訪問請求列的技術。 + +在我們的例子中,我們實際上並沒有進行連接,而是使用了帶有子查詢的 `where in`。使用這種技術,我們創建了一個可以通過特定索引進行優化的子查詢以達到最大速度,然後使用這些結果來獲取完整的行。 + +SQL 語句如下所示: + +```sql +select * from contacts -- The full data that you want to show your users. + where contacts.id in ( -- The "deferred join" or subquery, in our case. + select id from contacts -- The pagination, accessing as little data as possible - ID only. + limit 15 offset 150000 + ) +``` + +> 運行上述查詢時,您可能會遇到錯誤!例如 `This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery.` +> 在這個包中,我們將它們作為[兩個獨立的查詢](https://github.com/hammerstonedev/fast-paginate/blob/154da286f8160a9e75e64e8025b0da682aa2ba23/src/BuilderMixin.php#L62-L79)來運行以解決這個問題! + +根據您的數據集,性能提升可能會有所不同,但這種方法允許數據庫檢查儘可能少的數據以滿足用户的需求。 + +雖然這種方法不太可能比傳統的 `offset` / `limit` 性能更差,但也有可能,所以請務必在您的數據上進行測試! + +> 如果您想閲讀關於這個包理論的 3,000 字文章,可以訪問 [aaronfrancis.com/2022/efficient-pagination-using-deferred-joins](https://aaronfrancis.com/2022/efficient-pagination-using-deferred-joins)。 + +## 安裝 + +```shell +composer require friendsofhyperf/fast-paginate +``` + +無需執行其他操作,服務提供者將由 Hyperf 自動加載。 + +## 使用 + +在任何您會使用 `Model::query()->paginate()` 的地方,您都可以使用 `Model::query()->fastPaginate()`!就是這麼簡單!方法簽名是相同的。 + +關係也同樣支持: + +```php +User::first()->posts()->fastPaginate(); +``` diff --git a/docs/zh-hk/components/grpc-validation.md b/docs/zh-hk/components/grpc-validation.md new file mode 100644 index 000000000..24fa5a07c --- /dev/null +++ b/docs/zh-hk/components/grpc-validation.md @@ -0,0 +1,29 @@ +# Grpc Validation + +The GRPC validation component for Hyperf. + +## 安裝 + +```shell +composer require friendsofhyperf/grpc-validation +``` + +## 使用 + +```php + 'required|string|max:10', + 'message' => 'required|string|max:500', +])] +public function sayHello(HiUser $user) +{ + $message = new HiReply(); + $message->setMessage("Hello World"); + $message->setUser($user); + return $message; +} +``` diff --git a/docs/zh-hk/components/helpers.md b/docs/zh-hk/components/helpers.md new file mode 100644 index 000000000..c1da5a341 --- /dev/null +++ b/docs/zh-hk/components/helpers.md @@ -0,0 +1,41 @@ +# Helpers + +## 安裝 + +```shell +composer require friendsofhyperf/helpers +``` + +## 支持函數 + +- app +- base_path +- blank +- cache +- cookie +- class_namespace +- di +- dispatch +- environment +- enum_value +- event +- filled +- fluent +- info +- literal +- logger +- logs +- now +- object_get +- preg_replace_array +- resolve +- request +- response +- session +- today +- throw_if +- throw_unless +- transform +- validator +- when +- get_client_ip diff --git a/docs/zh-hk/components/http-client.md b/docs/zh-hk/components/http-client.md new file mode 100644 index 000000000..f44f86776 --- /dev/null +++ b/docs/zh-hk/components/http-client.md @@ -0,0 +1,21 @@ +# HTTP Client + +The HTTP Client component for Hyperf, from Laravel. + +## 安裝 + +```shell +composer require friendsofhyperf/http-client +``` + +## 使用 + +```php +use FriendsOfHyperf\Http\Client\Http; + +$response = Http::get('https://example.com'); +``` + +## 參考文檔 + +有關更多信息,請參閲 [Laravel HTTP Client 文檔](https://laravel.com/docs/9.x/http-client)。 diff --git a/docs/zh-hk/components/http-logger.md b/docs/zh-hk/components/http-logger.md new file mode 100644 index 000000000..eef329d61 --- /dev/null +++ b/docs/zh-hk/components/http-logger.md @@ -0,0 +1,25 @@ +# Http Logger + +The http logger component for Hyperf. + +## 安裝 + +```shell +composer require "friendsofhyperf/http-logger +``` + +## 發佈配置 + +```shell +php bin/hyperf.php vendor:publish friendsofhyperf/http-logger +``` + +## 使用 + +```php +return [ + 'http' => [ + \FriendsOfHyperf\Http\Logger\Middleware\HttpLogger::class, + ], +]; +``` diff --git a/docs/zh-hk/components/ide-helper.md b/docs/zh-hk/components/ide-helper.md new file mode 100644 index 000000000..9b6f68c37 --- /dev/null +++ b/docs/zh-hk/components/ide-helper.md @@ -0,0 +1,23 @@ +# IDE Helper + +The ide-helper component for Hyperf. + +## 安裝 + +```shell +composer require friendsofhyperf/ide-helper +``` + +## 使用 + +- 生成模型助手 + +```shell +php ./bin/hyperf.php ide-helper:model +``` + +- 生成宏助手 + +```shell +php ./bin/hyperf.php ide-helper:macro +``` diff --git a/docs/zh-hk/components/index.md b/docs/zh-hk/components/index.md new file mode 100644 index 000000000..8a0a4e72f --- /dev/null +++ b/docs/zh-hk/components/index.md @@ -0,0 +1,3 @@ +# 組件 + +👈 請看左邊菜單,選擇組件 diff --git a/docs/zh-hk/components/ipc-broadcaster.md b/docs/zh-hk/components/ipc-broadcaster.md new file mode 100644 index 000000000..5d067a591 --- /dev/null +++ b/docs/zh-hk/components/ipc-broadcaster.md @@ -0,0 +1,45 @@ +# Ipc Broadcaster + +Ipc Broadcaster component for Hyperf. + +## 安裝 + +```shell +composer require friendsofhyperf/ipc-broadcaster +``` + +## 使用 + +- 閉包 + +```php +use function FriendsOfHyperf\IpcBroadcaster\broadcast; + +broadcast(function () { + echo 'Hello world'; +}); +``` + +- 類 + +```php +namespace App\Broadcasting; + +class FooMessage extends IpcMessage +{ + public function __construct(private string $foo) + { + // + } + + public function handle(): void + { + echo $this->foo; + } +} + +use function FriendsOfHyperf\IpcBroadcaster\broadcast; + +broadcast(new FooMessage('bar')); + +``` diff --git a/docs/zh-hk/components/lock.md b/docs/zh-hk/components/lock.md new file mode 100644 index 000000000..9673a2128 --- /dev/null +++ b/docs/zh-hk/components/lock.md @@ -0,0 +1,81 @@ +# Lock + +Hyperf 原子鎖組件。 + +## 安裝 + +- 安裝 + +```shell +composer require friendsofhyperf/lock +``` + +- 發佈配置 + +```shell +php bin/hyperf.php vendor:publish friendsofhyperf/lock -i config +``` + +## 使用 + +你可以使用 `lock()` 方法來創建和管理鎖: + +```php +$lock = lock($name = 'foo', $seconds = 10, $owner = null); + +if ($lock->get()) { + // 獲取鎖定10秒... + + $lock->release(); +} +``` + +`get` 方法也可以接收一個閉包。在閉包執行之後,將會自動釋放鎖: + +```php +lock('foo')->get(function () { + // 獲取無限期鎖並自動釋放... +}); +``` + +如果你在請求時鎖無法使用,你可以控制等待指定的秒數。如果在指定的時間限制內無法獲取鎖,則會拋出 `FriendsOfHyperf\Lock\Exception\LockTimeoutException` + +```php +use FriendsOfHyperf\Lock\Exception\LockTimeoutException; + +$lock = lock('foo', 10); + +try { + $lock->block(5); + + // 等待最多5秒後獲取的鎖... +} catch (LockTimeoutException $e) { + // 無法獲取鎖... +} finally { + optional($lock)->release(); +} + +lock('foo', 10)->block(5, function () { + // 等待最多5秒後獲取的鎖... +}); +``` + +註解方式 + +```php +use FriendsOfHyperf\Lock\Annotation\Lock; +use FriendsOfHyperf\Lock\Driver\LockInterface; + +class Foo +{ + #[Lock(name:"foo", seconds:10)] + protected LockInterface $lock; + + public function bar() + { + $this->lock->get(function () { + // 獲取無限期鎖並自動釋放... + }); + } +} +``` diff --git a/docs/zh-hk/components/macros.md b/docs/zh-hk/components/macros.md new file mode 100644 index 000000000..fbf4a4179 --- /dev/null +++ b/docs/zh-hk/components/macros.md @@ -0,0 +1,81 @@ +# Macros + +## 安裝 + +```shell +composer require friendsofhyperf/macros +``` + +## 支持方法 + +### Hyperf\Collection\Arr + +- `Arr::sortByMany` + +### Hyperf\Collection\Collection + +- `Collection::collapseWithKeys` +- `Collection::isSingle` + +### Hyperf\Collection\LazyCollection + +- `LazyCollection::collapseWithKeys` + +### Hyperf\HttpServer\Request + +- `Request::allFiles` +- `Request::anyFilled` +- `Request::boolean` +- `Request::collect` +- `Request::date` +- `Request::enum` +- `Request::except` +- `Request::exists` +- `Request::fake` +- `Request::filled` +- `Request::float` +- `Request::fluent` +- `Request::getHost` +- `Request::getHttpHost` +- `Request::getPort` +- `Request::getPsrRequest` +- `Request::getScheme` +- `Request::getSchemeAndHttpHost` +- `Request::hasAny` +- `Request::host` +- `Request::httpHost` +- `Request::integer` +- `Request::isEmptyString` +- `Request::isJson` +- `Request::isNotFilled` +- `Request::isSecure` +- `Request::keys` +- `Request::merge` +- `Request::mergeIfMissing` +- `Request::missing` +- `Request::only` +- `Request::schemeAndHttpHost` +- `Request::str` +- `Request::string` +- `Request::validate` +- `Request::validateWithBag` +- `Request::wantsJson` +- `Request::whenFilled` +- `Request::whenHas` + +### Hyperf\Stringable\Str + +- `Str::createUuidsNormally` +- `Str::createUuidsUsing` +- `Str::deduplicate` +- `Str::inlineMarkdown` +- `Str::markdown` +- `Str::transliterate` + +### Hyperf\Stringable\Stringable + +- `Stringable::deduplicate` +- `Stringable::inlineMarkdown` +- `Stringable::markdown` +- `Stringable::toHtmlString` +- `Stringable::whenIsAscii` diff --git a/docs/zh-hk/components/mail.md b/docs/zh-hk/components/mail.md new file mode 100644 index 000000000..206eaaa27 --- /dev/null +++ b/docs/zh-hk/components/mail.md @@ -0,0 +1,246 @@ +# Mail + +Email component of Hyperf + +## 安裝 + +```shell +composer require friendsofhyperf/mail +php bin/hyperf vendor:publish friendsofhyperf/mail +# Publish the view configuration file. +php bin/hyperf vendor:publish hyperf/view + +``` + +## 使用 + +### 配置 + +```php +// config/autoload/mail.php +/** + * This file is part of friendsofhyperf/components. + * + * @link https://github.com/friendsofhyperf/components + * @document https://github.com/friendsofhyperf/components/blob/main/README.md + * @contact huangdijia@gmail.com + */ +use function Hyperf\Support\env; + +return [ + /* + |-------------------------------------------------------------------------- + | Default Mailer + |-------------------------------------------------------------------------- + | + | This option controls the default mailer that is used to send all email + | messages unless another mailer is explicitly specified when sending + | the message. All additional mailers can be configured within the + | "mailers" array. Examples of each type of mailer are provided. + | + */ + + 'default' => env('MAIL_MAILER', 'log'), + + /* + |-------------------------------------------------------------------------- + | Mailer Configurations + |-------------------------------------------------------------------------- + | + | Here you may configure all of the mailers used by your application plus + | their respective settings. Several examples have been configured for + | you and you are free to add your own as your application requires. + | + | Laravel supports a variety of mail "transport" drivers that can be used + | when delivering an email. You may specify which one you're using for + | your mailers below. You may also add additional mailers if needed. + | + | Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2", + | "postmark", "resend", "log", "array", + | "failover", "roundrobin" + | + */ + + 'mailers' => [ + 'smtp' => [ + 'transport' => 'smtp', + 'url' => env('MAIL_URL','smtp://xxx@xxx:xxx@xxx.com:465'), + 'host' => env('MAIL_HOST', '127.0.0.1'), + 'port' => env('MAIL_PORT', 2525), + 'encryption' => env('MAIL_ENCRYPTION', 'tls'), + 'username' => env('MAIL_USERNAME'), + 'password' => env('MAIL_PASSWORD'), + 'timeout' => null, + 'local_domain' => env('MAIL_EHLO_DOMAIN'), + ], + + 'ses' => [ + 'transport' => 'ses', + ], + + 'postmark' => [ + 'transport' => 'postmark', + // 'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'), + // 'client' => [ + // 'timeout' => 5, + // ], + ], + + 'resend' => [ + 'transport' => 'resend', + ], + + 'sendmail' => [ + 'transport' => 'sendmail', + 'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'), + ], + + 'log' => [ + 'transport' => 'log', + 'group' => env('MAIL_LOG_GROUP','default'), + 'name' => env('MAIL_LOG_NAME','mail'), + ], + + 'array' => [ + 'transport' => 'array', + ], + + 'failover' => [ + 'transport' => 'failover', + 'mailers' => [ + 'smtp', + 'log', + ], + ], + + 'roundrobin' => [ + 'transport' => 'roundrobin', + 'mailers' => [ + 'ses', + 'postmark', + ], + ], + ], + + /* + |-------------------------------------------------------------------------- + | Global "From" Address + |-------------------------------------------------------------------------- + | + | You may wish for all emails sent by your application to be sent from + | the same address. Here you may specify a name and address that is + | used globally for all emails that are sent by your application. + | + */ + + 'from' => [ + 'address' => env('MAIL_FROM_ADDRESS', 'hyperf@hyperf.com'), + 'name' => env('MAIL_FROM_NAME', 'Hyperf'), + ], + + /* + |-------------------------------------------------------------------------- + | Markdown Mail Settings + |-------------------------------------------------------------------------- + | + | If you are using Markdown based email rendering, you may configure your + | theme and component paths here, allowing you to customize the design + | of the emails. Or, you may simply stick with the Laravel defaults! + | + */ + + 'markdown' => [ + 'theme' => env('MAIL_MARKDOWN_THEME', 'default'), + 'paths' => [ + BASE_PATH . '/storage/views/mail', + ], + ], +]; +``` + +### 構建郵件類 + +```shell +php bin/hyperf.php gen:mail TestMail +``` + +```php +// app/Mail/TestMail.php + +namespace App\Mail; + +use FriendsOfHyperf\Mail\Mailable; +use FriendsOfHyperf\Mail\Mailable\Content; +use FriendsOfHyperf\Mail\Mailable\Envelope; + +class TestMail extends Mailable +{ + + /** + * Create a new message instance. + */ + public function __construct( + private readonly string $name, + ){} + + /** + * Get the message envelope. + */ + public function envelope(): Envelope + { + return new Envelope( + subject: 'Test Mail', + ); + } + + /** + * Get the message content definition. + */ + public function content(): Content + { + return new Content( + markdown: 'mail.test', + with: [ + 'name' => $this->name, + ], + ); + } + + /** + * Get the attachments for the message. + * + * @return array + */ + public function attachments(): array + { + return []; + } +} +``` + +### 定義控制器或 Service + +```php +// app/Controller/IndexController.php + +use FriendsOfHyperf\Mail\Facade\Mail; + +class IndexController extends AbstractController +{ + public function index() + { + $user = $this->request->input('user', 'Hyperf'); + $mailer = Mail::mailer('smtp'); + $mailer->alwaysFrom('root@imoi.cn','Hyperf'); + + $mailer->to('2771717608@qq.com')->send(new \App\Mail\TestMail($user)); + $method = $this->request->getMethod(); + + return [ + 'method' => $method, + 'message' => "Hello {$user}.", + ]; + } +} + +``` diff --git a/docs/zh-hk/components/middleware-plus.md b/docs/zh-hk/components/middleware-plus.md new file mode 100644 index 000000000..0ac022c59 --- /dev/null +++ b/docs/zh-hk/components/middleware-plus.md @@ -0,0 +1,75 @@ +# Middleware Plus + +The middleware plus component for Hyperf. + +## 安裝 + +```shell +composer require friendsofhyperf/middleware-plus +``` + +## 使用 + +- 定義中間件 + +```php +handle($request); + } +} + +``` + +- 在路由中設置中間件 + +```php +use App\Middleware\FooMiddleware; + +Router::addRoute(['GET', 'POST', 'HEAD'], '/', 'App\Controller\IndexController::index', [ + 'middleware' => [ + FooMiddleware::class . ':1,2,3', + ], +]); +``` + +- 設置中間件別名 + +```php +// config/autoload/dependencies.php + +return [ + 'foo-middleware' => App\Middleware\FooMiddleware::class, +]; +``` + +- 使用中間件別名 + +```php +use App\Middleware\FooMiddleware; + +Router::addRoute(['GET', 'POST', 'HEAD'], '/', 'App\Controller\IndexController::index', [ + 'middleware' => [ + 'foo-middleware:1,2,3', + ], +]); +``` diff --git a/docs/zh-hk/components/model-factory.md b/docs/zh-hk/components/model-factory.md new file mode 100644 index 000000000..af512a1dd --- /dev/null +++ b/docs/zh-hk/components/model-factory.md @@ -0,0 +1,66 @@ +# Model Factory + +## Installation + +```shell +composer require friendsofhyperf/model-factory --dev +``` + +另外,將供應商配置文件發佈到您的應用程序(依賴項所必需的): + +```shell +php bin/hyperf.php vendor:publish friendsofhyperf/model-factory +``` + +## 使用 + +- `/factories/user_factory.php` + +```php +define(User::class, function (Faker\Generator $faker) { + return [ + 'name' => $faker->name, + 'email' => $faker->unique()->email, + ]; +}); +``` + +- `/seeders/user_seeder.php` + +```php +create([ + 'name' => 'Admin' + ]); + + + // Create 20 random users + factory(User::class, 20)->create(); + } +} + +``` diff --git a/docs/zh-hk/components/model-hashids.md b/docs/zh-hk/components/model-hashids.md new file mode 100644 index 000000000..9a9d158a4 --- /dev/null +++ b/docs/zh-hk/components/model-hashids.md @@ -0,0 +1,185 @@ +# Model Hashids + +使用 hashids 代替 URL 和列表項中的整數 ID 可以更具吸引力和巧妙。更多信息請訪問 [hashids.org](https://hashids.org/)。 + +這個包通過動態編碼/解碼 hashids 來為 Hyperf 模型添加 hashids,而不是將它們持久化到數據庫中。因此,不需要額外的數據庫列,並且通過在查詢中使用主鍵可以獲得更高的性能。 + +功能包括: + +- 為模型生成 hashids +- 將 hashids 解析為模型 +- 能夠為每個模型自定義 hashid 設置 +- 使用 hashids 進行路由綁定(可選) + +## 安裝 + +```shell +composer require friendsofhyperf/model-hashids +``` + +另外,將供應商配置文件發佈到您的應用程序(依賴項所必需的): + +```shell +php bin/hyperf.php vendor:publish friendsofhyperf/model-hashids +``` + +## 設置 + +基本功能通過使用 `HasHashid` trait 提供,然後可以通過使用 `HashidRouting` 添加基於 hashids 的路由綁定。 + +```php + +use Hyperf\Database\Model\Model; +use FriendsOfHyperf\ModelHashids\Concerns\HasHashid; +use FriendsOfHyperf\ModelHashids\Concerns\HashidRouting; + +Class Item extends Model +{ + use HasHashid, HashidRouting; +} + +``` + +### 自定義 Hashid 設置 + +可以通過重寫 `getHashidsConnection()` 為每個模型自定義 hashids 設置。它必須返回 `config/autoload/hashids.php` 中連接的名稱。 + +## 使用 + +### 基礎 + +```php + +// Generating the model hashid based on its key +$item->hashid(); + +// Equivalent to the above but with the attribute style +$item->hashid; + +// Finding a model based on the provided hashid or +// returning null on failure +Item::findByHashid($hashid); + +// Finding a model based on the provided hashid or +// throwing a ModelNotFoundException on failure +Item::findByHashidOrFail($hashid); + +// Decoding a hashid to its equivalent id +$item->hashidToId($hashid); + +// Encoding an id to its equivalent hashid +$item->idToHashid($id); + +// Getting the name of the hashid connection +$item->getHashidsConnection(); + +``` + +### 將 hashid 添加到序列化模型 + +將其設置為默認值: + +```php + +use Hyperf\Database\Model\Model; +use FriendsOfHyperf\ModelHashids\Concerns\HasHashid; + +class Item extends Model +{ + use HasHashid; + + /** + * The accessors to append to the model's array form. + * + * @var array + */ + protected $appends = ['hashid']; +} + +``` + +將其設置為特定路由: + +`return $item->append('hashid')->toJson();` + +### 隱式路由綁定 + +如果您希望使用模型的 hashid 值解析隱式路由綁定,可以在模型中使用 `HashidRouting`。 + +```php + +use Hyperf\Database\Model\Model; +use FriendsOfHyperf\ModelHashids\Concerns\HasHashid; +use FriendsOfHyperf\ModelHashids\Concerns\HashidRouting; + +class Item extends Model +{ + use HasHashid, HashidRouting; +} + +``` + +它重寫了 `getRouteKeyName()`、`getRouteKey()` 和 `resolveRouteBindingQuery()` 以使用 hashids 作為路由鍵。 + +它支持 Laravel 的自定義特定路由鍵的功能。 + +```php + +Route::get('/items/{item:slug}', function (Item $item) { + return $item; +}); + +``` + +#### 自定義默認路由鍵名稱 + +如果您希望默認使用另一個字段解析隱式路由綁定,可以重寫 `getRouteKeyName()` 以返回解析過程中的字段名稱,並在鏈接中返回其值的 `getRouteKey()`。 + +```php + +use Hyperf\Database\Model\Model; +use FriendsOfHyperf\ModelHashids\Concerns\HasHashid; +use FriendsOfHyperf\ModelHashids\Concerns\HashidRouting; + +class Item extends Model +{ + use HasHashid, HashidRouting; + + public function getRouteKeyName() + { + return 'slug'; + } + + public function getRouteKey() + { + return $this->slug; + } +} + +``` + +您仍然可以為特定路由指定 hashid。 + +```php + +Route::get('/items/{item:hashid}', function (Item $item) { + return $item; +}); + +``` + +#### 支持 Laravel 的其他隱式路由綁定功能 + +使用 `HashidRouting` 時,您仍然可以使用軟刪除和子路由綁定。 + +```php + +Route::get('/items/{item}', function (Item $item) { + return $item; +})->withTrashed(); + +Route::get('/user/{user}/items/{item}', function (User $user, Item $item) { + return $item; +})->scopeBindings(); + +``` diff --git a/docs/zh-hk/components/model-morph-addon.md b/docs/zh-hk/components/model-morph-addon.md new file mode 100644 index 000000000..d178efd92 --- /dev/null +++ b/docs/zh-hk/components/model-morph-addon.md @@ -0,0 +1,98 @@ +# Model Morph Addon + +The model morph addon for Hyperf. + +## 安裝 + +```shell +composer require friendsofhyperf/model-morph-addon +``` + +## 之前用法 + +```php +morphTo(); + } +} + +class Book extends Model +{ + public function images() + { + return $this->morphMany(Image::class, 'imageable'); + } +} + +class User extends Model +{ + public function images() + { + return $this->morphMany(Image::class, 'imageable'); + } +} + +// Global +Relation::morphMap([ + 'user' => App\Model\User::class, + 'book' => App\Model\Book::class, +]); +``` + +## 現在用法 + +```php +morphTo(); + } + + // Privately-owned + public static function getActualClassNameForMorph($class) + { + $morphMap = [ + 'user' => User::class, + 'book' => Book::class, + ]; + + return Arr::get($morphMap, $class, $class); + } +} + +class Book extends Model +{ + public function images() + { + return $this->morphMany(Image::class, 'imageable'); + } + + public function getMorphClass() + { + return 'book'; + } +} + +class User extends Model +{ + public function images() + { + return $this->morphMany(Image::class, 'imageable'); + } + + public function getMorphClass() + { + return 'user'; + } +} +``` diff --git a/docs/zh-hk/components/model-observer.md b/docs/zh-hk/components/model-observer.md new file mode 100644 index 000000000..a5aede4e1 --- /dev/null +++ b/docs/zh-hk/components/model-observer.md @@ -0,0 +1,99 @@ +# Model Observer + +The model observer component for Hyperf. + +## 安裝 + +```shell +composer require friendsofhyperf/model-observer +``` + +## 用法 + +- 通過命令生成 + +```shell +php bin/hyperf.php gen:observer TestObserver --model="App\\Model\\User" +``` + +- 單個綁定 + +```php +namespace App\Observer; + +use App\Model\User; +use FriendsOfHyperf\ModelObserver\Annotation\Observer; + +#[Observer(model: User::class)] +class FooObserver +{ + public function creating(User $model) + { + // do sth... + } + + public function created(User $model) + { + // do sth... + } + + // another events +} +``` + +- 多個綁定 + +```php +namespace App\Observer; + +use App\Model\Post; +use App\Model\User; +use FriendsOfHyperf\ModelObserver\Annotation\Observer; + +#[Observer(model: [User::class, Post::class])] +class FooObserver +{ + public function creating($model) + { + // do sth... + } + + public function created($model) + { + // do sth... + } + + // another events +} +``` + +- 綁定到模型 + +```php +namespace App\Model; + +use App\Observer\FooObserver; + +#[ObservedBy(FooObserver::class)] +class User extends Model +{ + // ... +} +``` + +## 支持的事件 + +- `booting` +- `booted` +- `retrieved` +- `creating` +- `created` +- `updating` +- `updated` +- `saving` +- `saved` +- `restoring` +- `restored` +- `deleting` +- `deleted` +- `forceDeleted` diff --git a/docs/zh-hk/components/model-scope.md b/docs/zh-hk/components/model-scope.md new file mode 100644 index 000000000..4f46408e8 --- /dev/null +++ b/docs/zh-hk/components/model-scope.md @@ -0,0 +1,47 @@ +# Model Scope + +The model scope annotation for Hyperf. + +## 安裝 + +```shell +composer require friendsofhyperf/model-scope +``` + +## 使用 + +- 定義 Scope + +```php +namespace App\Model\Scope; + +use Hyperf\Database\Model\Builder; +use Hyperf\Database\Model\Model; +use Hyperf\Database\Model\Scope; + +class AncientScope implements Scope +{ + /** + * Apply the scope to a given Model query builder. + */ + public function apply(Builder $builder, Model $model): void + { + $builder->where('created_at', '<', now()->subYears(2000)); + } +} +``` + +- 綁定到模型 + +```php +namespace App\Model; + +use App\Model\Scope\AncientScope; +use FriendsOfHyperf\ModelScope\Annotation\ScopedBy; + +#[ScopedBy(AncientScope::class)] +class User extends Model +{ + // +} +``` diff --git a/docs/zh-hk/components/monolog-hook.md b/docs/zh-hk/components/monolog-hook.md new file mode 100644 index 000000000..a1f318a9b --- /dev/null +++ b/docs/zh-hk/components/monolog-hook.md @@ -0,0 +1,7 @@ +# Monolog Hook + +## 安裝 + +```shell +composer require friendsofhyperf/monolog-hook +``` diff --git a/docs/zh-hk/components/mysql-grammar-addon.md b/docs/zh-hk/components/mysql-grammar-addon.md new file mode 100644 index 000000000..60f6c4bcb --- /dev/null +++ b/docs/zh-hk/components/mysql-grammar-addon.md @@ -0,0 +1,45 @@ +# Mysql Grammar Addon + +The MySqlGrammar addon for Hyperf. + +## 安裝 + +```shell +composer require friendsofhyperf/mysql-grammar-addon --dev +``` + +## 使用之前 + +```php +/** + * @property int $id + * @property int $user_id ??id + * @property string $group_name ???? + * @property string $event_name ???? + * @property string $page_name ?? + * @property string $extra ???? + * @property string $device pc,android,ios,touch + * @property string $device_id ??? + * @property \Carbon\Carbon $created_at ???? + */ +class Event extends Model +{} +``` + +## 使用之後 + +```php +/** + * @property int $id + * @property int $user_id 用户id + * @property string $group_name 事件分組 + * @property string $event_name 事件名稱 + * @property string $page_name 頁面 + * @property string $extra 額外信息 + * @property string $device pc,android,ios,touch + * @property string $device_id 設備號 + * @property \Carbon\Carbon $created_at 創建時間 + */ +class Event extends Model +{} +``` diff --git a/docs/zh-hk/components/notification-easysms.md b/docs/zh-hk/components/notification-easysms.md new file mode 100644 index 000000000..cda32d512 --- /dev/null +++ b/docs/zh-hk/components/notification-easysms.md @@ -0,0 +1,98 @@ +# Notification EasySms Channel + +## 安裝 + +```shell +composer require friendsofhyperf/notification-easysms:~3.1.0 +``` + +## 使用 + +### Use `Notifiable` trait in Model + +```php +phone; + } +} +``` + +### SMS Notifications + +- Install the easy-sms package + +```shell +composer require overtrue/easy-sms:^3.0 +``` + +```php +namespace App\Notification; + +use FriendsOfHyperf\Notification\EasySms\Contract\EasySmsChannelToSmsArrayContract; +use FriendsOfHyperf\Notification\EasySms\Contract\Smsable; +use FriendsOfHyperf\Notification\Notification; +use Overtrue\EasySms\Message; + +// 通知類 +class TestNotification extends Notification implements Smsable +{ + public function __construct(private string $code) + { + } + + public function via() + { + return [ + 'easy-sms' + ]; + } + + /** + * 返回的內容將組裝到短信模型中 new Message($notification->toSms()). + * 文檔 https://github.com/overtrue/easy-sms?tab=readme-ov-file#%E5%AE%9A%E4%B9%89%E7%9F%AD%E4%BF%A1 + */ + public function toSms(mixed $notifiable): array|Message + { + return [ + 'code' => $this->code, + 'template' => 'SMS_123456789', + 'data' => [ + 'code' => $this->code, + ] + ]; + } + + // or return customer Message + // public function toSms(mixed $notifiable): array|Message + // { + // return new Message(); + // } + +} +``` diff --git a/docs/zh-hk/components/notification-mail.md b/docs/zh-hk/components/notification-mail.md new file mode 100644 index 000000000..d7e057ecc --- /dev/null +++ b/docs/zh-hk/components/notification-mail.md @@ -0,0 +1,111 @@ +# Notification Mail Channel + +## 安裝 + +```shell +composer require friendsofhyperf/notification-mail:~3.1.0 +``` + +## 使用 + +### Use `Notifiable` trait in Model + +```php +mail; + } +} +``` + +### Mail Notifications + +```shell +php bin/hyperf.php gen:markdown-mail Test +``` + +output + +```php + +namespace App\Mail; + +use FriendsOfHyperf\Notification\Mail\Message\MailMessage; +use FriendsOfHyperf\Notification\Notification; + +class Test extends Notification +{ + /** + * Create a new notification instance. + */ + public function __construct() + { + // + } + + /** + * Get the notification's delivery channels. + * + * @return array + */ + public function via(object $notifiable): array + { + return ['mail']; + } + + /** + * Get the mail representation of the notification. + */ + public function toMail(object $notifiable): MailMessage + { + return (new MailMessage)->from('xxx@xxx.cn','Hyperf')->replyTo('xxx@qq.com','zds')->markdown('email'); + } + + /** + * Get the array representation of the notification. + * + * @return array + */ + public function toArray(object $notifiable): array + { + return [ + // + ]; + } + +} +``` + +### Mail Template + +```php +// storage/view/email.blade.php +xxx +``` diff --git a/docs/zh-hk/components/notification.md b/docs/zh-hk/components/notification.md new file mode 100644 index 000000000..33f015e05 --- /dev/null +++ b/docs/zh-hk/components/notification.md @@ -0,0 +1,246 @@ +# Notification + +## 安裝 + +```shell +composer require friendsofhyperf/notification:~3.1.0 +``` + +## 使用 + +### 在 Model 中使用 `Notifiable` trait + +```php + 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime']; + + // 通知手機號 + public function routeNotificationForSms(): string|PhoneNumber + { + return $this->phone; + } +} +``` + +### Database Notifications + +```shell +# Install the database package +composer require hyperf/database:~3.1.0 + +# Publish the migration file +php bin/hyperf.php notification:table + +# Run the migration +php bin/hyperf.php migrate + +# Create a notification +php bin/hyperf.php make:notification TestNotification +``` + +--- + +```php + $this->message, + ]; + } +} +``` + +--- + +```php +// Your controller or service +// 通知一條消息 +$user->notify(new TestNotification('系統通知:xxx')); +$noReadCount = $user->unreadNotifications()->count(); +$this->output->success('發送成功,未讀消息數:' . $noReadCount); +$notifications = $user->unreadNotifications()->first(); +$this->output->success('消息內容:' . $notifications->data['message']); +$notifications->markAsRead(); +$noReadCount = $user->unreadNotifications()->count(); +$this->output->success('標記已讀,未讀消息數:' . $noReadCount); +``` + +### SMS Notifications + +- [notification-easy-sms](https://github.com/friendsofhyperf/notification-easysms) + +### Symfony Notifications + +Send notifications using Symfony Notifier. + +Email, SMS, Slack, Telegram, etc. + +```shell +composer require symfony/notifier +``` + +#### Email + +```shell +composer require symfony/mailer +``` + +--- + +```php +channels()); + } + + /** + * @return ChannelInterface[] + */ + public function channels(): array + { + return [ + 'email' => new EmailChannel( + transport: Transport::fromDsn( + // MAIL_DSN=smtp://user:password@localhost:1025 + env('MAIL_DSN'), + dispatcher: $this->dispatcher, + logger: $this->logger + ), + from: 'root@imoi.cn' + ), + ]; + } +} +``` + +```php +message,['email']))->content('The introduction to the notification.'); + } + + public function toRecipient(User $user) + { + return new Recipient('2771717608@qq.com'); + } + + +} +``` + +```php + \App\Factory\Notifier::class +]; + +``` + +#### 在控制器中使用 + +```php +$user = User::create(); +// 通知一條消息 +$user->notify(new TestNotification('系統通知:xxx')); +``` diff --git a/docs/zh-hk/components/openai-client.md b/docs/zh-hk/components/openai-client.md new file mode 100644 index 000000000..0966f571c --- /dev/null +++ b/docs/zh-hk/components/openai-client.md @@ -0,0 +1,68 @@ +# OpenAI Client + +------ + +**OpenAI PHP** for Laravel 是一個功能強大的社區 PHP API 客户端,允許您與 [Open AI API](https://beta.openai.com/docs/api-reference/introduction) 進行交互。 + +> **注意:** 此倉庫包含 **OpenAI PHP** 的 Hyperf 集成代碼。如果您想在與框架無關的方式中使用 **OpenAI PHP** 客户端,請查看 [openai-php/client](https://github.com/openai-php/client) 倉庫。 + +## 快速開始 + +> **Requires [PHP 8.1+](https://php.net/releases/)** + +首先,通過 [Composer](https://getcomposer.org/) 包管理器安裝 OpenAI: + +```shell +composer require friendsofhyperf/openai-client +``` + +接下來,發佈配置文件: + +```shell +php bin/hyperf.php vendor:publish friendsofhyperf/openai-client +``` + +這將在您的項目中創建一個 `config/autoload/openai.php` 配置文件,您可以使用環境變量根據需要進行修改: + +```env +OPENAI_API_KEY=sk-... +``` + +最後,您可以使用容器中的 `OpenAI\Client` 實例來訪問 OpenAI API: + +```php +use OpenAI\Client; + +$result = di(OpenAI\Client::class)->completions()->create([ + 'model' => 'text-davinci-003', + 'prompt' => 'PHP is', +]); + +echo $result['choices'][0]['text']; // an open-source, widely-used, server-side scripting language. +``` + +## Azure + +要使用 Azure OpenAI 服務,必須使用工廠手動構建客户端。 + +```php +$client = OpenAI::factory() + ->withBaseUri('{your-resource-name}.openai.azure.com/openai/deployments/{deployment-id}') + ->withHttpHeader('api-key', '{your-api-key}') + ->withQueryParam('api-version', '{version}') + ->make(); +``` + +要使用 Azure,您必須部署一個模型,該模型由 {deployment-id} 標識,已集成到 API 調用中。因此,您不必在調用期間提供模型,因為它已包含在 BaseUri 中。 + +因此,一個基本的示例完成調用將是: + +```php +$result = $client->completions()->create([ + 'prompt' => 'PHP is' +]); +``` + +## 官方指南 + +有關使用示例,請查看 [openai-php/client](https://github.com/openai-php/client) 倉庫。 diff --git a/docs/zh-hk/components/pest-plugin-hyperf.md b/docs/zh-hk/components/pest-plugin-hyperf.md new file mode 100644 index 000000000..98514a11f --- /dev/null +++ b/docs/zh-hk/components/pest-plugin-hyperf.md @@ -0,0 +1,37 @@ +# Pest Plugin Hyperf + +> 這是一個 [Pest](https://pestphp.com) 插件,使您的 Hyperf 項目的 Pest 能夠在基於 Swoole 的協程環境中運行。 + +## 安裝 + +```shell +composer require friendsofhyperf/pest-plugin-hyperf --dev +``` + +## 使用 + +```shell +php vendor/bin/pest --coroutine +# or +php vendor/bin/pest --prepend test/prepend.php --coroutine +``` + +- 配置 test/prepend.php + +```php +get(Hyperf\Contract\ApplicationInterface::class); +})(); + +``` diff --git a/docs/zh-hk/components/pretty-console.md b/docs/zh-hk/components/pretty-console.md new file mode 100644 index 000000000..55370016f --- /dev/null +++ b/docs/zh-hk/components/pretty-console.md @@ -0,0 +1,36 @@ +# Pretty Console + +The pretty console component for Hyperf. + +![Pretty Console](https://user-images.githubusercontent.com/5457236/178333036-b11abb56-ba70-4c0d-a2f6-79afe3a0a78c.png) + +## 安裝 + +```shell +composer require friendsofhyperf/pretty-console +``` + +## 使用 + +```php +components->info('Your message here.'); + } +} +``` + +## 鳴謝 + +- [nunomaduro/termwind](https://github.com/nunomaduro/termwind) +- [The idea from pr of laravel](https://github.com/laravel/framework/pull/43065) diff --git a/docs/zh-hk/components/purifier.md b/docs/zh-hk/components/purifier.md new file mode 100644 index 000000000..5a408db66 --- /dev/null +++ b/docs/zh-hk/components/purifier.md @@ -0,0 +1,173 @@ +# Purifier + +HTML 過濾器. 派生於 [mews/purifier](https://github.com/mewebstudio/Purifier). + +## 安裝 + +使用 Composer 安裝此組件: + +```shell +composer require friendsofhyperf/purifier +``` + +將自動發現服務提供商。您不需要在任何地方添加 Provider. + +## 用法 + +在請求或中間件中使用以下方法,以清理 HTML: + +```php +clean($request->get('inputname')); +``` + +或者 + +```php +ApplicationContext::getContainer()->get(Purifier::class)->clean($request->get('inputname')); +``` + +動態配置: + +```php +clean('This is my H1 title', 'titles'); +clean('This is my H1 title', array('Attr.EnableID' => true)); +``` + +或者 + +```php +ApplicationContext::getContainer()->get(Purifier::class)->clean('This is my H1 title', 'titles'); +ApplicationContext::getContainer()->get(Purifier::class)->clean('This is my H1 title', array('Attr.EnableID' => true)); +``` + +使用 [URI 過濾器](http://htmlpurifier.org/docs/enduser-uri-filter.html) + +```php +ApplicationContext::getContainer()->get(Purifier::class)->clean('This is my H1 title', 'titles', function (HTMLPurifier_Config $config) { + $uri = $config->getDefinition('URI'); + $uri->addFilter(new HTMLPurifier_URIFilter_NameOfFilter(), $config); +}); +``` + +或者,如果您想在數據庫模型中清理 HTML,可以使用我們自定義的 casts: + +```php + CleanHtml::class, // 在獲取和設置值時都會進行清理 + 'description' => CleanHtmlInput::class, // 設置值時清理 + 'history' => CleanHtmlOutput::class, // 獲取值時進行清理 + ]; +} +``` + +## 配置 + +要使用您自己的設置,請發佈配置. + +```shell +php bin/hyperf.php vendor:publish friendsofhyperf/purifier +``` + +配置文件 `config/autoload/purifier.php` 內容如下: + +```php + +return [ + 'encoding' => 'UTF-8', + 'finalize' => true, + 'ignoreNonStrings' => false, + 'cachePath' => storage_path('app/purifier'), + 'cacheFileMode' => 0755, + 'settings' => [ + 'default' => [ + 'HTML.Doctype' => 'HTML 4.01 Transitional', + 'HTML.Allowed' => 'div,b,strong,i,em,u,a[href|title],ul,ol,li,p[style],br,span[style],img[width|height|alt|src]', + 'CSS.AllowedProperties' => 'font,font-size,font-weight,font-style,font-family,text-decoration,padding-left,color,background-color,text-align', + 'AutoFormat.AutoParagraph' => true, + 'AutoFormat.RemoveEmpty' => true, + ], + 'test' => [ + 'Attr.EnableID' => 'true', + ], + "youtube" => [ + "HTML.SafeIframe" => 'true', + "URI.SafeIframeRegexp" => "%^(http://|https://|//)(www.youtube.com/embed/|player.vimeo.com/video/)%", + ], + 'custom_definition' => [ + 'id' => 'html5-definitions', + 'rev' => 1, + 'debug' => false, + 'elements' => [ + // https://developers.whatwg.org/sections.html + ['section', 'Block', 'Flow', 'Common'], + ['nav', 'Block', 'Flow', 'Common'], + ['article', 'Block', 'Flow', 'Common'], + ['aside', 'Block', 'Flow', 'Common'], + ['header', 'Block', 'Flow', 'Common'], + ['footer', 'Block', 'Flow', 'Common'], + + ['address', 'Block', 'Flow', 'Common'], + ['hgroup', 'Block', 'Required: h1 | h2 | h3 | h4 | h5 | h6', 'Common'], + + // https://developers.whatwg.org/grouping-content.html + ['figure', 'Block', 'Optional: (figcaption, Flow) | (Flow, figcaption) | Flow', 'Common'], + ['figcaption', 'Inline', 'Flow', 'Common'], + + // https://developers.whatwg.org/the-video-element.html#the-video-element + ['video', 'Block', 'Optional: (source, Flow) | (Flow, source) | Flow', 'Common', [ + 'src' => 'URI', + 'type' => 'Text', + 'width' => 'Length', + 'height' => 'Length', + 'poster' => 'URI', + 'preload' => 'Enum#auto,metadata,none', + 'controls' => 'Bool', + ]], + ['source', 'Block', 'Flow', 'Common', [ + 'src' => 'URI', + 'type' => 'Text', + ]], + + // https://developers.whatwg.org/text-level-semantics.html + ['s', 'Inline', 'Inline', 'Common'], + ['var', 'Inline', 'Inline', 'Common'], + ['sub', 'Inline', 'Inline', 'Common'], + ['sup', 'Inline', 'Inline', 'Common'], + ['mark', 'Inline', 'Inline', 'Common'], + ['wbr', 'Inline', 'Empty', 'Core'], + + // https://developers.whatwg.org/edits.html + ['ins', 'Block', 'Flow', 'Common', ['cite' => 'URI', 'datetime' => 'CDATA']], + ['del', 'Block', 'Flow', 'Common', ['cite' => 'URI', 'datetime' => 'CDATA']], + ], + 'attributes' => [ + ['iframe', 'allowfullscreen', 'Bool'], + ['table', 'height', 'Text'], + ['td', 'border', 'Text'], + ['th', 'border', 'Text'], + ['tr', 'width', 'Text'], + ['tr', 'height', 'Text'], + ['tr', 'border', 'Text'], + ], + ], + 'custom_attributes' => [ + ['a', 'target', 'Enum#_blank,_self,_target,_top'], + ], + 'custom_elements' => [ + ['u', 'Inline', 'Inline', 'Common'], + ], + ], + +]; +``` diff --git a/docs/zh-hk/components/recaptcha.md b/docs/zh-hk/components/recaptcha.md new file mode 100644 index 000000000..67c2037c4 --- /dev/null +++ b/docs/zh-hk/components/recaptcha.md @@ -0,0 +1,73 @@ +# ReCaptcha + +The Google recaptcha component for Hyperf. + +## 安裝 + +```shell +composer require friendsofhyperf/recaptcha +``` + +## 使用 + +- 定義中間件 + +```php +namespace App\Middleware; + +use FriendsOfHyperf\ReCaptcha\Middleware\ReCaptchaMiddleware; + +class V3CaptchaMiddleware extends ReCaptchaMiddleware +{ + protected string $version = 'v3'; + protected string $action = 'register'; + protected float $score = 0.35; + protected string $hostname; +} + +class V2CaptchaMiddleware extends ReCaptchaMiddleware +{ + protected string $version = 'v2'; + protected string $action = 'register'; + protected float $score = 0.35; + protected string $hostname; +} +``` + +- 驗證器使用 + +```php +validationFactory->make( + $request->all(), + [ + 'g-recaptcha' => 'required|recaptcha:register,0.34,hostname,v3', + ], + [ + 'g-recaptcha.required' => 'g-recaptcha is required', + 'g-recaptcha.recaptcha' => 'Google ReCaptcha Verify Fails', + ] + ); + + if ($validator->fails()){ + // Handle exception + $errorMessage = $validator->errors()->first(); + } + // Do something + } +} +``` diff --git a/docs/zh-hk/components/redis-subscriber.md b/docs/zh-hk/components/redis-subscriber.md new file mode 100644 index 000000000..44f482e11 --- /dev/null +++ b/docs/zh-hk/components/redis-subscriber.md @@ -0,0 +1,63 @@ +# Redis Subscriber + +Forked from [mix-php/redis-subscriber](https://github.com/mix-php/redis-subscriber) + +Redis native protocol Subscriber based on Swoole coroutine + +基於 Swoole 協程的 Redis 原生協議訂閲庫 + +使用 Socket 直接連接 Redis 服務器,不依賴 phpredis 擴展,該訂閲器有如下優點: + +- 平滑修改:可隨時增加、取消訂閲通道,實現無縫切換通道的需求。 +- 跨協程安全關閉:可在任意時刻關閉訂閲。 +- 通道獲取消息:該庫封裝風格參考 golang 語言 [go-redis](https://github.com/go-redis/redis) 庫封裝,通過 channel 獲取訂閲的消息。 + +## 安裝 + +```shell +composer require friendsofhyperf/redis-subscriber +``` + +## 訂閲頻道 + +- 連接、訂閲失敗會拋出異常 + +```php +$sub = new \FriendsOfHyperf\Redis\Subscriber\Subscriber('127.0.0.1', 6379, '', 5); // 連接失敗將拋出異常 +$sub->subscribe('foo', 'bar'); // 訂閲失敗將拋出異常 + +$chan = $sub->channel(); +while (true) { + $data = $chan->pop(); + if (empty($data)) { // 手動close與redis異常斷開都會導致返回false + if (!$sub->closed) { + // redis異常斷開處理 + var_dump('Redis connection is disconnected abnormally'); + } + break; + } + var_dump($data); +} +``` + +接收到訂閲消息: + +```shell +object(FriendsOfHyperf\Redis\Subscriber\Message)#8 (2) { + ["channel"]=> + string(2) "foo" + ["payload"]=> + string(4) "test" +} +``` + +## 全部方法 + +| 方法 | 描述 | +| --- | --- | +| subscribe(string ...$channels) : void | 增加訂閲 | +| unsubscribe(string ...$channels) : void | 取消訂閲 | +| psubscribe(string ...$channels) : void | 增加通配訂閲 | +| punsubscribe(string ...$channels) : void | 取消通配訂閲 | +| channel() : Hyperf\Engine\Channel | 獲取消息通道 | +| close() : void | 關閉訂閲 | diff --git a/docs/zh-hk/components/sentry.md b/docs/zh-hk/components/sentry.md new file mode 100644 index 000000000..39729aed5 --- /dev/null +++ b/docs/zh-hk/components/sentry.md @@ -0,0 +1,101 @@ +# Sentry + +The sentry component for Hyperf. + +## 安裝 + +```shell +composer require friendsofhyperf/sentry +``` + +## 發佈配置 + +```shell +php bin/hyperf.php vendor:publish friendsofhyperf/sentry +``` + +## 註冊 LoggerHandler + +```php +warning('this is a test warning issue!'); + +return [ + // ... + 'sentry' => [ + 'handler' => [ + 'class' => FriendsOfHyperf\Sentry\SentryHandler::class, + 'constructor' => [ + 'level' => \Monolog\Level::Debug, + ], + ], + 'formatter' => [ + 'class' => \Monolog\Formatter\LineFormatter::class, + 'constructor' => [ + 'format' => null, + 'dateFormat' => null, + 'allowInlineLineBreaks' => true, + ] + ], + ], + // ... +]; + +``` + +## 配置 Sentry 運行日誌 + +```php + Hyperf\Contract\StdoutLoggerInterface::class, + // ... +]; +``` + +## 註解 + +```php + [ + [ + 'name' => 'tcp', + 'type' => Server::SERVER_BASE, + 'host' => '0.0.0.0', + 'port' => 9401, + 'sock_type' => SWOOLE_SOCK_TCP, + 'callbacks' => [ + Event::ON_CONNECT => [TcpServer::class,'onConnect'], + Event::ON_CLOSE => [TcpServer::class,'onClose'], + Event::ON_RECEIVE => [TcpServer::class,'onReceive'], + ], + 'options' => [ + // Whether to enable request lifecycle event + 'enable_request_lifecycle' => false, + ], + ] + ], +``` + +### 異步風格 + +```php +send($fd, sprintf('Client %s connected.'.PHP_EOL, $fd)); + } + + public function onClose($server, int $fd, int $reactorId): void + { + $server->send($fd, sprintf('Client %s closed.'.PHP_EOL, $fd)); + } + + public function onReceive($server, int $fd, int $reactorId, string $data): void + { + $server->send($fd, sprintf('Client %s send: %s'.PHP_EOL, $fd, $data)); + var_dump($data); + } + + +} +``` + +### 協程風格 + +```php +namespace App; + +use Hyperf\Contract\OnReceiveInterface; +use FriendsOfHyperf\TcpSender\Sender; +use Swoole\Coroutine\Server\Connection; +use Swoole\Server; + +class TcpServer implements OnReceiveInterface +{ + public function __construct(private Sender $sender) + { + } + + public function onConnect(Connection $connection, $fd): void + { + // 設置 fd 和 connection 的映射關係 + $this->sender->setResponse($fd,$connection); + $connection->send(sprintf('Client %s connected.'.PHP_EOL, $fd)); + } + + public function onClose($connection, int $fd): void + { + // 刪除 fd 和 connection 的映射關係 + $this->sender->setResponse($fd,null); + } + + public function onReceive($server, int $fd, int $reactorId, string $data): void + { + $server->send($fd, sprintf('Client %s send: %s'.PHP_EOL, $fd, $data)); + } + + +} +``` + +## 在控制器中使用 + +```php +sender->send(1, 'Hello Hyperf.'); + $user = $this->request->input('user', 'Hyperf'); + $method = $this->request->getMethod(); + + return [ + 'method' => $method, + 'message' => "Hello {$user}.", + ]; + } +} + +``` diff --git a/docs/zh-hk/components/telescope-elasticsearch.md b/docs/zh-hk/components/telescope-elasticsearch.md new file mode 100644 index 000000000..b23b7d42c --- /dev/null +++ b/docs/zh-hk/components/telescope-elasticsearch.md @@ -0,0 +1,34 @@ +# Telescope Elasticsearch Driver + +它允許您從 SQL 數據庫切換到 Elasticsearch 作為數據存儲的驅動程序,並且它將消除死鎖,使 Telescope 成為一個可用於生產環境的日誌系統。 + +## 安裝 + +```shell +composer require friendsofhyperf/telescope-elasticsearch +``` + +## 發佈配置 + +```shell +php bin/hyperf.php vendor:publish friendsofhyperf/telescope --id=config +``` + +## 配置 + +```php +// config/autoload/telescope.php +return [ + 'driver' => 'elasticsearch', + 'storage' => [ + 'elasticsearch' => [ + 'driver' => FriendsOfHyperf\TelescopeElasticsearch\Storage\ElasticsearchEntriesRepository::class, + 'index' => 'telescope_entries', + + 'hosts' => ['127.0.0.1'], + 'username' => null, + 'password' => null, + ], + ], +]; +``` diff --git a/docs/zh-hk/components/telescope.md b/docs/zh-hk/components/telescope.md new file mode 100644 index 000000000..a4142b194 --- /dev/null +++ b/docs/zh-hk/components/telescope.md @@ -0,0 +1,142 @@ +# Telescope + +## 可用監聽器 + +- [x] 請求監視器 +- [x] 異常監視器 +- [x] 數據查詢監視器 +- [x] gRPC請求監視器 +- [x] Redis監視器 +- [x] 日誌監視器 +- [x] 命令行監視器 +- [x] 事件監視器 +- [x] HTTP Client 監視器 +- [x] 緩存監視器 +- [x] 定時任務監視器 + +## 安裝 + +```shell +composer require friendsofhyperf/telescope:~3.1.0 +``` + +使用 `vendor:publish` 命令來發布其公共資源 + +```shell +php bin/hyperf.php vendor:publish friendsofhyperf/telescope +``` + +運行 `migrate` 命令執行數據庫變更來創建和保存 Telescope 需要的數據 + +```shell +php bin/hyperf.php migrate +``` + +## 使用 + +> 監聽器和中間件,二選一即可 + +### 請求監聽器 + +在 `config/autoload/listeners.php`配置文件添加監聽器 + +```php + [ + FriendsOfHyperf\Telescope\Middleware\TelescopeMiddleware::class, + ], +]; +``` + +如需記錄gRPC請求,請使用`grpc`中間件 + +```php + [ + FriendsOfHyperf\Telescope\Middleware\TelescopeMiddleware::class, + ], +]; +``` + +## 查看儀表板 + +`http://127.0.0.1:9501/telescope` + +## 數據庫配置 + +在 `config/autoload/telescope.php`管理數據庫連接配置,默認使用`default`連接 + +```php +'connection' => env('TELESCOPE_DB_CONNECTION', 'default'), +``` + +## 標籤 + +您可能希望將自己的自定義標籤附加到條目。為此,您可以使用 **`Telescope::tag`** 方法。 + +## 批量過濾 + +您可能只想記錄某些特殊條件下的條目。為此,您可以使用 **`Telescope::filter`** 方法。 + +例子 + +```php +use FriendsOfHyperf\Telescope\Telescope; +use Hyperf\Event\Contract\ListenerInterface; +use Hyperf\Framework\Event\BootApplication; +use FriendsOfHyperf\Telescope\IncomingEntry; + +class TelescopeInitListener implements ListenerInterface +{ + public function listen(): array + { + return [ + BootApplication::class, + ]; + } + + public function process(object $event): void + { + // attach your own custom tags + Telescope::tag(function (IncomingEntry $entry) { + if ($entry->type === 'request') { + return [ + 'status:' . $entry->content['response_status'], + 'uri:'. $entry->content['uri'], + ]; + } + }); + + // filter entry + Telescope::filter(function (IncomingEntry $entry): bool { + if ($entry->type === 'request'){ + if ($entry->content['uri'] == 'xxxx') { + return false; + } + } + return true; + }); + + } +} +``` diff --git a/docs/zh-hk/components/tinker.md b/docs/zh-hk/components/tinker.md new file mode 100644 index 000000000..80daf5327 --- /dev/null +++ b/docs/zh-hk/components/tinker.md @@ -0,0 +1,189 @@ +# Tinker + +The Powerful REPL for Hyperf. + +## 安裝 + +```shell +composer require friendsofhyperf/tinker +``` + +## 發佈配置 + +```shell +php bin/hyperf.php vendor:publish friendsofhyperf/tinker +``` + +## 使用 + +```shell +php bin/hyperf.php tinker +``` + +## 命令 + +- 運行命令 + +````shell +Psy Shell v0.10.4 (PHP 7.3.11 — cli) +>>> $a=1 +=> 1 +>>> $a +=> 1 +>>> define('VERSION', 'v1.0.1') +=> true +>>> VERSION +=> "v1.0.1" +>>> +```` + +- 查看命令幫助 + +```shell +>>> help + help Show a list of commands. Type `help [foo]` for information about [foo]. Aliases: ? + ls List local, instance or class variables, methods and constants. Aliases: dir + dump Dump an object or primitive. + doc Read the documentation for an object, class, constant, method or property. Aliases: rtfm, man + show Show the code for an object, class, constant, method or property. + wtf Show the backtrace of the most recent exception. Aliases: last-exception, wtf? + whereami Show where you are in the code. + throw-up Throw an exception or error out of the Psy Shell. + timeit Profiles with a timer. + trace Show the current call stack. + buffer Show (or clear) the contents of the code input buffer. Aliases: buf + clear Clear the Psy Shell screen. + edit Open an external editor. Afterwards, get produced code in input buffer. + sudo Evaluate PHP code, bypassing visibility restrictions. + history Show the Psy Shell history. Aliases: hist + exit End the current session and return to caller. Aliases: quit, q + clear-compiled Remove the compiled class file + down Put the application into maintenance mode + env Display the current framework environment + optimize Cache the framework bootstrap files + up Bring the application out of maintenance mode + migrate Run the database migrations + inspire Display an inspiring quote +``` + +- 獲取環境變量 + +```shell +Psy Shell v0.10.4 (PHP 7.2.34 — cli) +>>> env("APP_NAME") +=> "skeleton" +>>> +``` + +- 模型操作 + +```shell +➜ t.hyperf.com git:(master) ✗ php bin/hyperf.php tinker +[DEBUG] Event Hyperf\Framework\Event\BootApplication handled by Hyperf\Config\Listener\RegisterPropertyHandlerListener listener. +[DEBUG] Event Hyperf\Framework\Event\BootApplication handled by Hyperf\Paginator\Listener\PageResolverListener listener. +[DEBUG] Event Hyperf\Framework\Event\BootApplication handled by Hyperf\ExceptionHandler\Listener\ExceptionHandlerListener listener. +[DEBUG] Event Hyperf\Framework\Event\BootApplication handled by Hyperf\DbConnection\Listener\RegisterConnectionResolverListener listener. +Psy Shell v0.10.4 (PHP 7.2.34 — cli) by Justin Hileman +Unable to check for updates +>>> $user = App\Model\User::find(1) +[DEBUG] Event Hyperf\Database\Model\Events\Booting handled by Hyperf\ModelListener\Listener\ModelHookEventListener listener. +[DEBUG] Event Hyperf\Database\Model\Events\Booting handled by Hyperf\ModelListener\Listener\ModelEventListener listener. +[DEBUG] Event Hyperf\Database\Model\Events\Booted handled by Hyperf\ModelListener\Listener\ModelHookEventListener listener. +[DEBUG] Event Hyperf\Database\Model\Events\Booted handled by Hyperf\ModelListener\Listener\ModelEventListener listener. +[DEBUG] Event Hyperf\Database\Events\QueryExecuted handled by App\Listener\DbQueryExecutedListener listener. +[DEBUG] Event Hyperf\Database\Model\Events\Retrieved handled by Hyperf\ModelListener\Listener\ModelHookEventListener listener. +[DEBUG] Event Hyperf\Database\Model\Events\Retrieved handled by Hyperf\ModelListener\Listener\ModelEventListener listener. +=> App\Model\User {#81816 + +incrementing: true, + +exists: true, + +wasRecentlyCreated: false, + +timestamps: true, + } +>>> var_dump($user) +object(App\Model\User)#81816 (28) { + ["table":protected]=> + string(5) "users" + ["fillable":protected]=> + array(2) { + [0]=> + string(2) "id" + [1]=> + string(4) "name" + } + ["casts":protected]=> + array(0) { + } + ["incrementing"]=> + bool(true) + ["exists"]=> + bool(true) + + ["attributes":protected]=> + array(4) { + ["id"]=> + int(1) + ["name"]=> + string(5) "arvin" + ["created_at"]=> + string(19) "2020-11-23 18:38:00" + ["updated_at"]=> + string(19) "2020-11-23 18:38:03" + } + ["original":protected]=> + array(4) { + ["id"]=> + int(1) + ["name"]=> + string(5) "arvin" + ["created_at"]=> + string(19) "2020-11-23 18:38:00" + ["updated_at"]=> + string(19) "2020-11-23 18:38:03" + } + +} +=> null +``` + +- 查看文檔 + +```shell +>>> doc md5 +function md5($str, $raw_output = unknown) + +PHP manual not found + To document core PHP functionality, download the PHP reference manual: + https://github.com/bobthecow/psysh/wiki/PHP-manual +>>> +``` + +- 查看源碼 + +```shell +>>> show App\Model\User + 7: /** + 8: */ + 9: class User extends Model +10: { +11: /** +12: * The table associated with the model. +13: * +14: * @var string +15: */ +16: protected $table = 'users'; +17: /** +18: * The attributes that are mass assignable. +19: * +20: * @var array +21: */ +22: protected $fillable = ['id','name']; +23: /** +24: * The attributes that should be cast to native types. +25: * +26: * @var array +27: */ +28: protected $casts = []; +29: } + +>>> +``` diff --git a/docs/zh-hk/components/trigger.md b/docs/zh-hk/components/trigger.md new file mode 100644 index 000000000..1b8cf8a42 --- /dev/null +++ b/docs/zh-hk/components/trigger.md @@ -0,0 +1,73 @@ +# Trigger + +## 安裝 + +- 安裝 + +```shell +composer require friendsofhyperf/trigger +``` + +- 發佈配置 + +```shell +php bin/hyperf.php vendor:publish friendsofhyperf/trigger +``` + +## 添加監聽器 + +```php +// config/autoload/listeners.php + +return [ + FriendsOfHyperf\Trigger\Listener\BindTriggerProcessesListener::class => PHP_INT_MAX, +]; +``` + +## 定義觸發器 + +```php +namespace App\Trigger; + +use FriendsOfHyperf\Trigger\Annotation\Trigger; +use FriendsOfHyperf\Trigger\Trigger\AbstractTrigger; +use MySQLReplication\Event\DTO\EventDTO; + +#[Trigger(table:"table", events:["*"], connection:"default")] +class FooTrigger extends AbstractTrigger +{ + public function onWrite(array $new) + { + var_dump($new); + } + + public function onUpdate(array $old, array $new) + { + var_dump($old, $new); + } + + public function onDelete(array $old) + { + var_dump($old); + } +} +``` + +## 定義訂閲者 + +```php +namespace App\Subscriber; + +use FriendsOfHyperf\Trigger\Annotation\Subscriber; +use FriendsOfHyperf\Trigger\Subscriber\AbstractSubscriber; +use MySQLReplication\Event\DTO\EventDTO; + +#[Subscriber(connection:"default")] +class BarSubscriber extends AbstractSubscriber +{ + protected function allEvents(EventDTO $event): void + { + // 一些代碼 + } +} +``` diff --git a/docs/zh-hk/components/validated-dto.md b/docs/zh-hk/components/validated-dto.md new file mode 100644 index 000000000..d76c1afdc --- /dev/null +++ b/docs/zh-hk/components/validated-dto.md @@ -0,0 +1,619 @@ +# Validated DTO + +## 官方文檔 + +[Laravel Validated DTO 官方文檔](https://wendell-adriel.gitbook.io/laravel-validated-dto) + +## 安裝 + +```shell +composer require friendsofhyperf/validated-dto +``` + +## 創建 DTO + +你可以使用 `gen:dto` 命令創建 `DTO`: + +```shell +php bin/hyperf.php gen:dto UserDTO +``` + +`DTO` 將會被創建在 `app/DTO` 目錄下。 + +## 定義驗證規則 + +你可以像驗證 `Request` 數據一樣驗證數據: + +```php + ['required', 'string'], + 'email' => ['required', 'email'], + 'password' => ['required'], + ]; + } +} +``` + +## 創建 DTO 實例 + +你可以通過多種方式創建 `DTO` 實例: + +### 從數組創建 + +```php +$dto = UserDTO::fromArray([ + 'name' => 'Deeka Wong', + 'email' => 'deeka@example.com', + 'password' => 'D3Crft!@1b2A' +]); +``` + +### 從 JSON 字符串創建 + +```php +$dto = UserDTO::fromJson('{"name": "Deeka Wong", "email": "deeka@example.com", "password": "D3Crft!@1b2A"}'); +``` + +### 從請求對象創建 + +```php +public function store(RequestInterface $request): JsonResponse +{ + $dto = UserDTO::fromRequest($request); +} +``` + +### 從模型創建 + +```php +$user = new User([ + 'name' => 'Deeka Wong', + 'email' => 'deeka@example.com', + 'password' => 'D3Crft!@1b2A' +]); + +$dto = UserDTO::fromModel($user); +``` + +注意,模型中 `$hidden` 屬性的字段不會被用於 `DTO`。 + +### 從 Artisan 命令創建 + +你有三種方式從 `Artisan Command` 創建 `DTO` 實例: + +#### 從命令參數創建 + +```php + 'Deeka Wong', + 'email' => 'deeka@example.com', + 'password' => 'D3Crft!@1b2A' +]); + +$dto->name; // 'Deeka Wong' +$dto->email; // 'deeka@example.com' +$dto->password; // 'D3Crft!@1b2A' +``` + +如果你傳遞的屬性不在 `DTO` 的 `rules` 方法中,這些數據將被忽略,並且不會在 `DTO` 中可用: + +```php +$dto = UserDTO::fromArray([ + 'name' => 'Deeka Wong', + 'email' => 'deeka@example.com', + 'password' => 'D3Crft!@1b2A', + 'username' => 'john_doe', +]); + +$dto->username; // 這個屬性在 DTO 中不可用 +``` + +## 定義默認值 + +有時我們可能有一些可選屬性,並且可以有默認值。你可以在 `defaults` 方法中定義 `DTO` 屬性的默認值: + +```php + ['required', 'string'], + 'email' => ['required', 'email'], + 'username' => ['sometimes', 'string'], + 'password' => ['required'], + ]; + } + + protected function defaults(): array + { + return [ + 'username' => Str::snake($this->name), + ]; + } +} +``` + +使用上面的 `DTO` 定義,你可以運行: + +```php +$dto = UserDTO::fromArray([ + 'name' => 'Deeka Wong', + 'email' => 'deeka@example.com', + 'password' => 'D3Crft!@1b2A' +]); + +$dto->username; // 'deeka_wong' +``` + +## 轉換 DTO 數據 + +你可以將你的 DTO 轉換為一些格式: + +### 轉換為數組 + +```php +$dto = UserDTO::fromArray([ + 'name' => 'Deeka Wong', + 'email' => 'deeka@example.com', + 'password' => 'D3Crft!@1b2A', +]); + +$dto->toArray(); +// [ +// "name" => "Deeka Wong", +// "email" => "deeka@example.com", +// "password" => "D3Crft!@1b2A", +// ] +``` + +### 轉換為 JSON 字符串 + +```php +$dto = UserDTO::fromArray([ + 'name' => 'Deeka Wong', + 'email' => 'deeka@example.com', + 'password' => 'D3Crft!@1b2A', +]); + +$dto->toJson(); +// '{"name":"Deeka Wong","email":"deeka@example.com","password":"D3Crft!@1b2A"}' + +$dto->toJson(true); // 你可以這樣調用它來美化打印你的 JSON +// { +// "name": "Deeka Wong", +// "email": "deeka@example.com", +// "password": "D3Crft!@1b2A" +// } +``` + +### 轉換為 Eloquent 模型 + +```php +$dto = UserDTO::fromArray([ + 'name' => 'Deeka Wong', + 'email' => 'deeka@example.com', + 'password' => 'D3Crft!@1b2A', +]); + +$dto->toModel(\App\Model\User::class); +// App\Model\User {#3776 +// name: "Deeka Wong", +// email: "deeka@example.com", +// password: "D3Crft!@1b2A", +// } + +``` + +## 自定義錯誤消息、屬性和異常 + +你可以通過在 `DTO` 類中實現 `messages` 和 `attributes` 方法來定義自定義消息和屬性: + +```php +/** + * 定義驗證器錯誤的自定義消息。 + */ +protected function messages() { + return []; +} + +/** + * 定義驗證器錯誤的自定義屬性。 + */ +protected function attributes() { + return []; +} +``` + +## 類型轉換 + +你可以通過在 `DTO` 中定義 `casts` 方法輕鬆轉換你的 DTO 屬性: + +```php +/** + * 定義 DTO 屬性的類型轉換。 + * + * @return array + */ +protected function casts(): array +{ + return [ + 'name' => new StringCast(), + 'age' => new IntegerCast(), + 'created_at' => new CarbonImmutableCast(), + ]; +} +``` + +## 可用類型 + +### 數組 + +對於 JSON 字符串,它將轉換為數組,對於其他類型,它將包裝在數組中。 + +```php +protected function casts(): array +{ + return [ + 'property' => new ArrayCast(), + ]; +} +``` + +### 布爾值 + +對於字符串值,這使用 `filter_var` 函數和 `FILTER_VALIDATE_BOOLEAN` 標誌。 + +```php +protected function casts(): array +{ + return [ + 'property' => new BooleanCast(), + ]; +} +``` + +### Carbon + +這接受 `Carbon` 構造函數接受的任何值。如果發現無效值,它將拋出 `\FriendsOfHyperf\ValidatedDTO\Exception\CastException` 異常。 + +```php +protected function casts(): array +{ + return [ + 'property' => new CarbonCast(), + ]; +} +``` + +你也可以在定義轉換時傳遞一個時區,如果需要的話,它將在轉換值時使用。 + +```php +protected function casts(): array +{ + return [ + 'property' => new CarbonCast('Europe/Lisbon'), + ]; +} +``` + +你也可以在定義轉換時傳遞一個格式來用於轉換值。如果屬性的格式與指定的不同,它將拋出 `\FriendsOfHyperf\ValidatedDTO\Exception\CastException` 異常。 + +```php +protected function casts(): array +{ + return [ + 'property' => new CarbonCast('Europe/Lisbon', 'Y-m-d'), + ]; +} +``` + +### CarbonImmutable + +這接受 `CarbonImmutable` 構造函數接受的任何值。如果發現無效值,它將拋出 `\FriendsOfHyperf\ValidatedDTO\Exception\CastException` 異常。 + +```php +protected function casts(): array +{ + return [ + 'property' => new CarbonImmutableCast(), + ]; +} +``` + +你也可以在定義轉換時傳遞一個時區,如果需要的話,它將在轉換值時使用。 + +```php +protected function casts(): array +{ + return [ + 'property' => new CarbonImmutableCast('Europe/Lisbon'), + ]; +} +``` + +你也可以在定義轉換時傳遞一個格式來用於轉換值。如果屬性的格式與指定的不同,它將拋出 `\FriendsOfHyperf\ValidatedDTO\Exception\CastException` 異常。 + +```php +protected function casts(): array +{ + return [ + 'property' => new CarbonImmutableCast('Europe/Lisbon', 'Y-m-d'), + ]; +} +``` + +### 集合 + +對於 JSON 字符串,它將首先轉換為數組,然後包裝到 `Collection` 對象中。 + +```php +protected function casts(): array +{ + return [ + 'property' => new CollectionCast(), + ]; +} +``` + +如果你想轉換 `Collection` 中的所有元素,你可以將 `Castable` 傳遞給 `CollectionCast` 構造函數。假設你想將 `Collection` 中的所有項目轉換為整數: + +```php +protected function casts(): array +{ + return [ + 'property' => new CollectionCast(new IntegerCast()), + ]; +} +``` + +這適用於所有 `Castable`,包括 `DTOCast` 和 `ModelCast` 用於嵌套數據。 + +### DTO + +這適用於數組和 JSON 字符串。這將驗證數據併為給定的 DTO 轉換數據。 + +如果數據對 DTO 無效,這將拋出 `Hyperf\Validation\ValidationException` 異常。 + +如果屬性不是有效的數組或有效的 JSON 字符串,這將拋出 `FriendsOfHyperf\ValidatedDTO\Exception\CastException` 異常。 + +如果傳遞給 `DTOCast` 構造函數的類不是 `ValidatedDTO` 實例,這將拋出 `FriendsOfHyperf\ValidatedDTO\Exception\CastTargetException` 異常。 + +```php +protected function casts(): array +{ + return [ + 'property' => new DTOCast(UserDTO::class), + ]; +} +``` + +### 浮點數 + +如果發現非數字值,它將拋出 `FriendsOfHyperf\ValidatedDTO\Exception\CastException` 異常。 + +```php +protected function casts(): array +{ + return [ + 'property' => new FloatCast(), + ]; +} +``` + +### 整數 + +如果發現非數字值,它將拋出 `FriendsOfHyperf\ValidatedDTO\Exception\CastException` 異常。 + +```php +protected function casts(): array +{ + return [ + 'property' => new IntegerCast(), + ]; +} +``` + +### 模型 + +這適用於數組和 JSON 字符串。 + +如果屬性不是有效的數組或有效的 JSON 字符串,這將拋出 `FriendsOfHyperf\ValidatedDTO\Exception\CastException` 異常。 + +如果傳遞給 `ModelCast` 構造函數的類不是 `Model` 實例,這將拋出 `FriendsOfHyperf\ValidatedDTO\Exception\CastTargetException` 異常。 + +```php +protected function casts(): array +{ + return [ + 'property' => new ModelCast(User::class), + ]; +} +``` + +### 對象 + +這適用於數組和 JSON 字符串。 + +如果屬性不是有效的數組或有效的 JSON 字符串,這將拋出 `FriendsOfHyperf\ValidatedDTO\Exception\CastException` 異常。 + +```php +protected function casts(): array +{ + return [ + 'property' => new ObjectCast(), + ]; +} +``` + +### 字符串 + +如果數據不能轉換為字符串,這將拋出 `FriendsOfHyperf\ValidatedDTO\Exception\CastException` 異常。 + +```php +protected function casts(): array +{ + return [ + 'property' => new StringCast(), + ]; +} +``` + +## 創建你自己的類型轉換 + +你可以通過實現 `FriendsOfHyperf\ValidatedDTO\Casting\Castable` 接口輕鬆為你的項目創建新的 `Castable` 類型。這個接口有一個必須實現的方法: + +```php +/** + * 轉換給定的值。 + * + * @param string $property + * @param mixed $value + * @return mixed + */ +public function cast(string $property, mixed $value): mixed; +``` + +假設你的項目中有一個 `URLWrapper` 類,並且你希望在將 URL 傳遞給你的 `DTO` 時,它總是返回一個 `URLWrapper` 實例而不是一個簡單的字符串: + +```php +class URLCast implements Castable +{ + /** + * @param string $property + * @param mixed $value + * @return URLWrapper + */ + public function cast(string $property, mixed $value): URLWrapper + { + return new URLWrapper($value); + } +} +``` + +然後你可以將其應用到你的 DTO: + +```php +class CustomDTO extends ValidatedDTO +{ + protected function rules(): array + { + return [ + 'url' => ['required', 'url'], + ]; + } + + protected function defaults(): array + { + return []; + } + + protected function casts(): array + { + return [ + 'url' => new URLCast(), + ]; + } +} +``` diff --git a/docs/zh-hk/components/web-tinker.md b/docs/zh-hk/components/web-tinker.md new file mode 100644 index 000000000..fb5144bc7 --- /dev/null +++ b/docs/zh-hk/components/web-tinker.md @@ -0,0 +1,29 @@ +# Web Tinker + +## 安裝 + +```shell +composer require friendsofhyperf/web-tinker --dev +``` + +## 發佈配置 + +```shell +php bin/hyperf.php vendor:publish friendsofhyperf/web-tinker +``` + +或 + +```shell +php bin/hyperf.php web-tinker:install +``` + +## 啓動 + +```shell +php bin/hyperf.php start +``` + +## 訪問 + +訪問 `http://127.0.0.1:9501/tinker` 即可進入 Web Tinker 頁面。 diff --git a/docs/zh-hk/faq/index.md b/docs/zh-hk/faq/index.md new file mode 100644 index 000000000..eba1c20b4 --- /dev/null +++ b/docs/zh-hk/faq/index.md @@ -0,0 +1 @@ +# 常見問題 \ No newline at end of file diff --git a/docs/zh-hk/guide/index.md b/docs/zh-hk/guide/index.md new file mode 100644 index 000000000..c2fce2d01 --- /dev/null +++ b/docs/zh-hk/guide/index.md @@ -0,0 +1 @@ +# 入門 diff --git a/docs/zh-hk/guide/introduce/about.md b/docs/zh-hk/guide/introduce/about.md new file mode 100644 index 000000000..d8790091f --- /dev/null +++ b/docs/zh-hk/guide/introduce/about.md @@ -0,0 +1 @@ +# 關於 FriendsOfHyperf diff --git a/docs/zh-hk/guide/start/components.md b/docs/zh-hk/guide/start/components.md new file mode 100644 index 000000000..8edeadce5 --- /dev/null +++ b/docs/zh-hk/guide/start/components.md @@ -0,0 +1,56 @@ +# Components + +## 支持的組件列表 + +|Repository|Stable Version|Total Downloads|Monthly Downloads| +|--|--|--|--| +|[amqp-job](https://github.com/friendsofhyperf/amqp-job)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/amqp-job/v)](https://packagist.org/packages/friendsofhyperf/amqp-job)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/amqp-job/downloads)](https://packagist.org/packages/friendsofhyperf/amqp-job)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/amqp-job/d/monthly)](https://packagist.org/packages/friendsofhyperf/amqp-job)| +|[cache](https://github.com/friendsofhyperf/cache)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/cache/v)](https://packagist.org/packages/friendsofhyperf/cache)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/cache/downloads)](https://packagist.org/packages/friendsofhyperf/cache)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/cache/d/monthly)](https://packagist.org/packages/friendsofhyperf/cache)| +|[command-signals](https://github.com/friendsofhyperf/command-signals)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/command-signals/v)](https://packagist.org/packages/friendsofhyperf/command-signals)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/command-signals/downloads)](https://packagist.org/packages/friendsofhyperf/command-signals)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/command-signals/d/monthly)](https://packagist.org/packages/friendsofhyperf/command-signals)| +|[command-validation](https://github.com/friendsofhyperf/command-validation)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/command-validation/v)](https://packagist.org/packages/friendsofhyperf/command-validation)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/command-validation/downloads)](https://packagist.org/packages/friendsofhyperf/command-validation)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/command-validation/d/monthly)](https://packagist.org/packages/friendsofhyperf/command-validation)| +|[compoships](https://github.com/friendsofhyperf/compoships)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/compoships/v)](https://packagist.org/packages/friendsofhyperf/compoships)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/compoships/downloads)](https://packagist.org/packages/friendsofhyperf/compoships)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/compoships/d/monthly)](https://packagist.org/packages/friendsofhyperf/compoships)| +|[confd](https://github.com/friendsofhyperf/confd)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/confd/v)](https://packagist.org/packages/friendsofhyperf/confd)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/confd/downloads)](https://packagist.org/packages/friendsofhyperf/confd)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/confd/d/monthly)](https://packagist.org/packages/friendsofhyperf/confd)| +|[config-consul](https://github.com/friendsofhyperf/config-consul)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/config-consul/v)](https://packagist.org/packages/friendsofhyperf/config-consul)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/config-consul/downloads)](https://packagist.org/packages/friendsofhyperf/config-consul)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/config-consul/d/monthly)](https://packagist.org/packages/friendsofhyperf/config-consul)| +|[console-spinner](https://github.com/friendsofhyperf/console-spinner)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/console-spinner/v)](https://packagist.org/packages/friendsofhyperf/console-spinner)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/console-spinner/downloads)](https://packagist.org/packages/friendsofhyperf/console-spinner)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/console-spinner/d/monthly)](https://packagist.org/packages/friendsofhyperf/console-spinner)| +|[di-plus](https://github.com/friendsofhyperf/di-plus)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/di-plus/v)](https://packagist.org/packages/friendsofhyperf/di-plus)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/di-plus/downloads)](https://packagist.org/packages/friendsofhyperf/di-plus)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/di-plus/d/monthly)](https://packagist.org/packages/friendsofhyperf/di-plus)| +|[elasticsearch](https://github.com/friendsofhyperf/elasticsearch)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/elasticsearch/v)](https://packagist.org/packages/friendsofhyperf/elasticsearch)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/elasticsearch/downloads)](https://packagist.org/packages/friendsofhyperf/elasticsearch)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/elasticsearch/d/monthly)](https://packagist.org/packages/friendsofhyperf/elasticsearch)| +|[encryption](https://github.com/friendsofhyperf/encryption)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/encryption/v)](https://packagist.org/packages/friendsofhyperf/encryption)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/encryption/downloads)](https://packagist.org/packages/friendsofhyperf/encryption)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/encryption/d/monthly)](https://packagist.org/packages/friendsofhyperf/encryption)| +|[exception-event](https://github.com/friendsofhyperf/exception-event)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/exception-event/v)](https://packagist.org/packages/friendsofhyperf/exception-event)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/exception-event/downloads)](https://packagist.org/packages/friendsofhyperf/exception-event)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/exception-event/d/monthly)](https://packagist.org/packages/friendsofhyperf/exception-event)| +|[facade](https://github.com/friendsofhyperf/facade)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/facade/v)](https://packagist.org/packages/friendsofhyperf/facade)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/facade/downloads)](https://packagist.org/packages/friendsofhyperf/facade)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/facade/d/monthly)](https://packagist.org/packages/friendsofhyperf/facade)| +|[fast-paginate](https://github.com/friendsofhyperf/fast-paginate)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/fast-paginate/v)](https://packagist.org/packages/friendsofhyperf/fast-paginate)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/fast-paginate/downloads)](https://packagist.org/packages/friendsofhyperf/fast-paginate)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/fast-paginate/d/monthly)](https://packagist.org/packages/friendsofhyperf/fast-paginate)| +|[grpc-validation](https://github.com/friendsofhyperf/grpc-validation)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/grpc-validation/v)](https://packagist.org/packages/friendsofhyperf/grpc-validation)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/grpc-validation/downloads)](https://packagist.org/packages/friendsofhyperf/grpc-validation)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/grpc-validation/d/monthly)](https://packagist.org/packages/friendsofhyperf/grpc-validation)| +|[helpers](https://github.com/friendsofhyperf/helpers)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/helpers/v)](https://packagist.org/packages/friendsofhyperf/helpers)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/helpers/downloads)](https://packagist.org/packages/friendsofhyperf/helpers)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/helpers/d/monthly)](https://packagist.org/packages/friendsofhyperf/helpers)| +|[http-client](https://github.com/friendsofhyperf/http-client)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/http-client/v)](https://packagist.org/packages/friendsofhyperf/http-client)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/http-client/downloads)](https://packagist.org/packages/friendsofhyperf/http-client)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/http-client/d/monthly)](https://packagist.org/packages/friendsofhyperf/http-client)| +|[http-logger](https://github.com/friendsofhyperf/http-logger)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/http-logger/v)](https://packagist.org/packages/friendsofhyperf/http-logger)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/http-logger/downloads)](https://packagist.org/packages/friendsofhyperf/http-logger)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/http-logger/d/monthly)](https://packagist.org/packages/friendsofhyperf/http-logger)| +|[ide-helper](https://github.com/friendsofhyperf/ide-helper)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/ide-helper/v)](https://packagist.org/packages/friendsofhyperf/ide-helper)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/ide-helper/downloads)](https://packagist.org/packages/friendsofhyperf/ide-helper)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/ide-helper/d/monthly)](https://packagist.org/packages/friendsofhyperf/ide-helper)| +|[ipc-broadcaster](https://github.com/friendsofhyperf/ipc-broadcaster)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/ipc-broadcaster/v)](https://packagist.org/packages/friendsofhyperf/ipc-broadcaster)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/ipc-broadcaster/downloads)](https://packagist.org/packages/friendsofhyperf/ipc-broadcaster)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/ipc-broadcaster/d/monthly)](https://packagist.org/packages/friendsofhyperf/ipc-broadcaster)| +|[lock](https://github.com/friendsofhyperf/lock)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/lock/v)](https://packagist.org/packages/friendsofhyperf/lock)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/lock/downloads)](https://packagist.org/packages/friendsofhyperf/lock)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/lock/d/monthly)](https://packagist.org/packages/friendsofhyperf/lock)| +|[macros](https://github.com/friendsofhyperf/macros)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/macros/v)](https://packagist.org/packages/friendsofhyperf/macros)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/macros/downloads)](https://packagist.org/packages/friendsofhyperf/macros)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/macros/d/monthly)](https://packagist.org/packages/friendsofhyperf/macros)| +|[mail](https://github.com/friendsofhyperf/mail)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/mail/v)](https://packagist.org/packages/friendsofhyperf/mail)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/mail/downloads)](https://packagist.org/packages/friendsofhyperf/mail)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/mail/d/monthly)](https://packagist.org/packages/friendsofhyperf/mail)| +|[middleware-plus](https://github.com/friendsofhyperf/middleware-plus)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/middleware-plus/v)](https://packagist.org/packages/friendsofhyperf/middleware-plus)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/middleware-plus/downloads)](https://packagist.org/packages/friendsofhyperf/middleware-plus)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/middleware-plus/d/monthly)](https://packagist.org/packages/friendsofhyperf/middleware-plus)| +|[model-factory](https://github.com/friendsofhyperf/model-factory)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/model-factory/v)](https://packagist.org/packages/friendsofhyperf/model-factory)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/model-factory/downloads)](https://packagist.org/packages/friendsofhyperf/model-factory)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/model-factory/d/monthly)](https://packagist.org/packages/friendsofhyperf/model-factory)| +|[model-hashids](https://github.com/friendsofhyperf/model-hashids)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/model-hashids/v)](https://packagist.org/packages/friendsofhyperf/model-hashids)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/model-hashids/downloads)](https://packagist.org/packages/friendsofhyperf/model-hashids)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/model-hashids/d/monthly)](https://packagist.org/packages/friendsofhyperf/model-hashids)| +|[model-morph-addon](https://github.com/friendsofhyperf/model-morph-addon)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/model-morph-addon/v)](https://packagist.org/packages/friendsofhyperf/model-morph-addon)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/model-morph-addon/downloads)](https://packagist.org/packages/friendsofhyperf/model-morph-addon)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/model-morph-addon/d/monthly)](https://packagist.org/packages/friendsofhyperf/model-morph-addon)| +|[model-observer](https://github.com/friendsofhyperf/model-observer)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/model-observer/v)](https://packagist.org/packages/friendsofhyperf/model-observer)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/model-observer/downloads)](https://packagist.org/packages/friendsofhyperf/model-observer)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/model-observer/d/monthly)](https://packagist.org/packages/friendsofhyperf/model-observer)| +|[model-scope](https://github.com/friendsofhyperf/model-scope)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/model-scope/v)](https://packagist.org/packages/friendsofhyperf/model-scope)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/model-scope/downloads)](https://packagist.org/packages/friendsofhyperf/model-scope)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/model-scope/d/monthly)](https://packagist.org/packages/friendsofhyperf/model-scope)| +|[monolog-hook](https://github.com/friendsofhyperf/monolog-hook)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/monolog-hook/v)](https://packagist.org/packages/friendsofhyperf/monolog-hook)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/monolog-hook/downloads)](https://packagist.org/packages/friendsofhyperf/monolog-hook)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/monolog-hook/d/monthly)](https://packagist.org/packages/friendsofhyperf/monolog-hook)| +|[mysql-grammar-addon](https://github.com/friendsofhyperf/mysql-grammar-addon)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/mysql-grammar-addon/v)](https://packagist.org/packages/friendsofhyperf/mysql-grammar-addon)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/mysql-grammar-addon/downloads)](https://packagist.org/packages/friendsofhyperf/mysql-grammar-addon)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/mysql-grammar-addon/d/monthly)](https://packagist.org/packages/friendsofhyperf/mysql-grammar-addon)| +|[notification](https://github.com/friendsofhyperf/notification)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/notification/v)](https://packagist.org/packages/friendsofhyperf/notification)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/notification/downloads)](https://packagist.org/packages/friendsofhyperf/notification)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/notification/d/monthly)](https://packagist.org/packages/friendsofhyperf/notification)| +|[notification-easysms](https://github.com/friendsofhyperf/notification-easysms)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/notification-easysms/v)](https://packagist.org/packages/friendsofhyperf/notification-easysms)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/notification-easysms/downloads)](https://packagist.org/packages/friendsofhyperf/notification-easysms)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/notification-easysms/d/monthly)](https://packagist.org/packages/friendsofhyperf/notification-easysms)| +|[notification-mail](https://github.com/friendsofhyperf/notification-mail)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/notification-mail/v)](https://packagist.org/packages/friendsofhyperf/notification-mail)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/notification-mail/downloads)](https://packagist.org/packages/friendsofhyperf/notification-mail)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/notification-mail/d/monthly)](https://packagist.org/packages/friendsofhyperf/notification-mail)| +|[openai-client](https://github.com/friendsofhyperf/openai-client)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/openai-client/v)](https://packagist.org/packages/friendsofhyperf/openai-client)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/openai-client/downloads)](https://packagist.org/packages/friendsofhyperf/openai-client)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/openai-client/d/monthly)](https://packagist.org/packages/friendsofhyperf/openai-client)| +|[pest-plugin-hyperf](https://github.com/friendsofhyperf/pest-plugin-hyperf)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/pest-plugin-hyperf/v)](https://packagist.org/packages/friendsofhyperf/pest-plugin-hyperf)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/pest-plugin-hyperf/downloads)](https://packagist.org/packages/friendsofhyperf/pest-plugin-hyperf)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/pest-plugin-hyperf/d/monthly)](https://packagist.org/packages/friendsofhyperf/pest-plugin-hyperf)| +|[pretty-console](https://github.com/friendsofhyperf/pretty-console)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/pretty-console/v)](https://packagist.org/packages/friendsofhyperf/pretty-console)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/pretty-console/downloads)](https://packagist.org/packages/friendsofhyperf/pretty-console)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/pretty-console/d/monthly)](https://packagist.org/packages/friendsofhyperf/pretty-console)| +|[purifier](https://github.com/friendsofhyperf/purifier)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/purifier/v)](https://packagist.org/packages/friendsofhyperf/purifier)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/purifier/downloads)](https://packagist.org/packages/friendsofhyperf/purifier)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/purifier/d/monthly)](https://packagist.org/packages/friendsofhyperf/purifier)| +|[recaptcha](https://github.com/friendsofhyperf/recaptcha)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/recaptcha/v)](https://packagist.org/packages/friendsofhyperf/recaptcha)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/recaptcha/downloads)](https://packagist.org/packages/friendsofhyperf/recaptcha)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/recaptcha/d/monthly)](https://packagist.org/packages/friendsofhyperf/recaptcha)| +|[redis-subscriber](https://github.com/friendsofhyperf/redis-subscriber)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/redis-subscriber/v)](https://packagist.org/packages/friendsofhyperf/redis-subscriber)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/redis-subscriber/downloads)](https://packagist.org/packages/friendsofhyperf/redis-subscriber)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/redis-subscriber/d/monthly)](https://packagist.org/packages/friendsofhyperf/redis-subscriber)| +|[sentry](https://github.com/friendsofhyperf/sentry)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/sentry/v)](https://packagist.org/packages/friendsofhyperf/sentry)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/sentry/downloads)](https://packagist.org/packages/friendsofhyperf/sentry)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/sentry/d/monthly)](https://packagist.org/packages/friendsofhyperf/sentry)| +|[support](https://github.com/friendsofhyperf/support)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/support/v)](https://packagist.org/packages/friendsofhyperf/support)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/support/downloads)](https://packagist.org/packages/friendsofhyperf/support)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/support/d/monthly)](https://packagist.org/packages/friendsofhyperf/support)| +|[tcp-sender](https://github.com/friendsofhyperf/tcp-sender)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/tcp-sender/v)](https://packagist.org/packages/friendsofhyperf/tcp-sender)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/tcp-sender/downloads)](https://packagist.org/packages/friendsofhyperf/tcp-sender)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/tcp-sender/d/monthly)](https://packagist.org/packages/friendsofhyperf/tcp-sender)| +|[telescope](https://github.com/friendsofhyperf/telescope)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/telescope/v)](https://packagist.org/packages/friendsofhyperf/telescope)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/telescope/downloads)](https://packagist.org/packages/friendsofhyperf/telescope)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/telescope/d/monthly)](https://packagist.org/packages/friendsofhyperf/telescope)| +|[telescope-elasticsearch](https://github.com/friendsofhyperf/telescope-elasticsearch)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/telescope-elasticsearch/v)](https://packagist.org/packages/friendsofhyperf/telescope-elasticsearch)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/telescope-elasticsearch/downloads)](https://packagist.org/packages/friendsofhyperf/telescope-elasticsearch)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/telescope-elasticsearch/d/monthly)](https://packagist.org/packages/friendsofhyperf/telescope-elasticsearch)| +|[tinker](https://github.com/friendsofhyperf/tinker)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/tinker/v)](https://packagist.org/packages/friendsofhyperf/tinker)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/tinker/downloads)](https://packagist.org/packages/friendsofhyperf/tinker)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/tinker/d/monthly)](https://packagist.org/packages/friendsofhyperf/tinker)| +|[trigger](https://github.com/friendsofhyperf/trigger)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/trigger/v)](https://packagist.org/packages/friendsofhyperf/trigger)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/trigger/downloads)](https://packagist.org/packages/friendsofhyperf/trigger)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/trigger/d/monthly)](https://packagist.org/packages/friendsofhyperf/trigger)| +|[validated-dto](https://github.com/friendsofhyperf/validated-dto)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/validated-dto/v)](https://packagist.org/packages/friendsofhyperf/validated-dto)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/validated-dto/downloads)](https://packagist.org/packages/friendsofhyperf/validated-dto)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/validated-dto/d/monthly)](https://packagist.org/packages/friendsofhyperf/validated-dto)| +|[web-tinker](https://github.com/friendsofhyperf/web-tinker)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/web-tinker/v)](https://packagist.org/packages/friendsofhyperf/web-tinker)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/web-tinker/downloads)](https://packagist.org/packages/friendsofhyperf/web-tinker)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/web-tinker/d/monthly)](https://packagist.org/packages/friendsofhyperf/web-tinker)| + diff --git a/docs/zh-hk/index.md b/docs/zh-hk/index.md new file mode 100644 index 000000000..ca7dc28c8 --- /dev/null +++ b/docs/zh-hk/index.md @@ -0,0 +1,52 @@ +--- +# https://vitepress.dev/reference/default-theme-home-page +layout: home + +hero: + name: "FriendsOfHyperf" + text: "最受歡迎的 Hyperf 組件" + tagline: "讓 Hyperf 像 Laravel 一樣簡單且強大 🚀" + actions: + # - theme: brand + # text: 入門 + # link: /zh-hk/guide/ + - theme: brand + text: 組件 + link: /zh-hk/components/ + - theme: alt + text: GitHub + link: "https://github.com/friendsofhyperf/components" + +features: + - title: Sentry + details: 這是 Sentry 的 Hyperf SDK。 + link: /zh-hk/components/sentry/ + - title: Telecope + details: Telescope 是 Hyperf 框架的優雅調試助手。 + link: /zh-hk/components/telescope/ + - title: Tinker + details: Tinker 是 Hyperf 框架的強大 REPL。 + link: /zh-hk/components/tinker/ + - title: Web Tinker + details: 在瀏覽器中使用 Tinker。 + link: /zh-hk/components/web-tinker/ + - title: Encryption + details: Encryption 提供了一個簡單、方便的加密和解密功能。 + link: /zh-hk/components/encryption/ + - title: Cache + details: Cache 提供了一個富有表現力的統一緩存 API。 + link: /zh-hk/components/cache/ + - title: HttpClient + details: HttpClient 提供了一個簡單、方便的 HTTP 客户端。 + link: /zh-hk/components/http-client/ + - title: Validated DTO + details: Validated DTO 提供了一個簡單、方便的數據驗證功能。 + link: /zh-hk/components/validated-dto/ + - title: Lock + details: Lock 提供了一個簡單、方便的分佈式鎖功能。 + link: /zh-hk/components/lock/ + - title: More + details: 更多組件… + link: /zh-hk/components/ +--- + diff --git a/docs/zh-tw/components/amqp-job.md b/docs/zh-tw/components/amqp-job.md new file mode 100644 index 000000000..ccfc97319 --- /dev/null +++ b/docs/zh-tw/components/amqp-job.md @@ -0,0 +1,64 @@ +# Amqp Job + +## 簡介 + +`friendsofhyperf/amqp-job` 是一個基於 `hyperf/amqp` 元件實現的非同步任務元件,支援將任務分發到 AMQP 服務,然後透過消費者消費任務。 +封裝了 `hyperf/amqp` 元件,提供了更加便捷的任務分發和消費方式。 + +## 安裝 + +```shell +composer require friendsofhyperf/amqp-job +``` + +## 用法 + +### 分發任務 + +```php +use FriendsOfHyperf\AmqpJob\Job; +use FriendsOfHyperf\AmqpJob\Annotations\AmqpJob; + +use function FriendsOfHyperf\AmqpJob\dispatch; + +#[AmqpJob( + exchange: 'hyperf.exchange', + routingKey: 'hyperf.routing.key', + pool: 'default', + queue: 'hyperf.queue', +)] +class FooJob extends Job +{ + public function handle() + { + var_dump('foo'); + } +} + +dispatch(new FooJob()); + +``` + +### 註冊消費者[可選] + +```php + +namespace App\Amqp\Consumer; + +use FriendsOfHyperf\AmqpJob\JobConsumer; +use Hyperf\Amqp\Annotation\Consumer; + +#[Consumer( + exchange: 'hyperf.exchange', + routingKey: 'hyperf.routing.key', + queue: 'hyperf.queue', + name: 'MyConsumer', + nums: 4 + +)] +class MyConsumer extends JobConsumer +{ + // +} + +``` diff --git a/docs/zh-tw/components/cache.md b/docs/zh-tw/components/cache.md new file mode 100644 index 000000000..8c68090a3 --- /dev/null +++ b/docs/zh-tw/components/cache.md @@ -0,0 +1,65 @@ +# Cache + +## 簡介 + +`friendsofhyperf/cache` 是一個基於 `hyperf/cache` 的元件。 提供更多簡潔性的擴充套件方法 + +## 安裝 + +```shell +composer require friendsofhyperf/cache +``` + +## 用法 + +### 註解 + +```php +namespace App\Controller; + +use FriendsOfHyperf\Cache\Contract\CacheInterface; +use Hyperf\Di\Annotation\Inject; + +class IndexController +{ + + #[Inject] + private CacheInterface $cache; + + public function index() + { + return $this->cache->remember($key, $ttl=60, function() { + // return sth + }); + } +} +``` + +### 門面 + +```php +use FriendsOfHyperf\Cache\Facade\Cache; + +Cache::remember($key, $ttl=60, function() { + // return sth +}); +``` + +### 切換驅動 + +```php +use FriendsOfHyperf\Cache\Facade\Cache; +use FriendsOfHyperf\Cache\CacheManager; + +Cache::store('co')->remember($key, $ttl=60, function() { + // return sth +}); + +di(CacheManager::class)->store('co')->remember($key, $ttl=60, function() { + // return sth +}); +``` + +## 參考 + +Likes [Laravel-Cache](https://laravel.com/docs/8.x/cache) diff --git a/docs/zh-tw/components/command-signals.md b/docs/zh-tw/components/command-signals.md new file mode 100644 index 000000000..9bae2713a --- /dev/null +++ b/docs/zh-tw/components/command-signals.md @@ -0,0 +1,65 @@ +# Command Signals + +The signals component for Hyperf Command. + +## 安裝 + +```shell +composer require friendsofhyperf/command-signals +``` + +## 使用 + +```php +namespace App\Command; + +use FriendsOfHyperf\CommandSignals\Traits\InteractsWithSignals; +use Hyperf\Command\Annotation\Command; +use Hyperf\Command\Command as HyperfCommand; +use Psr\Container\ContainerInterface; + +#[Command] +class FooCommand extends HyperfCommand +{ + use InteractsWithSignals; + + public function __construct(protected ContainerInterface $container) + { + parent::__construct('foo'); + } + + public function configure() + { + parent::configure(); + $this->setDescription('Hyperf Demo Command'); + } + + public function handle() + { + $this->trap([SIGINT, SIGTERM], function ($signo) { + $this->warn(sprintf('Received signal %d, exiting...', $signo)); + }); + + sleep(10); + + $this->info('Bye!'); + } +} +``` + +## 執行 + +- `Ctrl + C` + +```shell +$ hyperf foo +^CReceived signal 2, exiting... +``` + +- `killall php` + +```shell +$ hyperf foo +Received signal 15, exiting... +[1] 51936 terminated php bin/hyperf.php foo +``` diff --git a/docs/zh-tw/components/command-validation.md b/docs/zh-tw/components/command-validation.md new file mode 100644 index 000000000..82aa46f7e --- /dev/null +++ b/docs/zh-tw/components/command-validation.md @@ -0,0 +1,53 @@ +# Command Validation + +The command validation component for Hyperf. + +## 安裝 + +```shell +composer require friendsofhyperf/command-validation +``` + +## 使用 + +```php +info(sprintf('Hello %s.', $this->input->getArgument('name'))); + } + + protected function rules(): array + { + return [ + 'name' => 'required', + ]; + } + + protected function messages(): array + { + return [ + 'name.required' => 'The name is required.', + ]; + } +} +``` diff --git a/docs/zh-tw/components/compoships.md b/docs/zh-tw/components/compoships.md new file mode 100644 index 000000000..19757bfe5 --- /dev/null +++ b/docs/zh-tw/components/compoships.md @@ -0,0 +1,126 @@ +# Compoships + +**Compoships** 提供了在 Hyperf 的 Model ORM 中基於兩個(或更多)列指定關係的能力。當處理第三方或預先存在的模式/資料庫時,通常會出現需要在 Eloquent 關係的定義中匹配多個列的情況。 + +## 問題 + +Eloquent 不支援複合鍵。因此,無法透過匹配多個列來定義從一個模型到另一個模型的關係。嘗試使用 `where` 子句(如下例所示)在預載入關係時不起作用,因為在處理關係時 **$this->team_id** 為 null。 + +```php +namespace App; + +use Hyperf\Database\Model\Model; + +class User extends Model +{ + public function tasks() + { + //WON'T WORK WITH EAGER LOADING!!! + return $this->hasMany(Task::class)->where('team_id', $this->team_id); + } +} +``` + +## 安裝 + +推薦透過 [Composer](http://getcomposer.org/) 安裝 **Compoships** 元件。 + +```shell +composer require friendsofhyperf/compoships +``` + +## 使用 + +### 使用 `FriendsOfHyperf\Compoships\Database\Eloquent\Model` 類 + +只需讓您的模型類派生自 `FriendsOfHyperf\Compoships\Database\Eloquent\Model` 基類。`FriendsOfHyperf\Compoships\Database\Eloquent\Model` 擴充套件了 `Eloquent` 基類,而不改變其核心功能。 + +### 使用 `FriendsOfHyperf\Compoships\Compoships` trait + +如果由於某些原因您無法從 `FriendsOfHyperf\Compoships\Database\Eloquent\Model` 派生您的模型,您可以利用 `FriendsOfHyperf\Compoships\Compoships` trait。只需在您的模型中使用該 trait。 + +**注意:** 要從模型 *A* 到另一個模型 *B* 定義多列關係,**兩個模型都必須擴充套件 `FriendsOfHyperf\Compoships\Database\Eloquent\Model` 或使用 `FriendsOfHyperf\Compoships\Compoships` trait** + +### 用法 + +... 現在我們可以透過匹配兩個或更多列(透過傳遞列陣列而不是字串)來定義從模型 *A* 到另一個模型 *B* 的關係。 + +```php +namespace App; + +use Hyperf\Database\Model\Model; + +class A extends Model +{ + use \FriendsOfHyperf\Compoships\Compoships; + + public function b() + { + return $this->hasMany('B', ['foreignKey1', 'foreignKey2'], ['localKey1', 'localKey2']); + } +} +``` + +我們可以使用相同的語法來定義關係的反向關係: + +```php +namespace App; + +use Hyperf\Database\Model\Model; + +class B extends Model +{ + use \FriendsOfHyperf\Compoships\Compoships; + + public function a() + { + return $this->belongsTo('A', ['foreignKey1', 'foreignKey2'], ['ownerKey1', 'ownerKey2']); + } +} +``` + +### 例子 + +作為一個例子,假設我們有一個帶有分類的任務列表,由多個使用者團隊管理,其中: + +- 一個任務屬於一個分類 +- 一個任務被分配給一個團隊 +- 一個團隊有很多使用者 +- 一個使用者屬於一個團隊 +- 一個使用者負責一個分類的任務 + +負責特定任務的使用者是當前負責團隊內該分類的使用者。 + +```php +namespace App; + +use Hyperf\Database\Model\Model; + +class User extends Model +{ + use \FriendsOfHyperf\Compoships\Compoships; + + public function tasks() + { + return $this->hasMany(Task::class, ['team_id', 'category_id'], ['team_id', 'category_id']); + } +} +``` + +同樣的語法可以定義關係的反向關係: + +```php +namespace App; + +use Hyperf\Database\Model\Model; + +class Task extends Model +{ + use \FriendsOfHyperf\Compoships\Compoships; + + public function user() + { + return $this->belongsTo(User::class, ['team_id', 'category_id'], ['team_id', 'category_id']); + } +} +``` diff --git a/docs/zh-tw/components/confd.md b/docs/zh-tw/components/confd.md new file mode 100644 index 000000000..37e01900f --- /dev/null +++ b/docs/zh-tw/components/confd.md @@ -0,0 +1,60 @@ +# Confd + +The confd component for Hyperf. + +## 安裝 + +```shell +composer require friendsofhyperf/confd +composer require friendsofhyperf/etcd +# or +composer require friendsofhyperf/nacos +``` + +## 命令 + +從 `etcd/nacos` 獲取配置並更新 `.env`。 + +```shell +php bin/hyperf.php confd:env +``` + +## 定義監聽器 + +```php +logger->warning('[confd] ConfdChanged'); + // do something + } +} +``` + +## 支援 + +- [x] Etcd +- [x] Nacos +- [ ] Consul diff --git a/docs/zh-tw/components/config-consul.md b/docs/zh-tw/components/config-consul.md new file mode 100644 index 000000000..711ca4a71 --- /dev/null +++ b/docs/zh-tw/components/config-consul.md @@ -0,0 +1,36 @@ +# Config Consul + +The consul config component for Hyperf. + +## 安裝 + +```shell +composer require friendsofhyperf/config-consul +``` + +## 配置 + +```php +// config/autoload/config_center.php + +return [ + 'drivers' => [ + 'consul' => [ + 'driver' => FriendsOfHyperf\ConfigConsul\ConsulDriver::class, + 'packer' => Hyperf\Codec\Packer\JsonPacker::class, + 'client' => [ + 'uri' => env('CONSUL_URI'), + 'token' => env('CONSUL_TOKEN'), + ], + 'namespaces' => [ + '/application', + ], + 'mapping' => [ + // consul key => config key + '/application/test' => 'test', + ], + 'interval' => 5, + ], + ], +]; +``` diff --git a/docs/zh-tw/components/console-spinner.md b/docs/zh-tw/components/console-spinner.md new file mode 100644 index 000000000..e06904cb6 --- /dev/null +++ b/docs/zh-tw/components/console-spinner.md @@ -0,0 +1,46 @@ +# Console Spinner + +The progress bar component For Hyperf. + +## 安裝 + +```shell +composer require friendsofhyperf/console-spinner +``` + +## 釋出配置 + +```shell +php bin/hyperf.php vendor:publish friendsofhyperf/console-spinner +``` + +## 使用 + +```php +class FooCommand extends Command +{ + use Spinnerable; + + public function handle() + { + $spinner = $this->spinner($users->count()); + $spinner->setMessage('Loading...'); + $spinner->start(); + + foreach ($users as $user) { + // Do your stuff... + $spinner->advance(); + } + + $spinner->finish(); + } +} +``` + +`$spinner` 相容 Symfony ProgressBar,因此您可以執行此類的任何方法。或者,您也可以透過提供一個可迭代物件來使用 `withSpinner` 方法。 + +```php +$this->withSpinner(User::all(), function($user) { + // Do your stuff with $user +}, 'Loading...'); +``` diff --git a/docs/zh-tw/components/di-plus.md b/docs/zh-tw/components/di-plus.md new file mode 100644 index 000000000..71247cc21 --- /dev/null +++ b/docs/zh-tw/components/di-plus.md @@ -0,0 +1,60 @@ +# DI Plus + +The di plus component for Hyperf. + +## 安裝 + +```shell +composer require friendsofhyperf/di-plus +``` + +## 使用 + +```php + App\BarAtFoo1Factory::class, + 'App\Bar@App\Foo2' => App\BarAtFoo2Factory::class, +]; +``` + +```php +create()->build(); + // ... + } +} +``` diff --git a/docs/zh-tw/components/encryption.md b/docs/zh-tw/components/encryption.md new file mode 100644 index 000000000..1ae028534 --- /dev/null +++ b/docs/zh-tw/components/encryption.md @@ -0,0 +1,22 @@ +# Encryption + +The encryption component for Hyperf. + +## 安裝 + +```shell +composer require friendsofhyperf/encryption +``` + +## 釋出配置 + +```shell +php bin/hyperf.php vendor:publish friendsofhyperf/encryption +``` + +## 使用 + +```shell +$encryptString = encrypt($string); +$decryptString = decrypt($encryptString); +``` diff --git a/docs/zh-tw/components/exception-event.md b/docs/zh-tw/components/exception-event.md new file mode 100644 index 000000000..896be46f1 --- /dev/null +++ b/docs/zh-tw/components/exception-event.md @@ -0,0 +1,39 @@ +# Exception Event + +## 安裝 + +```shell +composer require friendsofhyperf/exception-event +``` + +## 使用 + +### 定義監聽器 + +```php +throwable; + $message = sprintf('Exception: %s in %s:%s', $exception->getMessage(), $exception->getFile(), $exception->getLine()); + $event->getLogger()->error($message); + } +} +``` diff --git a/docs/zh-tw/components/facade.md b/docs/zh-tw/components/facade.md new file mode 100644 index 000000000..f2f6d7c59 --- /dev/null +++ b/docs/zh-tw/components/facade.md @@ -0,0 +1,28 @@ +# Facades + +## 安裝 + +```shell +composer require friendsofhyperf/facade +``` + +## 支援元件 + +- [x] AMQP +- [x] App (DI alias) +- [x] AsyncQueue +- [x] Cache +- [x] Config +- [x] Cookie +- [x] DI +- [x] Event +- [x] Filesystem/File +- [x] Kafka +- [x] Log +- [x] Redis +- [x] Request +- [x] Response +- [x] Session +- [x] Translator +- [x] Validator +- [x] View diff --git a/docs/zh-tw/components/fast-paginate.md b/docs/zh-tw/components/fast-paginate.md new file mode 100644 index 000000000..529ec68d2 --- /dev/null +++ b/docs/zh-tw/components/fast-paginate.md @@ -0,0 +1,48 @@ +# Fast Paginate + +> Forked from [hammerstonedev/fast-paginate](https://github.com/hammerstonedev/fast-paginate) + +## 關於 + +這是一個用於 Hyperf 的快速 `limit`/`offset` 分頁宏。它可以替代標準的 `paginate` 方法。 + +這個包使用了一種類似於“延遲連線”的 SQL 方法來實現這種加速。延遲連線是一種在應用 `offset` 和 `limit` 之後才訪問請求列的技術。 + +在我們的例子中,我們實際上並沒有進行連線,而是使用了帶有子查詢的 `where in`。使用這種技術,我們建立了一個可以透過特定索引進行最佳化的子查詢以達到最大速度,然後使用這些結果來獲取完整的行。 + +SQL 語句如下所示: + +```sql +select * from contacts -- The full data that you want to show your users. + where contacts.id in ( -- The "deferred join" or subquery, in our case. + select id from contacts -- The pagination, accessing as little data as possible - ID only. + limit 15 offset 150000 + ) +``` + +> 執行上述查詢時,您可能會遇到錯誤!例如 `This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery.` +> 在這個包中,我們將它們作為[兩個獨立的查詢](https://github.com/hammerstonedev/fast-paginate/blob/154da286f8160a9e75e64e8025b0da682aa2ba23/src/BuilderMixin.php#L62-L79)來執行以解決這個問題! + +根據您的資料集,效能提升可能會有所不同,但這種方法允許資料庫檢查儘可能少的資料以滿足使用者的需求。 + +雖然這種方法不太可能比傳統的 `offset` / `limit` 效能更差,但也有可能,所以請務必在您的資料上進行測試! + +> 如果您想閱讀關於這個包理論的 3,000 字文章,可以訪問 [aaronfrancis.com/2022/efficient-pagination-using-deferred-joins](https://aaronfrancis.com/2022/efficient-pagination-using-deferred-joins)。 + +## 安裝 + +```shell +composer require friendsofhyperf/fast-paginate +``` + +無需執行其他操作,服務提供者將由 Hyperf 自動載入。 + +## 使用 + +在任何您會使用 `Model::query()->paginate()` 的地方,您都可以使用 `Model::query()->fastPaginate()`!就是這麼簡單!方法簽名是相同的。 + +關係也同樣支援: + +```php +User::first()->posts()->fastPaginate(); +``` diff --git a/docs/zh-tw/components/grpc-validation.md b/docs/zh-tw/components/grpc-validation.md new file mode 100644 index 000000000..24fa5a07c --- /dev/null +++ b/docs/zh-tw/components/grpc-validation.md @@ -0,0 +1,29 @@ +# Grpc Validation + +The GRPC validation component for Hyperf. + +## 安裝 + +```shell +composer require friendsofhyperf/grpc-validation +``` + +## 使用 + +```php + 'required|string|max:10', + 'message' => 'required|string|max:500', +])] +public function sayHello(HiUser $user) +{ + $message = new HiReply(); + $message->setMessage("Hello World"); + $message->setUser($user); + return $message; +} +``` diff --git a/docs/zh-tw/components/helpers.md b/docs/zh-tw/components/helpers.md new file mode 100644 index 000000000..d84b3efd6 --- /dev/null +++ b/docs/zh-tw/components/helpers.md @@ -0,0 +1,41 @@ +# Helpers + +## 安裝 + +```shell +composer require friendsofhyperf/helpers +``` + +## 支援函式 + +- app +- base_path +- blank +- cache +- cookie +- class_namespace +- di +- dispatch +- environment +- enum_value +- event +- filled +- fluent +- info +- literal +- logger +- logs +- now +- object_get +- preg_replace_array +- resolve +- request +- response +- session +- today +- throw_if +- throw_unless +- transform +- validator +- when +- get_client_ip diff --git a/docs/zh-tw/components/http-client.md b/docs/zh-tw/components/http-client.md new file mode 100644 index 000000000..9272f49e6 --- /dev/null +++ b/docs/zh-tw/components/http-client.md @@ -0,0 +1,21 @@ +# HTTP Client + +The HTTP Client component for Hyperf, from Laravel. + +## 安裝 + +```shell +composer require friendsofhyperf/http-client +``` + +## 使用 + +```php +use FriendsOfHyperf\Http\Client\Http; + +$response = Http::get('https://example.com'); +``` + +## 參考文件 + +有關更多資訊,請參閱 [Laravel HTTP Client 文件](https://laravel.com/docs/9.x/http-client)。 diff --git a/docs/zh-tw/components/http-logger.md b/docs/zh-tw/components/http-logger.md new file mode 100644 index 000000000..4ae4347ce --- /dev/null +++ b/docs/zh-tw/components/http-logger.md @@ -0,0 +1,25 @@ +# Http Logger + +The http logger component for Hyperf. + +## 安裝 + +```shell +composer require "friendsofhyperf/http-logger +``` + +## 釋出配置 + +```shell +php bin/hyperf.php vendor:publish friendsofhyperf/http-logger +``` + +## 使用 + +```php +return [ + 'http' => [ + \FriendsOfHyperf\Http\Logger\Middleware\HttpLogger::class, + ], +]; +``` diff --git a/docs/zh-tw/components/ide-helper.md b/docs/zh-tw/components/ide-helper.md new file mode 100644 index 000000000..9b6f68c37 --- /dev/null +++ b/docs/zh-tw/components/ide-helper.md @@ -0,0 +1,23 @@ +# IDE Helper + +The ide-helper component for Hyperf. + +## 安裝 + +```shell +composer require friendsofhyperf/ide-helper +``` + +## 使用 + +- 生成模型助手 + +```shell +php ./bin/hyperf.php ide-helper:model +``` + +- 生成宏助手 + +```shell +php ./bin/hyperf.php ide-helper:macro +``` diff --git a/docs/zh-tw/components/index.md b/docs/zh-tw/components/index.md new file mode 100644 index 000000000..9bc12d5c0 --- /dev/null +++ b/docs/zh-tw/components/index.md @@ -0,0 +1,3 @@ +# 元件 + +👈 請看左邊選單,選擇元件 diff --git a/docs/zh-tw/components/ipc-broadcaster.md b/docs/zh-tw/components/ipc-broadcaster.md new file mode 100644 index 000000000..5d067a591 --- /dev/null +++ b/docs/zh-tw/components/ipc-broadcaster.md @@ -0,0 +1,45 @@ +# Ipc Broadcaster + +Ipc Broadcaster component for Hyperf. + +## 安裝 + +```shell +composer require friendsofhyperf/ipc-broadcaster +``` + +## 使用 + +- 閉包 + +```php +use function FriendsOfHyperf\IpcBroadcaster\broadcast; + +broadcast(function () { + echo 'Hello world'; +}); +``` + +- 類 + +```php +namespace App\Broadcasting; + +class FooMessage extends IpcMessage +{ + public function __construct(private string $foo) + { + // + } + + public function handle(): void + { + echo $this->foo; + } +} + +use function FriendsOfHyperf\IpcBroadcaster\broadcast; + +broadcast(new FooMessage('bar')); + +``` diff --git a/docs/zh-tw/components/lock.md b/docs/zh-tw/components/lock.md new file mode 100644 index 000000000..d941dde79 --- /dev/null +++ b/docs/zh-tw/components/lock.md @@ -0,0 +1,81 @@ +# Lock + +Hyperf 原子鎖元件。 + +## 安裝 + +- 安裝 + +```shell +composer require friendsofhyperf/lock +``` + +- 釋出配置 + +```shell +php bin/hyperf.php vendor:publish friendsofhyperf/lock -i config +``` + +## 使用 + +你可以使用 `lock()` 方法來建立和管理鎖: + +```php +$lock = lock($name = 'foo', $seconds = 10, $owner = null); + +if ($lock->get()) { + // 獲取鎖定10秒... + + $lock->release(); +} +``` + +`get` 方法也可以接收一個閉包。在閉包執行之後,將會自動釋放鎖: + +```php +lock('foo')->get(function () { + // 獲取無限期鎖並自動釋放... +}); +``` + +如果你在請求時鎖無法使用,你可以控制等待指定的秒數。如果在指定的時間限制內無法獲取鎖,則會丟擲 `FriendsOfHyperf\Lock\Exception\LockTimeoutException` + +```php +use FriendsOfHyperf\Lock\Exception\LockTimeoutException; + +$lock = lock('foo', 10); + +try { + $lock->block(5); + + // 等待最多5秒後獲取的鎖... +} catch (LockTimeoutException $e) { + // 無法獲取鎖... +} finally { + optional($lock)->release(); +} + +lock('foo', 10)->block(5, function () { + // 等待最多5秒後獲取的鎖... +}); +``` + +註解方式 + +```php +use FriendsOfHyperf\Lock\Annotation\Lock; +use FriendsOfHyperf\Lock\Driver\LockInterface; + +class Foo +{ + #[Lock(name:"foo", seconds:10)] + protected LockInterface $lock; + + public function bar() + { + $this->lock->get(function () { + // 獲取無限期鎖並自動釋放... + }); + } +} +``` diff --git a/docs/zh-tw/components/macros.md b/docs/zh-tw/components/macros.md new file mode 100644 index 000000000..2500a3e67 --- /dev/null +++ b/docs/zh-tw/components/macros.md @@ -0,0 +1,81 @@ +# Macros + +## 安裝 + +```shell +composer require friendsofhyperf/macros +``` + +## 支援方法 + +### Hyperf\Collection\Arr + +- `Arr::sortByMany` + +### Hyperf\Collection\Collection + +- `Collection::collapseWithKeys` +- `Collection::isSingle` + +### Hyperf\Collection\LazyCollection + +- `LazyCollection::collapseWithKeys` + +### Hyperf\HttpServer\Request + +- `Request::allFiles` +- `Request::anyFilled` +- `Request::boolean` +- `Request::collect` +- `Request::date` +- `Request::enum` +- `Request::except` +- `Request::exists` +- `Request::fake` +- `Request::filled` +- `Request::float` +- `Request::fluent` +- `Request::getHost` +- `Request::getHttpHost` +- `Request::getPort` +- `Request::getPsrRequest` +- `Request::getScheme` +- `Request::getSchemeAndHttpHost` +- `Request::hasAny` +- `Request::host` +- `Request::httpHost` +- `Request::integer` +- `Request::isEmptyString` +- `Request::isJson` +- `Request::isNotFilled` +- `Request::isSecure` +- `Request::keys` +- `Request::merge` +- `Request::mergeIfMissing` +- `Request::missing` +- `Request::only` +- `Request::schemeAndHttpHost` +- `Request::str` +- `Request::string` +- `Request::validate` +- `Request::validateWithBag` +- `Request::wantsJson` +- `Request::whenFilled` +- `Request::whenHas` + +### Hyperf\Stringable\Str + +- `Str::createUuidsNormally` +- `Str::createUuidsUsing` +- `Str::deduplicate` +- `Str::inlineMarkdown` +- `Str::markdown` +- `Str::transliterate` + +### Hyperf\Stringable\Stringable + +- `Stringable::deduplicate` +- `Stringable::inlineMarkdown` +- `Stringable::markdown` +- `Stringable::toHtmlString` +- `Stringable::whenIsAscii` diff --git a/docs/zh-tw/components/mail.md b/docs/zh-tw/components/mail.md new file mode 100644 index 000000000..206eaaa27 --- /dev/null +++ b/docs/zh-tw/components/mail.md @@ -0,0 +1,246 @@ +# Mail + +Email component of Hyperf + +## 安裝 + +```shell +composer require friendsofhyperf/mail +php bin/hyperf vendor:publish friendsofhyperf/mail +# Publish the view configuration file. +php bin/hyperf vendor:publish hyperf/view + +``` + +## 使用 + +### 配置 + +```php +// config/autoload/mail.php +/** + * This file is part of friendsofhyperf/components. + * + * @link https://github.com/friendsofhyperf/components + * @document https://github.com/friendsofhyperf/components/blob/main/README.md + * @contact huangdijia@gmail.com + */ +use function Hyperf\Support\env; + +return [ + /* + |-------------------------------------------------------------------------- + | Default Mailer + |-------------------------------------------------------------------------- + | + | This option controls the default mailer that is used to send all email + | messages unless another mailer is explicitly specified when sending + | the message. All additional mailers can be configured within the + | "mailers" array. Examples of each type of mailer are provided. + | + */ + + 'default' => env('MAIL_MAILER', 'log'), + + /* + |-------------------------------------------------------------------------- + | Mailer Configurations + |-------------------------------------------------------------------------- + | + | Here you may configure all of the mailers used by your application plus + | their respective settings. Several examples have been configured for + | you and you are free to add your own as your application requires. + | + | Laravel supports a variety of mail "transport" drivers that can be used + | when delivering an email. You may specify which one you're using for + | your mailers below. You may also add additional mailers if needed. + | + | Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2", + | "postmark", "resend", "log", "array", + | "failover", "roundrobin" + | + */ + + 'mailers' => [ + 'smtp' => [ + 'transport' => 'smtp', + 'url' => env('MAIL_URL','smtp://xxx@xxx:xxx@xxx.com:465'), + 'host' => env('MAIL_HOST', '127.0.0.1'), + 'port' => env('MAIL_PORT', 2525), + 'encryption' => env('MAIL_ENCRYPTION', 'tls'), + 'username' => env('MAIL_USERNAME'), + 'password' => env('MAIL_PASSWORD'), + 'timeout' => null, + 'local_domain' => env('MAIL_EHLO_DOMAIN'), + ], + + 'ses' => [ + 'transport' => 'ses', + ], + + 'postmark' => [ + 'transport' => 'postmark', + // 'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'), + // 'client' => [ + // 'timeout' => 5, + // ], + ], + + 'resend' => [ + 'transport' => 'resend', + ], + + 'sendmail' => [ + 'transport' => 'sendmail', + 'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'), + ], + + 'log' => [ + 'transport' => 'log', + 'group' => env('MAIL_LOG_GROUP','default'), + 'name' => env('MAIL_LOG_NAME','mail'), + ], + + 'array' => [ + 'transport' => 'array', + ], + + 'failover' => [ + 'transport' => 'failover', + 'mailers' => [ + 'smtp', + 'log', + ], + ], + + 'roundrobin' => [ + 'transport' => 'roundrobin', + 'mailers' => [ + 'ses', + 'postmark', + ], + ], + ], + + /* + |-------------------------------------------------------------------------- + | Global "From" Address + |-------------------------------------------------------------------------- + | + | You may wish for all emails sent by your application to be sent from + | the same address. Here you may specify a name and address that is + | used globally for all emails that are sent by your application. + | + */ + + 'from' => [ + 'address' => env('MAIL_FROM_ADDRESS', 'hyperf@hyperf.com'), + 'name' => env('MAIL_FROM_NAME', 'Hyperf'), + ], + + /* + |-------------------------------------------------------------------------- + | Markdown Mail Settings + |-------------------------------------------------------------------------- + | + | If you are using Markdown based email rendering, you may configure your + | theme and component paths here, allowing you to customize the design + | of the emails. Or, you may simply stick with the Laravel defaults! + | + */ + + 'markdown' => [ + 'theme' => env('MAIL_MARKDOWN_THEME', 'default'), + 'paths' => [ + BASE_PATH . '/storage/views/mail', + ], + ], +]; +``` + +### 構建郵件類 + +```shell +php bin/hyperf.php gen:mail TestMail +``` + +```php +// app/Mail/TestMail.php + +namespace App\Mail; + +use FriendsOfHyperf\Mail\Mailable; +use FriendsOfHyperf\Mail\Mailable\Content; +use FriendsOfHyperf\Mail\Mailable\Envelope; + +class TestMail extends Mailable +{ + + /** + * Create a new message instance. + */ + public function __construct( + private readonly string $name, + ){} + + /** + * Get the message envelope. + */ + public function envelope(): Envelope + { + return new Envelope( + subject: 'Test Mail', + ); + } + + /** + * Get the message content definition. + */ + public function content(): Content + { + return new Content( + markdown: 'mail.test', + with: [ + 'name' => $this->name, + ], + ); + } + + /** + * Get the attachments for the message. + * + * @return array + */ + public function attachments(): array + { + return []; + } +} +``` + +### 定義控制器或 Service + +```php +// app/Controller/IndexController.php + +use FriendsOfHyperf\Mail\Facade\Mail; + +class IndexController extends AbstractController +{ + public function index() + { + $user = $this->request->input('user', 'Hyperf'); + $mailer = Mail::mailer('smtp'); + $mailer->alwaysFrom('root@imoi.cn','Hyperf'); + + $mailer->to('2771717608@qq.com')->send(new \App\Mail\TestMail($user)); + $method = $this->request->getMethod(); + + return [ + 'method' => $method, + 'message' => "Hello {$user}.", + ]; + } +} + +``` diff --git a/docs/zh-tw/components/middleware-plus.md b/docs/zh-tw/components/middleware-plus.md new file mode 100644 index 000000000..0117abe04 --- /dev/null +++ b/docs/zh-tw/components/middleware-plus.md @@ -0,0 +1,75 @@ +# Middleware Plus + +The middleware plus component for Hyperf. + +## 安裝 + +```shell +composer require friendsofhyperf/middleware-plus +``` + +## 使用 + +- 定義中介軟體 + +```php +handle($request); + } +} + +``` + +- 在路由中設定中介軟體 + +```php +use App\Middleware\FooMiddleware; + +Router::addRoute(['GET', 'POST', 'HEAD'], '/', 'App\Controller\IndexController::index', [ + 'middleware' => [ + FooMiddleware::class . ':1,2,3', + ], +]); +``` + +- 設定中介軟體別名 + +```php +// config/autoload/dependencies.php + +return [ + 'foo-middleware' => App\Middleware\FooMiddleware::class, +]; +``` + +- 使用中介軟體別名 + +```php +use App\Middleware\FooMiddleware; + +Router::addRoute(['GET', 'POST', 'HEAD'], '/', 'App\Controller\IndexController::index', [ + 'middleware' => [ + 'foo-middleware:1,2,3', + ], +]); +``` diff --git a/docs/zh-tw/components/model-factory.md b/docs/zh-tw/components/model-factory.md new file mode 100644 index 000000000..ebc68d9aa --- /dev/null +++ b/docs/zh-tw/components/model-factory.md @@ -0,0 +1,66 @@ +# Model Factory + +## Installation + +```shell +composer require friendsofhyperf/model-factory --dev +``` + +另外,將供應商配置檔案釋出到您的應用程式(依賴項所必需的): + +```shell +php bin/hyperf.php vendor:publish friendsofhyperf/model-factory +``` + +## 使用 + +- `/factories/user_factory.php` + +```php +define(User::class, function (Faker\Generator $faker) { + return [ + 'name' => $faker->name, + 'email' => $faker->unique()->email, + ]; +}); +``` + +- `/seeders/user_seeder.php` + +```php +create([ + 'name' => 'Admin' + ]); + + + // Create 20 random users + factory(User::class, 20)->create(); + } +} + +``` diff --git a/docs/zh-tw/components/model-hashids.md b/docs/zh-tw/components/model-hashids.md new file mode 100644 index 000000000..a40c12fcd --- /dev/null +++ b/docs/zh-tw/components/model-hashids.md @@ -0,0 +1,185 @@ +# Model Hashids + +使用 hashids 代替 URL 和列表項中的整數 ID 可以更具吸引力和巧妙。更多資訊請訪問 [hashids.org](https://hashids.org/)。 + +這個包透過動態編碼/解碼 hashids 來為 Hyperf 模型新增 hashids,而不是將它們持久化到資料庫中。因此,不需要額外的資料庫列,並且透過在查詢中使用主鍵可以獲得更高的效能。 + +功能包括: + +- 為模型生成 hashids +- 將 hashids 解析為模型 +- 能夠為每個模型自定義 hashid 設定 +- 使用 hashids 進行路由繫結(可選) + +## 安裝 + +```shell +composer require friendsofhyperf/model-hashids +``` + +另外,將供應商配置檔案釋出到您的應用程式(依賴項所必需的): + +```shell +php bin/hyperf.php vendor:publish friendsofhyperf/model-hashids +``` + +## 設定 + +基本功能透過使用 `HasHashid` trait 提供,然後可以透過使用 `HashidRouting` 新增基於 hashids 的路由繫結。 + +```php + +use Hyperf\Database\Model\Model; +use FriendsOfHyperf\ModelHashids\Concerns\HasHashid; +use FriendsOfHyperf\ModelHashids\Concerns\HashidRouting; + +Class Item extends Model +{ + use HasHashid, HashidRouting; +} + +``` + +### 自定義 Hashid 設定 + +可以透過重寫 `getHashidsConnection()` 為每個模型自定義 hashids 設定。它必須返回 `config/autoload/hashids.php` 中連線的名稱。 + +## 使用 + +### 基礎 + +```php + +// Generating the model hashid based on its key +$item->hashid(); + +// Equivalent to the above but with the attribute style +$item->hashid; + +// Finding a model based on the provided hashid or +// returning null on failure +Item::findByHashid($hashid); + +// Finding a model based on the provided hashid or +// throwing a ModelNotFoundException on failure +Item::findByHashidOrFail($hashid); + +// Decoding a hashid to its equivalent id +$item->hashidToId($hashid); + +// Encoding an id to its equivalent hashid +$item->idToHashid($id); + +// Getting the name of the hashid connection +$item->getHashidsConnection(); + +``` + +### 將 hashid 新增到序列化模型 + +將其設定為預設值: + +```php + +use Hyperf\Database\Model\Model; +use FriendsOfHyperf\ModelHashids\Concerns\HasHashid; + +class Item extends Model +{ + use HasHashid; + + /** + * The accessors to append to the model's array form. + * + * @var array + */ + protected $appends = ['hashid']; +} + +``` + +將其設定為特定路由: + +`return $item->append('hashid')->toJson();` + +### 隱式路由繫結 + +如果您希望使用模型的 hashid 值解析隱式路由繫結,可以在模型中使用 `HashidRouting`。 + +```php + +use Hyperf\Database\Model\Model; +use FriendsOfHyperf\ModelHashids\Concerns\HasHashid; +use FriendsOfHyperf\ModelHashids\Concerns\HashidRouting; + +class Item extends Model +{ + use HasHashid, HashidRouting; +} + +``` + +它重寫了 `getRouteKeyName()`、`getRouteKey()` 和 `resolveRouteBindingQuery()` 以使用 hashids 作為路由鍵。 + +它支援 Laravel 的自定義特定路由鍵的功能。 + +```php + +Route::get('/items/{item:slug}', function (Item $item) { + return $item; +}); + +``` + +#### 自定義預設路由鍵名稱 + +如果您希望預設使用另一個欄位解析隱式路由繫結,可以重寫 `getRouteKeyName()` 以返回解析過程中的欄位名稱,並在連結中返回其值的 `getRouteKey()`。 + +```php + +use Hyperf\Database\Model\Model; +use FriendsOfHyperf\ModelHashids\Concerns\HasHashid; +use FriendsOfHyperf\ModelHashids\Concerns\HashidRouting; + +class Item extends Model +{ + use HasHashid, HashidRouting; + + public function getRouteKeyName() + { + return 'slug'; + } + + public function getRouteKey() + { + return $this->slug; + } +} + +``` + +您仍然可以為特定路由指定 hashid。 + +```php + +Route::get('/items/{item:hashid}', function (Item $item) { + return $item; +}); + +``` + +#### 支援 Laravel 的其他隱式路由繫結功能 + +使用 `HashidRouting` 時,您仍然可以使用軟刪除和子路由繫結。 + +```php + +Route::get('/items/{item}', function (Item $item) { + return $item; +})->withTrashed(); + +Route::get('/user/{user}/items/{item}', function (User $user, Item $item) { + return $item; +})->scopeBindings(); + +``` diff --git a/docs/zh-tw/components/model-morph-addon.md b/docs/zh-tw/components/model-morph-addon.md new file mode 100644 index 000000000..d178efd92 --- /dev/null +++ b/docs/zh-tw/components/model-morph-addon.md @@ -0,0 +1,98 @@ +# Model Morph Addon + +The model morph addon for Hyperf. + +## 安裝 + +```shell +composer require friendsofhyperf/model-morph-addon +``` + +## 之前用法 + +```php +morphTo(); + } +} + +class Book extends Model +{ + public function images() + { + return $this->morphMany(Image::class, 'imageable'); + } +} + +class User extends Model +{ + public function images() + { + return $this->morphMany(Image::class, 'imageable'); + } +} + +// Global +Relation::morphMap([ + 'user' => App\Model\User::class, + 'book' => App\Model\Book::class, +]); +``` + +## 現在用法 + +```php +morphTo(); + } + + // Privately-owned + public static function getActualClassNameForMorph($class) + { + $morphMap = [ + 'user' => User::class, + 'book' => Book::class, + ]; + + return Arr::get($morphMap, $class, $class); + } +} + +class Book extends Model +{ + public function images() + { + return $this->morphMany(Image::class, 'imageable'); + } + + public function getMorphClass() + { + return 'book'; + } +} + +class User extends Model +{ + public function images() + { + return $this->morphMany(Image::class, 'imageable'); + } + + public function getMorphClass() + { + return 'user'; + } +} +``` diff --git a/docs/zh-tw/components/model-observer.md b/docs/zh-tw/components/model-observer.md new file mode 100644 index 000000000..b17a755d0 --- /dev/null +++ b/docs/zh-tw/components/model-observer.md @@ -0,0 +1,99 @@ +# Model Observer + +The model observer component for Hyperf. + +## 安裝 + +```shell +composer require friendsofhyperf/model-observer +``` + +## 用法 + +- 透過命令生成 + +```shell +php bin/hyperf.php gen:observer TestObserver --model="App\\Model\\User" +``` + +- 單個繫結 + +```php +namespace App\Observer; + +use App\Model\User; +use FriendsOfHyperf\ModelObserver\Annotation\Observer; + +#[Observer(model: User::class)] +class FooObserver +{ + public function creating(User $model) + { + // do sth... + } + + public function created(User $model) + { + // do sth... + } + + // another events +} +``` + +- 多個繫結 + +```php +namespace App\Observer; + +use App\Model\Post; +use App\Model\User; +use FriendsOfHyperf\ModelObserver\Annotation\Observer; + +#[Observer(model: [User::class, Post::class])] +class FooObserver +{ + public function creating($model) + { + // do sth... + } + + public function created($model) + { + // do sth... + } + + // another events +} +``` + +- 繫結到模型 + +```php +namespace App\Model; + +use App\Observer\FooObserver; + +#[ObservedBy(FooObserver::class)] +class User extends Model +{ + // ... +} +``` + +## 支援的事件 + +- `booting` +- `booted` +- `retrieved` +- `creating` +- `created` +- `updating` +- `updated` +- `saving` +- `saved` +- `restoring` +- `restored` +- `deleting` +- `deleted` +- `forceDeleted` diff --git a/docs/zh-tw/components/model-scope.md b/docs/zh-tw/components/model-scope.md new file mode 100644 index 000000000..725462b11 --- /dev/null +++ b/docs/zh-tw/components/model-scope.md @@ -0,0 +1,47 @@ +# Model Scope + +The model scope annotation for Hyperf. + +## 安裝 + +```shell +composer require friendsofhyperf/model-scope +``` + +## 使用 + +- 定義 Scope + +```php +namespace App\Model\Scope; + +use Hyperf\Database\Model\Builder; +use Hyperf\Database\Model\Model; +use Hyperf\Database\Model\Scope; + +class AncientScope implements Scope +{ + /** + * Apply the scope to a given Model query builder. + */ + public function apply(Builder $builder, Model $model): void + { + $builder->where('created_at', '<', now()->subYears(2000)); + } +} +``` + +- 繫結到模型 + +```php +namespace App\Model; + +use App\Model\Scope\AncientScope; +use FriendsOfHyperf\ModelScope\Annotation\ScopedBy; + +#[ScopedBy(AncientScope::class)] +class User extends Model +{ + // +} +``` diff --git a/docs/zh-tw/components/monolog-hook.md b/docs/zh-tw/components/monolog-hook.md new file mode 100644 index 000000000..a1f318a9b --- /dev/null +++ b/docs/zh-tw/components/monolog-hook.md @@ -0,0 +1,7 @@ +# Monolog Hook + +## 安裝 + +```shell +composer require friendsofhyperf/monolog-hook +``` diff --git a/docs/zh-tw/components/mysql-grammar-addon.md b/docs/zh-tw/components/mysql-grammar-addon.md new file mode 100644 index 000000000..d3d024935 --- /dev/null +++ b/docs/zh-tw/components/mysql-grammar-addon.md @@ -0,0 +1,45 @@ +# Mysql Grammar Addon + +The MySqlGrammar addon for Hyperf. + +## 安裝 + +```shell +composer require friendsofhyperf/mysql-grammar-addon --dev +``` + +## 使用之前 + +```php +/** + * @property int $id + * @property int $user_id ??id + * @property string $group_name ???? + * @property string $event_name ???? + * @property string $page_name ?? + * @property string $extra ???? + * @property string $device pc,android,ios,touch + * @property string $device_id ??? + * @property \Carbon\Carbon $created_at ???? + */ +class Event extends Model +{} +``` + +## 使用之後 + +```php +/** + * @property int $id + * @property int $user_id 使用者id + * @property string $group_name 事件分組 + * @property string $event_name 事件名稱 + * @property string $page_name 頁面 + * @property string $extra 額外資訊 + * @property string $device pc,android,ios,touch + * @property string $device_id 裝置號 + * @property \Carbon\Carbon $created_at 建立時間 + */ +class Event extends Model +{} +``` diff --git a/docs/zh-tw/components/notification-easysms.md b/docs/zh-tw/components/notification-easysms.md new file mode 100644 index 000000000..9ccb021de --- /dev/null +++ b/docs/zh-tw/components/notification-easysms.md @@ -0,0 +1,98 @@ +# Notification EasySms Channel + +## 安裝 + +```shell +composer require friendsofhyperf/notification-easysms:~3.1.0 +``` + +## 使用 + +### Use `Notifiable` trait in Model + +```php +phone; + } +} +``` + +### SMS Notifications + +- Install the easy-sms package + +```shell +composer require overtrue/easy-sms:^3.0 +``` + +```php +namespace App\Notification; + +use FriendsOfHyperf\Notification\EasySms\Contract\EasySmsChannelToSmsArrayContract; +use FriendsOfHyperf\Notification\EasySms\Contract\Smsable; +use FriendsOfHyperf\Notification\Notification; +use Overtrue\EasySms\Message; + +// 通知類 +class TestNotification extends Notification implements Smsable +{ + public function __construct(private string $code) + { + } + + public function via() + { + return [ + 'easy-sms' + ]; + } + + /** + * 返回的內容將組裝到簡訊模型中 new Message($notification->toSms()). + * 文件 https://github.com/overtrue/easy-sms?tab=readme-ov-file#%E5%AE%9A%E4%B9%89%E7%9F%AD%E4%BF%A1 + */ + public function toSms(mixed $notifiable): array|Message + { + return [ + 'code' => $this->code, + 'template' => 'SMS_123456789', + 'data' => [ + 'code' => $this->code, + ] + ]; + } + + // or return customer Message + // public function toSms(mixed $notifiable): array|Message + // { + // return new Message(); + // } + +} +``` diff --git a/docs/zh-tw/components/notification-mail.md b/docs/zh-tw/components/notification-mail.md new file mode 100644 index 000000000..d7e057ecc --- /dev/null +++ b/docs/zh-tw/components/notification-mail.md @@ -0,0 +1,111 @@ +# Notification Mail Channel + +## 安裝 + +```shell +composer require friendsofhyperf/notification-mail:~3.1.0 +``` + +## 使用 + +### Use `Notifiable` trait in Model + +```php +mail; + } +} +``` + +### Mail Notifications + +```shell +php bin/hyperf.php gen:markdown-mail Test +``` + +output + +```php + +namespace App\Mail; + +use FriendsOfHyperf\Notification\Mail\Message\MailMessage; +use FriendsOfHyperf\Notification\Notification; + +class Test extends Notification +{ + /** + * Create a new notification instance. + */ + public function __construct() + { + // + } + + /** + * Get the notification's delivery channels. + * + * @return array + */ + public function via(object $notifiable): array + { + return ['mail']; + } + + /** + * Get the mail representation of the notification. + */ + public function toMail(object $notifiable): MailMessage + { + return (new MailMessage)->from('xxx@xxx.cn','Hyperf')->replyTo('xxx@qq.com','zds')->markdown('email'); + } + + /** + * Get the array representation of the notification. + * + * @return array + */ + public function toArray(object $notifiable): array + { + return [ + // + ]; + } + +} +``` + +### Mail Template + +```php +// storage/view/email.blade.php +xxx +``` diff --git a/docs/zh-tw/components/notification.md b/docs/zh-tw/components/notification.md new file mode 100644 index 000000000..aab9fa635 --- /dev/null +++ b/docs/zh-tw/components/notification.md @@ -0,0 +1,246 @@ +# Notification + +## 安裝 + +```shell +composer require friendsofhyperf/notification:~3.1.0 +``` + +## 使用 + +### 在 Model 中使用 `Notifiable` trait + +```php + 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime']; + + // 通知手機號 + public function routeNotificationForSms(): string|PhoneNumber + { + return $this->phone; + } +} +``` + +### Database Notifications + +```shell +# Install the database package +composer require hyperf/database:~3.1.0 + +# Publish the migration file +php bin/hyperf.php notification:table + +# Run the migration +php bin/hyperf.php migrate + +# Create a notification +php bin/hyperf.php make:notification TestNotification +``` + +--- + +```php + $this->message, + ]; + } +} +``` + +--- + +```php +// Your controller or service +// 通知一條訊息 +$user->notify(new TestNotification('系統通知:xxx')); +$noReadCount = $user->unreadNotifications()->count(); +$this->output->success('傳送成功,未讀訊息數:' . $noReadCount); +$notifications = $user->unreadNotifications()->first(); +$this->output->success('訊息內容:' . $notifications->data['message']); +$notifications->markAsRead(); +$noReadCount = $user->unreadNotifications()->count(); +$this->output->success('標記已讀,未讀訊息數:' . $noReadCount); +``` + +### SMS Notifications + +- [notification-easy-sms](https://github.com/friendsofhyperf/notification-easysms) + +### Symfony Notifications + +Send notifications using Symfony Notifier. + +Email, SMS, Slack, Telegram, etc. + +```shell +composer require symfony/notifier +``` + +#### Email + +```shell +composer require symfony/mailer +``` + +--- + +```php +channels()); + } + + /** + * @return ChannelInterface[] + */ + public function channels(): array + { + return [ + 'email' => new EmailChannel( + transport: Transport::fromDsn( + // MAIL_DSN=smtp://user:password@localhost:1025 + env('MAIL_DSN'), + dispatcher: $this->dispatcher, + logger: $this->logger + ), + from: 'root@imoi.cn' + ), + ]; + } +} +``` + +```php +message,['email']))->content('The introduction to the notification.'); + } + + public function toRecipient(User $user) + { + return new Recipient('2771717608@qq.com'); + } + + +} +``` + +```php + \App\Factory\Notifier::class +]; + +``` + +#### 在控制器中使用 + +```php +$user = User::create(); +// 通知一條訊息 +$user->notify(new TestNotification('系統通知:xxx')); +``` diff --git a/docs/zh-tw/components/openai-client.md b/docs/zh-tw/components/openai-client.md new file mode 100644 index 000000000..eaef097d2 --- /dev/null +++ b/docs/zh-tw/components/openai-client.md @@ -0,0 +1,68 @@ +# OpenAI Client + +------ + +**OpenAI PHP** for Laravel 是一個功能強大的社群 PHP API 客戶端,允許您與 [Open AI API](https://beta.openai.com/docs/api-reference/introduction) 進行互動。 + +> **注意:** 此倉庫包含 **OpenAI PHP** 的 Hyperf 整合程式碼。如果您想在與框架無關的方式中使用 **OpenAI PHP** 客戶端,請檢視 [openai-php/client](https://github.com/openai-php/client) 倉庫。 + +## 快速開始 + +> **Requires [PHP 8.1+](https://php.net/releases/)** + +首先,透過 [Composer](https://getcomposer.org/) 包管理器安裝 OpenAI: + +```shell +composer require friendsofhyperf/openai-client +``` + +接下來,釋出配置檔案: + +```shell +php bin/hyperf.php vendor:publish friendsofhyperf/openai-client +``` + +這將在您的專案中建立一個 `config/autoload/openai.php` 配置檔案,您可以使用環境變數根據需要進行修改: + +```env +OPENAI_API_KEY=sk-... +``` + +最後,您可以使用容器中的 `OpenAI\Client` 例項來訪問 OpenAI API: + +```php +use OpenAI\Client; + +$result = di(OpenAI\Client::class)->completions()->create([ + 'model' => 'text-davinci-003', + 'prompt' => 'PHP is', +]); + +echo $result['choices'][0]['text']; // an open-source, widely-used, server-side scripting language. +``` + +## Azure + +要使用 Azure OpenAI 服務,必須使用工廠手動構建客戶端。 + +```php +$client = OpenAI::factory() + ->withBaseUri('{your-resource-name}.openai.azure.com/openai/deployments/{deployment-id}') + ->withHttpHeader('api-key', '{your-api-key}') + ->withQueryParam('api-version', '{version}') + ->make(); +``` + +要使用 Azure,您必須部署一個模型,該模型由 {deployment-id} 標識,已整合到 API 呼叫中。因此,您不必在呼叫期間提供模型,因為它已包含在 BaseUri 中。 + +因此,一個基本的示例完成呼叫將是: + +```php +$result = $client->completions()->create([ + 'prompt' => 'PHP is' +]); +``` + +## 官方指南 + +有關使用示例,請檢視 [openai-php/client](https://github.com/openai-php/client) 倉庫。 diff --git a/docs/zh-tw/components/pest-plugin-hyperf.md b/docs/zh-tw/components/pest-plugin-hyperf.md new file mode 100644 index 000000000..1d75aeab3 --- /dev/null +++ b/docs/zh-tw/components/pest-plugin-hyperf.md @@ -0,0 +1,37 @@ +# Pest Plugin Hyperf + +> 這是一個 [Pest](https://pestphp.com) 外掛,使您的 Hyperf 專案的 Pest 能夠在基於 Swoole 的協程環境中執行。 + +## 安裝 + +```shell +composer require friendsofhyperf/pest-plugin-hyperf --dev +``` + +## 使用 + +```shell +php vendor/bin/pest --coroutine +# or +php vendor/bin/pest --prepend test/prepend.php --coroutine +``` + +- 配置 test/prepend.php + +```php +get(Hyperf\Contract\ApplicationInterface::class); +})(); + +``` diff --git a/docs/zh-tw/components/pretty-console.md b/docs/zh-tw/components/pretty-console.md new file mode 100644 index 000000000..55370016f --- /dev/null +++ b/docs/zh-tw/components/pretty-console.md @@ -0,0 +1,36 @@ +# Pretty Console + +The pretty console component for Hyperf. + +![Pretty Console](https://user-images.githubusercontent.com/5457236/178333036-b11abb56-ba70-4c0d-a2f6-79afe3a0a78c.png) + +## 安裝 + +```shell +composer require friendsofhyperf/pretty-console +``` + +## 使用 + +```php +components->info('Your message here.'); + } +} +``` + +## 鳴謝 + +- [nunomaduro/termwind](https://github.com/nunomaduro/termwind) +- [The idea from pr of laravel](https://github.com/laravel/framework/pull/43065) diff --git a/docs/zh-tw/components/purifier.md b/docs/zh-tw/components/purifier.md new file mode 100644 index 000000000..fb0c394a4 --- /dev/null +++ b/docs/zh-tw/components/purifier.md @@ -0,0 +1,173 @@ +# Purifier + +HTML 過濾器. 派生於 [mews/purifier](https://github.com/mewebstudio/Purifier). + +## 安裝 + +使用 Composer 安裝此元件: + +```shell +composer require friendsofhyperf/purifier +``` + +將自動發現服務提供商。您不需要在任何地方新增 Provider. + +## 用法 + +在請求或中介軟體中使用以下方法,以清理 HTML: + +```php +clean($request->get('inputname')); +``` + +或者 + +```php +ApplicationContext::getContainer()->get(Purifier::class)->clean($request->get('inputname')); +``` + +動態配置: + +```php +clean('This is my H1 title', 'titles'); +clean('This is my H1 title', array('Attr.EnableID' => true)); +``` + +或者 + +```php +ApplicationContext::getContainer()->get(Purifier::class)->clean('This is my H1 title', 'titles'); +ApplicationContext::getContainer()->get(Purifier::class)->clean('This is my H1 title', array('Attr.EnableID' => true)); +``` + +使用 [URI 過濾器](http://htmlpurifier.org/docs/enduser-uri-filter.html) + +```php +ApplicationContext::getContainer()->get(Purifier::class)->clean('This is my H1 title', 'titles', function (HTMLPurifier_Config $config) { + $uri = $config->getDefinition('URI'); + $uri->addFilter(new HTMLPurifier_URIFilter_NameOfFilter(), $config); +}); +``` + +或者,如果您想在資料庫模型中清理 HTML,可以使用我們自定義的 casts: + +```php + CleanHtml::class, // 在獲取和設定值時都會進行清理 + 'description' => CleanHtmlInput::class, // 設定值時清理 + 'history' => CleanHtmlOutput::class, // 獲取值時進行清理 + ]; +} +``` + +## 配置 + +要使用您自己的設定,請釋出配置. + +```shell +php bin/hyperf.php vendor:publish friendsofhyperf/purifier +``` + +配置檔案 `config/autoload/purifier.php` 內容如下: + +```php + +return [ + 'encoding' => 'UTF-8', + 'finalize' => true, + 'ignoreNonStrings' => false, + 'cachePath' => storage_path('app/purifier'), + 'cacheFileMode' => 0755, + 'settings' => [ + 'default' => [ + 'HTML.Doctype' => 'HTML 4.01 Transitional', + 'HTML.Allowed' => 'div,b,strong,i,em,u,a[href|title],ul,ol,li,p[style],br,span[style],img[width|height|alt|src]', + 'CSS.AllowedProperties' => 'font,font-size,font-weight,font-style,font-family,text-decoration,padding-left,color,background-color,text-align', + 'AutoFormat.AutoParagraph' => true, + 'AutoFormat.RemoveEmpty' => true, + ], + 'test' => [ + 'Attr.EnableID' => 'true', + ], + "youtube" => [ + "HTML.SafeIframe" => 'true', + "URI.SafeIframeRegexp" => "%^(http://|https://|//)(www.youtube.com/embed/|player.vimeo.com/video/)%", + ], + 'custom_definition' => [ + 'id' => 'html5-definitions', + 'rev' => 1, + 'debug' => false, + 'elements' => [ + // https://developers.whatwg.org/sections.html + ['section', 'Block', 'Flow', 'Common'], + ['nav', 'Block', 'Flow', 'Common'], + ['article', 'Block', 'Flow', 'Common'], + ['aside', 'Block', 'Flow', 'Common'], + ['header', 'Block', 'Flow', 'Common'], + ['footer', 'Block', 'Flow', 'Common'], + + ['address', 'Block', 'Flow', 'Common'], + ['hgroup', 'Block', 'Required: h1 | h2 | h3 | h4 | h5 | h6', 'Common'], + + // https://developers.whatwg.org/grouping-content.html + ['figure', 'Block', 'Optional: (figcaption, Flow) | (Flow, figcaption) | Flow', 'Common'], + ['figcaption', 'Inline', 'Flow', 'Common'], + + // https://developers.whatwg.org/the-video-element.html#the-video-element + ['video', 'Block', 'Optional: (source, Flow) | (Flow, source) | Flow', 'Common', [ + 'src' => 'URI', + 'type' => 'Text', + 'width' => 'Length', + 'height' => 'Length', + 'poster' => 'URI', + 'preload' => 'Enum#auto,metadata,none', + 'controls' => 'Bool', + ]], + ['source', 'Block', 'Flow', 'Common', [ + 'src' => 'URI', + 'type' => 'Text', + ]], + + // https://developers.whatwg.org/text-level-semantics.html + ['s', 'Inline', 'Inline', 'Common'], + ['var', 'Inline', 'Inline', 'Common'], + ['sub', 'Inline', 'Inline', 'Common'], + ['sup', 'Inline', 'Inline', 'Common'], + ['mark', 'Inline', 'Inline', 'Common'], + ['wbr', 'Inline', 'Empty', 'Core'], + + // https://developers.whatwg.org/edits.html + ['ins', 'Block', 'Flow', 'Common', ['cite' => 'URI', 'datetime' => 'CDATA']], + ['del', 'Block', 'Flow', 'Common', ['cite' => 'URI', 'datetime' => 'CDATA']], + ], + 'attributes' => [ + ['iframe', 'allowfullscreen', 'Bool'], + ['table', 'height', 'Text'], + ['td', 'border', 'Text'], + ['th', 'border', 'Text'], + ['tr', 'width', 'Text'], + ['tr', 'height', 'Text'], + ['tr', 'border', 'Text'], + ], + ], + 'custom_attributes' => [ + ['a', 'target', 'Enum#_blank,_self,_target,_top'], + ], + 'custom_elements' => [ + ['u', 'Inline', 'Inline', 'Common'], + ], + ], + +]; +``` diff --git a/docs/zh-tw/components/recaptcha.md b/docs/zh-tw/components/recaptcha.md new file mode 100644 index 000000000..4295ea238 --- /dev/null +++ b/docs/zh-tw/components/recaptcha.md @@ -0,0 +1,73 @@ +# ReCaptcha + +The Google recaptcha component for Hyperf. + +## 安裝 + +```shell +composer require friendsofhyperf/recaptcha +``` + +## 使用 + +- 定義中介軟體 + +```php +namespace App\Middleware; + +use FriendsOfHyperf\ReCaptcha\Middleware\ReCaptchaMiddleware; + +class V3CaptchaMiddleware extends ReCaptchaMiddleware +{ + protected string $version = 'v3'; + protected string $action = 'register'; + protected float $score = 0.35; + protected string $hostname; +} + +class V2CaptchaMiddleware extends ReCaptchaMiddleware +{ + protected string $version = 'v2'; + protected string $action = 'register'; + protected float $score = 0.35; + protected string $hostname; +} +``` + +- 驗證器使用 + +```php +validationFactory->make( + $request->all(), + [ + 'g-recaptcha' => 'required|recaptcha:register,0.34,hostname,v3', + ], + [ + 'g-recaptcha.required' => 'g-recaptcha is required', + 'g-recaptcha.recaptcha' => 'Google ReCaptcha Verify Fails', + ] + ); + + if ($validator->fails()){ + // Handle exception + $errorMessage = $validator->errors()->first(); + } + // Do something + } +} +``` diff --git a/docs/zh-tw/components/redis-subscriber.md b/docs/zh-tw/components/redis-subscriber.md new file mode 100644 index 000000000..689e2d4a6 --- /dev/null +++ b/docs/zh-tw/components/redis-subscriber.md @@ -0,0 +1,63 @@ +# Redis Subscriber + +Forked from [mix-php/redis-subscriber](https://github.com/mix-php/redis-subscriber) + +Redis native protocol Subscriber based on Swoole coroutine + +基於 Swoole 協程的 Redis 原生協議訂閱庫 + +使用 Socket 直接連線 Redis 伺服器,不依賴 phpredis 擴充套件,該訂閱器有如下優點: + +- 平滑修改:可隨時增加、取消訂閱通道,實現無縫切換通道的需求。 +- 跨協程安全關閉:可在任意時刻關閉訂閱。 +- 通道獲取訊息:該庫封裝風格參考 golang 語言 [go-redis](https://github.com/go-redis/redis) 庫封裝,透過 channel 獲取訂閱的訊息。 + +## 安裝 + +```shell +composer require friendsofhyperf/redis-subscriber +``` + +## 訂閱頻道 + +- 連線、訂閱失敗會丟擲異常 + +```php +$sub = new \FriendsOfHyperf\Redis\Subscriber\Subscriber('127.0.0.1', 6379, '', 5); // 連線失敗將丟擲異常 +$sub->subscribe('foo', 'bar'); // 訂閱失敗將丟擲異常 + +$chan = $sub->channel(); +while (true) { + $data = $chan->pop(); + if (empty($data)) { // 手動close與redis異常斷開都會導致返回false + if (!$sub->closed) { + // redis異常斷開處理 + var_dump('Redis connection is disconnected abnormally'); + } + break; + } + var_dump($data); +} +``` + +接收到訂閱訊息: + +```shell +object(FriendsOfHyperf\Redis\Subscriber\Message)#8 (2) { + ["channel"]=> + string(2) "foo" + ["payload"]=> + string(4) "test" +} +``` + +## 全部方法 + +| 方法 | 描述 | +| --- | --- | +| subscribe(string ...$channels) : void | 增加訂閱 | +| unsubscribe(string ...$channels) : void | 取消訂閱 | +| psubscribe(string ...$channels) : void | 增加通配訂閱 | +| punsubscribe(string ...$channels) : void | 取消通配訂閱 | +| channel() : Hyperf\Engine\Channel | 獲取訊息通道 | +| close() : void | 關閉訂閱 | diff --git a/docs/zh-tw/components/sentry.md b/docs/zh-tw/components/sentry.md new file mode 100644 index 000000000..9af6fd9f4 --- /dev/null +++ b/docs/zh-tw/components/sentry.md @@ -0,0 +1,101 @@ +# Sentry + +The sentry component for Hyperf. + +## 安裝 + +```shell +composer require friendsofhyperf/sentry +``` + +## 釋出配置 + +```shell +php bin/hyperf.php vendor:publish friendsofhyperf/sentry +``` + +## 註冊 LoggerHandler + +```php +warning('this is a test warning issue!'); + +return [ + // ... + 'sentry' => [ + 'handler' => [ + 'class' => FriendsOfHyperf\Sentry\SentryHandler::class, + 'constructor' => [ + 'level' => \Monolog\Level::Debug, + ], + ], + 'formatter' => [ + 'class' => \Monolog\Formatter\LineFormatter::class, + 'constructor' => [ + 'format' => null, + 'dateFormat' => null, + 'allowInlineLineBreaks' => true, + ] + ], + ], + // ... +]; + +``` + +## 配置 Sentry 執行日誌 + +```php + Hyperf\Contract\StdoutLoggerInterface::class, + // ... +]; +``` + +## 註解 + +```php + [ + [ + 'name' => 'tcp', + 'type' => Server::SERVER_BASE, + 'host' => '0.0.0.0', + 'port' => 9401, + 'sock_type' => SWOOLE_SOCK_TCP, + 'callbacks' => [ + Event::ON_CONNECT => [TcpServer::class,'onConnect'], + Event::ON_CLOSE => [TcpServer::class,'onClose'], + Event::ON_RECEIVE => [TcpServer::class,'onReceive'], + ], + 'options' => [ + // Whether to enable request lifecycle event + 'enable_request_lifecycle' => false, + ], + ] + ], +``` + +### 非同步風格 + +```php +send($fd, sprintf('Client %s connected.'.PHP_EOL, $fd)); + } + + public function onClose($server, int $fd, int $reactorId): void + { + $server->send($fd, sprintf('Client %s closed.'.PHP_EOL, $fd)); + } + + public function onReceive($server, int $fd, int $reactorId, string $data): void + { + $server->send($fd, sprintf('Client %s send: %s'.PHP_EOL, $fd, $data)); + var_dump($data); + } + + +} +``` + +### 協程風格 + +```php +namespace App; + +use Hyperf\Contract\OnReceiveInterface; +use FriendsOfHyperf\TcpSender\Sender; +use Swoole\Coroutine\Server\Connection; +use Swoole\Server; + +class TcpServer implements OnReceiveInterface +{ + public function __construct(private Sender $sender) + { + } + + public function onConnect(Connection $connection, $fd): void + { + // 設定 fd 和 connection 的對映關係 + $this->sender->setResponse($fd,$connection); + $connection->send(sprintf('Client %s connected.'.PHP_EOL, $fd)); + } + + public function onClose($connection, int $fd): void + { + // 刪除 fd 和 connection 的對映關係 + $this->sender->setResponse($fd,null); + } + + public function onReceive($server, int $fd, int $reactorId, string $data): void + { + $server->send($fd, sprintf('Client %s send: %s'.PHP_EOL, $fd, $data)); + } + + +} +``` + +## 在控制器中使用 + +```php +sender->send(1, 'Hello Hyperf.'); + $user = $this->request->input('user', 'Hyperf'); + $method = $this->request->getMethod(); + + return [ + 'method' => $method, + 'message' => "Hello {$user}.", + ]; + } +} + +``` diff --git a/docs/zh-tw/components/telescope-elasticsearch.md b/docs/zh-tw/components/telescope-elasticsearch.md new file mode 100644 index 000000000..f3b83cbdd --- /dev/null +++ b/docs/zh-tw/components/telescope-elasticsearch.md @@ -0,0 +1,34 @@ +# Telescope Elasticsearch Driver + +它允許您從 SQL 資料庫切換到 Elasticsearch 作為資料儲存的驅動程式,並且它將消除死鎖,使 Telescope 成為一個可用於生產環境的日誌系統。 + +## 安裝 + +```shell +composer require friendsofhyperf/telescope-elasticsearch +``` + +## 釋出配置 + +```shell +php bin/hyperf.php vendor:publish friendsofhyperf/telescope --id=config +``` + +## 配置 + +```php +// config/autoload/telescope.php +return [ + 'driver' => 'elasticsearch', + 'storage' => [ + 'elasticsearch' => [ + 'driver' => FriendsOfHyperf\TelescopeElasticsearch\Storage\ElasticsearchEntriesRepository::class, + 'index' => 'telescope_entries', + + 'hosts' => ['127.0.0.1'], + 'username' => null, + 'password' => null, + ], + ], +]; +``` diff --git a/docs/zh-tw/components/telescope.md b/docs/zh-tw/components/telescope.md new file mode 100644 index 000000000..eb47a7a00 --- /dev/null +++ b/docs/zh-tw/components/telescope.md @@ -0,0 +1,142 @@ +# Telescope + +## 可用監聽器 + +- [x] 請求監視器 +- [x] 異常監視器 +- [x] 資料查詢監視器 +- [x] gRPC請求監視器 +- [x] Redis監視器 +- [x] 日誌監視器 +- [x] 命令列監視器 +- [x] 事件監視器 +- [x] HTTP Client 監視器 +- [x] 快取監視器 +- [x] 定時任務監視器 + +## 安裝 + +```shell +composer require friendsofhyperf/telescope:~3.1.0 +``` + +使用 `vendor:publish` 命令來發布其公共資源 + +```shell +php bin/hyperf.php vendor:publish friendsofhyperf/telescope +``` + +執行 `migrate` 命令執行資料庫變更來建立和儲存 Telescope 需要的資料 + +```shell +php bin/hyperf.php migrate +``` + +## 使用 + +> 監聽器和中介軟體,二選一即可 + +### 請求監聽器 + +在 `config/autoload/listeners.php`配置檔案新增監聽器 + +```php + [ + FriendsOfHyperf\Telescope\Middleware\TelescopeMiddleware::class, + ], +]; +``` + +如需記錄gRPC請求,請使用`grpc`中介軟體 + +```php + [ + FriendsOfHyperf\Telescope\Middleware\TelescopeMiddleware::class, + ], +]; +``` + +## 檢視儀表板 + +`http://127.0.0.1:9501/telescope` + +## 資料庫配置 + +在 `config/autoload/telescope.php`管理資料庫連線配置,預設使用`default`連線 + +```php +'connection' => env('TELESCOPE_DB_CONNECTION', 'default'), +``` + +## 標籤 + +您可能希望將自己的自定義標籤附加到條目。為此,您可以使用 **`Telescope::tag`** 方法。 + +## 批次過濾 + +您可能只想記錄某些特殊條件下的條目。為此,您可以使用 **`Telescope::filter`** 方法。 + +例子 + +```php +use FriendsOfHyperf\Telescope\Telescope; +use Hyperf\Event\Contract\ListenerInterface; +use Hyperf\Framework\Event\BootApplication; +use FriendsOfHyperf\Telescope\IncomingEntry; + +class TelescopeInitListener implements ListenerInterface +{ + public function listen(): array + { + return [ + BootApplication::class, + ]; + } + + public function process(object $event): void + { + // attach your own custom tags + Telescope::tag(function (IncomingEntry $entry) { + if ($entry->type === 'request') { + return [ + 'status:' . $entry->content['response_status'], + 'uri:'. $entry->content['uri'], + ]; + } + }); + + // filter entry + Telescope::filter(function (IncomingEntry $entry): bool { + if ($entry->type === 'request'){ + if ($entry->content['uri'] == 'xxxx') { + return false; + } + } + return true; + }); + + } +} +``` diff --git a/docs/zh-tw/components/tinker.md b/docs/zh-tw/components/tinker.md new file mode 100644 index 000000000..7f18d8b0d --- /dev/null +++ b/docs/zh-tw/components/tinker.md @@ -0,0 +1,189 @@ +# Tinker + +The Powerful REPL for Hyperf. + +## 安裝 + +```shell +composer require friendsofhyperf/tinker +``` + +## 釋出配置 + +```shell +php bin/hyperf.php vendor:publish friendsofhyperf/tinker +``` + +## 使用 + +```shell +php bin/hyperf.php tinker +``` + +## 命令 + +- 執行命令 + +````shell +Psy Shell v0.10.4 (PHP 7.3.11 — cli) +>>> $a=1 +=> 1 +>>> $a +=> 1 +>>> define('VERSION', 'v1.0.1') +=> true +>>> VERSION +=> "v1.0.1" +>>> +```` + +- 檢視命令幫助 + +```shell +>>> help + help Show a list of commands. Type `help [foo]` for information about [foo]. Aliases: ? + ls List local, instance or class variables, methods and constants. Aliases: dir + dump Dump an object or primitive. + doc Read the documentation for an object, class, constant, method or property. Aliases: rtfm, man + show Show the code for an object, class, constant, method or property. + wtf Show the backtrace of the most recent exception. Aliases: last-exception, wtf? + whereami Show where you are in the code. + throw-up Throw an exception or error out of the Psy Shell. + timeit Profiles with a timer. + trace Show the current call stack. + buffer Show (or clear) the contents of the code input buffer. Aliases: buf + clear Clear the Psy Shell screen. + edit Open an external editor. Afterwards, get produced code in input buffer. + sudo Evaluate PHP code, bypassing visibility restrictions. + history Show the Psy Shell history. Aliases: hist + exit End the current session and return to caller. Aliases: quit, q + clear-compiled Remove the compiled class file + down Put the application into maintenance mode + env Display the current framework environment + optimize Cache the framework bootstrap files + up Bring the application out of maintenance mode + migrate Run the database migrations + inspire Display an inspiring quote +``` + +- 獲取環境變數 + +```shell +Psy Shell v0.10.4 (PHP 7.2.34 — cli) +>>> env("APP_NAME") +=> "skeleton" +>>> +``` + +- 模型操作 + +```shell +➜ t.hyperf.com git:(master) ✗ php bin/hyperf.php tinker +[DEBUG] Event Hyperf\Framework\Event\BootApplication handled by Hyperf\Config\Listener\RegisterPropertyHandlerListener listener. +[DEBUG] Event Hyperf\Framework\Event\BootApplication handled by Hyperf\Paginator\Listener\PageResolverListener listener. +[DEBUG] Event Hyperf\Framework\Event\BootApplication handled by Hyperf\ExceptionHandler\Listener\ExceptionHandlerListener listener. +[DEBUG] Event Hyperf\Framework\Event\BootApplication handled by Hyperf\DbConnection\Listener\RegisterConnectionResolverListener listener. +Psy Shell v0.10.4 (PHP 7.2.34 — cli) by Justin Hileman +Unable to check for updates +>>> $user = App\Model\User::find(1) +[DEBUG] Event Hyperf\Database\Model\Events\Booting handled by Hyperf\ModelListener\Listener\ModelHookEventListener listener. +[DEBUG] Event Hyperf\Database\Model\Events\Booting handled by Hyperf\ModelListener\Listener\ModelEventListener listener. +[DEBUG] Event Hyperf\Database\Model\Events\Booted handled by Hyperf\ModelListener\Listener\ModelHookEventListener listener. +[DEBUG] Event Hyperf\Database\Model\Events\Booted handled by Hyperf\ModelListener\Listener\ModelEventListener listener. +[DEBUG] Event Hyperf\Database\Events\QueryExecuted handled by App\Listener\DbQueryExecutedListener listener. +[DEBUG] Event Hyperf\Database\Model\Events\Retrieved handled by Hyperf\ModelListener\Listener\ModelHookEventListener listener. +[DEBUG] Event Hyperf\Database\Model\Events\Retrieved handled by Hyperf\ModelListener\Listener\ModelEventListener listener. +=> App\Model\User {#81816 + +incrementing: true, + +exists: true, + +wasRecentlyCreated: false, + +timestamps: true, + } +>>> var_dump($user) +object(App\Model\User)#81816 (28) { + ["table":protected]=> + string(5) "users" + ["fillable":protected]=> + array(2) { + [0]=> + string(2) "id" + [1]=> + string(4) "name" + } + ["casts":protected]=> + array(0) { + } + ["incrementing"]=> + bool(true) + ["exists"]=> + bool(true) + + ["attributes":protected]=> + array(4) { + ["id"]=> + int(1) + ["name"]=> + string(5) "arvin" + ["created_at"]=> + string(19) "2020-11-23 18:38:00" + ["updated_at"]=> + string(19) "2020-11-23 18:38:03" + } + ["original":protected]=> + array(4) { + ["id"]=> + int(1) + ["name"]=> + string(5) "arvin" + ["created_at"]=> + string(19) "2020-11-23 18:38:00" + ["updated_at"]=> + string(19) "2020-11-23 18:38:03" + } + +} +=> null +``` + +- 檢視文件 + +```shell +>>> doc md5 +function md5($str, $raw_output = unknown) + +PHP manual not found + To document core PHP functionality, download the PHP reference manual: + https://github.com/bobthecow/psysh/wiki/PHP-manual +>>> +``` + +- 檢視原始碼 + +```shell +>>> show App\Model\User + 7: /** + 8: */ + 9: class User extends Model +10: { +11: /** +12: * The table associated with the model. +13: * +14: * @var string +15: */ +16: protected $table = 'users'; +17: /** +18: * The attributes that are mass assignable. +19: * +20: * @var array +21: */ +22: protected $fillable = ['id','name']; +23: /** +24: * The attributes that should be cast to native types. +25: * +26: * @var array +27: */ +28: protected $casts = []; +29: } + +>>> +``` diff --git a/docs/zh-tw/components/trigger.md b/docs/zh-tw/components/trigger.md new file mode 100644 index 000000000..58e0085cf --- /dev/null +++ b/docs/zh-tw/components/trigger.md @@ -0,0 +1,73 @@ +# Trigger + +## 安裝 + +- 安裝 + +```shell +composer require friendsofhyperf/trigger +``` + +- 釋出配置 + +```shell +php bin/hyperf.php vendor:publish friendsofhyperf/trigger +``` + +## 新增監聽器 + +```php +// config/autoload/listeners.php + +return [ + FriendsOfHyperf\Trigger\Listener\BindTriggerProcessesListener::class => PHP_INT_MAX, +]; +``` + +## 定義觸發器 + +```php +namespace App\Trigger; + +use FriendsOfHyperf\Trigger\Annotation\Trigger; +use FriendsOfHyperf\Trigger\Trigger\AbstractTrigger; +use MySQLReplication\Event\DTO\EventDTO; + +#[Trigger(table:"table", events:["*"], connection:"default")] +class FooTrigger extends AbstractTrigger +{ + public function onWrite(array $new) + { + var_dump($new); + } + + public function onUpdate(array $old, array $new) + { + var_dump($old, $new); + } + + public function onDelete(array $old) + { + var_dump($old); + } +} +``` + +## 定義訂閱者 + +```php +namespace App\Subscriber; + +use FriendsOfHyperf\Trigger\Annotation\Subscriber; +use FriendsOfHyperf\Trigger\Subscriber\AbstractSubscriber; +use MySQLReplication\Event\DTO\EventDTO; + +#[Subscriber(connection:"default")] +class BarSubscriber extends AbstractSubscriber +{ + protected function allEvents(EventDTO $event): void + { + // 一些程式碼 + } +} +``` diff --git a/docs/zh-tw/components/validated-dto.md b/docs/zh-tw/components/validated-dto.md new file mode 100644 index 000000000..bcc22f7a3 --- /dev/null +++ b/docs/zh-tw/components/validated-dto.md @@ -0,0 +1,619 @@ +# Validated DTO + +## 官方文件 + +[Laravel Validated DTO 官方文件](https://wendell-adriel.gitbook.io/laravel-validated-dto) + +## 安裝 + +```shell +composer require friendsofhyperf/validated-dto +``` + +## 建立 DTO + +你可以使用 `gen:dto` 命令建立 `DTO`: + +```shell +php bin/hyperf.php gen:dto UserDTO +``` + +`DTO` 將會被建立在 `app/DTO` 目錄下。 + +## 定義驗證規則 + +你可以像驗證 `Request` 資料一樣驗證資料: + +```php + ['required', 'string'], + 'email' => ['required', 'email'], + 'password' => ['required'], + ]; + } +} +``` + +## 建立 DTO 例項 + +你可以透過多種方式建立 `DTO` 例項: + +### 從陣列建立 + +```php +$dto = UserDTO::fromArray([ + 'name' => 'Deeka Wong', + 'email' => 'deeka@example.com', + 'password' => 'D3Crft!@1b2A' +]); +``` + +### 從 JSON 字串建立 + +```php +$dto = UserDTO::fromJson('{"name": "Deeka Wong", "email": "deeka@example.com", "password": "D3Crft!@1b2A"}'); +``` + +### 從請求物件建立 + +```php +public function store(RequestInterface $request): JsonResponse +{ + $dto = UserDTO::fromRequest($request); +} +``` + +### 從模型建立 + +```php +$user = new User([ + 'name' => 'Deeka Wong', + 'email' => 'deeka@example.com', + 'password' => 'D3Crft!@1b2A' +]); + +$dto = UserDTO::fromModel($user); +``` + +注意,模型中 `$hidden` 屬性的欄位不會被用於 `DTO`。 + +### 從 Artisan 命令建立 + +你有三種方式從 `Artisan Command` 建立 `DTO` 例項: + +#### 從命令引數建立 + +```php + 'Deeka Wong', + 'email' => 'deeka@example.com', + 'password' => 'D3Crft!@1b2A' +]); + +$dto->name; // 'Deeka Wong' +$dto->email; // 'deeka@example.com' +$dto->password; // 'D3Crft!@1b2A' +``` + +如果你傳遞的屬性不在 `DTO` 的 `rules` 方法中,這些資料將被忽略,並且不會在 `DTO` 中可用: + +```php +$dto = UserDTO::fromArray([ + 'name' => 'Deeka Wong', + 'email' => 'deeka@example.com', + 'password' => 'D3Crft!@1b2A', + 'username' => 'john_doe', +]); + +$dto->username; // 這個屬性在 DTO 中不可用 +``` + +## 定義預設值 + +有時我們可能有一些可選屬性,並且可以有預設值。你可以在 `defaults` 方法中定義 `DTO` 屬性的預設值: + +```php + ['required', 'string'], + 'email' => ['required', 'email'], + 'username' => ['sometimes', 'string'], + 'password' => ['required'], + ]; + } + + protected function defaults(): array + { + return [ + 'username' => Str::snake($this->name), + ]; + } +} +``` + +使用上面的 `DTO` 定義,你可以執行: + +```php +$dto = UserDTO::fromArray([ + 'name' => 'Deeka Wong', + 'email' => 'deeka@example.com', + 'password' => 'D3Crft!@1b2A' +]); + +$dto->username; // 'deeka_wong' +``` + +## 轉換 DTO 資料 + +你可以將你的 DTO 轉換為一些格式: + +### 轉換為陣列 + +```php +$dto = UserDTO::fromArray([ + 'name' => 'Deeka Wong', + 'email' => 'deeka@example.com', + 'password' => 'D3Crft!@1b2A', +]); + +$dto->toArray(); +// [ +// "name" => "Deeka Wong", +// "email" => "deeka@example.com", +// "password" => "D3Crft!@1b2A", +// ] +``` + +### 轉換為 JSON 字串 + +```php +$dto = UserDTO::fromArray([ + 'name' => 'Deeka Wong', + 'email' => 'deeka@example.com', + 'password' => 'D3Crft!@1b2A', +]); + +$dto->toJson(); +// '{"name":"Deeka Wong","email":"deeka@example.com","password":"D3Crft!@1b2A"}' + +$dto->toJson(true); // 你可以這樣呼叫它來美化列印你的 JSON +// { +// "name": "Deeka Wong", +// "email": "deeka@example.com", +// "password": "D3Crft!@1b2A" +// } +``` + +### 轉換為 Eloquent 模型 + +```php +$dto = UserDTO::fromArray([ + 'name' => 'Deeka Wong', + 'email' => 'deeka@example.com', + 'password' => 'D3Crft!@1b2A', +]); + +$dto->toModel(\App\Model\User::class); +// App\Model\User {#3776 +// name: "Deeka Wong", +// email: "deeka@example.com", +// password: "D3Crft!@1b2A", +// } + +``` + +## 自定義錯誤訊息、屬性和異常 + +你可以透過在 `DTO` 類中實現 `messages` 和 `attributes` 方法來定義自定義訊息和屬性: + +```php +/** + * 定義驗證器錯誤的自定義訊息。 + */ +protected function messages() { + return []; +} + +/** + * 定義驗證器錯誤的自定義屬性。 + */ +protected function attributes() { + return []; +} +``` + +## 型別轉換 + +你可以透過在 `DTO` 中定義 `casts` 方法輕鬆轉換你的 DTO 屬性: + +```php +/** + * 定義 DTO 屬性的型別轉換。 + * + * @return array + */ +protected function casts(): array +{ + return [ + 'name' => new StringCast(), + 'age' => new IntegerCast(), + 'created_at' => new CarbonImmutableCast(), + ]; +} +``` + +## 可用型別 + +### 陣列 + +對於 JSON 字串,它將轉換為陣列,對於其他型別,它將包裝在陣列中。 + +```php +protected function casts(): array +{ + return [ + 'property' => new ArrayCast(), + ]; +} +``` + +### 布林值 + +對於字串值,這使用 `filter_var` 函式和 `FILTER_VALIDATE_BOOLEAN` 標誌。 + +```php +protected function casts(): array +{ + return [ + 'property' => new BooleanCast(), + ]; +} +``` + +### Carbon + +這接受 `Carbon` 建構函式接受的任何值。如果發現無效值,它將丟擲 `\FriendsOfHyperf\ValidatedDTO\Exception\CastException` 異常。 + +```php +protected function casts(): array +{ + return [ + 'property' => new CarbonCast(), + ]; +} +``` + +你也可以在定義轉換時傳遞一個時區,如果需要的話,它將在轉換值時使用。 + +```php +protected function casts(): array +{ + return [ + 'property' => new CarbonCast('Europe/Lisbon'), + ]; +} +``` + +你也可以在定義轉換時傳遞一個格式來用於轉換值。如果屬性的格式與指定的不同,它將丟擲 `\FriendsOfHyperf\ValidatedDTO\Exception\CastException` 異常。 + +```php +protected function casts(): array +{ + return [ + 'property' => new CarbonCast('Europe/Lisbon', 'Y-m-d'), + ]; +} +``` + +### CarbonImmutable + +這接受 `CarbonImmutable` 建構函式接受的任何值。如果發現無效值,它將丟擲 `\FriendsOfHyperf\ValidatedDTO\Exception\CastException` 異常。 + +```php +protected function casts(): array +{ + return [ + 'property' => new CarbonImmutableCast(), + ]; +} +``` + +你也可以在定義轉換時傳遞一個時區,如果需要的話,它將在轉換值時使用。 + +```php +protected function casts(): array +{ + return [ + 'property' => new CarbonImmutableCast('Europe/Lisbon'), + ]; +} +``` + +你也可以在定義轉換時傳遞一個格式來用於轉換值。如果屬性的格式與指定的不同,它將丟擲 `\FriendsOfHyperf\ValidatedDTO\Exception\CastException` 異常。 + +```php +protected function casts(): array +{ + return [ + 'property' => new CarbonImmutableCast('Europe/Lisbon', 'Y-m-d'), + ]; +} +``` + +### 集合 + +對於 JSON 字串,它將首先轉換為陣列,然後包裝到 `Collection` 物件中。 + +```php +protected function casts(): array +{ + return [ + 'property' => new CollectionCast(), + ]; +} +``` + +如果你想轉換 `Collection` 中的所有元素,你可以將 `Castable` 傳遞給 `CollectionCast` 建構函式。假設你想將 `Collection` 中的所有專案轉換為整數: + +```php +protected function casts(): array +{ + return [ + 'property' => new CollectionCast(new IntegerCast()), + ]; +} +``` + +這適用於所有 `Castable`,包括 `DTOCast` 和 `ModelCast` 用於巢狀資料。 + +### DTO + +這適用於陣列和 JSON 字串。這將驗證資料併為給定的 DTO 轉換資料。 + +如果資料對 DTO 無效,這將丟擲 `Hyperf\Validation\ValidationException` 異常。 + +如果屬性不是有效的陣列或有效的 JSON 字串,這將丟擲 `FriendsOfHyperf\ValidatedDTO\Exception\CastException` 異常。 + +如果傳遞給 `DTOCast` 建構函式的類不是 `ValidatedDTO` 例項,這將丟擲 `FriendsOfHyperf\ValidatedDTO\Exception\CastTargetException` 異常。 + +```php +protected function casts(): array +{ + return [ + 'property' => new DTOCast(UserDTO::class), + ]; +} +``` + +### 浮點數 + +如果發現非數字值,它將丟擲 `FriendsOfHyperf\ValidatedDTO\Exception\CastException` 異常。 + +```php +protected function casts(): array +{ + return [ + 'property' => new FloatCast(), + ]; +} +``` + +### 整數 + +如果發現非數字值,它將丟擲 `FriendsOfHyperf\ValidatedDTO\Exception\CastException` 異常。 + +```php +protected function casts(): array +{ + return [ + 'property' => new IntegerCast(), + ]; +} +``` + +### 模型 + +這適用於陣列和 JSON 字串。 + +如果屬性不是有效的陣列或有效的 JSON 字串,這將丟擲 `FriendsOfHyperf\ValidatedDTO\Exception\CastException` 異常。 + +如果傳遞給 `ModelCast` 建構函式的類不是 `Model` 例項,這將丟擲 `FriendsOfHyperf\ValidatedDTO\Exception\CastTargetException` 異常。 + +```php +protected function casts(): array +{ + return [ + 'property' => new ModelCast(User::class), + ]; +} +``` + +### 物件 + +這適用於陣列和 JSON 字串。 + +如果屬性不是有效的陣列或有效的 JSON 字串,這將丟擲 `FriendsOfHyperf\ValidatedDTO\Exception\CastException` 異常。 + +```php +protected function casts(): array +{ + return [ + 'property' => new ObjectCast(), + ]; +} +``` + +### 字串 + +如果資料不能轉換為字串,這將丟擲 `FriendsOfHyperf\ValidatedDTO\Exception\CastException` 異常。 + +```php +protected function casts(): array +{ + return [ + 'property' => new StringCast(), + ]; +} +``` + +## 建立你自己的型別轉換 + +你可以透過實現 `FriendsOfHyperf\ValidatedDTO\Casting\Castable` 介面輕鬆為你的專案建立新的 `Castable` 型別。這個介面有一個必須實現的方法: + +```php +/** + * 轉換給定的值。 + * + * @param string $property + * @param mixed $value + * @return mixed + */ +public function cast(string $property, mixed $value): mixed; +``` + +假設你的專案中有一個 `URLWrapper` 類,並且你希望在將 URL 傳遞給你的 `DTO` 時,它總是返回一個 `URLWrapper` 例項而不是一個簡單的字串: + +```php +class URLCast implements Castable +{ + /** + * @param string $property + * @param mixed $value + * @return URLWrapper + */ + public function cast(string $property, mixed $value): URLWrapper + { + return new URLWrapper($value); + } +} +``` + +然後你可以將其應用到你的 DTO: + +```php +class CustomDTO extends ValidatedDTO +{ + protected function rules(): array + { + return [ + 'url' => ['required', 'url'], + ]; + } + + protected function defaults(): array + { + return []; + } + + protected function casts(): array + { + return [ + 'url' => new URLCast(), + ]; + } +} +``` diff --git a/docs/zh-tw/components/web-tinker.md b/docs/zh-tw/components/web-tinker.md new file mode 100644 index 000000000..4f6c9a4b8 --- /dev/null +++ b/docs/zh-tw/components/web-tinker.md @@ -0,0 +1,29 @@ +# Web Tinker + +## 安裝 + +```shell +composer require friendsofhyperf/web-tinker --dev +``` + +## 釋出配置 + +```shell +php bin/hyperf.php vendor:publish friendsofhyperf/web-tinker +``` + +或 + +```shell +php bin/hyperf.php web-tinker:install +``` + +## 啟動 + +```shell +php bin/hyperf.php start +``` + +## 訪問 + +訪問 `http://127.0.0.1:9501/tinker` 即可進入 Web Tinker 頁面。 diff --git a/docs/zh-tw/faq/index.md b/docs/zh-tw/faq/index.md new file mode 100644 index 000000000..eba1c20b4 --- /dev/null +++ b/docs/zh-tw/faq/index.md @@ -0,0 +1 @@ +# 常見問題 \ No newline at end of file diff --git a/docs/zh-tw/guide/index.md b/docs/zh-tw/guide/index.md new file mode 100644 index 000000000..c2fce2d01 --- /dev/null +++ b/docs/zh-tw/guide/index.md @@ -0,0 +1 @@ +# 入門 diff --git a/docs/zh-tw/guide/introduce/about.md b/docs/zh-tw/guide/introduce/about.md new file mode 100644 index 000000000..d8790091f --- /dev/null +++ b/docs/zh-tw/guide/introduce/about.md @@ -0,0 +1 @@ +# 關於 FriendsOfHyperf diff --git a/docs/zh-tw/guide/start/components.md b/docs/zh-tw/guide/start/components.md new file mode 100644 index 000000000..41232351a --- /dev/null +++ b/docs/zh-tw/guide/start/components.md @@ -0,0 +1,56 @@ +# Components + +## 支援的元件列表 + +|Repository|Stable Version|Total Downloads|Monthly Downloads| +|--|--|--|--| +|[amqp-job](https://github.com/friendsofhyperf/amqp-job)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/amqp-job/v)](https://packagist.org/packages/friendsofhyperf/amqp-job)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/amqp-job/downloads)](https://packagist.org/packages/friendsofhyperf/amqp-job)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/amqp-job/d/monthly)](https://packagist.org/packages/friendsofhyperf/amqp-job)| +|[cache](https://github.com/friendsofhyperf/cache)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/cache/v)](https://packagist.org/packages/friendsofhyperf/cache)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/cache/downloads)](https://packagist.org/packages/friendsofhyperf/cache)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/cache/d/monthly)](https://packagist.org/packages/friendsofhyperf/cache)| +|[command-signals](https://github.com/friendsofhyperf/command-signals)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/command-signals/v)](https://packagist.org/packages/friendsofhyperf/command-signals)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/command-signals/downloads)](https://packagist.org/packages/friendsofhyperf/command-signals)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/command-signals/d/monthly)](https://packagist.org/packages/friendsofhyperf/command-signals)| +|[command-validation](https://github.com/friendsofhyperf/command-validation)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/command-validation/v)](https://packagist.org/packages/friendsofhyperf/command-validation)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/command-validation/downloads)](https://packagist.org/packages/friendsofhyperf/command-validation)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/command-validation/d/monthly)](https://packagist.org/packages/friendsofhyperf/command-validation)| +|[compoships](https://github.com/friendsofhyperf/compoships)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/compoships/v)](https://packagist.org/packages/friendsofhyperf/compoships)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/compoships/downloads)](https://packagist.org/packages/friendsofhyperf/compoships)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/compoships/d/monthly)](https://packagist.org/packages/friendsofhyperf/compoships)| +|[confd](https://github.com/friendsofhyperf/confd)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/confd/v)](https://packagist.org/packages/friendsofhyperf/confd)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/confd/downloads)](https://packagist.org/packages/friendsofhyperf/confd)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/confd/d/monthly)](https://packagist.org/packages/friendsofhyperf/confd)| +|[config-consul](https://github.com/friendsofhyperf/config-consul)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/config-consul/v)](https://packagist.org/packages/friendsofhyperf/config-consul)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/config-consul/downloads)](https://packagist.org/packages/friendsofhyperf/config-consul)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/config-consul/d/monthly)](https://packagist.org/packages/friendsofhyperf/config-consul)| +|[console-spinner](https://github.com/friendsofhyperf/console-spinner)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/console-spinner/v)](https://packagist.org/packages/friendsofhyperf/console-spinner)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/console-spinner/downloads)](https://packagist.org/packages/friendsofhyperf/console-spinner)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/console-spinner/d/monthly)](https://packagist.org/packages/friendsofhyperf/console-spinner)| +|[di-plus](https://github.com/friendsofhyperf/di-plus)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/di-plus/v)](https://packagist.org/packages/friendsofhyperf/di-plus)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/di-plus/downloads)](https://packagist.org/packages/friendsofhyperf/di-plus)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/di-plus/d/monthly)](https://packagist.org/packages/friendsofhyperf/di-plus)| +|[elasticsearch](https://github.com/friendsofhyperf/elasticsearch)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/elasticsearch/v)](https://packagist.org/packages/friendsofhyperf/elasticsearch)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/elasticsearch/downloads)](https://packagist.org/packages/friendsofhyperf/elasticsearch)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/elasticsearch/d/monthly)](https://packagist.org/packages/friendsofhyperf/elasticsearch)| +|[encryption](https://github.com/friendsofhyperf/encryption)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/encryption/v)](https://packagist.org/packages/friendsofhyperf/encryption)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/encryption/downloads)](https://packagist.org/packages/friendsofhyperf/encryption)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/encryption/d/monthly)](https://packagist.org/packages/friendsofhyperf/encryption)| +|[exception-event](https://github.com/friendsofhyperf/exception-event)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/exception-event/v)](https://packagist.org/packages/friendsofhyperf/exception-event)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/exception-event/downloads)](https://packagist.org/packages/friendsofhyperf/exception-event)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/exception-event/d/monthly)](https://packagist.org/packages/friendsofhyperf/exception-event)| +|[facade](https://github.com/friendsofhyperf/facade)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/facade/v)](https://packagist.org/packages/friendsofhyperf/facade)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/facade/downloads)](https://packagist.org/packages/friendsofhyperf/facade)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/facade/d/monthly)](https://packagist.org/packages/friendsofhyperf/facade)| +|[fast-paginate](https://github.com/friendsofhyperf/fast-paginate)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/fast-paginate/v)](https://packagist.org/packages/friendsofhyperf/fast-paginate)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/fast-paginate/downloads)](https://packagist.org/packages/friendsofhyperf/fast-paginate)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/fast-paginate/d/monthly)](https://packagist.org/packages/friendsofhyperf/fast-paginate)| +|[grpc-validation](https://github.com/friendsofhyperf/grpc-validation)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/grpc-validation/v)](https://packagist.org/packages/friendsofhyperf/grpc-validation)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/grpc-validation/downloads)](https://packagist.org/packages/friendsofhyperf/grpc-validation)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/grpc-validation/d/monthly)](https://packagist.org/packages/friendsofhyperf/grpc-validation)| +|[helpers](https://github.com/friendsofhyperf/helpers)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/helpers/v)](https://packagist.org/packages/friendsofhyperf/helpers)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/helpers/downloads)](https://packagist.org/packages/friendsofhyperf/helpers)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/helpers/d/monthly)](https://packagist.org/packages/friendsofhyperf/helpers)| +|[http-client](https://github.com/friendsofhyperf/http-client)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/http-client/v)](https://packagist.org/packages/friendsofhyperf/http-client)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/http-client/downloads)](https://packagist.org/packages/friendsofhyperf/http-client)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/http-client/d/monthly)](https://packagist.org/packages/friendsofhyperf/http-client)| +|[http-logger](https://github.com/friendsofhyperf/http-logger)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/http-logger/v)](https://packagist.org/packages/friendsofhyperf/http-logger)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/http-logger/downloads)](https://packagist.org/packages/friendsofhyperf/http-logger)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/http-logger/d/monthly)](https://packagist.org/packages/friendsofhyperf/http-logger)| +|[ide-helper](https://github.com/friendsofhyperf/ide-helper)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/ide-helper/v)](https://packagist.org/packages/friendsofhyperf/ide-helper)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/ide-helper/downloads)](https://packagist.org/packages/friendsofhyperf/ide-helper)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/ide-helper/d/monthly)](https://packagist.org/packages/friendsofhyperf/ide-helper)| +|[ipc-broadcaster](https://github.com/friendsofhyperf/ipc-broadcaster)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/ipc-broadcaster/v)](https://packagist.org/packages/friendsofhyperf/ipc-broadcaster)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/ipc-broadcaster/downloads)](https://packagist.org/packages/friendsofhyperf/ipc-broadcaster)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/ipc-broadcaster/d/monthly)](https://packagist.org/packages/friendsofhyperf/ipc-broadcaster)| +|[lock](https://github.com/friendsofhyperf/lock)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/lock/v)](https://packagist.org/packages/friendsofhyperf/lock)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/lock/downloads)](https://packagist.org/packages/friendsofhyperf/lock)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/lock/d/monthly)](https://packagist.org/packages/friendsofhyperf/lock)| +|[macros](https://github.com/friendsofhyperf/macros)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/macros/v)](https://packagist.org/packages/friendsofhyperf/macros)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/macros/downloads)](https://packagist.org/packages/friendsofhyperf/macros)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/macros/d/monthly)](https://packagist.org/packages/friendsofhyperf/macros)| +|[mail](https://github.com/friendsofhyperf/mail)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/mail/v)](https://packagist.org/packages/friendsofhyperf/mail)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/mail/downloads)](https://packagist.org/packages/friendsofhyperf/mail)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/mail/d/monthly)](https://packagist.org/packages/friendsofhyperf/mail)| +|[middleware-plus](https://github.com/friendsofhyperf/middleware-plus)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/middleware-plus/v)](https://packagist.org/packages/friendsofhyperf/middleware-plus)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/middleware-plus/downloads)](https://packagist.org/packages/friendsofhyperf/middleware-plus)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/middleware-plus/d/monthly)](https://packagist.org/packages/friendsofhyperf/middleware-plus)| +|[model-factory](https://github.com/friendsofhyperf/model-factory)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/model-factory/v)](https://packagist.org/packages/friendsofhyperf/model-factory)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/model-factory/downloads)](https://packagist.org/packages/friendsofhyperf/model-factory)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/model-factory/d/monthly)](https://packagist.org/packages/friendsofhyperf/model-factory)| +|[model-hashids](https://github.com/friendsofhyperf/model-hashids)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/model-hashids/v)](https://packagist.org/packages/friendsofhyperf/model-hashids)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/model-hashids/downloads)](https://packagist.org/packages/friendsofhyperf/model-hashids)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/model-hashids/d/monthly)](https://packagist.org/packages/friendsofhyperf/model-hashids)| +|[model-morph-addon](https://github.com/friendsofhyperf/model-morph-addon)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/model-morph-addon/v)](https://packagist.org/packages/friendsofhyperf/model-morph-addon)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/model-morph-addon/downloads)](https://packagist.org/packages/friendsofhyperf/model-morph-addon)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/model-morph-addon/d/monthly)](https://packagist.org/packages/friendsofhyperf/model-morph-addon)| +|[model-observer](https://github.com/friendsofhyperf/model-observer)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/model-observer/v)](https://packagist.org/packages/friendsofhyperf/model-observer)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/model-observer/downloads)](https://packagist.org/packages/friendsofhyperf/model-observer)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/model-observer/d/monthly)](https://packagist.org/packages/friendsofhyperf/model-observer)| +|[model-scope](https://github.com/friendsofhyperf/model-scope)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/model-scope/v)](https://packagist.org/packages/friendsofhyperf/model-scope)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/model-scope/downloads)](https://packagist.org/packages/friendsofhyperf/model-scope)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/model-scope/d/monthly)](https://packagist.org/packages/friendsofhyperf/model-scope)| +|[monolog-hook](https://github.com/friendsofhyperf/monolog-hook)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/monolog-hook/v)](https://packagist.org/packages/friendsofhyperf/monolog-hook)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/monolog-hook/downloads)](https://packagist.org/packages/friendsofhyperf/monolog-hook)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/monolog-hook/d/monthly)](https://packagist.org/packages/friendsofhyperf/monolog-hook)| +|[mysql-grammar-addon](https://github.com/friendsofhyperf/mysql-grammar-addon)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/mysql-grammar-addon/v)](https://packagist.org/packages/friendsofhyperf/mysql-grammar-addon)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/mysql-grammar-addon/downloads)](https://packagist.org/packages/friendsofhyperf/mysql-grammar-addon)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/mysql-grammar-addon/d/monthly)](https://packagist.org/packages/friendsofhyperf/mysql-grammar-addon)| +|[notification](https://github.com/friendsofhyperf/notification)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/notification/v)](https://packagist.org/packages/friendsofhyperf/notification)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/notification/downloads)](https://packagist.org/packages/friendsofhyperf/notification)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/notification/d/monthly)](https://packagist.org/packages/friendsofhyperf/notification)| +|[notification-easysms](https://github.com/friendsofhyperf/notification-easysms)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/notification-easysms/v)](https://packagist.org/packages/friendsofhyperf/notification-easysms)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/notification-easysms/downloads)](https://packagist.org/packages/friendsofhyperf/notification-easysms)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/notification-easysms/d/monthly)](https://packagist.org/packages/friendsofhyperf/notification-easysms)| +|[notification-mail](https://github.com/friendsofhyperf/notification-mail)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/notification-mail/v)](https://packagist.org/packages/friendsofhyperf/notification-mail)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/notification-mail/downloads)](https://packagist.org/packages/friendsofhyperf/notification-mail)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/notification-mail/d/monthly)](https://packagist.org/packages/friendsofhyperf/notification-mail)| +|[openai-client](https://github.com/friendsofhyperf/openai-client)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/openai-client/v)](https://packagist.org/packages/friendsofhyperf/openai-client)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/openai-client/downloads)](https://packagist.org/packages/friendsofhyperf/openai-client)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/openai-client/d/monthly)](https://packagist.org/packages/friendsofhyperf/openai-client)| +|[pest-plugin-hyperf](https://github.com/friendsofhyperf/pest-plugin-hyperf)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/pest-plugin-hyperf/v)](https://packagist.org/packages/friendsofhyperf/pest-plugin-hyperf)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/pest-plugin-hyperf/downloads)](https://packagist.org/packages/friendsofhyperf/pest-plugin-hyperf)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/pest-plugin-hyperf/d/monthly)](https://packagist.org/packages/friendsofhyperf/pest-plugin-hyperf)| +|[pretty-console](https://github.com/friendsofhyperf/pretty-console)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/pretty-console/v)](https://packagist.org/packages/friendsofhyperf/pretty-console)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/pretty-console/downloads)](https://packagist.org/packages/friendsofhyperf/pretty-console)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/pretty-console/d/monthly)](https://packagist.org/packages/friendsofhyperf/pretty-console)| +|[purifier](https://github.com/friendsofhyperf/purifier)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/purifier/v)](https://packagist.org/packages/friendsofhyperf/purifier)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/purifier/downloads)](https://packagist.org/packages/friendsofhyperf/purifier)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/purifier/d/monthly)](https://packagist.org/packages/friendsofhyperf/purifier)| +|[recaptcha](https://github.com/friendsofhyperf/recaptcha)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/recaptcha/v)](https://packagist.org/packages/friendsofhyperf/recaptcha)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/recaptcha/downloads)](https://packagist.org/packages/friendsofhyperf/recaptcha)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/recaptcha/d/monthly)](https://packagist.org/packages/friendsofhyperf/recaptcha)| +|[redis-subscriber](https://github.com/friendsofhyperf/redis-subscriber)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/redis-subscriber/v)](https://packagist.org/packages/friendsofhyperf/redis-subscriber)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/redis-subscriber/downloads)](https://packagist.org/packages/friendsofhyperf/redis-subscriber)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/redis-subscriber/d/monthly)](https://packagist.org/packages/friendsofhyperf/redis-subscriber)| +|[sentry](https://github.com/friendsofhyperf/sentry)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/sentry/v)](https://packagist.org/packages/friendsofhyperf/sentry)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/sentry/downloads)](https://packagist.org/packages/friendsofhyperf/sentry)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/sentry/d/monthly)](https://packagist.org/packages/friendsofhyperf/sentry)| +|[support](https://github.com/friendsofhyperf/support)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/support/v)](https://packagist.org/packages/friendsofhyperf/support)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/support/downloads)](https://packagist.org/packages/friendsofhyperf/support)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/support/d/monthly)](https://packagist.org/packages/friendsofhyperf/support)| +|[tcp-sender](https://github.com/friendsofhyperf/tcp-sender)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/tcp-sender/v)](https://packagist.org/packages/friendsofhyperf/tcp-sender)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/tcp-sender/downloads)](https://packagist.org/packages/friendsofhyperf/tcp-sender)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/tcp-sender/d/monthly)](https://packagist.org/packages/friendsofhyperf/tcp-sender)| +|[telescope](https://github.com/friendsofhyperf/telescope)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/telescope/v)](https://packagist.org/packages/friendsofhyperf/telescope)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/telescope/downloads)](https://packagist.org/packages/friendsofhyperf/telescope)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/telescope/d/monthly)](https://packagist.org/packages/friendsofhyperf/telescope)| +|[telescope-elasticsearch](https://github.com/friendsofhyperf/telescope-elasticsearch)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/telescope-elasticsearch/v)](https://packagist.org/packages/friendsofhyperf/telescope-elasticsearch)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/telescope-elasticsearch/downloads)](https://packagist.org/packages/friendsofhyperf/telescope-elasticsearch)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/telescope-elasticsearch/d/monthly)](https://packagist.org/packages/friendsofhyperf/telescope-elasticsearch)| +|[tinker](https://github.com/friendsofhyperf/tinker)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/tinker/v)](https://packagist.org/packages/friendsofhyperf/tinker)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/tinker/downloads)](https://packagist.org/packages/friendsofhyperf/tinker)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/tinker/d/monthly)](https://packagist.org/packages/friendsofhyperf/tinker)| +|[trigger](https://github.com/friendsofhyperf/trigger)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/trigger/v)](https://packagist.org/packages/friendsofhyperf/trigger)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/trigger/downloads)](https://packagist.org/packages/friendsofhyperf/trigger)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/trigger/d/monthly)](https://packagist.org/packages/friendsofhyperf/trigger)| +|[validated-dto](https://github.com/friendsofhyperf/validated-dto)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/validated-dto/v)](https://packagist.org/packages/friendsofhyperf/validated-dto)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/validated-dto/downloads)](https://packagist.org/packages/friendsofhyperf/validated-dto)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/validated-dto/d/monthly)](https://packagist.org/packages/friendsofhyperf/validated-dto)| +|[web-tinker](https://github.com/friendsofhyperf/web-tinker)|[![Latest Stable Version](https://poser.pugx.org/friendsofhyperf/web-tinker/v)](https://packagist.org/packages/friendsofhyperf/web-tinker)|[![Total Downloads](https://poser.pugx.org/friendsofhyperf/web-tinker/downloads)](https://packagist.org/packages/friendsofhyperf/web-tinker)|[![Monthly Downloads](https://poser.pugx.org/friendsofhyperf/web-tinker/d/monthly)](https://packagist.org/packages/friendsofhyperf/web-tinker)| + diff --git a/docs/zh-tw/index.md b/docs/zh-tw/index.md new file mode 100644 index 000000000..c0c2951c2 --- /dev/null +++ b/docs/zh-tw/index.md @@ -0,0 +1,52 @@ +--- +# https://vitepress.dev/reference/default-theme-home-page +layout: home + +hero: + name: "FriendsOfHyperf" + text: "最受歡迎的 Hyperf 元件" + tagline: "讓 Hyperf 像 Laravel 一樣簡單且強大 🚀" + actions: + # - theme: brand + # text: 入門 + # link: /zh-tw/guide/ + - theme: brand + text: 元件 + link: /zh-tw/components/ + - theme: alt + text: GitHub + link: "https://github.com/friendsofhyperf/components" + +features: + - title: Sentry + details: 這是 Sentry 的 Hyperf SDK。 + link: /zh-tw/components/sentry/ + - title: Telecope + details: Telescope 是 Hyperf 框架的優雅偵錯助手。 + link: /zh-tw/components/telescope/ + - title: Tinker + details: Tinker 是 Hyperf 框架的強大 REPL。 + link: /zh-tw/components/tinker/ + - title: Web Tinker + details: 在瀏覽器中使用 Tinker。 + link: /zh-tw/components/web-tinker/ + - title: Encryption + details: Encryption 提供了一個簡單、方便的加密和解密功能。 + link: /zh-tw/components/encryption/ + - title: Cache + details: Cache 提供了一個富有表現力的統一快取 API。 + link: /zh-tw/components/cache/ + - title: HttpClient + details: HttpClient 提供了一個簡單、方便的 HTTP 客戶端。 + link: /zh-tw/components/http-client/ + - title: Validated DTO + details: Validated DTO 提供了一個簡單、方便的資料驗證功能。 + link: /zh-tw/components/validated-dto/ + - title: Lock + details: Lock 提供了一個簡單、方便的分散式鎖功能。 + link: /zh-tw/components/lock/ + - title: More + details: 更多元件… + link: /zh-tw/components/ +--- +