分页器 🗂

Swift Version Vapor Version Circle CI codebeat badge codecov Readme Score GitHub license

GIF of paginator

该软件包目前提供对 ArrayQueryBuilder 的偏移分页支持。

📦 安装

Paginator 添加到软件包依赖项 (在你的 Package.swift 文件中)

dependencies: [
    ...,
    .package(url: "https://github.com/nodes-vapor/paginator.git", from: "4.0.0")
]

以及你的目标 (例如 "App")

targets: [
    ...
    .target(
        name: "App",
        dependencies: [... "Paginator" ...]
    ),
    ...
]

接下来,将 Resources/Views/Paginator 文件夹复制/粘贴到你的项目中,以便能够使用提供的 Leaf 标签。 可以更改这些文件,如 Leaf 标签 部分中所述,但建议无论如何都将此文件夹复制到你的项目中。 这使你可以更轻松地跟踪更新,并且如果稍后你决定不使用自己的自定义 Leaf 文件,你的项目将可以正常工作。

开始使用 🚀

首先,请确保已在所有需要的地方导入 Paginator

import Paginator

添加 Leaf 标签

为了在 Leaf 中进行分页,请务必添加 Leaf 标签

public func configure(_ config: inout Config, _ env: inout Environment, _ services: inout Services) throws {
    services.register { _ -> LeafTagConfig in
        var tags = LeafTagConfig.default()
        tags.use([
            "offsetPaginator": OffsetPaginatorTag(templatePath: "Paginator/offsetpaginator")
        ])

        return tags
    }
}

如果你想完全自定义分页控件的生成方式,可以自由覆盖模板路径。

QueryBuilder

要从 QueryBuilder 返回分页结果,可以执行以下操作

router.get("galaxies") { (req: Request) -> Future<OffsetPaginator<Galaxy>> in
    return Galaxy.query(on: req).paginate(on: req)
}

Array

为方便起见,Paginator 还支持对 Array 进行分页

router.get("galaxies") { (req: Request) -> Future<OffsetPaginator<Galaxy>> in
    let galaxies = [Galaxy(), Galaxy(), Galaxy()]
    return galaxies.paginate(on: req)
}

RawSQL

Paginator 还支持对原始 SQL 查询进行分页,用于与 Fluent 不兼容的复杂表达式。

使用 PostgreSQL 的简单示例

router.get("galaxies") { (req: Request) -> Future<OffsetPaginator<Galaxy>> in
    return req.withPooledConnection(to: .psql) { conn -> Future<OffsetPaginator<Galaxy>> in
        let rawBuilder = RawSQLBuilder<PostgreSQLDatabase, Galaxy>(
            query: """
                SELECT *
                FROM public."Galaxy"
            """, countQuery: """
                SELECT COUNT(*) as "count"
                FROM public."Galaxy"
            """, connection: conn)
        return try rawBuilder.paginate(on: req)
    }
}

注意:计数查询应具有一个结果,该结果具有一个名为 count 的列,并且第一行包含总列数值

Leaf 标签

要将 Paginator 与 Leaf 一起使用,可以执行以下操作

struct GalaxyList: Codable {
    let galaxies: [Galaxy]
}

router.get("galaxies") { (req: Request) -> Response in
    let paginator: Future<OffsetPaginator<Galaxy>> = Galaxy.query(on: req).paginate(on: req)
    return paginator.flatMap(to: Response.self) { paginator in
        return try req.view().render(
            "MyLeafFile", 
            GalaxyList(galaxies: paginator.data ?? []), 
            userInfo: try paginator.userInfo(),
            on: req
        )
        .encode(for: req)
    }
}

请注意如何使用 render 调用中的 userInfo 传入 Paginator 数据。忘记传入此项将导致抛出错误。

然后在你的 MyLeafFile.leaf 中,你可以这样做

<ul>
    #for(galaxy in galaxies) {
        <li>#(galaxy.name)</li>
    }
</ul>

#offsetPaginator()

调用 OffsetPaginator 的 Leaf 标签将自动生成 Bootstrap 4 HTML 以显示分页控件

<nav class="paginator">
    <ul class="pagination justify-content-center table-responsive">
        <li class="page-item">
            <a href="/admin/users?page=16" class="page-link" rel="prev" aria-label="Previous">
                <span aria-hidden="true">«</span>
                <span class="sr-only">Previous</span>
            </a>
        </li>
        <li class="page-item "><a href="/admin/users?page=1" class="page-link">1</a></li>
        <li class="disabled page-item"><a href="#" class="page-link">...</a></li>
        <li class="page-item "><a href="" class="page-link">12</a></li>
        <li class="page-item "><a href="" class="page-link">13</a></li>
        <li class="page-item "><a href="" class="page-link">14</a></li>
        <li class="page-item "><a href="" class="page-link">15</a></li>
        <li class="page-item "><a href="" class="page-link">16</a></li>
        <li class="page-item  active "><a href="" class="page-link">17</a></li>
        <li class="page-item "><a href="/admin/users?page=18" class="page-link">18</a></li>
        <li class="page-item">
            <a href="/admin/users?page=18" class="page-link" rel="next" aria-label="Next">
                <span aria-hidden="true">»</span>
                <span class="sr-only">Next</span>
            </a>
        </li>
    </ul>
</nav>

转换

可以通过映射分页器并一次转换每个元素来转换 OffsetPaginator 中的数据

Galaxy.query(on: req).paginate(on: req).map { paginator in
    paginator.map { (galaxy: Galaxy) -> GalaxyViewModel in 
        GalaxyViewModel(galaxy: galaxy)
    }
}

你也可以一次转换一整页结果

Galaxy.query(on: req).paginate(on: req).map { paginator in
    paginator.map { (galaxies: [Galaxy]) -> [GalaxyViewModel] in 
        galaxies.map(GalaxyViewModel.init)
    }
}

如果转换需要异步工作,你可以这样做

Galaxy.query(on: req).paginate(on: req).map { paginator in
    paginator.flatMap { (galaxies: [Galaxy]) -> Future<[GalaxyViewModel]> in 
        galaxies.someAsyncMethod()
    }
}

配置

OffsetPaginator 有一个配置文件 (OffsetPaginatorConfig),如果需要,可以覆盖它。 这可以在 configure.swift 中完成

public func configure(_ config: inout Config, _ env: inout Environment, _ services: inout Services) throws {
    // ..
    services.register(OffsetPaginatorConfig(
        perPage: 1,
        defaultPage: 1
    ))
}

🏆 贡献者

该软件包由 Nodes 的 Vapor 团队开发和维护。

📄 许可

该软件包是根据 MIT 许可证 获得许可的开源软件