定制 akosnoavek/models-data-extractor 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

akosnoavek/models-data-extractor

Composer 安装命令:

composer require akosnoavek/models-data-extractor

包简介

Extract information from models or other data structures based on a given schema to the formats you prefer

README 文档

README

Extract information from Eloquent models, plain PHP arrays, or generic objects, based on a declarative schema, and render the result as an IteratorElement tree, a plain array, JSON, HTML, CSV, or XLSX.

Installation

composer require akosnoavek/models-data-extractor

The package ships a Laravel service provider (AkosNoavek\DataExtractor\Support\ServiceProvider) and a DataExtractor facade, both auto-discoverable via the laravel.providers / laravel.aliases entries in composer.json.

Configuration

Publish the config file to customize it:

php artisan vendor:publish --tag=config

This creates config/data_extractor.php:

return [
    // Custom base classes that should be treated as a single Eloquent model
    // when resolved through a section's "root" (see below).
    'model_classes' => [],

    // Default format used to render fields declared with "date" => true.
    'date_format' => 'd/m/Y',
];
  • model_classes — a root section decides whether it points at a single related model or a collection by inspecting the parent class of the resolved value: Illuminate\Database\Eloquent\Model is always recognized. If your application extends a shared intermediate base class (e.g. App\Models\BaseModel extends Model), list that class's FQCN here so a root resolving to one of its instances is treated as a single model instead of being (mis-)treated as a collection.

    This matters because the failure mode is silent: an Eloquent model instance is not iterable and exposes no public properties, so if its base class isn't recognized, the root section's foreach produces zero clones and the entire section silently disappears from the output — no error is raised.

    // config/data_extractor.php
    return [
        'model_classes' => [\App\Models\BaseModel::class],
    ];
  • date_format — the date()/Carbon::format() pattern applied to any field declared with "date" => true (default d/m/Y).

Core concepts

Every extraction involves two independent things:

  1. The target — the data you are extracting from. It can be an Eloquent Model, a plain PHP array, or a generic object (e.g. the result of json_decode).
  2. The schema — a declarative description of what to extract and how to label it: which fields to pull out, how to group them into sections, and how to format them.
use AkosNoavek\DataExtractor\Facades\DataExtractor;

$array = DataExtractor::make($target)->toArray(data: $schema);

DataExtractor::make($target) binds the target; the terminal method (toArray, toJson, toHtml, toCsv, toXlsx, extract) binds the schema and runs the extraction.

The schema data structure

A schema is a nested associative array (or its JSON equivalent) built from two kinds of nodes: fields and sections.

Field

A leaf node that reads a single value out of the target.

[
    "path"  => "field_one",      // required — dot-notation path into the target
    "label" => "Field One",      // required — human-readable label used in output
    "date"  => false,            // optional — format the value as a date (d/m/Y)
    "view"  => null,             // optional — Blade view name for custom HTML rendering
    "evaluate_when_empty" => true, // optional — keep the field even if its value is empty
    "extra_attributes" => null,  // optional — arbitrary array passed through to the output/view
]
  • path supports dot notation for nested properties/relations, e.g. "path" => "address.city".
  • date parses the resolved value with Carbon and formats it as d/m/Y (falls back to an empty string if it can't be parsed).
  • evaluate_when_empty: false removes the field from the output entirely when its resolved value is empty — useful for optional data.

Section

A node that groups other fields/sections together under a label.

[
    "type"   => "section",       // required — marks this node as a section
    "label"  => "Section label", // required
    "fields" => [                // required, non-empty — child fields/sections, keyed by
                                  // any array key (only used while building the schema)
        "field_one" => ["path" => "field_one", "label" => "Field One"],
        "field_two" => ["path" => "field_two", "label" => "Field Two"],
    ],
]

Sections can be nested arbitrarily deep — a section's fields array can itself contain other sections.

Internally, every node (field or section) is parsed into an AkosNoavek\DataExtractor\Iterators\IteratorElement. The keys of the fields array are only used as PHP array keys while you author the schema; they are not part of the output. If a child node doesn't declare its own label, its schema key is used as the label.

Root sections (relations / collections)

A section can pull its target from a relation or nested collection on the parent target, instead of the outer target, via root:

[
    "type"  => "section",
    "label" => "Manager",
    "root"  => "manager",        // path to a single related model
    "fields" => [
        "manager_name" => ["path" => "name", "label" => "Manager name"],
    ],
]
  • If root resolves to a single related model (or a plain array/object), the section's fields are simply resolved against that related record.
  • If root resolves to a collection (e.g. a hasMany relation, or an array of arrays), the section is cloned once per item, in order, and each clone's fields are resolved against that item. This means a schema with a single root section template can expand into N sections/rows in the output.

A resolved Eloquent model is only recognized as a "single related model" if its base class is Illuminate\Database\Eloquent\Model or is registered via data_extractor.model_classes — otherwise it is (mis-)treated as an empty collection and the whole section silently disappears from the output. If your models extend a shared base class, make sure to register it.

$parent->setRelation('children', new Collection([$child_one, $child_two]));

$schema = [
    "type" => "section",
    "label" => "outer",
    "fields" => [
        "children_section" => [
            "type" => "section",
            "label" => "Child section",
            "root" => "children",
            "fields" => [
                "child_name" => ["path" => "name", "label" => "Child name"],
            ],
        ],
    ],
];
  • toArray() / toJson() / toHtml() / extract() reflect this naturally: you get one section/row per related record.
  • toCsv() / toXlsx() are flat, single-row formats. When multiple clones share the same label (as they do here, since they come from the same template), their values are merged into a single column, joined with "; " (e.g. "Child One; Child Two") rather than overwriting one another.

Multi-value fields (.*. path syntax)

A single field can also flatten a to-many relation into one value, without declaring a nested section, using .*. in its path:

["path" => "children.*.name", "label" => "Children names"]

This resolves name on every item of the children relation/collection and joins them into one string, using the separator configured for the output format (toHtml/toCsv/toXlsx/toArray use "<br>").

Providing the schema: inline array vs. JSON file

Every terminal method (extract, toArray, toJson, toHtml, toCsv, toXlsx) accepts the schema in one of two equivalent ways:

1. As an inline PHP array, via the data argument:

$result = DataExtractor::make($target)->toArray(data: [
    "type" => "section",
    "label" => "Section label",
    "fields" => [
        "field_one" => ["path" => "field_one", "label" => "Field One"],
    ],
]);

Under the hood, the array is json_encoded to a temporary file and read back as JSON — so an inline array and a JSON file are handled identically.

2. As a path to a JSON schema file, via the filename argument:

// schema.json
// {
//     "type": "section",
//     "label": "Section label",
//     "fields": {
//         "field_one": { "path": "field_one", "label": "Field One" }
//     }
// }

$result = DataExtractor::make($target)->toArray(filename: base_path('schemas/schema.json'));

You must pass at least one of data or filename — an exception is thrown if neither is provided. If both are given, data takes precedence.

A JSON file can also contain multiple named schemas (top-level keys), and you extract a specific one via the section argument:

// schema.json: { "employees": { "type": "section", ... }, "managers": { "type": "section", ... } }

DataExtractor::make($target)->toArray(filename: base_path('schemas/schema.json'), section: 'employees');

Working with different target types

The same schema works unchanged against any of the three supported target shapes — only how path is resolved differs internally (array key lookup vs. object/model property access):

use Illuminate\Database\Eloquent\Model;

// Eloquent model
$model = SomeModel::find(1);
DataExtractor::make($model)->toArray(data: $schema);

// Plain array
$data = ["field_one" => "value one"];
DataExtractor::make($data)->toArray(data: $schema);

// Generic object (e.g. decoded JSON)
$object = json_decode(json_encode(["field_one" => "value one"]));
DataExtractor::make($object)->toArray(data: $schema);

Output formats

$schema = [
    "type" => "section",
    "label" => "Section label",
    "fields" => [
        "field_one" => ["path" => "field_one", "label" => "Field One"],
        "field_two" => ["path" => "field_two", "label" => "Field Two"],
    ],
];

$el    = DataExtractor::make($target)->extract(data: $schema); // AkosNoavek\DataExtractor\Iterators\IteratorElement tree
$array = DataExtractor::make($target)->toArray(data: $schema); // nested PHP array
$json  = DataExtractor::make($target)->toJson(data: $schema);  // JSON string
$html  = DataExtractor::make($target)->toHtml(data: $schema);  // rendered HTML (Blade)
$path  = DataExtractor::make($target)->toCsv(data: $schema);   // path to a generated .csv file
$path  = DataExtractor::make($target)->toXlsx(data: $schema);  // path to a generated .xlsx file
  • extract() returns the raw IteratorElement tree (each element exposes type, label, data, path, fields, root, etc.) — useful when you want to post-process the result yourself.
  • toArray() / toJson() return a nested structure of {label, value, ...} leaves grouped under {label, data: [...]} sections, mirroring the schema's shape (including root-generated clones).
  • toHtml() renders an unordered list per section by default; a field/section can opt into a custom Blade view for full control over its markup.
  • toCsv() / toXlsx() flatten the whole tree into a single header row + single data row — one column per unique label. See Root sections above for how duplicate labels (typically from a collection root) are merged.

Running the tests

composer test
# or
vendor/bin/pest

统计信息

  • 总下载量: 0
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 1
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-07-10

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固