Перейти к содержимому

Фильтры

Фильтры определяют, какие данные включаются в снимок. Исключайте таблицы кэша, игнорируйте изменчивые столбцы с временными метками или пропускайте временные строки. Фильтры применяются при создании снимка и влияют на все последующие операции (сравнение, слияние, применение).

PHP
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

Определяет, какие таблицы включаются в снимок. Поддерживаются шаблоны glob через PHP и fnmatch().

Исключение таблиц

PHP
use Merql\Filter\TableFilter;

$filter = TableFilter::exclude(['sessions', 'cache_*', 'wp_*_log']);

Любая таблица, соответствующая шаблону исключения, не попадает в снимок. Подстановочный знак * соответствует любой последовательности символов.

Включение таблиц

PHP
$filter = TableFilter::include(['posts', 'comments', 'users']);

В снимок попадают только таблицы, соответствующие шаблону включения. Все остальные исключаются.

Как это работает

Метод фильтра apply() принимает список имен таблиц и возвращает отфильтрованную выборку:

PHP
$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

Удаляет определенные столбцы из данных строк при создании снимка. Полезно для изменчивых столбцов, которые меняются при каждой записи и вызывают ложные различия.

PHP
use Merql\Filter\ColumnFilter;

$filter = ColumnFilter::ignore(['updated_at', 'modified_date', 'last_login']);

Типичные сценарии использования

  • Временные метки updated_at / modified_at, меняющиеся при каждом сохранении.
  • Счетчики revision с автоматическим увеличением.
  • session_token и другие временные поля.

Как это работает

Фильтр удаляет именованные столбцы из каждой строки после вычисления ключа строки:

PHP
$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, чтобы исключить ее.

PHP
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() вызывается для каждой строки при создании снимка:

PHP
$include = $filter->shouldInclude('posts', ['id' => 1, 'status' => 'draft']);
// false

Исключенные строки не используются для вычисления отпечатков, не сохраняются в снимке и не появляются в результатах сравнения или слияния.

Объединение фильтров

Передайте несколько фильтров в capture(). Используется не более одного фильтра каждого типа:

PHP
$snapshot = $snapshotter->capture('baseline', filters: [
    TableFilter::exclude(['cache_*']),
    ColumnFilter::ignore(['updated_at']),
    RowFilter::create(fn($table, $row) => ($row['active'] ?? '1') === '1'),
]);

Порядок обработки:

  1. Сначала работает TableFilter, определяя, какие таблицы включить в снимок.
  2. Для каждой таблицы считываются строки из базы данных.
  3. RowFilter проверяет каждую строку и исключает неподходящие.
  4. По исходной строке вычисляется ключ строки (столбцы идентификатора сохраняются).
  5. ColumnFilter удаляет игнорируемые столбцы из данных строки.
  6. По отфильтрованным данным строки вычисляется отпечаток.

Такой порядок гарантирует, что столбцы идентификатора всегда доступны для вычисления ключа, даже если фильтр столбцов удалит их позднее.

Фильтры и согласованность

Для всех трех снимков при слиянии (база, наш вариант, их вариант) следует использовать одинаковые фильтры. Если применить разные фильтры, строки или столбцы могут выглядеть добавленными или удаленными, хотя на самом деле они лишь по-разному отфильтрованы.

PHP
// 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);