Laravel paginate 后遍历用 foreach 还是 transform?

有些教程是 foreach 有些是 transform ,那么到底应该用那个呢?

《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
最佳答案

建议用 Resource 定义数据输出转换。如果不想用 Resource,那么你需要明白几点:

如果分页里面的数据项是对象,处理后还是原对象,除了 transform 用哪个无所谓。 否则

transform 会改变当前数据,需要返回修改后的数据,否则将是一场空,它是 map 后赋值的快捷方法

foreach 只是循环,要变更数据,需要依个 put 或 最后统一赋值(类似 map 后修改)

each 类似 foreach,但只能单个修改,否则不会对原值有影响

5个月前 评论
讨论数量: 17

我一般都是把它转化为数组后再处理数据

5个月前 评论
sunny123456 (楼主) 5个月前
冯小胖同学 (作者) 5个月前
sunny123456 (楼主) 5个月前

其实有更优解针对API分页

app/Providers/AppServiceProvider.php 中:

<?php

namespace App\Providers;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Arr;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     */
    public function register(): void
    {
        //
    }

    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        JsonResource::withoutWrapping();
        JsonResource::macro('paginationInformation', function (Request $request, array $paginated, array $default) {
            return ['meta' => Arr::only($paginated, [
                'current_page',
                'last_page',
                'per_page',
                'total',
            ])];
        });
    }
}

只要加这个就行,其他都不用变,也不需要创建 Collection 类,只要基础的 Resource 类就行

5个月前 评论
sunny123456 (楼主) 5个月前

那我给个完整版的

app/Providers/AppServiceProvider.php:

<?php

namespace App\Providers;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Arr;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        //
    }

    public function boot(): void
    {
        JsonResource::withoutWrapping();
        JsonResource::macro('paginationInformation', function (Request $request, array $paginated, array $default) {
            return ['meta' => Arr::only($paginated, [
                'current_page',
                'last_page',
                'per_page',
                'total',
            ])];
        });
    }
}

app/Http/Resources/Api/UserResource.php:

<?php

namespace App\Http\Resources\Api;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

class UserResource extends JsonResource
{
    public function toArray(Request $request): array
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'avatar' => $this->avatar,
        ];
    }
}

app/Http/Controllers/Api/UsersController.php:

<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Http\Resources\Api\UserResource;
use App\Models\User;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;

class UsersController extends Controller
{
    public function index(): AnonymousResourceCollection
    {
        return UserResource::collection(User::query()->latest()->paginate());
    }
}

routes/api.php:

<?php

use App\Http\Controllers\Api\UsersController;
use Illuminate\Support\Facades\Route;

Route::get('/users', [UsersController::class, 'index']);

访问接口 /api/users 返回的数据就是这样:

{
    "data": [
        {
            "id": 1,
            "name": "user",
            "avatar": ""
        }
    ],
    "meta": {
        "current_page": 1,
        "last_page": 1,
        "per_page": 15,
        "total": 1
    }
}
5个月前 评论
sunny123456 (楼主) 5个月前

如果是要对分页后的数据进行整理输出的话,可以使用through方法

return Product::paginate()->through(fn($p) => [
    'id' => $p->id,
    'name' => $p->name,
    'price' => Number::currency($p->price, 'CNY', 'zh')   
]);

这个返回的是一个AbstractPaginator对象,需要注意的是,这种方式不会走模型中定义的casts,当然你可以为casts数组中定义的字段定义对应的访问器,也可以用toArray方法来触发casts, 或者你也可以像下面这样处理,这种直接返回模型,同样会走casts相关的数据转换

return Product::paginate()->through(function($p) {
    $p->marked = true;
    unset($p->name);
    return $p;
});
5个月前 评论
nff93

你直接 foreach 就行,transform 是类似 array_map 的东西,用哪个取决于你的需求

5个月前 评论
sunny123456 (楼主) 5个月前

transform是集合的一个遍历方法,foreach是迭代语法结构, 这2个有什么关系呢?

分页数据处理,laravel不是有很多方法吗?你的目标是遍历处理数据,方法只是实现工具,因为每个业务数据处理是不同的,没必要过度封装,我个人一般都是ResourceCollection处理数据就行了,transform/map等等都可以,如果用JsonResource,每次遍历都会实例化一个JsonResource对象,如果不在乎实例开销,也可以只用JsonResource,我一般倾向于单独用ResourceCollection实现。

file file

5个月前 评论
sunny123456 (楼主) 5个月前
lovewei (作者) 5个月前

我是使用的foreach

5个月前 评论

我都用, 不过现在用 transform 多一些, 如果你要封装,统一结构体, 那么你就要考虑 到底是用集合 还是数组

5个月前 评论

建议用 Resource 定义数据输出转换。如果不想用 Resource,那么你需要明白几点:

如果分页里面的数据项是对象,处理后还是原对象,除了 transform 用哪个无所谓。 否则

transform 会改变当前数据,需要返回修改后的数据,否则将是一场空,它是 map 后赋值的快捷方法

foreach 只是循环,要变更数据,需要依个 put 或 最后统一赋值(类似 map 后修改)

each 类似 foreach,但只能单个修改,否则不会对原值有影响

5个月前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!