小滕的博客

小滕的技术点滴

Dingo/api 下的数据分页

1 year ago · 2 MIN READ
#Laravel 

默认情况下我们直接读取数据库的数据最后调用 paginate(10) 方法就可以直接获取到 laravel 的 Paginator 的对象了,但是,现在我们的数据分页无法直接通过读取数据库的数据就可以完成,而是需要先将数据读取出来然后进行一定的条件判断得到最后的结果再进行分页。此种情况下我们应该怎么办呢?可以这样:

首先,读取出数据(这里仅作演示):

$users = App\User::all();

然后再 App\Foundations\Pagination 目录下面创建 Paginator.php 文件,内容如下:

<?php

namespace App\Foundations\Pagination;

use Countable;
use ArrayAccess;
use Illuminate\Pagination\AbstractPaginator;
use JsonSerializable;
use IteratorAggregate;
use Illuminate\Support\Collection;
use Illuminate\Support\HtmlString;
use Illuminate\Contracts\Support\Jsonable;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Contracts\Pagination\Paginator as PaginatorContract;

class Paginator extends AbstractPaginator implements Arrayable, ArrayAccess, Countable, IteratorAggregate, JsonSerializable, Jsonable, PaginatorContract
{
    /**
     * Determine if there are more items in the data source.
     *
     * @return bool
     */
    protected $hasMore;

    /**
     * Create a new paginator instance.
     *
     * @param  mixed  $items
     * @param  int  $perPage
     * @param  int|null  $currentPage
     * @param  array  $options (path, query, fragment, pageName)
     * @return void
     */
    public function __construct($items, $perPage, $currentPage = null, array $options = [])
    {
        foreach ($options as $key => $value) {
            $this->{$key} = $value;
        }

        $this->perPage = $perPage;
        $this->currentPage = $this->setCurrentPage($currentPage);
        $this->path = $this->path !== '/' ? rtrim($this->path, '/') : $this->path;

        $this->setItems($items);
    }

    /**
     * Get the current page for the request.
     *
     * @param  int  $currentPage
     * @return int
     */
    protected function setCurrentPage($currentPage)
    {
        $currentPage = $currentPage ?: static::resolveCurrentPage();

        return $this->isValidPageNumber($currentPage) ? (int) $currentPage : 1;
    }

    /**
     * Set the items for the paginator.
     *
     * @param  mixed  $items
     * @return void
     */
    protected function setItems($items)
    {
        $this->items = $items instanceof Collection ? $items : Collection::make($items);

        $this->hasMore = $this->items->count() > $this->perPage;

        $this->items = $this->items->slice(0, $this->perPage);
    }

    /**
     * Get the URL for the next page.
     *
     * @return string|null
     */
    public function nextPageUrl()
    {
        if ($this->hasMorePages()) {
            return $this->url($this->currentPage() + 1);
        }
    }

    /**
     * Render the paginator using the given view.
     *
     * @param  string|null  $view
     * @param  array  $data
     * @return string
     */
    public function links($view = null, $data = [])
    {
        return $this->render($view, $data);
    }

    /**
     * Render the paginator using the given view.
     *
     * @param  string|null  $view
     * @param  array  $data
     * @return string
     */
    public function render($view = null, $data = [])
    {
        return new HtmlString(
            static::viewFactory()->make($view ?: static::$defaultSimpleView, array_merge($data, [
                'paginator' => $this,
            ]))->render()
        );
    }

    /**
     * Manually indicate that the paginator does have more pages.
     *
     * @param  bool  $hasMore
     * @return $this
     */
    public function hasMorePagesWhen($hasMore = true)
    {
        $this->hasMore = $hasMore;

        return $this;
    }

    /**
     * Determine if there are more items in the data source.
     *
     * @return bool
     */
    public function hasMorePages()
    {
        return $this->hasMore;
    }

    /**
     * Get the instance as an array.
     *
     * @return array
     */
    public function toArray()
    {
        return [
            'current_page' => $this->currentPage(),
            'data' => $this->items->toArray(),
            'first_page_url' => $this->url(1),
            'from' => $this->firstItem(),
            'next_page_url' => $this->nextPageUrl(),
            'path' => $this->path,
            'per_page' => $this->perPage(),
            'prev_page_url' => $this->previousPageUrl(),
            'to' => $this->lastItem(),
        ];
    }

    /**
     * Convert the object into something JSON serializable.
     *
     * @return array
     */
    public function jsonSerialize()
    {
        return $this->toArray();
    }

    /**
     * Convert the object to its JSON representation.
     *
     * @param  int  $options
     * @return string
     */
    public function toJson($options = 0)
    {
        return json_encode($this->jsonSerialize(), $options);
    }

    public function getUrlRange($start, $end)
    {

    }

    public function total()
    {
        return $this->items->count();
    }

    public function lastPage()
    {

    }

}

接着我们手动的进行分页:

$users = \App\User::all();

$users = new \App\Foundations\Pagination\Paginator($users, 10);

这样的话,我们就可以自己手动的获取 Paginator 的实例了。可能有的小伙伴们会说?这里为什么要自己创建 Paginator 文件而不用 Laravel 自带的 Paginator 呢?因为标题已经说明里,当前我们是在安装了 Dingo/api 扩展包下面使用,按照上面的步骤,我们应该输出结果了:

return $this->response()->paginator($users, new UserIndexTransformer);

此时访问接口你会发现报错:

Type error: Argument 1 passed to League\\Fractal\\Pagination\\IlluminatePaginatorAdapter::__construct() must be an instance of Illuminate\\Contracts\\Pagination\\LengthAwarePaginator, instance of App\\Foundations\\Pagination\\Paginator given, called in D:\\hcar-php\\PHP\\phpstudy\\WWW\\weifengchuxin\\vendor\\dingo\\api\\src\\Transformer\\Adapter\\Fractal.php on line 132

我们可以找到 IlluminatePaginatorAdapter 文件:

<?php

/*
 * This file is part of the League\Fractal package.
 *
 * (c) Phil Sturgeon <me@philsturgeon.uk>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace League\Fractal\Pagination;

use Illuminate\Contracts\Pagination\LengthAwarePaginator;

/**
 * A paginator adapter for illuminate/pagination.
 *
 * @author Maxime Beaudoin <firalabs@gmail.com>
 * @author Marc Addeo <marcaddeo@gmail.com>
 */
class IlluminatePaginatorAdapter implements PaginatorInterface
{
    /**
     * The paginator instance.
     *
     * @var \Illuminate\Contracts\Pagination\LengthAwarePaginator
     */
    protected $paginator;

    /**
     * Create a new illuminate pagination adapter.
     *
     * @param \Illuminate\Contracts\Pagination\LengthAwarePaginator $paginator
     *
     * @return void
     */
    public function __construct(LengthAwarePaginator $paginator)
    {
        $this->paginator = $paginator;
    }

    // ... 省略代码
}

可以看到它的初始化函数直接限定了 LengthAwarePaginator 。现在明白我们为什么单独创建 Paginator 文件了吧,现在我们还需要修改这个文件:

use Illuminate\Contracts\Pagination\LengthAwarePaginator as PaginatorContract;

这样的话,就可以啦。

到此在 Dingo/api 的扩展包下面,我们实现了自定义分页的功能。

···

xiao teng



备案号:皖ICP备14012032号-5