Skip to content

fix: support partial updates in PATCH /api/v1/users/{id} / ユーザー更新APIの部分更新対応 - #547

Merged
zigzagdev merged 4 commits into
feat/userfrom
fix/update-user-method
Jul 26, 2026
Merged

fix: support partial updates in PATCH /api/v1/users/{id} / ユーザー更新APIの部分更新対応#547
zigzagdev merged 4 commits into
feat/userfrom
fix/update-user-method

Conversation

@zigzagdev

@zigzagdev zigzagdev commented Jul 26, 2026

Copy link
Copy Markdown
Owner

Motivation / 目的

PATCH /api/v1/users/{id} に一部のフィールドだけを送ると、UpdateUserCommand::fromArray がすべての必須キー(first_name, last_name, email, age_range, subscription_tier)を要求していたため InvalidArgumentException が発生していた。この例外は UserController::updateUser の汎用 catch に落ちて 500 Internal Server Error として返っていた。

Sending only a subset of fields to PATCH /api/v1/users/{id} caused UpdateUserCommand::fromArray to throw InvalidArgumentException, since it required every field (first_name, last_name, email, age_range, subscription_tier) to be present. That exception fell through UserController::updateUser's generic catch block and surfaced as a 500 Internal Server Error.

PATCHの意味的にも部分更新を許可すべきという判断のもと、更新対象フィールドのみ送れば良い設計に変更した。

また、修正の過程で別の問題も見つかった。UserController::updateUsercatch (\Exception) のみで、\Error 系(例: 無効な age_range を渡した際に AgeRange::from() が投げる \ValueError)を一切キャッチできず、生のLaravelデバッグエラーページがそのまま返っていた(createUser は既に \Throwable をキャッチしており一貫性がなかった)。これも合わせて修正した。

While fixing this, we also found that UserController::updateUser only caught \Exception, so \Error-family exceptions (e.g. the \ValueError thrown by AgeRange::from() on an invalid age_range value) were never caught, leaking Laravel's raw debug error page instead of a clean JSON response (createUser already caught \Throwable, so this was an inconsistency). Fixed that too.

What I have done / 実施内容

  • UpdateUserCommand: id 以外の全フィールドを任意(nullable)にし、fromArrayid の存在のみを必須チェックするように変更
  • UpdateUserUseCase: handle() の冒頭で既存ユーザーを findById で取得し、コマンドで指定されなかったフィールドは既存値をそのまま使う(未指定=変更なし)ようにマージするロジックを追加。ユーザーが存在しない場合は従来通り User not found. を投げる
  • UserController::updateUsercatch (\Exception)catch (\Throwable) に変更し、\ValueError 等でも正しくJSONの Internal Server Error を返すように修正
  • 既存の UpdateUserCommandTest / UpdateUserUseCaseTest を部分更新の挙動に合わせて更新
  • UpdateUserTesttest_update_user_returns_500_when_required_key_is_missing(今回の設計変更で前提が崩れた既存テスト)を削除し、代わりに無効な age_range を送った際にクリーンなJSON 500が返ることを確認するテストに置き換え

Test Results / テスト結果

  • UpdateUserCommandTest::test_fromArray_creates_command_with_valid_data
  • UpdateUserCommandTest::test_fromArray_sets_subscription_expires_at_when_provided
  • UpdateUserCommandTest::test_fromArray_throws_when_id_is_missing
  • UpdateUserCommandTest::test_fromArray_allows_optional_key_to_be_omitted(first_name/last_name/email/age_range/subscription_tier)
  • UpdateUserUseCaseTest::test_handle_passes_entity_to_repository_and_returns_dto
  • UpdateUserUseCaseTest::test_handle_keeps_existing_value_when_field_is_omitted
  • UpdateUserUseCaseTest::test_handle_throws_when_user_not_found
  • UpdateUserTest::test_update_user_returns_200_with_updated_data
  • UpdateUserTest::test_update_user_returns_404_when_user_not_found
  • UpdateUserTest::test_update_user_returns_500_with_json_when_age_range_is_invalid
  • 実リクエストで PATCH /api/v1/users/1first_name のみ送信し、他フィールドが保持されたまま更新されることを確認
  • 既存の LoginTest / LogoutTest / MeTest の回帰なしを確認済み

@github-actions github-actions Bot added bug Something isn't working backend labels Jul 26, 2026
@codecov

codecov Bot commented Jul 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.73418% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 62.72%. Comparing base (eb5da0c) to head (2fbbf27).

Files with missing lines Patch % Lines
...es/Tests/CommandUseCases/UpdateUserCommandTest.php 90.90% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@               Coverage Diff               @@
##             feat/user     #547      +/-   ##
===============================================
+ Coverage        62.51%   62.72%   +0.21%     
- Complexity        1615     1619       +4     
===============================================
  Files              136      136              
  Lines             8329     8374      +45     
===============================================
+ Hits              5207     5253      +46     
+ Misses            3122     3121       -1     
Files with missing lines Coverage Δ
...CommandUseCases/UseCase/User/UpdateUserUseCase.php 100.00% <100.00%> (ø)
...mandUseCases/UseCommand/User/UpdateUserCommand.php 100.00% <100.00%> (ø)
...pp/Packages/Features/Controller/UserController.php 79.74% <100.00%> (ø)
...es/Tests/CommandUseCases/UpdateUserUseCaseTest.php 100.00% <100.00%> (ø)
src/app/Packages/Features/Tests/UpdateUserTest.php 100.00% <100.00%> (ø)
...es/Tests/CommandUseCases/UpdateUserCommandTest.php 82.60% <90.90%> (+5.10%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@zigzagdev zigzagdev self-assigned this Jul 26, 2026

@zigzagdev zigzagdev left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

@zigzagdev
zigzagdev merged commit 8a9ca0a into feat/user Jul 26, 2026
27 checks passed
@zigzagdev
zigzagdev deleted the fix/update-user-method branch July 26, 2026 07:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant