clickbar/ag-grid-laravel

Laravel的AG Grid服务器端适配器。

0.2.1 2023-10-05 08:36 UTC

This package is auto-updated.

Last update: 2024-09-24 11:48:17 UTC


README

Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status Total Downloads

此软件包实现了对AG Grid的服务器端适配器,支持过滤、排序、导出和服务器端选择。

安装

您可以通过composer安装此包

composer require clickbar/ag-grid-laravel

您可以使用以下命令发布配置文件

php artisan vendor:publish --tag="ag-grid-laravel-config"

这是发布配置文件的内容

return [
    /*
     * The class that contains the provider for determining the timezone 
     * to use for DateTime formatting in exports.
     */
    'export_timezone_provider' => \Clickbar\AgGrid\AgGridDefaultExportTimezoneProvider::class
];

用法

查询资源

简单地接受控制器中的AgGridGetRowsRequest,并返回您想要查询的模型的AgGridQueryBuilder实例。过滤、排序和导出将自动为您处理。您还可以向查询构建器传递一个JSON资源来包装您的模型。

class FlamingoGridController extends Controller
{
    public function __invoke(AgGridGetRowsRequest $request): AgGridQueryBuilder
    {
        $query = Flamingo::query()
            ->with(['keeper'])
            ->orderByDesc('id');

        return AgGridQueryBuilder::forRequest($request, $query)
            ->resource(FlamingoResource::class);
    }
}

设置过滤值

当使用具有serverSide行模型的AG Grid时,您负责提供设置值过滤器的值(前端只知道整个数据的一个子集,因此无法知道所有可能的设置值)。

class FlamingoGridSetValuesController extends Controller
{
    public function __invoke(AgGridSetValuesRequest $request)
    {
        $query = Flamingo::query()
            ->with(['keeper']);
    
        return AgGridQueryBuilder::forSetValuesRequest($request, $query)
            ->toSetValues(['*']);
    }
}

请参阅AgGridSetValuesRequest类以获取请求的结构。

重要
您需要将可以检索设置过滤值的列列入白名单。这可以通过提供一个包含列/点关系的数组来完成。

->toSetValues(['name', 'kepper.name']);

如果您的模型不公开任何关系或敏感列,您也可以使用['*']作为通配符。

以下是前端实现的示例

const colDef = {
    filterParams: {
        excelMode: 'windows',
        values: (parameters: SetFilterValuesFuncParams) => {
            axios
                .post('url',
                    {
                        column,
                        filterModel: parameters.api.getFilterModel(),
                    },
                )
                .then((response) => {
                    parameters.success(response.data)
                }).catch(() => {
                parameters.success([])
            })
        },
    },
}

服务器端选择

当使用具有serverSide行模型的AG Grid时,在执行批处理操作时,您不能只是将选定的ID传递给服务器。在这种情况下,您可以将网格的当前选择状态传递给服务器,并在那里解决选择。

为此,请将以下内容添加到您的请求中

class FeedFlamingosRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'selection' => ['required', new AgGridSelection()],
            'food_type' => ['required', 'string'],
        ];
    }
}

在您的控制器中,使用AgGridQueryBuilder来解决选择

class FeedFlamingosController extends Controller
{
    public function __invoke(FeedFlamingsRequest $request): AgGridQueryBuilder
    {
        $flamingos = AgGridQueryBuilder::forSelection($request->validated('selection'))->get();

        foreach($flamingos as $flamingo){
            $flamingo->feed($request->validated('food_type'));
        }
        
        return $flamingos;
    }
}

导出

要为您的模型启用服务器端导出,您必须实现AgGridExportable接口。之后,您只需将exportFormat作为请求的一部分传递给网格控制器,库将处理将您的模型转换为Excel、CSV或TSV文件。

class Flamingo extends Model implements AgGridExportable {

    // ... your model definitions
    
    public static function getAgGridColumnDefinitions(): array
    {
        return [
            new AgGridColumnDefinition(
                'id',
                __('ID'),
            ),
            new AgGridColumnDefinition(
                'name',
                __('Name'),
            ),
            new AgGridColumnDefinition(
                'keeper_id',
                __('Keeper'),
                null,
                fn ($data) => $data->keeper->name,
            ),
            new AgGridColumnDefinition(
                'created_at',
                __('Created At'),
                new AgGridDateFormatter(),
            ),
        ];
    }

}

自定义过滤器

有时您可能需要向查询添加自定义过滤作用域或其他约束,这些约束超出了标准AG Grid过滤器的范围。在这种情况下,您可以在请求的customFilters对象中填充自己的数据。在后端,您的模型必须实现如下的AgGridCustomFilterable接口

class Flamingo extends Model implements AgGridCustomFilterable {

    use SoftDeletes;

    // ... your model definitions
    
    public function applyAgGridCustomFilters(Builder $query, array $filters): void
    {
        $query->when($filters['showTrashed'] ?? false, function ($query) {
            return $query->withTrashed();
        });
    }
}

类型定义

您可以使用以下TypeScript类型定义作为在前端实现请求的参考

interface AgGridSelection {
    rowModel: 'serverSide' | 'clientSide'
    selectAll: boolean
    toggledNodes: (string | number)[]
    filterModel?: any
    customFilters?: any
}

interface AgGridGetRowsRequest extends IServerSideGetRowsRequest {
    exportFormat?: 'excel' | 'csv' | 'tsv'
    exportColumns?: string[]
    customFilters?: any
}

interface AgGridGetRowsResponse<T> {
    total: number
    data: T[]
}

前端实现

您可以使用任何您选择的Frontend技术或框架。但是,以下是一些可以作为您自己实现的起点使用的示例

创建数据源

为了使用服务器端行模型,您必须创建一个数据源。以下是一个示例实现

 function makeDataSource<T>(
    url: string,
    customFilters?: Record<string, any>
): IServerSideDatasource {
    return {
        // called by the grid when more rows are required
        async getRows(parameters) {
            const request = {
                ...parameters.request,
                customFilters,
            }
            // get data for request from server
            try {
                const response = await axios.post<AgGridGetRowsResponse<T>>(url, request)
                parameters.success({
                    rowData: response.data.data,
                    rowCount: response.data.total,
                })
            } catch {
                parameters.fail()
            }
        },
    }
}

触发服务器端导出

AG Grid默认不实现服务器端导出。但是,您可以在自定义上下文菜单中创建一个按钮或其他位置来触发服务器端导出。处理函数可能看起来像这样

async function exportServerSide(grid: GridApi, format: 'excel' | 'csv' | 'tsv', onlySelected: boolean) {
    // using a private api here to oget the ssrm parameters
    const parameters = api.getModel().getRootStore().getSsrmParams()
    // only request the visible columns
    const cols = columnApi?.getAllDisplayedColumns().map((column) => column.getColId())
    // download the file
    const response = await axios.post(
        props.dataSourceUrl!,
        {
            ...parameters,
            ...(onlySelected ? api.getServerSideSelectionState() : {}),
            exportFormat: format,
            exportColumns: cols,
        },
        {
            responseType: 'blob',
        }
    )
    // create an object url from the response
    const url = URL.createObjectURL(response.data)
    // create a link to trigger the download
    const a = document.createElement('a')
    a.href = url
    a.download = true
    a.click()
}

跟踪选择状态

如果您想使用服务器端选择,您必须跟踪网格当前的选择和筛选状态

// use the selection in any batch requests to the server
let selection: AgGridSelection 

function onSelectionChanged(event: SelectionChangedEvent) {
    if (event.api.getModel().getType() !== 'serverSide') {
        throw new Error('Only the serverSide row model is supported.')
    }
    
    const selectionState = event.api.getServerSideSelectionState() as IServerSideSelectionState
    selection = {
        rowModel: 'serverSide',
        selectAll: selectionState.selectAll,
        toggledNodes: selectionState.toggledNodes,
        filterModel: event.api.getFilterModel()
    }
}

function onFilterChanged(event: FilterChangedEvent) {
    if (!selection) {
        return
    }
    
    selection.filterModel = event.api.getFilterModel()
}

限制

  • 由于集合和json查询中使用了一些特殊的SQL运算符,因此仅与PostgreSQL作为存储后端一起工作
  • 不支持每个筛选器(AND,OR)多个条件
  • 不支持AG Grid的交叉模式的服务器端分组
  • 对于关系中的值进行筛选仅支持一级。例如,您可以筛选 relation.value 但不能筛选 relation.otherRelation.value

待办事项

  • 实现嵌套json字段的集合筛选
  • 实现每个筛选器(ANDOR)多个条件
  • 为选择和请求数据添加类型安全的结构
  • 在mysql上进行测试

变更日志

请参阅变更日志以获取有关最近更改的更多信息。

贡献

请参阅贡献指南以获取详细信息。

致谢

许可协议

MIT许可协议(MIT)。请参阅许可文件以获取更多信息。