arkschools/data-input-sheets

从脊类和yml配置文件构建数据表的库

安装: 657

依赖项: 0

建议者: 0

安全性: 0

星星: 0

关注者: 1

分支: 0

类型:package

1.0.0 2016-12-06 16:31 UTC

This package is not auto-updated.

Last update: 2024-09-24 23:28:24 UTC


README

库将自定义数据源抽象为表格的脊,并使用通过yml配置文件定义的列构建数据表。该库处理显示表格和存储用户输入值的逻辑。每一行都可以通过新页面访问,该页面将显示一个表格,其中每个对象列对应一行

说明

使用composer下载

composer require arkschools/data-input-sheets

在内核中启用捆绑

// app/AppKernel.php

public function registerBundles()
{
    $bundles = array(
        // ...
        new Arkschools\DataInputSheets\Bridge\Symfony\DataInputSheetsBundle(),
        // ...
    );
}

创建yml配置

# app/config/config.yml
data_input_sheets:
  extra_column_types:
    color: AppBundle/DataInputSheets/ColumnType/Color  
  sheets:
    cars:
      views:
        "Brand and model":
          columns: ["Brand name", "Model name", "Description", "Color"]
        "Performance":
          columns: ["Brand name", "Top speed", "Acceleration"]
      columns:
        "Brand name": string
        "Model name": string
        "Description": text
        "Top speed": integer
        "Acceleration": float
        "Color": color
  • extra_column_types是一个可选部分,允许通过新的列类型扩展库
  • 为每个自定义数据源创建一个表
  • 创建所需的所有视图,列可以属于多个视图
  • 为表创建所有需要的列,并设置每个列的适当类型

高级yml配置

有时一个视图有太多的列,不能为每个脊值显示为表格,在这种情况下,视图可以声明如下

  sheets:
    cars:
      views:
        "Brand and model":
            columns:
                - "Brand name"
                - "Model name"
                - { column: "Description", hide: true }
                - "Color"

当访问单个脊元素时,将显示所有列

可以通过配置锁定列以防止修改,这在您不希望用户修改某些列的时期非常有用

# app/config/config.yml
data_input_sheets:
  sheets:
    cars:
      views:
        "Brand and model":
          columns: ["Brand name", "Model name", "Description"]
      columns:
        "Brand name":
            type: string
            read_only: true
        "Model name": string
        "Description": text

创建脊及其服务

这是最重要的文件,从中查询脊列数据,我们需要创建一个扩展自 Arkschools\DataInputSheets\Spine 的类,并添加我们的逻辑到 load__construct

class CarsSpine extends Arkschools\DataInputSheets\Spine
{
    private $carRepository;

    public function __construct(CarRepository $carRepository)
    {
        parent::__construct(
            'Available Cars',
            []
        );

        $this->carRepository = $carRepository;
    }

    protected function load()
    {
        if (empty($this->spine)) {
            $this->spine = [];

            $cars = $this->carRepository->findAll();

            foreach ($cars as $car) {
                $this->spine[$car->id()] = $car->getName();
            }

            asort($this->spine);
        }

        return $this;
    }
}

下一步是创建一个标记了我们的脊的有标签的服务,为此,添加标签 data_input_sheets.spine 以标记服务为数据输入表脊,并通过sheet属性将其与配置的表链接

    AppBundle\DataInputSheets\CarsSpine:
        tags:
            - { name: data_input_sheets.spine, sheet: cars }

更新您的数据库模式

php bin/console doctrine:schema:update --force

创建/重用控制器和视图

提供了一个基本控制器和模板,但我们建议根据项目需求构建自己的一个

如果您想使用默认的,只需添加以下路由配置

# app/config/routing.yml
data_input_sheets:
    resource: "@DataInputSheetsBundle/Controller/DataInputSheetsController"
    type:     annotation

可用的列类型

integer

  • 使用输入html元素捕获数据
  • 将输入值转换为整数并将其存储在数据库中作为整数

float

  • 使用输入html元素捕获数据
  • 将输入值转换为浮点数并将其存储在数据库中作为浮点数

string

  • 使用输入html元素捕获数据
  • 将输入值存储在数据库中作为字符串,将空字符串转换为null值

text

  • 使用textarea html元素捕获数据
  • 将输入值存储在数据库中作为字符串,将空字符串转换为null值

date

  • 使用输入html元素捕获数据
  • 将输入值存储在数据库中作为datetime对象,将空字符串或格式不正确的日期转换为null值
  • 默认情况下,它使用PHP DateTime格式 d/m/Y H:i 并向用户显示提示 DD/MM/YYYY 这些值可以通过配置更改,如下所示
    - column: 'Manufacturing Date'
      type:   'date'
      option: ['d/m/Y', 'DD/MM/YYYY']

yes/no

  • 使用带有选项 '' => '', Y' => yes 和 'N' => no 的选择元素
  • 使用 '', 'Y' 或 'N' 值,将它们转换为null或布尔值,并将它们存储在数据库中为空或布尔值

gender

  • 使用带有选项 '' => '', 'M' => M 和 'F' => F 的选择元素
  • 使用 '', M' 或 'F' 值并将它们存储在数据库中为字符串或null

serviceList

  • 需要使用包含数组的属性选项,第一个元素是服务名称,第二个元素是该服务的操作名称
    - column: 'Car Design'
      type:   'serviceList'
      option: ['app.data_input_sheets.car_lists', 'getCarDesigns']
  • 应存在一个名为 app.data_input_sheets.car_lists 的服务,其中包含一个名为 getCarDesigns 的方法,该方法将返回一个包含允许值的数组
class CarLists
{
    public function getCarDesigns()
    {
        return ['Coupé', 'Sedan', 'SUV', 'Crossover'];
    }
}
  • 使用 select 元素,从列表中添加选项,并在开始处添加一个空选项
  • 将选定的值以字符串的形式存储在数据库中,将空字符串转换为 null

->methodName

  • methodName 可以是任何可以用于 PHP 对象方法名称的字符串
  • 要求 load 方法中的 Spine 将 spine 对象数组存储在 spineObjects 属性中,并按 spineId 索引
    protected function load()
    {
        if (null === $this->spine) {
            $this->spine        = [];
            $this->spineObjects = [];

            foreach ($this->cars as $car) {
                $this->spine[$car->id] = $car->getModel();
                $this->spineObjects[$car->id] = $car;
            }

            asort($this->spine);
        }

        return $this;
    }
  • Spine 对象应有一个名为 methodName 的方法,并且可以通过 option 属性可选地传递给该方法参数
    - column: 'Car length'
      type:   '->getLength'
      option: ['meters']
  • 仅用于显示值,不会在数据库中存储任何内容

高级用法

默认情况下,此库允许用户将数据存储在由库创建的表中,但此行为可以更改

未存储在默认实体管理器中

可以通过在 parameters.yml 文件中添加参数来设置不同的实体管理器

    data_input_sheets.entity_manager_name: data

存储在库用户控制表中

可以将自定义表设置为存储特定工作表的数据输入

创建一个与 data_input_sheets_cell 相同结构的表,例如称为 'cars',并相应地修改 spine

class CarsSpine extends Arkschools\DataInputSheets\Spine
{
    private $carRepository;

    public function __construct(CarRepository $carRepository)
    {
        parent::__construct(
            'Available Cars',
             [],
             'cars'
         );

        $this->carRepository = $carRepository;
    }
...

从现在起,该工作表数据将存储在自定义表中

存储在库用户控制实体中

可以通过设置一个实体类来获得更多控制,该实体类将存储特定工作表的数据

创建所需的实体类,包含工作表所需的列数属性,例如需要 idbrandmodeldescription,请考虑设置实体属性类型时列的类型

然后相应地修改 spine 类

class CarsSpine extends Arkschools\DataInputSheets\Spine
{
    private $carRepository;

    public function __construct(CarRepository $carRepository)
    {
        parent::__construct(
            'Available Cars',
             [],
             null
             AppBundle\Entity\Car::class,
             'id'
         );

        $this->carRepository = $carRepository;
    }
...

对于上一个示例,这将应该是新的配置文件内容

# app/config/config.yml
data_input_sheets:
  sheets:
    cars:
      views:
        "Brand and model": ["Brand name", "Model name", "Description"]
      columns:
        "Brand name":
            type: string
            field: brand
        "Model name":
            type: string
            field: model
        "Description": 
            type: text
            field: description

创建自定义列类型

如上所述,可以通过配置参数 extra_column_types 添加新列

  • 扩展 Column 抽象类并实现抽象方法
  • 根据需要扩展其他方法
  • 使用 extra_column_types 配置添加新创建的类

将自定义列类型作为服务创建

    AppBundle\DataInputSheets\ColumnState:
        tags:
            - { name: data_input_sheets.column, type: state }
  • 标签 data_input_sheets.column 标记服务为数据输入表额外列,type 属性将其与新增的列类型链接
  • 列类必须扩展 Arkschools\DataInputSheets\ColumnType\AbstractColumn 并添加至少 __construct 的逻辑

在视图级别过滤 spine

可以在每个视图中添加过滤器,以限制在该视图中显示的 spine 元素

  sheets:
    cars:
      views:
        "Classics":
          filters: {age: '>25'}
          columns: ["Brand name", "Model name", "Description", "Color"]

在上一个示例中,“Classics” 视图将仅显示 25 年以上的汽车,为了实现这一点,我们需要修改 spine 类以具有默认过滤器并使用它进行加载

class CarsSpine extends Arkschools\DataInputSheets\Spine
{
    private $carRepository;

    public function __construct(CarRepository $carRepository)
    {
        parent::__construct(
            'Available Cars',
            []
        );

        $this->carRepository = $carRepository;
    }

    protected function defaultFilter()
    {
        return ['age' => null];
    }
        
    protected function load()
    {
        if (empty($this->spine) || $this->filtersChanged) {
            $this->spine = [];
            
            if (empty($this->filters['age']) {
                $cars = $this->carRepository->findAll();
            else {
                $cars = $this->carRepository->findByAge($this->filters['age']);
            }

            foreach ($cars as $car) {
                $this->spine[$car->id()] = $car->getName();
            }

            $this->filtersChanged = false;
            asort($this->spine);
        }

        return $this;
    }
}

通过用户选择过滤 spine

除了可以在配置文件视图级别添加的过滤器外,还可以在选择视图并在显示其内容之前添加自定义选择器。此选择器可用于进一步缩小在视图中显示的 spine 元素。

正如我们之前看到的,一个Spine可以通过扩展defaultFilter方法来添加一个配置级别的过滤器,以及通过load方法来使用它。这个新过滤器背后的想法是将用户选择过滤器添加到这个地方。

为了实现这一点,我们需要创建一个新的类,它继承自Arkschools\DataInputSheets\Selector\AbstractSelector

class DealerSelector extends Arkschools\DataInputSheets\Selector\AbstractSelector
{
    const DEALER = 'dealer';

    private $dealerRepository;

    public function __construct(DealerRepository $dealerRepository)
    {
        $this->dealerRepository = $dealerRepository;
        $this->filters          = [self::DEALER => null];
    }

    public function render(\Twig_Environment $twig, array $filters): string
    {
        // $filters contains filters that are declared in the spine, like age in the previous example
        
        $dealers = $this->dealerRepository->findAll();

        return $twig->render(
            'AppBundle:selector:dealer_selector.html.twig',
            ['dealers' => $dealers]
        );
    }

    public function applyFilters(Request $request): bool
    {
        $dealer  = $request->query->get(self::DEALER);
        $changed = false;

        if ($this->filters[self::DEALER] !== $dealer) {
            $this->filters[self::DEALER] = $dealer;
            $changed = true;
        }

        return $changed;
    }

    public function isRequired(): bool
    {
        return empty($this->filters[self::DEALER]);
    }
}

然后添加一个带有data_input_sheets.selector标签的selector服务,以及将在配置中使用的类型

    AppBundle\DataInputSheets\DealerSelector:
        tags:
            - { name: data_input_sheets.selector, type: dealer }

下一步是创建一个Twig模板来显示这个选择器,它应该像这样

<form name="selector" method="get">
    <label for="{{ constant('AppBundle\\DataInputSheets\\DealerSelector::DEALER') }}">Dealer:</label>
    <select class="form-control" name="{{ constant('AppBundle\\DataInputSheets\\DealerSelector::DEALER') }}">
        <option value="">Dealers:</option>
        {% for dealer in dealers %}
            <option value="{{ dealer.code }}">{{ dealer.name }}</option>
        {% endfor %}
    </select>
    <button type="submit" class="btn btn-primary">Continue</button>
</form>

最后,修改表单配置文件以使用新的选择器

# app/config/config.yml
data_input_sheets:
  sheets:
    cars:
      selector: 'dealer'
      views:
        "Brand and model":
          columns: ["Brand name", "Model name", "Description"]
      columns:
        "Brand name": string
        "Model name": string
        "Description": text