본문으로 건너뛰기

PHP API 참조 문서

merql의 모든 공개 클래스와 메서드에 대한 전체 참조 문서입니다.

Merql (파사드)

Merql\Merql은 일반 작업을 위한 정적 진입점입니다. 하위 수준 클래스를 감싸고 스냅샷 저장을 자동으로 관리합니다.

PHP
use Merql\Connection;
use Merql\Identity\IdentityRule;
use Merql\Identity\IdentityRuleSet;
use Merql\Merql;

$connection = Connection::sqlite(':memory:');
Merql::init($connection, identityRules: new IdentityRuleSet([
    'options' => IdentityRule::natural(['option_name']),
]));

init(DatabaseConnection $connection, ?Driver $driver = null, ?IdentityRuleSet $identityRules = null): void

데이터베이스 연결로 파사드를 초기화합니다. snapshot(), apply() 또는 applyGuarded()보다 먼저 호출해야 합니다. 데이터베이스를 전환하려면 다시 호출하세요. 예를 들어 서로 다른 두 복사본에서 ours와 theirs를 스냅샷으로 만들 때 사용할 수 있습니다. $driver이 없으면 DriverFactory::create()를 호출합니다. 드라이버가 등록되지 않은 경우 이 메서드는 MySQL 및 SQLite 이외의 엔진에서는 예외를 발생시킵니다. 자동 감지를 재정의하려면 드라이버를 전달하고, 스냅샷을 캡처할 때 테이블별 행 식별 규칙을 재정의하려면 식별 규칙 집합을 전달하세요.

snapshot(string $name, array $tables = []): Snapshot

현재 데이터베이스 상태를 캡처해 디스크에 저장합니다. 특정 테이블의 스냅샷을 만들려면 테이블 이름 배열을 전달하고, 모든 테이블을 대상으로 하려면 생략하세요.

PHP
$snapshot = Merql::snapshot('baseline');
$snapshot = Merql::snapshot('baseline', ['posts', 'users']);

diff(string $base, string $current): Changeset

이름이 지정된 스냅샷 두 개를 불러와 그 사이의 변경 집합을 계산합니다. 저장된 스냅샷을 사용하므로 init()이 필요하지 않습니다.

PHP
$changeset = Merql::diff('before', 'after');

merge(string $base, string $ours, string $theirs): MergeResult

이름이 지정된 스냅샷 세 개를 3방향 병합합니다. 충돌 없이 적용 가능한 작업과 충돌을 포함한 결과를 반환합니다. 열 수준에서 병합합니다. 파사드는 CellMergeConfig 없이 ThreeWayMerge을 구성하므로 셀 수준 병합은 적용되지 않습니다. 셀 수준 병합에는 ThreeWayMerge를 직접 사용하세요.

PHP
$result = Merql::merge('base', 'ours', 'theirs');

patch(string $base, string $changes): MergeResult

2방향 병합: base에 변경 사항을 적용합니다. merge($base, $base, $changes)과 동일합니다. 동시 편집이 없으므로 항상 충돌 없는 결과를 생성합니다. merge()과 마찬가지로 열 수준에서만 병합합니다.

PHP
$result = Merql::patch('base', 'changes');

apply(MergeResult $result): ApplyResult

init()에 전달된 데이터베이스에 충돌 없이 적용 가능한 병합 결과를 실행합니다. init()이 필요합니다. 해결되지 않은 충돌이 남아 있으면 ConflictException를 발생시킵니다.

결과의 모든 작업이 실행되므로 merge()의 전체 결과는 여전히 base 상태인 데이터베이스에만 충돌 없이 적용됩니다. 데이터베이스에 이미 한쪽 변경 사항이 적용된 경우, 아직 적용되지 않은 작업을 선택해 적용하세요. Guarded Apply를 참조하세요.

PHP
$applied = Merql::apply($result);
echo $applied->rowsAffected();

plan(string $id, string $base, string $ours, string $theirs, array $metadata = []): MergePlan

이름이 지정된 스냅샷 세 개에서 UI에 사용할 병합 계획을 만듭니다. 계획에는 안정적인 작업 ID, 변경 그룹 ID, 작업 컨텍스트, 충돌, 스키마 불일치, 요약, 메타데이터, 계획 해시가 포함됩니다. merge()과 마찬가지로 열 수준 병합을 수행합니다. 셀 수준 병합을 하려면 셀 병합 결과를 MergePlanBuilder::build()에 전달하세요.

PHP
$plan = Merql::plan('plan-1', 'base', 'ours', 'theirs', [
    'source' => 'review-ui',
]);

applyGuarded(MergeResult $result, Snapshot $expectedLive): ApplyResult

라이브 행에 대한 낙관적 사전 조건을 적용해 충돌 없이 병합 결과를 적용합니다. 업데이트와 삭제는 현재 라이브 행이 $expectedLive과 일치해야 합니다. 삽입은 대상 식별자가 아직 존재하지 않아야 합니다. $expectedLive은 대상 데이터베이스의 스냅샷입니다(ours를 데이터베이스에 반영할 때는 theirs를 가져온 데이터베이스). 영향을 받은 행이 0개인 문이 하나라도 있으면 트랜잭션을 롤백합니다.

PHP
use Merql\Snapshot\SnapshotStore;

$applied = Merql::applyGuarded($selectedResult, SnapshotStore::load('theirs'));

reset(): void

내부 연결 및 Snapshotter 인스턴스를 초기화합니다. 주로 테스트 사이에 상태를 재설정할 때 사용합니다.


연결

Merql\Connection은 적절한 기본값으로 연결 어댑터를 구성합니다. PDO 연결은 오류 시 예외를 사용하고, 연관 배열 가져오기 모드를 설정하며, 에뮬레이션된 프리페어를 비활성화하고, 가져온 값을 문자열로 변환합니다. mysqli 어댑터는 가져온 값을 같은 문자열 또는 null 형식으로 정규화합니다.

mysql(string $host, string $database, string $username, string $password = '', int $port = 3306, string $charset = 'utf8mb4'): PdoDatabaseConnection

PHP
use Merql\Connection;

$connection = Connection::mysql('127.0.0.1', 'my_app', 'root', 'secret');

mysqli(string $host, string $database, string $username, string $password = '', int $port = 3306, string $charset = 'utf8mb4'): MysqliDatabaseConnection

PHP
$connection = Connection::mysqli('127.0.0.1', 'my_app', 'root', 'secret');

sqlite(string $path = ':memory:'): PdoDatabaseConnection

SQLite 연결을 생성합니다. PRAGMA foreign_keys = ON을 통해 외래 키를 자동으로 활성화합니다.

PHP
$connection = Connection::sqlite('/var/data/app.sqlite');
$connection = Connection::sqlite(':memory:');

fromDsn(string $dsn, string $username = '', string $password = ''): PdoDatabaseConnection

모든 PDO DSN 문자열로 연결합니다. merql은 mysql 및 sqlite 드라이버만 제공합니다. 다른 DSN으로도 연결은 열리지만, 드라이버를 DriverFactory::register()에 등록하지 않으면 Merql::init(), Snapshotter 및 적용기가 Unsupported database driver를 발생시킵니다.

PHP
$connection = Connection::fromDsn('mysql:host=localhost;dbname=app', 'root', 'pass');

fromPdo(PDO $pdo): PdoDatabaseConnection

기존 PDO 인스턴스를 감쌉니다.

fromMysqli(mysqli $mysqli, string $charset = 'utf8mb4'): MysqliDatabaseConnection

기존 mysqli 인스턴스를 감쌉니다.

어댑터 계약

모든 어댑터는 Merql\Database\DatabaseConnection를 구현합니다:

PHP
interface DatabaseConnection
{
    public function driverName(): string;
    public function execute(string $sql, array $params = []): int;
    public function query(string $sql, array $params = []): array;
    public function scalar(string $sql, array $params = []): ?string;
    public function beginTransaction(): void;
    public function commit(): void;
    public function rollBack(): void;
    public function lastInsertId(): string;
}

execute(), query() 및 scalar()는 ? 플레이스홀더에 위치 기반 list<scalar|null> 매개변수를 받습니다. query()는 list<array<string, scalar|null>>을 반환합니다. 값은 PDO와 mysqli 어댑터에서 동일한 문자열 또는 null 형식으로 정규화됩니다.


스냅샷 생성기

Merql\Snapshot\Snapshotter은 데이터베이스 상태를 캡처합니다. 파사드와 달리 필터를 완전히 제어할 수 있으며 자동으로 저장하지 않습니다.

__construct(DatabaseConnection $connection, ?Driver $driver = null, ?IdentityRuleSet $identityRules = null)

드라이버를 지정하지 않으면 연결 식별 정보에서 자동으로 감지합니다. 테이블별 행 식별 규칙이 스키마 기본값을 재정의해야 한다면 IdentityRuleSet을 전달하세요.

capture(string $name, array $tables = [], array $filters = []): Snapshot

현재 상태를 캡처합니다. 범위를 제한하려면 테이블 이름을 전달하세요. 테이블, 열 또는 행을 제외하려면 필터 객체를 전달하세요.

PHP
use Merql\Snapshot\Snapshotter;
use Merql\Filter\TableFilter;
use Merql\Filter\ColumnFilter;
use Merql\Filter\RowFilter;
use Merql\Identity\IdentityRule;
use Merql\Identity\IdentityRuleSet;

$snapshotter = new Snapshotter(
    $connection,
    identityRules: new IdentityRuleSet([
        'wp_options' => IdentityRule::natural(['option_name']),
    ]),
);

// All tables.
$snapshot = $snapshotter->capture('full');

// Specific tables with filters.
$snapshot = $snapshotter->capture('filtered', ['posts', 'users'], [
    TableFilter::exclude(['cache_*', 'sessions']),
    ColumnFilter::ignore(['updated_at', 'created_at']),
    RowFilter::create(fn(string $table, array $row) => $row['status'] !== 'trash'),
]);

captureAliased(string $name, array $physicalToCanonicalTableMap, array $filters = []): Snapshot

실제 테이블을 정규 테이블 이름으로 캡처합니다. 환경마다 동일한 논리 테이블의 실제 이름이 다를 때 사용하세요.

PHP
$snapshot = $snapshotter->captureAliased('sandbox', [
    'wp_onumia_abcd_posts' => 'wp_posts',
    'wp_onumia_abcd_postmeta' => 'wp_postmeta',
]);

결과 스냅샷에는 정규 테이블 이름이 저장되며, 실제 테이블 스키마, 설정된 식별 열, 행 키, 필터는 유지됩니다.

fromData(string $name, array $tableData): Snapshot (정적)

데이터베이스 연결 없이 원시 데이터에서 스냅샷을 만듭니다. 테스트나 다른 소스의 데이터를 가져올 때 유용합니다.

PHP
use Merql\Snapshot\Snapshotter;
use Merql\Snapshot\TableSnapshotData;
use Merql\Schema\TableSchema;

$schema = new TableSchema('posts', ['id' => 'int', 'title' => 'varchar'], ['id']);

$snapshot = Snapshotter::fromData('test', [
    'posts' => new TableSnapshotData($schema, [
        ['id' => '1', 'title' => 'Hello'],
        ['id' => '2', 'title' => 'World'],
    ], ['id']),
]);

스냅샷

Merql\Snapshot\Snapshot은 전체 데이터베이스 상태를 나타내는 읽기 전용 값 객체입니다.

PHP
$snapshot->name;                    // string: snapshot identifier
$snapshot->tables;                  // array<string, TableSnapshot>
$snapshot->tableNames();            // list<string>
$snapshot->hasTable('posts');       // bool
$snapshot->getTable('posts');       // ?TableSnapshot

TableSnapshot

Merql\Snapshot\TableSnapshot은 한 테이블의 행, 지문, 스키마를 보유합니다.

PHP
$table = $snapshot->getTable('posts');

$table->schema;                    // TableSchema
$table->identityColumns;           // list<string>: columns used for row identity
$table->fingerprints;              // array<string, string>: row key to hash
$table->rows;                      // array<string, array<string, mixed>>

$table->rowKeys();                 // list<string>
$table->rowCount();                // int
$table->hasRow('42');              // bool
$table->getRow('42');              // ?array<string, mixed>
$table->getFingerprint('42');      // ?string

TableSnapshotData

Merql\Snapshot\TableSnapshotData은 Snapshotter::fromData()의 입력 데이터입니다.

PHP
use Merql\Snapshot\TableSnapshotData;
use Merql\Schema\TableSchema;

$data = new TableSnapshotData(
    schema: new TableSchema('posts', ['id' => 'int', 'title' => 'varchar'], ['id']),
    rows: [
        ['id' => '1', 'title' => 'First Post'],
    ],
    identityColumns: ['id'],
);

IdentityRule

Merql\Identity\IdentityRule은 행 키를 만드는 데 사용할 열을 정의합니다.

PHP
use Merql\Identity\IdentityRule;

$primary = IdentityRule::primary(['id']);
$natural = IdentityRule::natural(['option_name']);
$composite = IdentityRule::composite(['object_id', 'term_taxonomy_id']);
$content = IdentityRule::content(['name', 'value']);

$key = $natural->key(['option_name' => 'siteurl']);
// "siteurl"

forSchema(TableSchema $schema): IdentityRule (정적)

스키마에서 기본 식별 규칙을 확인합니다: 기본 키, 첫 번째 고유 키, 그다음 콘텐츠 대체 규칙 순입니다.

toArray(): array

규칙을 ['type' => string, 'columns' => list<string>]로 직렬화합니다.

fromArray(array $data): IdentityRule (정적)

직렬화된 데이터에서 규칙을 복원합니다.


IdentityRuleSet

Merql\Identity\IdentityRuleSet은 테이블별 식별 재정의 설정을 저장합니다.

PHP
use Merql\Identity\IdentityRule;
use Merql\Identity\IdentityRuleSet;

$rules = new IdentityRuleSet([
    'wp_options' => IdentityRule::natural(['option_name']),
]);

$rule = $rules->ruleFor($schema);

with(string $table, IdentityRule $rule): IdentityRuleSet

테이블 재정의 항목 하나가 추가된 새 규칙 집합을 반환합니다.

conflictsFor(string $table, IdentityRule $rule, array $rows): array

규칙에 따라 여러 행에서 같은 키가 생성될 때 IdentityConflict 항목을 반환합니다.

toArray(): array

테이블 규칙을 JSON에 안전한 배열로 직렬화합니다.

fromArray(array $data): IdentityRuleSet (정적)

직렬화된 데이터에서 규칙 집합을 복원합니다.


SnapshotStore

Merql\Snapshot\SnapshotStore은 스냅샷을 JSON 파일로 저장하고 불러옵니다.

setDirectory(string $directory): void

저장 디렉터리를 설정합니다. 기본값은 .merql/snapshots입니다.

save(Snapshot $snapshot): void

스냅샷을 디스크에 기록합니다.

load(string $name): Snapshot

이름으로 스냅샷을 불러옵니다. 찾을 수 없으면 SnapshotException을 발생시킵니다.

exists(string $name): bool

스냅샷 파일이 있는지 확인합니다.

delete(string $name): void

스냅샷 파일을 제거합니다.


비교기

Merql\Diff\Differ은 두 스냅샷을 비교해 변경 집합을 생성합니다.

diff(Snapshot $base, Snapshot $current): Changeset

PHP
use Merql\Diff\Differ;

$differ = new Differ();
$changeset = $differ->diff($baseSnapshot, $currentSnapshot);

valuesEqual(mixed $a, mixed $b): bool (정적)

문자열로 변환해 두 값을 비교합니다. NULL은 별개의 값으로 취급합니다: NULL === NULL은 true이고 NULL === ''은 false입니다.


변경 집합

Merql\Diff\Changeset은 행 수준 작업의 읽기 전용 컬렉션입니다.

PHP
$changeset->inserts();             // list<RowInsert>
$changeset->updates();             // list<RowUpdate>
$changeset->deletes();             // list<RowDelete>
$changeset->isEmpty();             // bool
$changeset->count();               // int: total operations
$changeset->forTable('posts');     // Changeset: filtered to one table

RowInsert, RowUpdate, RowDelete

개별 행 변경을 나타내는 읽기 전용 값 객체입니다.

RowInsert

PHP
$insert->table;                    // string
$insert->rowKey;                   // string
$insert->values;                   // array<string, mixed>

RowUpdate

PHP
$update->table;                    // string
$update->rowKey;                   // string
$update->columnDiffs;              // list<ColumnDiff>
$update->fullRow;                  // array<string, mixed>: complete current row
$update->columnDiffMap();          // array<string, ColumnDiff>: keyed by column name

RowDelete

PHP
$delete->table;                    // string
$delete->rowKey;                   // string
$delete->oldValues;                // array<string, mixed>: values before deletion

ColumnDiff

Merql\Diff\ColumnDiff은 행 업데이트 내의 단일 열 변경을 나타냅니다.

PHP
$diff->column;                     // string
$diff->oldValue;                   // mixed
$diff->newValue;                   // mixed

ThreeWayMerge

Merql\Merge\ThreeWayMerge은 핵심 병합 알고리즘입니다.

__construct(?CellMergeConfig $cellMergeConfig = null)

TEXT 및 JSON 열에 셀 수준 병합을 사용하려면 CellMergeConfig을 전달하세요.

PHP
use Merql\Merge\ThreeWayMerge;
use Merql\CellMerge\CellMergeConfig;

// Default: column-level merge only.
$merge = new ThreeWayMerge();

// With cell-level merge for TEXT and JSON columns.
$merge = new ThreeWayMerge(CellMergeConfig::auto());

merge(Snapshot $base, Snapshot $ours, Snapshot $theirs): MergeResult

3방향 병합을 수행합니다. 변경 집합(base와 ours, base와 theirs)을 내부적으로 계산하고 병합합니다.

PHP
$result = $merge->merge($baseSnapshot, $oursSnapshot, $theirsSnapshot);

patch(Snapshot $base, Snapshot $changes): MergeResult

2방향 병합 단축 경로입니다. merge($base, $base, $changes)과 동일합니다.

schemaMismatches(): array

마지막 병합에서 감지된 모든 스키마 차이(스냅샷 간에 추가되거나 제거된 열)에 대해 SchemaException[]을 반환합니다.


MergeResult

Merql\Merge\MergeResult은 병합 결과를 담은 읽기 전용 값 객체입니다.

PHP
$result->isClean();                // bool: no conflicts
$result->operations();             // list<MergeOperation>
$result->conflicts();              // list<Conflict>
$result->operationCount();         // int
$result->conflictCount();          // int
$result->baseSnapshot();           // ?Snapshot
$result->schemaMismatches();       // list<SchemaException>
$result->hasSchemaMismatches();    // bool

MergePlan

Merql\Plan\MergePlan은 병합을 검토하기 위한 직렬화 가능한 모델입니다.

PHP
$plan->id;                        // string
$plan->baseSnapshot;              // string
$plan->oursSnapshot;              // string
$plan->theirsSnapshot;            // string
$plan->summary;                   // MergePlanSummary
$plan->changeGroups;              // list<MergePlanChangeGroup>
$plan->operations;                // list<MergePlanOperation>
$plan->conflicts;                 // list<MergePlanConflict>
$plan->schemaMismatches;          // list<string>
$plan->hash;                      // string
$plan->metadata;                  // array<string, mixed>

안정적인 JSON 왕복 처리를 위해 MergePlanSerializer::toJson() 및 ::fromJson()을 사용하세요.

선택 항목

ChangeGroupSelection은 스테이징된 변경 그룹 ID를 저장합니다. 선택한 병합 결과나 롤백 계획을 만들 때 이를 OperationSelection로 변환하세요.

PHP
use Merql\Plan\ChangeGroupSelection;
use Merql\Plan\SelectedMergeResultFactory;

$selection = ChangeGroupSelection::fromIds([$groupId]);
$operationSelection = $selection->toOperationSelection($plan);

$selected = (new SelectedMergeResultFactory())
    ->fromOperationSelection($plan, $operationSelection);

RollbackPlan

Merql\Rollback\RollbackPlan은 선택 적용을 위한 역연산과 드리프트 예상값을 저장합니다.

PHP
use Merql\Rollback\RollbackPlanBuilder;
use Merql\Rollback\RollbackPlanSerializer;

$rollback = (new RollbackPlanBuilder())->build(
    'rollback-1',
    $plan,
    $operationSelection,
    $liveBeforeRows,
);

$json = RollbackPlanSerializer::toJson($rollback);

롤백을 적용하기 전에 RollbackDrift을 사용한 다음, 역연산을 실행하려면 RollbackPlanApplier을 사용하세요.


MergeOperation

Merql\Merge\MergeOperation은 병합에서 해결된 단일 작업입니다.

PHP
$op->type;                         // string: 'insert' | 'update' | 'delete'
$op->table;                        // string
$op->rowKey;                       // string
$op->values;                       // array<string, mixed>
$op->source;                       // string: 'ours' | 'theirs' | 'merged'

유형 상수:

PHP
MergeOperation::TYPE_INSERT;       // 'insert'
MergeOperation::TYPE_UPDATE;       // 'update'
MergeOperation::TYPE_DELETE;       // 'delete'

충돌

Merql\Merge\Conflict은 해결되지 않은 병합 충돌을 나타냅니다.

PHP
$conflict->table();                // string
$conflict->rowKey();               // string
$conflict->type();                 // string: 'update_update' | 'update_delete' | 'delete_update' | 'insert_insert'
$conflict->column();               // ?string (null for structural conflicts)
$conflict->oursValue();            // mixed
$conflict->theirsValue();          // mixed
$conflict->baseValue();            // mixed
$conflict->primaryKey(['id']);     // array<string, string>: decoded from rowKey

충돌 유형

유형 의미
update_update 양쪽에서 같은 열을 서로 다른 값으로 변경함
update_delete ours에서 업데이트한 행을 theirs에서 삭제함
delete_update ours에서 삭제한 행을 theirs에서 업데이트함
insert_insert 양쪽에서 같은 기본 키를 가진 행을 삽입함

ConflictResolver

Merql\Merge\ConflictResolver은 병합 결과의 모든 충돌에 해결 정책을 적용합니다.

resolve(MergeResult $result, ConflictPolicy $policy): MergeResult (정적)

정책에 따라 충돌을 해결한 새 MergeResult을 반환합니다.

PHP
use Merql\Merge\ConflictResolver;
use Merql\Merge\ConflictPolicy;

// Accept all "theirs" values when conflicts arise.
$resolved = ConflictResolver::resolve($result, ConflictPolicy::TheirsWins);

// Accept all "ours" values.
$resolved = ConflictResolver::resolve($result, ConflictPolicy::OursWins);

// Return unchanged (for manual inspection).
$resolved = ConflictResolver::resolve($result, ConflictPolicy::Manual);

ConflictPolicy

Merql\Merge\ConflictPolicy은 세 가지 케이스가 있는 백드 enum입니다.

PHP
use Merql\Merge\ConflictPolicy;

ConflictPolicy::OursWins;          // Resolve all conflicts in favor of "ours"
ConflictPolicy::TheirsWins;        // Resolve all conflicts in favor of "theirs"
ConflictPolicy::Manual;            // Leave conflicts unresolved for manual handling

ColumnMerge

Merql\Merge\ColumnMerge은 단일 행에 대해 열별 병합 로직을 수행합니다. 내부적으로 ThreeWayMerge에서 사용합니다.

merge(string $table, string $rowKey, array $base, array $ours, array $theirs, ?CellMergeConfig $cellMergeConfig = null, array $columnTypes = []): array (정적)

['values' => array, 'conflicts' => list<Conflict>]을 반환합니다.

PHP
use Merql\Merge\ColumnMerge;

$result = ColumnMerge::merge(
    'posts',
    '42',
    ['title' => 'Hello', 'status' => 'draft'],     // base
    ['title' => 'Hello', 'status' => 'published'],  // ours
    ['title' => 'New Title', 'status' => 'draft'],   // theirs
);

// $result['values'] = ['title' => 'New Title', 'status' => 'published']
// $result['conflicts'] = [] (clean merge)

적용기

Merql\Apply\Applier은 데이터베이스에 병합 결과를 실행합니다.

__construct(DatabaseConnection $connection, ?Driver $driver = null)

apply(MergeResult $result, ?Snapshot $base = null): ApplyResult

모든 작업을 단일 트랜잭션에서 실행합니다. 오류가 발생하면 롤백하고 예외를 발생시키는 대신 ApplyResult::errors()에 오류를 보고합니다. 결과에 해결되지 않은 충돌이 있으면 ConflictException을 발생시킵니다.

PHP
use Merql\Apply\Applier;

$applier = new Applier($connection);
$applied = $applier->apply($result);

echo "Rows affected: {$applied->rowsAffected()}\n";
if ($applied->hasErrors()) {
    print_r($applied->errors());
}

외래 키 순서는 자동으로 처리합니다. 삽입은 종속성 순서대로 정렬하고 삭제는 종속성의 역순으로 정렬합니다.


ApplyResult

Merql\Apply\ApplyResult은 적용기가 반환하는 읽기 전용 값 객체입니다.

PHP
$applied->rowsAffected();         // int
$applied->errors();               // list<string>
$applied->hasErrors();            // bool

롤백된 적용 후에도 rowsAffected()에는 실패 전에 실행된 문이 포함됩니다(#4). 이 횟수를 신뢰하기 전에 hasErrors()을 확인하세요.


DryRun

Merql\Apply\DryRun은 아무것도 실행하지 않고 사람이 읽을 수 있는 SQL을 생성합니다.

generate(MergeResult $result, ?Snapshot $base = null, array $fkDependencies = [], ?Driver $driver = null): array (정적)

표시 목적으로 값이 인라인 삽입된 SQL 문자열 목록을 반환합니다.

PHP
use Merql\Apply\DryRun;

$statements = DryRun::generate($result);

foreach ($statements as $sql) {
    echo "{$sql};\n";
}

// Output:
// INSERT INTO `posts` (`id`, `title`) VALUES ('10', 'New Post');
// UPDATE `posts` SET `title` = 'Updated' WHERE `id` = '5';
// DELETE FROM `comments` WHERE `id` = '3';

SqlGenerator

Merql\Apply\SqlGenerator은 매개변수화된 SQL 문을 생성합니다. Applier와 DryRun 모두 내부적으로 사용합니다.

generate(MergeResult $result, ?Snapshot $base = null, array $fkDependencies = [], ?Driver $driver = null): array (정적)

list<array{sql: string, params: list<scalar|null>}>을 반환합니다.

PHP
use Merql\Apply\SqlGenerator;

$statements = SqlGenerator::generate($result);

foreach ($statements as $stmt) {
    $connection->execute($stmt['sql'], $stmt['params']);
}

CellMergeConfig

Merql\CellMerge\CellMergeConfig은 특정 열 또는 열 유형의 셀 수준 병합 전략을 설정합니다.

auto(): self (정적)

편의 팩토리로, TEXT/LONGTEXT 열에는 TextCellMerger을, JSON 열에는 JsonCellMerger을 할당합니다.

PHP
use Merql\CellMerge\CellMergeConfig;

$config = CellMergeConfig::auto();

forColumn(string $column, CellMerger $merger): self

특정 열에 병합기를 할당합니다. 정규화된 이름에는 table.column을, 모든 테이블에는 column만 사용하세요.

PHP
use Merql\CellMerge\CellMergeConfig;
use Merql\CellMerge\TextCellMerger;

$config = (new CellMergeConfig())
    ->forColumn('posts.content', new TextCellMerger())
    ->forColumn('description', new TextCellMerger());

forType(string $typePattern, CellMerger $merger): self

유형 패턴과 일치하는 모든 열에 병합기를 할당합니다(대소문자를 구분하지 않는 부분 문자열 일치).

PHP
use Merql\CellMerge\CellMergeConfig;
use Merql\CellMerge\JsonCellMerger;

$config = (new CellMergeConfig())
    ->forType('json', new JsonCellMerger());

getMerger(string $table, string $column, string $columnType = ''): CellMerger

테이블, 열, 유형에 해당하는 병합기를 조회합니다. 우선순위: 정규화된 열 이름, 정규화되지 않은 열 이름, 유형 패턴, 기본값(불투명).


CellMerger (인터페이스)

Merql\CellMerge\CellMerger은 셀 수준 병합 전략의 인터페이스입니다.

PHP
use Merql\CellMerge\CellMerger;
use Merql\CellMerge\CellMergeResult;

interface CellMerger
{
    public function merge(mixed $base, mixed $ours, mixed $theirs): CellMergeResult;
}

기본 제공 구현은 세 가지입니다.

TextCellMerger

Merql\CellMerge\TextCellMerger은 3방향 줄 단위 diff(파일 콘텐츠에 git이 사용하는 알고리즘)로 여러 줄의 텍스트를 병합합니다. 양쪽에서 서로 다른 줄을 수정하면 깔끔하게 병합됩니다. 같은 줄을 서로 다르게 수정하면 충돌을 보고합니다.

PHP
use Merql\CellMerge\TextCellMerger;

$merger = new TextCellMerger();
$result = $merger->merge(
    "line 1\nline 2\nline 3",
    "line 1\nline 2 modified\nline 3",
    "line 1\nline 2\nline 3 modified",
);
// $result->clean === true
// $result->value === "line 1\nline 2 modified\nline 3 modified"

JsonCellMerger

Merql\CellMerge\JsonCellMerger은 최상위 키를 각각 비교해 JSON 값을 병합합니다. 각 키는 3방향 로직으로 병합합니다. 중첩 객체는 불투명한 JSON 문자열로 비교하며(재귀적으로 병합하지 않음),

PHP
use Merql\CellMerge\JsonCellMerger;

$merger = new JsonCellMerger();
$result = $merger->merge(
    '{"a": 1, "b": 2}',
    '{"a": 10, "b": 2}',        // changed "a"
    '{"a": 1, "b": 20}',         // changed "b"
);
// $result->clean === true
// $result->value === '{"a":10,"b":20}'

OpaqueCellMerger

Merql\CellMerge\OpaqueCellMerger은 기본값입니다. 값을 불투명한 문자열로 취급하며 값이 다르면 항상 충돌을 보고합니다.


CellMergeResult

Merql\CellMerge\CellMergeResult은 모든 셀 병합기의 반환 유형입니다.

PHP
$result->clean;                    // bool: true if merge succeeded
$result->value;                    // mixed: merged value (or ours on conflict)
$result->conflicts;                // int: number of conflicts

// Factory methods.
CellMergeResult::resolved($value);           // clean merge
CellMergeResult::conflict($oursValue, $n);   // conflict with count

TableSchema

Merql\Schema\TableSchema은 테이블 구조를 읽기 전용으로 표현합니다.

PHP
use Merql\Schema\TableSchema;

$schema = new TableSchema(
    name: 'posts',
    columns: ['id' => 'int', 'title' => 'varchar(255)', 'content' => 'text'],
    primaryKey: ['id'],
    uniqueKeys: [['slug']],
);

$schema->name;                     // string
$schema->columns;                  // array<string, string>: name to type
$schema->primaryKey;               // list<string>
$schema->uniqueKeys;               // list<list<string>>
$schema->hasPrimaryKey();          // bool
$schema->hasUniqueKeys();          // bool
$schema->columnNames();            // list<string>

드라이버 시스템

merql은 데이터베이스별 작업을 추상화하기 위해 플러그형 드라이버 시스템을 사용합니다. 드라이버는 연결 드라이버 식별자를 바탕으로 자동 감지됩니다.

Driver (인터페이스)

Merql\Driver\Driver은 모든 데이터베이스 드라이버가 구현하는 규약을 정의합니다.

PHP
interface Driver
{
    public function quoteIdentifier(string $name): string;
    public function listTables(DatabaseConnection $connection): array;
    public function readSchema(DatabaseConnection $connection, string $table): TableSchema;
    public function readForeignKeys(DatabaseConnection $connection): array;
    public function selectAll(string $table): string;
}

기본 제공 구현: MysqlDriver 및 SqliteDriver.

DriverFactory

Merql\Driver\DriverFactory은 연결에서 드라이버를 자동 감지합니다.

PHP
use Merql\Driver\DriverFactory;

$driver = DriverFactory::create($connection);

다른 데이터베이스용 사용자 지정 드라이버 등록:

PHP
DriverFactory::register('pgsql', MyPostgresDriver::class);

필터

TableFilter

Merql\Filter\TableFilter은 glob 패턴을 사용해 테이블을 포함하거나 제외합니다.

PHP
use Merql\Filter\TableFilter;

$filter = TableFilter::exclude(['cache_*', 'sessions', 'temp_*']);
$filter = TableFilter::include(['posts', 'users', 'comments']);

ColumnFilter

Merql\Filter\ColumnFilter은 스냅샷에서 특정 열을 제거합니다.

PHP
use Merql\Filter\ColumnFilter;

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

RowFilter

Merql\Filter\RowFilter은 조건자 함수로 행을 필터링합니다.

PHP
use Merql\Filter\RowFilter;

$filter = RowFilter::create(function (string $table, array $row): bool {
    // Exclude trashed posts.
    if ($table === 'posts' && ($row['status'] ?? '') === 'trash') {
        return false;
    }
    return true;
});

예외

SnapshotException

스냅샷을 찾을 수 없거나 이름이 잘못된 경우 Merql\Exceptions\SnapshotException이 발생합니다.

PHP
SnapshotException::notFound('baseline', '/path/to/baseline.json');

MergeException

병합에 필요한 스냅샷이 없는 경우 Merql\Exceptions\MergeException이 발생합니다.

PHP
MergeException::snapshotRequired('base');

ConflictException

해결되지 않은 충돌이 있는 병합 결과를 적용하려고 하면 Merql\Exceptions\ConflictException이 발생합니다.

PHP
ConflictException::unresolved(3);
// Message: "Cannot apply merge with 3 unresolved conflict(s)"

SchemaException

스냅샷 간 스키마가 다르면 Merql\Exceptions\SchemaException이 발생하거나 수집됩니다.

PHP
SchemaException::mismatch('posts', 'column "tags" exists in current but not in base');

스키마 불일치가 병합을 중단시키지는 않습니다. 불일치는 수집되며 검사할 수 있도록 $result->schemaMismatches()을 통해 제공됩니다.