granedirval/data-dog-pager-bundle

symfony2 和 doctrine orm 的分页器包,支持通过过滤器排序器进行自定义

安装次数: 3,329

依赖者: 0

建议者: 0

安全性: 0

星标: 0

关注者: 0

分支: 17

类型:symfony-bundle

v0.2.8 2017-12-31 16:11 UTC

README

这个分页器在以下方面有所不同

  • 它只有一个包含大约300行注释代码的通用类。其余的源代码都是针对 symfony2 框架、twig 辅助函数和模板的。
  • 它允许创建自定义分页过滤器 - 搜索、选择等,并根据特定用例修改数据库查询。
  • 它还以传统方式处理排序。
  • 它非常小巧,可以在其他框架中重复使用,只需进行必要的修改。
  • 它只能分页 Doctrine2 ORM QueryBuilder。为了保持库小巧和向后兼容,不会支持其他任何内容。对于您的自定义修改,请克隆或复制源代码。
  • 每个请求可能只有一个分页,因为 URL 查询参数是恒定的。

演示

要查看功能,最好的方式是查看实际演示。只需克隆包并运行

make

访问 https://:8000 查看带有自定义过滤器和排序器的分页假项目。

演示应用程序的源代码位于 example 目录中,它是一个基本的 symfony 应用程序。

安装

首先,使用 composer 安装它

composer require data-dog/pager-bundle

然后,在您的 AppKernel 包中添加它。

使用方法

在您的控制器中的一般使用示例

<?php

namespace AppBundle\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Doctrine\ORM\QueryBuilder;
use DataDog\PagerBundle\Pagination;

class ProjectController extends Controller
{
    /**
     * @Method("GET")
     * @Template
     * @Route("/", name="homepage")
     */
    public function indexAction(Request $request)
    {
        $qb = $this->getDoctrine()->getManager()->getRepository("AppBundle:Project")
            ->createQueryBuilder('p')
            ->addSelect('l')
            ->innerJoin('p.language', 'l');

        $projects = new Pagination($qb, $request);
        return compact('projects');
    }
}

您需要使用 doctrine 查询构建器 和请求来构建分页。分页对象就像一个数组,因此您可以将其传递到视图中并迭代分页项。

视图

<table class="table table-hover">
<thead>
  <tr>
    <th>#</th>
    <th>{{ sorter_link(projects, "p.code", "Code") }}</th>
    <th>{{ sorter_link(projects, "p.name", "Name") }}</th>
    <th>{{ sorter_link(projects, "p.hoursSpent", "Hours Spent") }}</th>
    <th>{{ sorter_link(projects, "l.code", "Language") }}</th>
  </tr>
</thead>
<tbody>
  {% for project in projects %}
    <tr>
      <td>{{ project.id }}</td>
      <td>{{ project.code }}</td>
      <td>{{ project.name }}</td>
      {% if project.isOverDeadline %}
        <td class="text-danger">{{ project.hoursSpent }}</td>
      {% else %}
        <td class="text-success">{{ project.hoursSpent }}</td>
      {% endif %}
      <td>{{ project.language.code }}</td>
    </tr>
  {% endfor %}
</tbody>
</table>

<div class="panel-footer">
{{ pagination(projects) }}
</div>

使用了 twig 辅助函数

  • sorter_link - 使用 twig 模板生成带有排序顺序类等的链接。
  • pagination - 创建用于导航页面的分页 HTML 代码。

这些模板可以通过标准的 symfony 方式进行修改,请参阅配置部分。

过滤器

为了以不同方式过滤分页结果,您可以扩展代码。在控制器中提供一些分页选项。

<?php

namespace AppBundle\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Doctrine\ORM\QueryBuilder;
use DataDog\PagerBundle\Pagination;

class ProjectController extends Controller
{
    /**
     * Our filter handler function, which allows us to
     * modify the query builder specifically for our filter option
     */
    public function projectFilters(QueryBuilder $qb, $key, $val)
    {
        switch ($key) {
        case 'p.name':
            if ($val) {
                $qb->andWhere($qb->expr()->like('p.name', ':name'));
                $qb->setParameter('name', "%$val%");
            }
            break;
        case 'p.hoursSpent':
            switch ($val) {
            case 'lessThan10':
                $qb->andWhere($qb->expr()->lt('p.hoursSpent', 10));
                break;
            case 'upTo20':
                $qb->andWhere($qb->expr()->lte('p.hoursSpent', 20));
                break;
            case 'moreThan2weeks':
                $qb->andWhere($qb->expr()->gte('p.hoursSpent', 80));
                break;
            case 'overDeadline':
                $qb->andWhere($qb->expr()->gt('p.hoursSpent', 'p.deadline'));
                break;
            }
            break;
        case 'l.code':
            $qb->andWhere($qb->expr()->eq('l.code', ':code'));
            $qb->setParameter('code', $val);
            break;
        default:
            // Do not allow filtering by anything else
            throw new \Exception("filter not allowed");
            // You can also enable automatic filtering
            //$paramName = preg_replace('/[^A-z]/', '_', $key);
            //$qb->andWhere($qb->expr()->eq($key, ":$paramName"));
            //$qb->setParameter($paramName, $val);
        }
    }

    /**
     * @Method("GET")
     * @Template
     * @Route("/", name="homepage")
     */
    public function indexAction(Request $request)
    {
        $qb = $this->getDoctrine()->getManager()->getRepository("AppBundle:Project")
            ->createQueryBuilder('p')
            ->addSelect('l')
            ->innerJoin('p.language', 'l');

        $options = [
            'sorters' => ['l.code' => 'ASC'], // sorted by language code by default
            'filters' => ['p.hoursSpent' => 'overDeadline'], // we can apply a filter option by default
            'applyFilter' => [$this, 'projectFilters'], // custom filter handling
        ];

        // our language filter options, the key will be used in where statemt by default
        // and the value as title.
        // The $filterAny key is a placeholder to skip the filter, so the any value could be ok.
        $languages = [
            Pagination::$filterAny => 'Any',
            'php' => 'PHP',
            'hs' => 'Haskell',
            'go' => 'Golang',
        ];

        // our spent time filter options, has specific keys so we know how to customize
        $spentTimeGroups = [
            Pagination::$filterAny => 'Any',
            'lessThan10' => 'Less than 10h',
            'upTo20' => 'Up to 20h',
            'moreThan2weeks' => 'More than 2weeks',
            'overDeadline' => 'Over deadline',
        ];

        $projects = new Pagination($qb, $request, $options);
        return compact('projects', 'languages', 'spentTimeGroups');
    }
}

现在我们添加了三个过滤器

$languages$spentTimeGroups 将用作 filter_select 选项。语言选项很简单,它们引用直接值,因此不需要修改 where 语句。但是花费时间组是自定义的,所以我们使用自定义选项。在这种情况下,我们需要将 applyFilter 选项设置为可调用,以便根据我们的自定义选项相应地修改 QueryBuilder。

注意:如果您管理自定义过滤,请确保使用参数或使用 $qb->expr()->literal("string") 以防止 SQL 注入。如果您有自定义过滤器处理程序,您必须管理所有您的过滤器,默认处理程序将不会激活。

那么视图是如何改变的

<table class="table table-hover">
<thead>
  <tr>
    <th>#</th>
    <th>{{ sorter_link(projects, "p.code", "Code") }}</th>
    <th>{{ sorter_link(projects, "p.name", "Name") }}</th>
    <th>{{ sorter_link(projects, "p.hoursSpent", "Hours Spent") }}</th>
    <th>{{ sorter_link(projects, "l.code", "Language") }}</th>
  </tr>

  <tr role="row" class="filter">
    <td></td>
    <td></td>
    <td>{{ filter_search(projects, "p.name") }}</td>
    <td>{{ filter_select(projects, "p.hoursSpent", spentTimeGroups) }}</td>
    <td>{{ filter_select(projects, "l.code", languages) }}</td>
  </tr>
</thead>
<tbody>
  {% for project in projects %}
    <tr>
      <td>{{ project.id }}</td>
      <td>{{ project.code }}</td>
      <td>{{ project.name }}</td>
      {% if project.isOverDeadline %}
        <td class="text-danger">{{ project.hoursSpent }}</td>
      {% else %}
        <td class="text-success">{{ project.hoursSpent }}</td>
      {% endif %}
      <td>{{ project.language.code }}</td>
    </tr>
  {% endfor %}
</tbody>
</table>

<div class="panel-footer">
{{ pagination(projects) }}
</div>

我们使用了两个新的 twig 函数来处理过滤器

  • filter_search - 用于按名称搜索项目。
  • filter_select - 用于基本选项过滤器。

这些函数用于渲染我们过滤器的twig模板。

链接

如果您需要创建一个链接并保持搜索过滤器和排序器的应用,请使用$pagination->query()函数获取所有必要的URL参数,并将其与您的链接参数合并。

示例示例在单独的控制器操作中处理项目的启用和禁用切换,并保持所有分页属性。

配置

一般使用不需要配置。但为了自定义分页,可能需要在app.php中设置全局选项,例如。

<?php

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Debug\Debug;
use DataDog\PagerBundle\Pagination;

$loader = require_once __DIR__.'/../app/autoload.php';
Debug::enable();

Pagination::$defaults = array_merge(Pagination::$defaults, [
    'limit' => 15,
    'range' => 9,
]);
Pagination::$maxPerPage = 200;

require_once __DIR__.'/../app/AppKernel.php';

$kernel = new AppKernel('prod', false);
$kernel->loadClassCache();
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);

模板

过滤器和分页的默认模板基于twitter bootstrapfontawesome。您可以像其他任何bundle模板一样自定义它们,例如

  • 分页 - app/Resources/DataDogPagerBundle/views/pagination.html.twig
  • 搜索过滤器 - app/Resources/DataDogPagerBundle/views/filters/search.html.twig

添加更多过滤器以扩展

自定义过滤器的最佳方式是扩展twig扩展或创建一个新的扩展。如果我们提供许多选项,最终会让人困惑,所以我们添加了一些基本模板。在您的bundle services.yml中更新参数

parameters:
  datadog.pager.twig_extension.class: AppBundle\Twig\PaginationExtension

然后创建一个类

<?php

namespace AppBundle\Twig;

use DataDog\PagerBundle\Twig\PaginationExtension as Base;
use DataDog\PagerBundle\Pagination;

class PaginationExtension extends Base
{
    /**
     * {@inheritdoc}
     */
    public function getFunctions()
    {
        $defaults = [
            'is_safe' => ['html'],
            'needs_environment' => true,
        ];

        $funcs = parent::getFunctions();
        $funcs['filter_search_placeholder'] = new \Twig_Function_Method($this, 'filterSearchPlaceholder', $defaults);

        return $funcs;
    }

    public function filterSearchPlaceholder(\Twig_Environment $twig, Pagination $pagination, $key, $placeholder)
    {
        $value = isset($pagination->query()['filters'][$key]) ? $pagination->query()['filters'][$key] : '';
        return $twig->render('AppBundle::filters/search_placeholder.html.twig', compact('key', 'pagination', 'value', 'placeholder'));
    }
}

最后,根据您的需求复制并修改模板

截图

Screenshot

Screenshot

许可协议

分页器可以免费使用,并使用MIT许可协议