フィルター
フィルターは、スナップショットに含めるデータを制御します。キャッシュテーブルを除外したり、変化しやすいタイムスタンプ列を無視したり、一時的な行をスキップしたりできます。フィルターはスナップショット取得時に適用され、後続のすべての操作(差分比較、マージ、適用)に影響します。
use Merql\Filter\TableFilter;
use Merql\Filter\ColumnFilter;
use Merql\Snapshot\Snapshotter;
$snapshotter = new Snapshotter($connection);
$snapshot = $snapshotter->capture('baseline', filters: [
TableFilter::exclude(['sessions', 'cache_*']),
ColumnFilter::ignore(['updated_at', 'modified_date']),
]);
TableFilter
スナップショットに含めるテーブルを制御します。PHPのfnmatch()を使ったglobパターンに対応しています。
テーブルを除外する
use Merql\Filter\TableFilter;
$filter = TableFilter::exclude(['sessions', 'cache_*', 'wp_*_log']);
除外パターンに一致するテーブルはスナップショットから省かれます。*ワイルドカードは任意の文字列に一致します。
テーブルを含める
$filter = TableFilter::include(['posts', 'comments', 'users']);
includeパターンに一致するテーブルのみが取得され、それ以外はすべて除外されます。
仕組み
フィルターのapply()メソッドはテーブル名のリストを受け取り、フィルター後のサブセットを返します。
$allTables = ['posts', 'comments', 'sessions', 'cache_pages', 'users'];
$filter = TableFilter::exclude(['sessions', 'cache_*']);
$filtered = $filter->apply($allTables);
// ['posts', 'comments', 'users']
$filter = TableFilter::include(['posts', 'users']);
$filtered = $filter->apply($allTables);
// ['posts', 'users']
別々のフィルターインスタンスでincludeパターンとexcludeパターンの両方を指定した場合、excludeが先に評価されます。保持するには、テーブルがどのexcludeパターンにも一致せず、少なくとも1つのincludeパターンに一致する必要があります。
ColumnFilter
取得中に行データから特定の列を削除します。書き込みのたびに値が変わり、誤った差分を引き起こす可能性のある変動列に便利です。
use Merql\Filter\ColumnFilter;
$filter = ColumnFilter::ignore(['updated_at', 'modified_date', 'last_login']);
よくある使用例
- 保存のたびに変化する
updated_at/modified_atタイムスタンプ。 - 自動的に増加する
revisionカウンター。 session_tokenなどの一時的なフィールド。
仕組み
フィルターは行キーの計算後、各行から指定された列を取り除きます。
$filter = ColumnFilter::ignore(['updated_at']);
$row = ['id' => 1, 'title' => 'Hello', 'updated_at' => '2026-01-15 10:30:00'];
$filtered = $filter->applyToRow($row);
// ['id' => 1, 'title' => 'Hello']
行キー(識別子)は、フィルター適用前の行から計算されます。そのため、識別列は列フィルターの影響を受けません。フィンガープリントと差分比較に使うデータのみがフィルターされます。
RowFilter
述語クロージャを使って個々の行をフィルタリングします。クロージャはテーブル名と行全体のデータを受け取り、行を含める場合はtrue、除外する場合はfalseを返します。
use Merql\Filter\RowFilter;
// Exclude draft posts.
$filter = RowFilter::create(function (string $table, array $row): bool {
if ($table === 'posts' && ($row['status'] ?? '') === 'draft') {
return false;
}
return true;
});
// Exclude rows with NULL email.
$filter = RowFilter::create(function (string $table, array $row): bool {
if ($table === 'users') {
return $row['email'] !== null;
}
return true;
});
仕組み
取得中、すべての行に対してshouldInclude()メソッドが呼び出されます。
$include = $filter->shouldInclude('posts', ['id' => 1, 'status' => 'draft']);
// false
除外された行はフィンガープリントが計算されず、スナップショットにも保存されず、差分比較やマージにも表示されません。
フィルターの組み合わせ
capture()に複数のフィルターを渡せます。各フィルタータイプは最大1つまで使用されます。
$snapshot = $snapshotter->capture('baseline', filters: [
TableFilter::exclude(['cache_*']),
ColumnFilter::ignore(['updated_at']),
RowFilter::create(fn($table, $row) => ($row['active'] ?? '1') === '1'),
]);
処理順序:
- TableFilterが最初に実行され、取得するテーブルを決定します。
- 各テーブルについて、データベースから行を読み込みます。
- RowFilterが各行に適用され、一致しない行を除外します。
- フィルター適用前の行(識別列を保持)から行キーを計算します。
- ColumnFilterが行データから無視する列を取り除きます。
- フィルター後の行データからフィンガープリントを計算します。
この順序により、列フィルターで後から削除される場合でも、キーの計算に識別列を常に利用できます。
フィルターと整合性
マージする3つのスナップショット(base、ours、theirs)すべてに同じフィルターを使ってください。異なるフィルターを使うと、実際にはフィルターの違いだけなのに、行や列が追加または削除されたように見えることがあります。
// Use the same filter set for all snapshots.
$filters = [
TableFilter::exclude(['sessions']),
ColumnFilter::ignore(['updated_at']),
];
$base = $snapshotter->capture('base', filters: $filters);
$ours = $snapshotter->capture('ours', filters: $filters);
$theirs = $snapshotter->capture('theirs', filters: $filters);