필터
필터는 스냅샷에 포함할 데이터를 제어합니다. 캐시 테이블을 제외하고, 변경이 잦은 타임스탬프 열을 무시하거나, 일시적인 행을 건너뛸 수 있습니다. 필터는 스냅샷 캡처 중 적용되며 이후의 모든 작업(diff, merge, apply)에 영향을 줍니다.
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']);
포함 패턴과 일치하는 테이블만 캡처됩니다. 나머지는 모두 제외됩니다.
작동 방식
필터의 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']
별도의 필터 인스턴스를 통해 포함 패턴과 제외 패턴을 모두 지정하면 제외를 먼저 평가합니다. 유지할 테이블은 어떤 제외 패턴과도 일치하지 않고, 하나 이상의 포함 패턴과 일치해야 합니다.
ColumnFilter
캡처 중 행 데이터에서 특정 열을 제거합니다. 매번 쓰기 작업 때마다 바뀌어 잘못된 diff를 일으킬 수 있는 변경이 잦은 열에 유용합니다.
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']
행 키(식별자)는 필터링되지 않은 행을 기준으로 계산됩니다. 따라서 열 필터가 적용되어도 식별 열에는 영향을 주지 않습니다. 지문 계산 및 diff 처리에 사용하는 데이터만 필터링됩니다.
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
제외된 행은 지문 계산이나 스냅샷 저장 대상이 되지 않으며 diff나 merge에도 표시되지 않습니다.
필터 조합
capture()에 여러 필터를 전달합니다. 각 필터 유형은 최대 하나만 사용됩니다:
$snapshot = $snapshotter->capture('baseline', filters: [
TableFilter::exclude(['cache_*']),
ColumnFilter::ignore(['updated_at']),
RowFilter::create(fn($table, $row) => ($row['active'] ?? '1') === '1'),
]);
처리 순서:
- TableFilter가 먼저 실행되어 캡처할 테이블을 결정합니다.
- 각 테이블의 행을 데이터베이스에서 읽습니다.
- RowFilter가 각 행에 적용되어 조건에 맞지 않는 행을 제외합니다.
- 필터링되지 않은 행에서 행 키를 계산합니다(식별 열 유지).
- ColumnFilter가 행 데이터에서 무시할 열을 제거합니다.
- 필터링된 행 데이터로 지문을 계산합니다.
이 순서 덕분에 열 필터가 식별 열을 나중에 제거하더라도 키 계산에 필요한 열을 항상 사용할 수 있습니다.
필터와 일관성
병합에 사용하는 세 스냅샷(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);