-
Notifications
You must be signed in to change notification settings - Fork 0
Harden FakeQuery release baseline #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
koriym
wants to merge
11
commits into
1.x
Choose a base branch
from
codex/fake-query-release-hardening
base: 1.x
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
cff4250
Harden FakeQuery release baseline
koriym c1bab11
Support select result wrappers
koriym 779391a
Ensure FakeQuery overrides MediaQuery
koriym 5d77462
Replace MediaQuery interceptor binding
koriym ea50646
Harden fake query factory handling
koriym 77d6002
Document FakeQuery 1.0 design
koriym 500d29d
Fix fake directory validation paths
koriym c94ab02
Normalize fake directory separators
koriym 7639a89
Fix CI workflow trailing blank line
koriym eaf8d88
Support multiple fake dirs and query sources
koriym 57aa1fc
Aura Sql 6でPostQueryContextを生成
koriym File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| name: CI | ||
|
|
||
| on: | ||
| pull_request: | ||
| push: | ||
| branches: | ||
| - 1.x | ||
| - '*.x' | ||
| - 'codex/**' | ||
|
|
||
| jobs: | ||
| unit: | ||
| name: Unit PHP ${{ matrix.php }} | ||
| runs-on: ubuntu-latest | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| php: | ||
| - '8.2' | ||
| - '8.3' | ||
| - '8.4' | ||
| - '8.5' | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - uses: shivammathur/setup-php@v2 | ||
| with: | ||
| php-version: ${{ matrix.php }} | ||
| extensions: mbstring, pdo, dom, xml, xmlwriter | ||
| tools: composer:v2 | ||
| coverage: none | ||
|
|
||
| - name: Install dependencies | ||
| run: composer install --no-interaction --prefer-dist --no-scripts | ||
|
|
||
| - name: Run PHPUnit | ||
| run: vendor/bin/phpunit | ||
|
|
||
| quality: | ||
| name: Quality | ||
| runs-on: ubuntu-latest | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - uses: shivammathur/setup-php@v2 | ||
| with: | ||
| php-version: '8.5' | ||
| extensions: mbstring, pdo, dom, xml, xmlwriter | ||
| tools: composer:v2 | ||
| coverage: none | ||
|
|
||
| - name: Install dependencies and tools | ||
| run: composer install --no-interaction --prefer-dist | ||
|
|
||
| - name: Run style, static analysis, and tests | ||
| run: composer tests | ||
|
|
||
| - name: Check declared dependencies | ||
| run: composer crc |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,7 @@ | |
| /build/ | ||
| /tests/tmp/ | ||
| .phpcs-cache | ||
| .phpunit.cache/ | ||
| .phpunit.result.cache | ||
| phpstan-baseline.neon | ||
| psalm-baseline.xml | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,147 +1,66 @@ | ||
| # Findings — Ray.FakeQuery 実装調査 | ||
|
|
||
| ## Ray.MediaQuery ソースから判明した事実 | ||
|
|
||
| ### DbQuery アトリビュート | ||
| ```php | ||
| // Ray\MediaQuery\Annotation\DbQuery | ||
| #[Attribute(Attribute::TARGET_METHOD)] | ||
| final class DbQuery { | ||
| public function __construct( | ||
| public string $id, | ||
| public string $type = 'row_list', // 'row' | 'row_list' | ||
| public string $factory = '', | ||
| ) {} | ||
| } | ||
| ``` | ||
| - `$id` でクエリIDを取得 | ||
| - `$type` で single/list を明示できる(デフォルト row_list) | ||
|
|
||
| ### Ray.Aop getAnnotation API | ||
| `DbQueryInterceptor` では: | ||
| ```php | ||
| $dbQuery = $method->getAnnotation(DbQuery::class); // Ray\Aop\ReflectionMethod API | ||
| ``` | ||
| `getAttributes()` ではなく `getAnnotation()` を使う(Ray.Aop固有メソッド)。 | ||
|
|
||
| ### Interface スキャン(Queries::fromDir) | ||
| ```php | ||
| // Queries::fromDir($dir) → ClassesInDirectories::list($dir) | ||
| // - PHPファイルをトークン解析してclass/interfaceを取得 | ||
| // - class_exists() / interface_exists() でオートロード確認 | ||
| // - Generator で class-string を yield | ||
| ``` | ||
| `ray/media-query` の `Queries::fromDir()` をそのまま使える。 | ||
|
|
||
| ### Interface バインディングパターン | ||
| ```php | ||
| // MediaQueryBaseModule::configure() | ||
| foreach ($this->queries->classes as $class) { | ||
| $this->bind($class)->toNull(); // Null object にバインド | ||
| } | ||
| // → InterceptorがNull objectのメソッド呼び出しをインターセプト | ||
| ``` | ||
|
|
||
| ### Interceptor バインディング | ||
| ```php | ||
| // MediaQueryDbModule::configure() | ||
| $this->bindInterceptor( | ||
| $this->matcher->any(), | ||
| $this->matcher->annotatedWith(DbQuery::class), | ||
| [DbQueryInterceptor::class], | ||
| ); | ||
| ``` | ||
| `any()` クラス × `#[DbQuery]` メソッド にインターセプトをバインド。 | ||
|
|
||
| ### ReturnEntity(PHPDoc解析) | ||
| ```php | ||
| // ReturnEntityInterface::__invoke(ReflectionMethod) → ?class-string | ||
| // - 戻り値型がクラスなら そのFQCN を返す | ||
| // - array<Entity> (PHPDoc) なら Entity のFQCN を返す | ||
| // - void / null / raw array → null を返す | ||
| // phpdocumentor/reflection-docblock に依存(ray/media-queryが持つ) | ||
| ``` | ||
| `ReturnEntityInterface` は public → そのままバインドして再利用できる。 | ||
|
|
||
| ### row/row_list 判定ロジック(DbQueryInterceptor より) | ||
| ```php | ||
| $isRow = $dbQuery->type === 'row' | ||
| || $returnType instanceof ReflectionUnionType | ||
| || ($returnType instanceof ReflectionNamedType && $returnType->getName() !== 'array'); | ||
| ``` | ||
| FakeQuery でも同じロジックを採用する。 | ||
|
|
||
| ### エンティティ種別と PDO fetch との対応 | ||
|
|
||
| | 条件 | PDO fetch | FakeQuery hydration | | ||
| |------|-----------|---------------------| | ||
| | entity = null(raw array) | FETCH_ASSOC | JSON data をそのまま返す | | ||
| | entity あり、__construct なし | FETCH_CLASS(プロパティ直設定) | `new $entity()` → public property を set | | ||
| | entity あり、__construct あり | FETCH_FUNC(位置引数) | Reflection で param名→JSON値 マッピング → `new $entity(...)` | | ||
|
|
||
| ### snake_case → camelCase 変換 | ||
| ```php | ||
| // Ray\MediaQuery\StringCase::camel('todo_id') → 'todoId' | ||
| // ray/media-query に含まれるため再利用可能 | ||
| ``` | ||
|
|
||
| ### void メソッド(Command)の確認 | ||
| ```php | ||
| // TodoAddInterface | ||
| #[DbQuery('todo_add')] | ||
| public function __invoke(string $id, string $title): void; | ||
| // → JSON ファイル不要、no-op | ||
| ``` | ||
|
|
||
| ## 依存関係 | ||
|
|
||
| ### 現在の composer.json(Ray.FakeQuery) | ||
| ```json | ||
| "require": { "php": "^8.1" } | ||
| ``` | ||
| **追加が必要:** | ||
| - `ray/di: ^2.18`(AbstractModule, bindInterceptor等) | ||
| - `ray/media-query: ^1.0`(Queries, ReturnEntity, DbQuery, StringCase等) | ||
|
|
||
| transitively 取得できるもの: | ||
| - `ray/aop`(MethodInterceptor, MethodInvocation) | ||
| - `phpdocumentor/reflection-docblock`(ReturnEntity内部で使用) | ||
|
|
||
| ### Ray.MediaQuery の PHP 要件 | ||
| `ray/media-query` は `php: ^8.2` を要求している。 | ||
| → Ray.FakeQuery の `require.php` も `^8.2` に上げる必要あり。 | ||
|
|
||
| ## テスト用 Fake データ構造 | ||
|
|
||
| DESIGN.md のテスト例: | ||
| ``` | ||
| tests/ | ||
| ├── FakeQueryModuleTest.php | ||
| └── Fake/ | ||
| ├── Interface/ ← interfaceDir として渡す | ||
| │ └── TodoQueryInterface.php | ||
| ├── Entity/ | ||
| │ └── TodoEntity.php | ||
| ├── todo_item.json ← fakeDir として渡す | ||
| └── todo_list.json | ||
| ``` | ||
|
|
||
| ## 未解決事項(設計判断が必要) | ||
|
|
||
| ### Q1: エンティティ constructor 引数マッピング | ||
| `FetchNewInstance` は PDO の列順に `new $entity(...$args)` を呼ぶ。 | ||
| JSON は名前付きキーなので、**constructor パラメータ名でマッピング**が必要。 | ||
| - snake_case JSON キー → camelCase → コンストラクタパラメータ名 でマッピング? | ||
|
|
||
| ### Q2: DbQuery::type の扱い | ||
| `$type = 'row'` の場合に single fetch を強制するか? | ||
| → MediaQuery と同じロジックで yes(`$type === 'row'` → row) | ||
|
|
||
| ### Q3: nullable の JSON ファイルが存在しない場合 | ||
| `?Entity` 戻り値型で JSON ファイルが存在しない場合: | ||
| - DESIGN.md: 「Missing files: throw FakeJsonNotFoundException」 | ||
| - ただし nullable なら null を返すべきか? or 明示的に `null` を JSON に書く? | ||
|
|
||
| ### Q4: `array<Entity>` で要素が Entity かどうかのチェック | ||
| `ReturnEntity` が null を返す(PHPDoc なし or raw array)かつ戻り型が `array` なら raw array として扱う。 | ||
| これは MediaQuery と同じ動作で OK? | ||
| # Findings — Ray.FakeQuery Release Hardening | ||
|
|
||
| ## Current Package State | ||
| - Repository: `/Users/akihito/git/Ray.FakeQuery` | ||
| - Branch at start: `1.x` | ||
| - Baseline commit: `e9bf11042a41c03d879319b2c94b7d4ccdb1a8db` | ||
| - No release tags were present in the remote tag listing. | ||
| - Packagist/composer metadata exposes `1.x-dev` and no stable tag. | ||
|
|
||
| ## Current Implementation | ||
| - `FakeQueryModule` scans query interfaces with `Ray\MediaQuery\Queries::fromDir()`. | ||
| - Query interfaces are bound to Ray.Di null objects and intercepted on | ||
| `#[DbQuery]` methods. | ||
| - `FakeQueryInterceptor` maps: | ||
| - row return paths to `<query_id>.json` | ||
| - row-list return paths to `<query_id>.jsonl` | ||
| - `void` methods to no-op. | ||
| - `JsonHydrator` supports: | ||
| - raw arrays when no entity type is resolved, | ||
| - public-property entities, | ||
| - constructor entities with camelCase / snake_case key matching. | ||
| - Current tests cover basic row, row-list, explicit `type: 'row'`, constructor | ||
| hydration, void no-op, unknown fixture files, invalid fake dir, and nullable | ||
| missing-file behavior. | ||
|
|
||
| ## Baseline Commands | ||
| - `composer tests` passed on PHP 8.5. | ||
| - `composer coverage` passed with line coverage 96.55%. | ||
| - `composer crc` failed because currently used symbols from `phpdocumentor` and | ||
| `ray/aop` are not declared as direct dependencies. | ||
|
|
||
| ## Documentation Drift | ||
| - `.planning` was older than the implementation. | ||
| - The old plan expected row-list `.json`; implementation and README now use | ||
| `.jsonl`. | ||
| - JSONL is the right direction for row-list fixtures because diffs are stable and | ||
| each line is a canonical row example. | ||
| - The old plan expected missing nullable fixture files to throw, while current | ||
| tests return `null` for nullable methods. For release compatibility and | ||
| Ray.MediaQuery "no row" parity, the current default remains `null`; stricter | ||
| fixture-completeness validation can be added separately. | ||
|
|
||
| ## BEAR.AppKata / MyVendor.Cms Release Criteria | ||
| - Fake fixtures must be reusable as shared domain vocabulary, not just local | ||
| mocks. | ||
| - Select-side BDR result support matters: | ||
| - custom `PostQueryInterface` wrappers for SELECT row lists | ||
| - typed selection result objects such as `ArticleSelection` | ||
| - Pager support matters for `PagesInterface` / `#[Pager]`. | ||
| - `#[DbQuery(factory: ...)]` matters because MyVendor.Cms uses factories for | ||
| Article entity hydration. | ||
| - Nested query IDs matter because BEAR.AppKata uses ids such as | ||
| `admins/admin_selection_list`; fake file validation must recurse and preserve | ||
| query id paths. | ||
| - Parameter-aware resolver support is likely a post-1.0 extension unless the | ||
| first stable release aims to replace MyVendor.Cms' stateful `FakeSqlQuery`. | ||
|
|
||
| ## Open Technical Questions | ||
| - Whether `AffectedRows` and `InsertedRow` belong in Ray.FakeQuery 1.0. If they | ||
| do, they should be built directly from metadata fixtures, not by creating fake | ||
| PDO objects. | ||
| - Exact fake fixture shape for `PagesInterface`. | ||
| - Whether custom SELECT wrappers should remain constructor-based only or support | ||
| `fromContext()` through a public Ray.MediaQuery helper in the future. | ||
| - Whether 1.0.0 must include a stateful resolver layer for write/read round trips | ||
| or whether static fixtures plus BDR metadata fixtures are sufficient. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,29 +1,76 @@ | ||
| # Progress — Ray.FakeQuery 実装 | ||
| # Progress — Ray.FakeQuery Release Hardening | ||
|
|
||
| ## セッション: 2026-03-02 | ||
| ## Session: 2026-05-15 | ||
|
|
||
| ### 完了 | ||
| - [x] Ray.MediaQuery ソース調査 | ||
| - DbQuery アトリビュート構造(id, type, factory) | ||
| - Interceptor バインディングパターン(any() × annotatedWith(DbQuery)) | ||
| - Interface スキャン(Queries::fromDir → ClassesInDirectories) | ||
| - ReturnEntity(phpdocumentor による PHPDoc 解析) | ||
| - StringCase::camel(snake → camel 変換) | ||
| - エンティティ種別判定(constructor あり / なし) | ||
| - row/row_list 判定ロジック | ||
| ### Completed | ||
| - Reviewed Ray.FakeQuery public README, composer metadata, source, tests, and | ||
| `.planning` files. | ||
| - Verified the current implementation uses `.json` for row and `.jsonl` for | ||
| row-list fixtures. | ||
| - Verified `composer tests` passes locally on PHP 8.5. | ||
| - Verified `composer coverage` passes locally with 96.55% line coverage. | ||
| - Verified `composer crc` currently fails on undeclared direct dependencies. | ||
| - Created branch `codex/fake-query-release-hardening`. | ||
| - Replaced obsolete `.planning` implementation notes with a release-hardening | ||
| plan driven by BEAR.AppKata / MyVendor.Cms use cases. | ||
| - Spawned sub-agents for: | ||
| - Ray.MediaQuery 1.1 / BDR feature gap analysis. | ||
| - composer / CI / release hygiene review. | ||
| - BEAR.AppKata and MyVendor.Cms acceptance criteria extraction. | ||
| - Received sub-agent findings and incorporated the high-priority criteria: | ||
| nested query ids, factory hydration, select-side result wrappers, | ||
| pager support, and parameter-aware resolver as an extension point. | ||
| - Fixed the first release-hygiene blocker: | ||
| - direct `ray/aop` and `phpdocumentor/reflection-docblock` dependencies, | ||
| - `ray/media-query:^1.1` baseline, | ||
| - package metadata, | ||
| - GitHub Actions workflow, | ||
| - `composer crc` passing. | ||
| - Added support and tests for: | ||
| - static `#[DbQuery(factory: ...)]` hydration, | ||
| - injected factory hydration, | ||
| - factory row-list hydration, | ||
| - nested query id fixture loading, | ||
| - recursive unknown fixture validation, | ||
| - constructor-based SELECT result wrappers. | ||
| - Removed the fake PDO direction from the current scope. DML metadata results | ||
| can be added later as direct metadata fixture handling if needed. | ||
|
|
||
| ### 確定(Phase 0 完了) | ||
| - [x] Phase 0: 設計判断の確定 | ||
| - Constructor 引数: 名前ベースマッピング(snake_case→camelCase) | ||
| - nullable + ファイル不在: FakeJsonNotFoundException スロー | ||
| - PHP バージョン: ^8.2 に変更 | ||
| - DbQuery::type: 尊重する(MediaQuery 互換) | ||
| ### In Progress | ||
| - Implement Ray.MediaQuery 1.1 result support in priority order. | ||
|
|
||
| ### 未着手 | ||
| - [ ] Phase 1: 依存関係セットアップ(composer.json 更新) | ||
| - [ ] Phase 2: コア実装(5ファイル) | ||
| - [ ] Phase 3: テスト実装 | ||
| - [ ] Phase 4: 品質チェック | ||
| ## Test Results | ||
|
|
||
| ## エラーログ | ||
| (なし) | ||
| | Date | Command | Result | Notes | | ||
| |------|---------|--------|-------| | ||
| | 2026-05-15 | `composer tests` | pass | phpcs, phpstan, psalm, phpunit passed. | | ||
| | 2026-05-15 | `composer coverage` | pass | Line coverage 96.55%. | | ||
| | 2026-05-15 | `composer crc` | fail | Missing direct dependency declarations for phpdocumentor and ray/aop symbols. | | ||
| | 2026-05-15 | `composer validate --strict && composer crc && vendor/bin/phpunit` | pass | Composer metadata, direct dependency scan, and unit tests passed. | | ||
| | 2026-05-15 | `composer tests && composer crc && composer validate --strict` | pass | phpcs, phpstan, psalm, phpunit, CRC, and composer validation passed. | | ||
|
|
||
| ## Files Modified | ||
| - `.planning/task_plan.md` | ||
| - `.planning/findings.md` | ||
| - `.planning/progress.md` | ||
| - `.github/workflows/ci.yml` | ||
| - `README.md` | ||
| - `composer.json` | ||
| - `composer.lock` | ||
| - `vendor-bin/require-checker/composer.lock` | ||
| - `vendor-bin/tools/composer.lock` | ||
| - `src/FakeQueryModule.php` | ||
| - `src/FakeQueryInterceptor.php` | ||
| - `src/JsonHydrator.php` | ||
| - `tests/FakeQueryModuleTest.php` | ||
| - `tests/Fake/Entity/FactoryTodoEntity.php` | ||
| - `tests/Fake/Factory/InjectedTodoFactory.php` | ||
| - `tests/Fake/Factory/StaticTodoFactory.php` | ||
| - `tests/Fake/Query/FactoryTodoQueryInterface.php` | ||
| - `tests/Fake/Query/TodoSelectionQueryInterface.php` | ||
| - `tests/Fake/Result/TodoSelection.php` | ||
| - `tests/Fake/factory_static_item.json` | ||
| - `tests/Fake/factory_injected_item.json` | ||
| - `tests/Fake/factory_static_list.jsonl` | ||
| - `tests/Fake/nested/factory_static_item.json` | ||
| - `tests/FakeUnknownNested/nested/stray_query.json` | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.