数百件のレコードを変更する操作は、ルール上は正しくても、想定以上のデータに影響することがあります。「本当によろしいですか?」という一般的な確認だけでは、影響範囲を判断したり、想定外の条件に気づいたりできません。PHPで変更をプレビューする場合は、影響を受けるレコード、変更される値、操作を実行できる条件を示す必要があります。
プレビューによって予想外の結果を減らせますが、その後の実行でまったく同じ結果になる保証はありません。両方の段階の間に、データ、権限、操作の範囲が変わる可能性があります。この不確実性を明示し、書き込みの前に必要な項目を再検証する設計にしましょう。
プレビューが有効な場面

価格の更新、ステータスの変更、アカウントの再割り当て、情報の削除、派生データの再計算など、大量のデータを対象とする操作、慎重さを要する操作、取り消しが難しい操作では、プレビューを検討しましょう。操作を開始する人が影響を確認したり、実行前に承認を得たりする必要がある場合にも役立ちます。
すべての操作にプレビュー画面が必要なわけではありません。小規模で取り消し可能、かつ結果が明確な操作なら、状況に応じた確認だけで十分なこともあります。一方、ユーザーが確認の意味を判断できないのであれば、ダイアログを追加するのではなく、具体的なデータを表示する必要があります。
プレビューは、バックアップ、ロールバックの仕組み、承認ポリシーの代わりにはなりません。ミスが深刻な結果を招く可能性がある場合は、別の人による承認、段階的な実行、検証済みの復旧プロセスなど、追加の制御を検討してください。
計算と適用を分離する
ビジネスルールは、データベースを変更せずに変更案を計算できるようにします。実用的な方法は、選択と計算を2つのモードで共有することです。一方は変更内容を提示し、もう一方は変更を適用します。別々に実装するのを避ければ、プレビューと実行でルールの解釈が異なるリスクを減らせます。
変更案には、影響を受ける各項目について、次の情報を含められます。
- レコードを特定できる識別子。
- 現在の関連値と変更後の想定値。
- 対象に含める、または除外する理由。
- 実施した検証と検出した競合。
- フィルター、アカウント、期間などの操作範囲。
たとえば、注文のステータスを変更する操作では、現在のステータス、変更案のステータス、対象となる理由を表示できます。画面にすべての項目を表示するには多すぎる場合、合計件数とサンプルを提示してもかまいません。その際は何を表示しているかを明確にし、必要に応じて詳細を確認またはエクスポートできるようにします。
PHPでは、選択と計算を責務の明確なコンポーネントにカプセル化します。ドメインサービスが変更案を生成し、別のコンポーネントがそれを表示し、アプリケーションのルートが検証と永続化を担当する構成が考えられます。読み取りの副作用によって、プレビューが間接的に書き込みを実行することがあってはなりません。
データ、権限、範囲を検証する
有用な変更案は、何が変わるかだけでなく、なぜ変更できるのかも説明します。現在のデータに対してビジネス条件を検証し、ユーザーにその操作を閲覧・実行する権限があることを確認してください。レコードの閲覧権限があっても、変更権限があるとは限りません。
操作範囲は明示します。フィルター、選択内容、ファイルのアップロードに基づく操作なら、そのパラメーターを理解しやすい形で記録し、上限、除外、エラーを伝えてください。検索範囲が広すぎたり、フィルターが省略されたりして、限定的な操作が意図せず全体に及ぶことを避けましょう。
確実に予測できないことも示す必要があります。プレビューでは、他のプロセスによる変更、外部システムで実行されるルール、将来の状態に依存する副作用を自動的に予測できません。連携、キュー、後続の計算がある場合は、それらの影響を別途説明し、確定した結果の一部として提示しないでください。
古くなった変更案の適用を防ぐ
プレビューは、特定の時点のデータを表しています。実行前にデータが変わると、注文が条件を満たさなくなったり、ユーザーが権限を失ったり、別のプロセスがレコードを更新したりする可能性があります。そのため、表示された結果を恒久的な書き込み許可として扱ってはいけません。
実行時には、対象となる条件と権限を再確認してください。状況に応じて、レコードのバージョン、タイムスタンプ、または変更案の計算時に確認した関連値を比較します。不一致があった場合は、異なる判断を黙って適用せず、競合を通知し、新しいプレビューの生成を求めるか、明示的な解決を促してください。
厳密な一致が必要なプロセスでは、変更案への参照を保持し、適用時にも同じ範囲とルールが使われていることを検証します。トランザクションは、途中の障害から一連の書き込みを保護できますが、プレビューから実行までの経過時間そのものをなくすことはできません。並行処理の戦略は、操作のリスクと規模に合わせてください。
バッチで実行し、結果を記録する
大量のデータを一度に適用すると、ロック時間が長くなり、復旧が難しくなる可能性があります。データベースとアプリケーションの動作に適したバッチ上限を設定してください。一部だけ失敗する場合、ユーザーが適用済み、スキップ済み、要確認のレコードを区別できるようにします。
結果を調査するのに十分な情報を記録します。操作を開始・承認した人、準備・実行の日時、指定された範囲、処理した項目数、発生したエラーなどです。必要のない個人データや機密値の保存は避けてください。監査ログにも、適用されるアクセス制御と保持期間のルールを守る必要があります。
操作を再試行する場合は、冪等性を考慮してください。同じ要求を繰り返しても、予期しない効果が重複しないようにします。操作IDと実行状態があれば、重複要求を識別し、準備、承認、実行、完了を分けて管理できます。コードのデプロイを、操作のリリースや有効化と同じものとして扱わないでください。これらは別の判断であり、それぞれに固有の制御が必要です。
プレビューでデータが変更されないことをテストする
テストでは、計算結果に加えて、プレビューモードで書き込みが発生しないことも確認します。代表的なデータを使って、対象・除外、現在値・変更後の値、権限不足、検証失敗を検証してください。
さらに、実行時に期待どおりの変更案が適用されること、並行して変更があった場合に定義どおりの動作をすることを確認します。該当する場合は、バッチ内の障害、再試行、連携エラーもテストしてください。データベースとの統合テストは重要です。計算のユニットテストだけでは、プレビューのルートに副作用がないことを証明できません。
境界ケースには、空のフィルター、大量データ、2つの段階の間に削除または変更されたレコード、アクセス範囲の異なるユーザーを含めます。また、実行時に対象集合を再計算するのであれば、画面が「これらの項目が正確に更新されます」と断言していないことも確認しましょう。
必要な制御を判断するためのチェックリスト

- 操作は多数のレコードに影響するか、取り消しが難しい効果を持つか、承認を必要とするか。
- 操作範囲、対象項目、現在値、変更後の想定値を表示しているか。
- プレビューが書き込みやその副作用から分離されているか。
- 準備時と実行時の両方で、権限、ビジネスルール、データを検証しているか。
- 2つの段階の間に発生した重要な変更を検出できるか。
- バッチ上限、一部失敗への対応方針、適切な監査ログが用意されているか。
- プレビューでデータが変更されず、実行が検証済みの結果に従うことをテストで確認しているか。
プレビューは、十分な情報に基づく判断に役立ち、その限界が明確な場合に適切です。影響を信頼できる形で見積もれないなら、数値や一部のリストを結果の保証に見せかけるのではなく、範囲を縮小する、管理された環境で先に実行する、追加レビューを必須にするといった対応が望まれます。



