{"id":22400422,"url":"https://github.com/bearsunday/bear.resource","last_synced_at":"2026-02-03T01:25:41.609Z","repository":{"id":405379,"uuid":"2679152","full_name":"bearsunday/BEAR.Resource","owner":"bearsunday","description":"A hypermedia framework for an object as a service","archived":false,"fork":false,"pushed_at":"2025-03-24T11:26:49.000Z","size":3116,"stargazers_count":45,"open_issues_count":2,"forks_count":19,"subscribers_count":8,"default_branch":"1.x","last_synced_at":"2025-04-12T19:48:26.112Z","etag":null,"topics":["api","bearsunday","hypermedia","rest"],"latest_commit_sha":null,"homepage":"https://packagist.org/packages/bear/resource","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bearsunday.png","metadata":{"files":{"readme":"README.ja.md","changelog":null,"contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":".github/SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2011-10-31T07:14:20.000Z","updated_at":"2025-02-26T06:17:01.000Z","dependencies_parsed_at":"2023-12-19T04:46:39.711Z","dependency_job_id":"e190e3e1-ff27-4596-8eb8-bb523ad68706","html_url":"https://github.com/bearsunday/BEAR.Resource","commit_stats":{"total_commits":1916,"total_committers":18,"mean_commits":"106.44444444444444","dds":0.02609603340292277,"last_synced_commit":"d488d94e047f42a3d8791532a5bca120789101e6"},"previous_names":[],"tags_count":135,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bearsunday%2FBEAR.Resource","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bearsunday%2FBEAR.Resource/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bearsunday%2FBEAR.Resource/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bearsunday%2FBEAR.Resource/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bearsunday","download_url":"https://codeload.github.com/bearsunday/BEAR.Resource/tar.gz/refs/heads/1.x","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248625500,"owners_count":21135513,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","bearsunday","hypermedia","rest"],"created_at":"2024-12-05T08:12:55.424Z","updated_at":"2026-02-03T01:25:41.575Z","avatar_url":"https://github.com/bearsunday.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# BEAR.Resource\n\n## Hypermedia framework for object as a service\n\n[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/bearsunday/BEAR.Resource/badges/quality-score.png?b=1.x)](https://scrutinizer-ci.com/g/bearsunday/BEAR.Resource/?branch=1.x)\n[![codecov](https://codecov.io/gh/bearsunday/BEAR.Resource/branch/1.x/graph/badge.svg?token=eh3c9AF4Mr)](undefined)\n[![Build Status](https://travis-ci.org/bearsunday/BEAR.Resource.svg?branch=1.x)](https://travis-ci.org/bearsunday/BEAR.Resource)\n[![Build Status](https://scrutinizer-ci.com/g/bearsunday/BEAR.Resource/badges/build.png?b=1.x)](https://scrutinizer-ci.com/g/bearsunday/BEAR.Resource/build-status/1.x)\n\n**BEAR.Resource** はオブジェクトがリソースの振る舞いを持つHypermediaフレームワークです。\nクライアントーサーバー、統一インターフェイス、ステートレス、相互接続したリソース表現、レイヤードコンポーネント等の\nRESTのWebサービスの特徴をオブジェクトに持たせます。\n\n既存のドメインモデルやアプリケーションの持つ情報を柔軟で長期運用を可能にするために、 アプリケーションをRESTセントリックなものにしAPI駆動開発を可能にします。\n\n### リソースオブジェクト\n\nリソースとして振る舞うオブジェクトがリソースオブジェクトです。\n\n * １つのURIのリソースが1クラスにマップされ、リソースクライアントを使ってリクエストします。\n * 統一されたリソースリクエストに対応したメソッドを持ち名前付き引き数でリクエストします。\n * メソッドはリクエストに応じてリソース状態を変更して自身`$this`を返します。\n\n```php\n\u003c?php\nnamespace MyVendor\\Sandbox\\Blog;\n\nclass Author extends ResourceObject\n{\n    public $code = 200;\n\n    public $headers = [\n        'Content-Type' =\u003e 'application/json'\n    ];\n\n    public $body = [\n        'id' =\u003e1,\n        'name' =\u003e 'koriym'\n    ];\n\n    /**\n     * @Link(rel=\"blog\", href=\"app://self/blog/post?author_id={id}\")\n     */\n    public function onGet(int $id) : ResourceObject\n    {\n        return $this;\n    }\n\n    public function onPost(string $name) : ResourceObject\n    {\n        $this-\u003ecode = 201; // created\n        // ...\n        return $this;\n    }\n\n    public function onPut(int $id, string $name) : ResourceObject\n    {\n        $this-\u003ecode = 203; // no content\n        //...\n        return $this;\n    }\n\n    public function onDelete($id) : ResourceObject\n    {\n        $this-\u003ecode = 203; // no content\n        //...\n        return $this;\n    }\n}\n```\n\n### インスタンスの取得\n\nディペンデンシーインジェクターを使ってクライアントインスタンスを取得します。\n\n```php\nuse BEAR\\Resource\\ResourceInterface;\n\n$resource = (new Injector(new ResourceModule('FakeVendor/Sandbox')))-\u003egetInstance(ResourceInterface::class);\n```\n\n### リソースリクエスト\n\nURIとクエリーを使ってリソースをリクエストします。\n\n```php\n$user = $resource-\u003eget('app://self/user', ['id' =\u003e 1]);\n```\n\n * このリクエストは[PSR0](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md)に準拠した **MyVendor\\Sandbox\\Resource\\App\\User** クラスの **onGet($id)** メソッドに1を渡します。\n * 得られたリソースは **code**, **headers** それに **body**の３つのプロパティを持ちます。\n\n```php\nvar_dump($user-\u003ebody);\n```\n\n```php\nArray\n(\n [name] =\u003e Athos\n [age] =\u003e 15\n [blog_id] =\u003e 0\n)\n```\n\nリソースリクエストには２種類の方法があります。即時実行(eager request)と遅延実行(lazy request)です。\n\n### 即時実行\n\n即時実行は通常のPHPのメソッドと同様にすぐに実行して結果を求めます。いくつかの書き方があります。\n\n```php\n$user = $resource\n  -\u003eget\n  -\u003euri('app://self/user')\n  -\u003ewithQuery(['id' =\u003e 1])\n  -\u003eeager\n  -\u003erequest();\n```\n\n```php\n$user = $resource-\u003eget-\u003euri('app://self/user')(['id' =\u003e 1]);\n```\n\n```php\n$user = $resource-\u003euri('app://self/user')(['id' =\u003e 1]); // 'get' request method can be omitted\n```\n\n### 遅延実行\n \n[遅延評価](https://ja.wikipedia.org/wiki/%E9%81%85%E5%BB%B6%E8%A9%95%E4%BE%A1)として Request to resource until the point at which it is needed. One common use case is that assign to template `the resource request object` not the instance.\n\n```php\n// get `ResourceRequest` objcet \n$user = $resource-\u003eget-\u003euri('app://self/user')-\u003ewithQuery(['id' =\u003e 1]);\n// assign to the template\necho \"User resource body is {$user}\"; // same in the template enigne template\n```\n\nIt is callable object, you can invoke with other parameters;\n\n```php\n// invoke\n$user1 = $user(); // $id = 1\n$user2 = $user(['id' =\u003e 2);\n```\n\n## ハイパーメディア\n\nリソースは関連するリソースの [ハイパーリンク](http://en.wikipedia.org/wiki/Hyperlink)を持つ事ができます\n**@Link**アノテーションをメソッドにアノテートしてハイパーリンクを表します。\n\n```php\n\nuse BEAR\\Resource\\Annotation\\Link;\n\n/**\n * @Link(rel=\"blog\", href=\"app://self/blog?author_id={id}\")\n */\n```\n\n**rel** でリレーション名を **href** (hyper reference)でリンク先URIを指定します。\nURIは [URIテンプレート](http://code.google.com/p/uri-templates/)([rfc6570](http://tools.ietf.org/html/rfc6570))を用いて現在のリソースの値をアサインすることができます。\n\nリンクには **self**, **new**, **crawl** といくつか種類があり効果的にリソースグラフを作成することができます。\n\n### selfリンク\n\n`linkSelf`はリンク先のリソースを取得します。\n\n```php\n$blog = $resource\n    -\u003eget\n    -\u003euri('app://self/user')\n    -\u003ewithQuery(['id' =\u003e 0])\n    -\u003elinkSelf('blog')\n    -\u003eeager\n    -\u003erequest();\n```\n**app://self/user** リソースをリクエストした結果で **blog** リンクを辿り **app://self/blog**リソースを取得します。\nWebページでリンクをクリックしたように次のリソースに入れ替わります。\n\n### newリンク\n\n`linkNew` はリンク先のリソースも追加取得します。\n\n```php\n$user = $resource\n    -\u003eget\n    -\u003euri('app://self/user')\n    -\u003ewithQuery(['id' =\u003e 0])\n    -\u003elinkNew('blog')\n    -\u003eeager\n    -\u003erequest();\n    \n$blog = $user['blog'];\n```\n\nWebページで「新しいウインドウでリンクを表示」を行うように現在のリソースは保持したまま次のリソースを取得します。\n\n### クロール\n\nクロールはリスト（配列）になっているリソースを順番にリンクを辿り、複雑なリソースグラフを構成することができます。\nクローラーがwebページをクロールするように、リソースクライアントはハイパーリンクをクロールしソースグラフを生成します。\n\nauthor, post, meta, tag, tag/name がそれぞれ関連づけられてあるリソースグラフを考えてみます。\nそれぞれのリソースはハイパーリンクを持ちます。\nこのリソースグラフに **post-tree** という名前を付け、それぞれのリソースの@Linkアノテーションでハイパーリファレンス **href** を指定します。\n\nauthorリソースにはpostリソースへのハイパーリンクがあります。1:nの関係です。\n```php\n/**\n * @Link(crawl=\"post-tree\", rel=\"post\", href=\"app://self/post?author_id={id}\")\n */\npublic function onGet($id = null)\n```\n\npostリソースにはmetaリソースとtagリソースのハイパーリンクがあります。1:nの関係です。\n```php\n/**\n * @Link(crawl=\"post-tree\", rel=\"meta\", href=\"app://self/meta?post_id={id}\")\n * @Link(crawl=\"post-tree\", rel=\"tag\",  href=\"app://self/tag?post_id={id}\")\n */\npublic function onGet($author_id)\n{\n```\n\ntagリソースはIDだけでそのIDに対応するtag/nameリソースへのハイパーリンクがあります。1:1の関係です。\n\n```php\n/**\n * @Link(crawl=\"post-tree\", rel=\"tag_name\",  href=\"app://self/tag/name?tag_id={tag_id}\")\n */\npublic function onGet($post_id)\n```\n\nクロール名を指定してリクエストします。\n\n```php\n$graph = $resource\n  -\u003eget\n  -\u003euri('app://self/marshal/author')\n  -\u003elinkCrawl('post-tree')\n  -\u003eeager\n  -\u003erequest();\n```\n\nリソースクライアントは@Linkアノテーションに指定されたクロール名を発見するとその **rel** 名でリソースを接続してリソースグラフを作成します。\n\n```\nvar_export($graph-\u003ebody);\n\narray (\n    0 =\u003e\n    array (\n        'name' =\u003e 'Athos',\n        'post' =\u003e\n        array (\n            0 =\u003e\n            array (\n                'author_id' =\u003e '1',\n                'body' =\u003e 'Anna post #1',\n                'meta' =\u003e\n                array (\n                    0 =\u003e\n                    array (\n                        'data' =\u003e 'meta 1',\n                    ),\n                ),\n                'tag' =\u003e\n                array (\n                    0 =\u003e\n                    array (\n                        'tag_name' =\u003e\n                        array (\n                            0 =\u003e\n                            array (\n                                'name' =\u003e 'zim',\n                            ),\n                        ),\n                    ),\n ...\n```\n\n### HATEOAS アプリケーション状態のエンジンとしてのハイパーメディア\n\nリソースはクライアントの次の動作をハイパーリンクにして、クライアントはそのリンクを辿りアプリケーションの状態を変更します。\n例えば注文リソースに **POST** して注文を作成、その注文の状態から支払リソースに **PUT**して支払を行います。\n\nOrder リソース\n```php\n/**\n * @Link(rel=\"payment\", href=\"app://self/payment{?order_id, credit_card_number, expires, name, amount}\", method=\"put\")\n */\npublic function onPost($drink)\n```\n\nクライアントコード\n```php\n    $order = $resource\n        -\u003epost\n        -\u003euri('app://self/order')\n        -\u003ewithQuery(['drink' =\u003e 'latte'])\n        -\u003eeager\n        -\u003erequest();\n\n    $payment = [\n        'credit_card_number' =\u003e '123456789',\n        'expires' =\u003e '07/07',\n        'name' =\u003e 'Koriym',\n        'amount' =\u003e '4.00'\n    ];\n\n    // then use hyper link to pay\n    $response = $resource-\u003ehref('payment', $payment);\n    \n    echo $response-\u003ecode; // 201\n```\n\n支払の方法は注文リソースがハイパーリンクと提供しています。\n支払と注文の関係が変更されてもクライアントコードに変更はありません。\nHATEOAS について詳しくは[How to GET a Cup of Coffee](http://www.infoq.com/articles/webber-rest-workflow)をご覧ください。\n\n### リソース表現\n\nリソースはそれぞれ表現のためのリソースレンダラーを持っています。\n文字列評価されるとリソースはインジェクトされたリソースレンダラーを使ってリソース表現になります。\n\n```php\necho $user;\n\n// {\n//     \"name\": \"Aramis\",\n//     \"age\": 16,\n//     \"blog_id\": 1\n// }\n```\nこのときの`$user`はレンダラーが内蔵された`ResourceObject`リソースオブジェクトです。\n配列やオブジェクトとしても取り扱うことができます。\n\n```php\n\necho $user['name'];\n\n// Aramis\n\necho $user-\u003eonGet(2);\n\n// {\n//     \"name\": \"Yumi\",\n//     \"age\": 15,\n//     \"blog_id\": 2\n// }\n```\n\n### 遅延評価\n\n```php\n$user = $resource\n  -\u003eget\n  -\u003euri('app://self/user')\n  -\u003ewithQuery(['id' =\u003e 1])\n  -\u003erequest();\n\n$templateEngine-\u003eassign('user', $user);\n```\n\n`eager`のない`request()`ではリソースリクエストの結果ではなく、リクエストオブジェクトが取得できます。\nテンプレートエンジンにアサインするとテンプレートにリソースリクエスト`{$user}`が現れたタイミングで`リソースリクエスト`と`リソースレンダリング`を行い文字列表現になります。\nリソース表現はAPI用の他にも、テンプレートエンジンを用いてHTMLにする事もできます。\n\n## 埋め込みリソース\n\n`@Embed`アノテーションを使って他のリソースを自身のリソースに埋め込む事が出来ます。`HTML`の`\u003cimg src=\"image_url\"\u003e`や`\u003ciframe src=\"content_url\"\u003e`と同じ様に`src`で埋め込むリソースを指定します。\n\n```php\nclass News extends ResourceObject\n{\n    /**\n     * @Embed(rel=\"weather\",src=\"app://self/weather/today\")\n     */\n    public function onGet()\n    {\n        $this['headline'] = \"...\";\n        $this['sports'] = \"...\";\n        \n        return $this;\n    }\n}\n```\n\nこのNewsリソースでは`headline`と`sports`と同様に`weather`というリソースのリクエストを埋め込みます。\n\n### HAL (Hypertext Application Language)\n\nHAL Moduleを使うとリソース表現が[HAL](http://stateless.co/hal_specification.html)になります。リソースに埋め込まれたリクエストはHALでも埋め込みリソースとして評価されます。\n\n```php\n    // create resource client with HalModule\n    $resource = Injector::create([new ResourceModule('MyVendor\\MyApp'), new HalModule])-\u003egetInstance('BEAR\\Resource\\ResourceInterface');\n    // request\n    $news = $resource\n        -\u003eget\n        -\u003euri('app://self/news')\n        -\u003ewithQuery(['date' =\u003e 'today'])\n        -\u003erequest();\n    // output\n    echo $news . PHP_EOL;\n\n```\n\n結果\n\n```javascript\n{\n    \"headline\": \"40th anniversary of Rubik's Cube invention.\",\n    \"sports\": \"Pieter Weening wins Giro d'Italia.\",\n    \"_links\": {\n        \"self\": {\n            \"href\": \"/api/news?date=today\"\n        }\n    },\n    \"_embedded\": {\n        \"weather\": [\n            {\n                \"today\": \"the weather of today is sunny\",\n                \"_links\": {\n                    \"self\": {\n                        \"href\": \"/api/weather?date=today\"\n                    },\n                    \"tomorrow\": {\n                        \"href\": \"/api/weather/tomorrow\"\n                    }\n                }\n            }\n        ]\n    }\n}\n\n```\n## リソース表現\n\n`ResourceObject` を文字列評価するとリソース表現（リプレゼンテーション）が取得できます。\n\n```php\n$userView = (string) $resource-\u003eget('app://self/user?id=1');\necho $userView; // get JSON\n```\n\nレンダラーを変えて、リソースを他のメディアタイプで表現する事ができます。通常はDIでレンダラーを依存として注入します。\n\n```php\nclass User extends ResourceObject\n{\n    public function __construct()\n    {\n        $this-\u003esetRenderer(new class implements RenderInterface{\n            public function render(ResourceObject $ro)\n            {\n                $ro-\u003eheaders['content-type'] = 'application/json';\n                $ro-\u003eview = json_encode($ro-\u003ebody);\n\n                return $ro-\u003eview;\n            }\n        });\n    }\n}\n```\n\n## 転送\n\nREST は representational state \"transfer\" の略です。 `ResourceObject`の`transfer()` メソッドでリソースをクライアントに出力します。\n\n```php\n$user = $resource-\u003eget('app://self/user?id=1');\n$user-\u003etransfer(new class implements TransferInterface {\n\tpublic function __invoke(ResourceObject $ro, array $server)\n\t{\n\t    foreach ($ro-\u003eheaders as $label =\u003e $value) {\n\t        header(\"{$label}: {$value}\", false);\n\t    }\n\t    http_response_code($ro-\u003ecode);\n\t    echo $ro-\u003eview;\n\t}\n);\n```\n\n## インストール\n\n```javascript\ncomposer require bear/resource ^1.10\n```\n\n## A Resource Oriented Framework\n\n__BEAR.Sunday__ はリソース指向のフレームワークです。BEAR.Resourceに Webでの振る舞いやアプリケーションスタックの機能を、\nGoogle GuiceスタイルのDI/AOPシステムの[Ray](https://github.com/koriym/Ray.Di)で追加してフルスタックのWebアプリケーションフレームワークとして機能します。\n[BEAR.Sunday GitHub](https://github.com/koriym/BEAR.Sunday)をご覧下さい。\n\n## See Also\n\n * [BEAR.QueryRepository](https://github.com/bearsunday/BEAR.QueryRepository) - 読み込みと書き込みのレポジトリを分離します。\n * [Ray.WebParamModule](https://github.com/ray-di/Ray.WebParamModule) - Webコンテキストをパラメーターにバインドします。\n \n## Testing BEAR.Resource\n\n以下はインストールしてテスト実行するための手順です。\n\n```\ncomposer create-project bear/resource BEAR.Resource\ncd BEAR.Resource\n./vendor/bin/phpunit\nphp demo/run.php\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbearsunday%2Fbear.resource","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbearsunday%2Fbear.resource","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbearsunday%2Fbear.resource/lists"}