Skip to content

Latest commit

 

History

History
246 lines (165 loc) · 11.1 KB

File metadata and controls

246 lines (165 loc) · 11.1 KB

使い方

bypassflow/value-kit の accessor と structure ヘルパーを、対象データの性質ごとに使い分けるための補助文書です。利用者向けの入口は ArrayPathAccessor を正本としつつ、厳密版、object 専用版、mixed ヘルパー、Structure 系をどう選ぶかをまとめます。

ArrayPathAccessor

ArrayPathAccessor は、配列系データを普段使いで触るための利用者向け正本 accessor です。arrayArrayAccess の両方を受けます。

use bypassflow\ValueKit\Access\ArrayPathAccessor;
use ArrayObject;

$payload = new ArrayObject([
    'user' => ['name' => 'alice'],
]);

$name = ArrayPathAccessor::get($payload, ['user', 'name']);

ArrayAccessarray と同じ形で使えますが、存在判定は offsetExists()isset() の実装に従います。class 側の実装次第で native array と差が出ることがあるため、厳密に native array へ寄せたい場合は StrictArrayPathAccessor を使います。

Accessor 比較

4 種 accessor の差分を最初に把握したい時は、対象型と途中パス生成の違いを見ると選びやすくなります。

推奨系統

  • ArrayPathAccessor
    • 利用者向けの正本です
    • array / ArrayAccess を受けます
    • 途中パスは配列で生成します
  • StrictArrayPathAccessor
    • native array のみを受けます
    • 途中パスは配列で生成します
  • ObjectPathAccessor
    • object public property のみを受けます
    • magic method や private / protected property は扱いません
    • stdClass の途中パスは object 連鎖で生成します

下位ヘルパー

  • PathAccessor
    • mixed ヘルパーです
    • array / ArrayAccess / object public property を受けます
    • mixed payload を 1 本で辿りたい時にだけ選びます
    • stdClass の途中パスは object 連鎖で生成します

Structure 比較

Structure 系は、パス 1 本を読む代わりに全体の形を変えたい時に選びます。用途ごとに 3 本へ分かれています。

  • KeyFlattener
    • nested array を flat key/value へ潰します
    • 一覧化や field map 生成に向きます
  • RecursiveFilter
    • nested value 全体へ predicate をかけます
    • cleanup や export 前の不要値除去に向きます
  • NestedGrouper
    • row list を grouped map へ再構成します
    • row shaping や grouped report に向きます

Structure の返却形

Structure 系では、ヘルパーごとに返り値の形が異なります。使い分けを先に固定すると迷いにくくなります。

  • KeyFlattener
    • 平坦な key map
    • path/value の組
  • RecursiveFilter
    • keepStdClass() の同形返却
    • keep() / keepObjectAsArray() の配列返却
  • NestedGrouper
    • grouped list
    • grouped one-map

Structure の policy

Structure 系の公開語彙は、なるべく skipnormalizekeep firstkeep lastthrow on collision に寄せています。ヘルパーごとに挙動は違っても、説明語を揃えておくと選び分けやすくなります。

StrictArrayPathAccessor

StrictArrayPathAccessor は、native array だけに入力を絞りたい時に使います。ArrayAccess は受けません。

use bypassflow\ValueKit\Access\StrictArrayPathAccessor;

$payload = [
    'user' => ['name' => 'alice'],
];

$updated = StrictArrayPathAccessor::set($payload, ['user', 'name'], 'bob');

ObjectPathAccessor

ObjectPathAccessor は、object public property だけを辿りたい時の特化版です。stdClass では途中 path の生成も行えます。

use bypassflow\ValueKit\Access\ObjectPathAccessor;

$payload = (object) [
    'user' => (object) ['name' => 'alice'],
];

$name = ObjectPathAccessor::get($payload, ['user', 'name']);

この accessor は public property のみを対象にします。magic method、private / protected property、reflection 的な access は行いません。

PathAccessor

PathAccessor は、arrayArrayAccess、object public property が混在するデータを 1 本で辿りたい時の下位ヘルパーです。公開面の最初の入口というより、mixed payload をそのまま扱う必要がある時の選択肢として使います。

use bypassflow\ValueKit\Access\PathAccessor;

$payload = (object) [
    'user' => ['name' => 'alice'],
];

$name = PathAccessor::get($payload, ['user', 'name']);

KeyFlattener

KeyFlattener は、nested array 全体を平坦な key/value へ潰したい時の Structure 系の入口です。パス 1 本を読むのではなく、全体の形を一覧化したい場面で使います。

use bypassflow\ValueKit\Structure\KeyFlattener;

$dot = KeyFlattener::dot([
    'payload' => [
        'user' => ['name' => 'alice'],
    ],
]);

$paths = KeyFlattener::paths([
    'payload' => [
        'roles' => ['admin', 'editor'],
    ],
]);

dot() は平坦な key map を返し、paths() は path/value の組の list を返します。empty array は leaf として保持します。flatten() を使うと separator、leaf transform、throw on collision policy を切り替えられ、escaped() を使うと escape 付き key string を返せます。

dot key はパス要素を . で連結した表現です。そのため、要素自体に . を含む場合は見た目の曖昧さが残ります。escaped() はこの曖昧さを緩和する separator escape ヘルパーです。reversible encoding はまだ別軸で、必要なら KeyFlattener 本体ではなく別 helper として切り出す余地があります。

root や branch には array、走査可能な ArrayAccess、object public property を使えます。ArrayAccess は iteration できる実装を前提にしています。

Flat key map 系メソッド

dot()flatten()escaped() は平坦な key map を返すメソッド群です。最短では dot() を使い、separator や collision policy を触りたい時だけ flatten()、separator escape が必要な時だけ escaped() を選ぶ形が基本です。

path/value pair 系メソッド

paths() は path/value の組を返すメソッドです。フラットな文字列 key ではなく path list をそのまま扱いたい時だけこちらを選びます。

RecursiveFilter

RecursiveFilter は、nested value 全体へ predicate をかけて不要 leaf を落としたい時のヘルパーです。汎用の keep() と、よく使う fixed helper を用意しています。

use bypassflow\ValueKit\Structure\RecursiveFilter;

$filtered = RecursiveFilter::removeNullAndEmptyString([
    'payload' => [
        'name' => '',
        'email' => null,
        'active' => true,
    ],
]);

keep() の predicate は leaf 値だけに適用します。branch が filter の結果として空になった場合は削除し、元から空の array leaf だけは preserveEmptyArrays 引数で保持できます。branch 自体にも policy を掛けたい時は branchPredicate を使います。fixed helper として removeFalsy()removeEmpty() も使えます。

object support は返却形を分けています。

  • keepObjectAsArray()
    • object public property を読んで配列で返します
  • keepStdClass()
    • stdClass-only rebuild です
    • nested branch には arraystdClass だけを許可します

branchPredicate のシグネチャは fn (array $branch, array $path, bool $explicitlyEmpty): bool です。

基本 keep メソッド群

keep()keepObjectAsArray()keepStdClass() は基本 keep メソッド群です。keep() は array 正本、keepObjectAsArray() は object public property を読んで配列で返す variant、keepStdClass() は同じ形を維持したい時の variant として使い分けます。

固定 policy メソッド群

removeNull()removeEmptyString()removeNullAndEmptyString()removeFalsy()removeEmpty() は固定 policy メソッド群です。custom predicate を書かずに固定 policy で掃除したい時に使います。

NestedGrouper

NestedGrouper は、row list を group key の組み合わせで nested map へ再構成したい時のヘルパーです。grouped list と grouped one-map の返り方を持ち、strict な groupBy() に加えて、missing row を飛ばす variant、keep first、keep last、non-scalar key を正規化する variant も使えます。

use bypassflow\ValueKit\Structure\NestedGrouper;

$grouped = NestedGrouper::groupBy(
    [
        ['group' => 'staff', 'status' => 'active', 'name' => 'alice'],
        ['group' => 'staff', 'status' => 'inactive', 'name' => 'bob'],
    ],
    ['group', 'status'],
);

selector は row 内の key、key path、または row を受ける callable を使えます。公開面の正本は path selector で、callable selector は寛容対応です。row は array、走査可能な ArrayAccess、object public property を受けます。strict 系では missing path は default bucket へ落とさず例外にし、group key も int|string に固定します。必要なら groupBySkippingMissing()groupByNormalized()groupOneBy()groupLastBy() で policy を切り替えられます。group order は PHP 配列の insertion order をそのまま保持します。

grouped list 系メソッド群

groupBy()groupBySkippingMissing()groupByNormalized() は grouped list 系メソッドです。row list をそのまま grouped list へ落としたい時の正本で、skip missingnormalize key はこの系統側の policy variant です。

grouped one-map 系メソッド群

groupOneBy()groupOneBySkippingMissing()groupLastBy()groupLastBySkippingMissing() は grouped one-map 系メソッドです。groupOneBy()keep firstgroupLastBy()keep last を選ぶ variant として扱います。

存在判定

存在判定では、パスの存在と isset() 相当の判定を分けています。

use bypassflow\ValueKit\Access\PathAccessor;

$payload = ['user' => ['name' => null]];

PathAccessor::existsPath($payload, ['user', 'name']); // true
PathAccessor::issetPath($payload, ['user', 'name']);  // false
PathAccessor::has($payload, ['user', 'name']);        // true

パス配列は list<int|string> を正本としますが、実装は寛容です。非 list 配列を渡した場合も array_values() で詰め直して処理するため、連想配列パスでは key ではなく value が要素として使われます。

更新操作

set()remove() は、対象型に応じた accessor を選ぶと挙動が読みやすくなります。普段使いは ArrayPathAccessor、厳密な型制約を掛けたい時は StrictArrayPathAccessorObjectPathAccessor、mixed payload をまとめて扱う必要がある時だけ PathAccessor を選ぶのが基本です。

use bypassflow\ValueKit\Access\ArrayPathAccessor;

$payload = ['user' => ['name' => 'alice', 'email' => 'alice@example.com']];

$updated = ArrayPathAccessor::remove($payload, ['user', 'email']);