首页 >后端开发 >php教程 >PHP Fractal-使您的API json变得漂亮,永远!

PHP Fractal-使您的API json变得漂亮,永远!

Lisa Kudrow
Lisa Kudrow原创
2025-02-10 09:01:09423浏览

PHP Fractal - Make Your API's JSON Pretty, Always!

本文经Viraj Khatavkar同行评审。感谢所有SitePoint的同行评审员,使SitePoint的内容达到最佳状态!


如果您之前构建过API,我敢打赌您习惯于直接将数据作为响应输出。如果操作正确,这可能不会造成危害,但有一些实际的替代方案可以帮助解决这个问题。

其中一个可用的解决方案是Fractal。它允许我们在将模型作为响应返回之前,为模型创建一个新的转换层。它非常灵活,易于集成到任何应用程序或框架中。

PHP Fractal - Make Your API's JSON Pretty, Always!

关键要点

  • PHP Fractal是一种解决方案,允许开发人员在将模型作为响应返回之前为其模型创建新的转换层,从而使JSON数据更易于管理和保持一致性。
  • Fractal灵活且易于集成到任何应用程序或框架中。它的工作原理是使用Transformer将复杂的数据结构转换为更简单的格式,并使用Serializer来格式化最终输出。
  • Fractal还允许在用户请求时包含子资源(关系)到响应中,从而为数据呈现增加了另一层灵活性和控制。
  • 使用Fractal可以通过一次性急切加载关系来优化查询性能,从而解决了Eloquent延迟加载经常遇到的n 1问题。

安装

我们将使用Laravel 5.3应用程序来构建示例并将Fractal包与其集成,因此请继续使用安装程序或通过Composer创建一个新的Laravel应用程序。

<code>laravel new demo</code>

<code>composer create-project laravel/laravel demo</code>

然后,在文件夹内,我们需要Fractal包。

<code>composer require league/fractal</code>

创建数据库

我们的数据库包含users和roles表。每个用户都有一个角色,每个角色都有一个权限列表。

<code class="language-php">// app/User.php

class User extends Authenticatable
{
    protected $fillable = [
        'name',
        'email',
        'password',
        'role_id',
    ];

    protected $hidden = [
        'password', 'remember_token',
    ];

    /**
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function role()
    {
        return $this->belongsTo(Role::class);
    }
}</code>
<code class="language-php">// app/Role.php

class Role extends Model
{
    protected $fillable = [
        'name',
        'slug',
        'permissions'
    ];

    /**
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function users()
    {
        return $this->hasMany(User::class);
    }
}</code>

创建Transformer

我们将为每个模型创建一个Transformer。我们的UserTransformer类如下所示:

<code class="language-php">// app/Transformers/UserTransformer.php

namespace App\Transformers;

use App\User;
use League\Fractal\TransformerAbstract;

class UserTransformer extends TransformerAbstract
{
    public function transform(User $user)
    {
        return [
            'name' => $user->name,
            'email' => $user->email
        ];
    }
}</code>

是的,创建Transformer就这么简单!它只是以开发人员可以管理的方式转换数据,而不是留给ORM或存储库。

我们扩展TransformerAbstract类并定义transform方法,该方法将使用User实例调用。RoleTransformer类也是如此。

<code class="language-php">namespace App\Transformers;

use App\Role;
use League\Fractal\TransformerAbstract;

class RoleTransformer extends TransformerAbstract
{
    public function transform(Role $role)
    {
        return [
            'name' => $role->name,
            'slug' => $role->slug,
            'permissions' => $role->permissions
        ];
    }
}</code>

创建控制器

我们的控制器应该在将数据发送回用户之前转换数据。我们现在将处理UsersController类,暂时只定义index和show操作。

<code class="language-php">// app/Http/Controllers/UsersController.php

class UsersController extends Controller
{
    /**
     * @var Manager
     */
    private $fractal;

    /**
     * @var UserTransformer
     */
    private $userTransformer;

    function __construct(Manager $fractal, UserTransformer $userTransformer)
    {
        $this->fractal = $fractal;
        $this->userTransformer = $userTransformer;
    }

    public function index(Request $request)
    {
        $users = User::all(); // 从数据库获取用户
        $users = new Collection($users, $this->userTransformer); // 创建资源集合转换器
        $users = $this->fractal->createData($users); // 转换数据

        return $users->toArray(); // 获取转换后的数据数组
    }
}</code>

index操作将从数据库查询所有用户,使用用户列表和转换器创建一个资源集合,然后执行实际的转换过程。

<code class="language-json">{
  "data": [
    {
      "name": "Nyasia Keeling",
      "email": "crooks.maurice@example.net"
    },
    {
      "name": "Laron Olson",
      "email": "helen55@example.com"
    },
    {
      "name": "Prof. Fanny Dach III",
      "email": "edgardo13@example.net"
    },
    {
      "name": "Athena Olson Sr.",
      "email": "halvorson.jules@example.com"
    }
    // ...
  ]
}</code>

当然,一次返回所有用户是没有意义的,我们应该为此实现分页器。

分页

Laravel倾向于简化事情。我们可以像这样实现分页:

<code>laravel new demo</code>

但是为了使这与Fractal一起工作,我们可能需要添加一些代码来转换数据,然后再调用分页器。

<code>composer create-project laravel/laravel demo</code>

第一步是从模型分页数据。接下来,我们像以前一样创建一个资源集合,然后在集合上设置分页器。

Fractal为Laravel提供了一个分页器适配器来转换LengthAwarePaginator类,它还为Symfony和Zend提供了一个适配器。

<code>composer require league/fractal</code>

请注意,它为分页详细信息添加了额外的字段。您可以在文档中阅读更多关于分页的信息。

包含子资源

现在我们已经熟悉了Fractal,是时候学习如何在用户请求时包含子资源(关系)到响应中了。

我们可以请求包含额外资源到响应中,例如http://demo.vaprobash.dev/users?include=role。我们的转换器可以自动检测正在请求的内容并解析include参数。

<code class="language-php">// app/User.php

class User extends Authenticatable
{
    protected $fillable = [
        'name',
        'email',
        'password',
        'role_id',
    ];

    protected $hidden = [
        'password', 'remember_token',
    ];

    /**
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function role()
    {
        return $this->belongsTo(Role::class);
    }
}</code>

$availableIncludes属性告诉转换器我们可能需要包含一些额外的数据到响应中。如果include查询参数请求用户角色,它将调用includeRole方法。

<code class="language-php">// app/Role.php

class Role extends Model
{
    protected $fillable = [
        'name',
        'slug',
        'permissions'
    ];

    /**
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function users()
    {
        return $this->hasMany(User::class);
    }
}</code>

$this->fractal->parseIncludes行负责解析include查询参数。如果我们请求用户列表,我们应该看到类似这样的内容:

<code class="language-php">// app/Transformers/UserTransformer.php

namespace App\Transformers;

use App\User;
use League\Fractal\TransformerAbstract;

class UserTransformer extends TransformerAbstract
{
    public function transform(User $user)
    {
        return [
            'name' => $user->name,
            'email' => $user->email
        ];
    }
}</code>

如果每个用户都有一个角色列表,我们可以将转换器更改为如下所示:

<code class="language-php">namespace App\Transformers;

use App\Role;
use League\Fractal\TransformerAbstract;

class RoleTransformer extends TransformerAbstract
{
    public function transform(Role $role)
    {
        return [
            'name' => $role->name,
            'slug' => $role->slug,
            'permissions' => $role->permissions
        ];
    }
}</code>

包含子资源时,我们可以使用点表示法嵌套关系。假设每个角色都有一个存储在单独表中的权限列表,并且我们想列出具有其角色和权限的用户。我们可以这样做include=role.permissions。

有时,我们需要默认包含一些必要的关联,例如地址关联。我们可以通过在转换器中使用$defaultIncludes属性来实现。

<code class="language-php">// app/Http/Controllers/UsersController.php

class UsersController extends Controller
{
    /**
     * @var Manager
     */
    private $fractal;

    /**
     * @var UserTransformer
     */
    private $userTransformer;

    function __construct(Manager $fractal, UserTransformer $userTransformer)
    {
        $this->fractal = $fractal;
        $this->userTransformer = $userTransformer;
    }

    public function index(Request $request)
    {
        $users = User::all(); // 从数据库获取用户
        $users = new Collection($users, $this->userTransformer); // 创建资源集合转换器
        $users = $this->fractal->createData($users); // 转换数据

        return $users->toArray(); // 获取转换后的数据数组
    }
}</code>

我最喜欢Fractal包的一件事是能够将参数传递给include参数。文档中的一个很好的例子是按顺序排列。我们可以将其应用到我们的示例中,如下所示:

<code class="language-json">{
  "data": [
    {
      "name": "Nyasia Keeling",
      "email": "crooks.maurice@example.net"
    },
    {
      "name": "Laron Olson",
      "email": "helen55@example.com"
    },
    {
      "name": "Prof. Fanny Dach III",
      "email": "edgardo13@example.net"
    },
    {
      "name": "Athena Olson Sr.",
      "email": "halvorson.jules@example.com"
    }
    // ...
  ]
}</code>

这里重要的部分是list($orderCol, $orderBy) = $paramBag->get('order') ?: ['created_at', 'desc'];,这将尝试从用户include获取order参数,并将其应用于查询构建器。

我们现在可以通过传递参数来按顺序排列包含的用户列表(/roles?include=users:order(name|asc))。您可以在文档中阅读更多关于包含资源的信息。

但是,如果用户没有任何关联的角色会怎样?它将停止并出现错误,因为它期望的是有效数据而不是null。让我们从响应中删除该关系,而不是显示其null值。

<code>laravel new demo</code>

急切加载

因为Eloquent在访问模型时会延迟加载模型,所以我们可能会遇到n 1问题。这可以通过一次性急切加载关系来解决,以优化查询。

<code>composer create-project laravel/laravel demo</code>

这样,在访问模型关系时,我们将不会有任何额外的查询。

结论

我在阅读Phil Sturgeon撰写的《构建你不会讨厌的API》时偶然发现了Fractal,这是一本很棒且内容丰富的读物,我强烈推荐。

您在构建API时是否使用过转换器?您是否有任何首选的执行相同工作的包,或者您只是使用json_encode?请在下面的评论部分告诉我们!

PHP Fractal常见问题解答

什么是PHP Fractal,为什么它很重要?

PHP Fractal是一个强大的工具,有助于为API呈现和转换数据。它很重要,因为它提供了一种标准化的方法来输出复杂、嵌套的数据结构,确保API的数据输出一致、结构良好且易于理解。这使得开发人员更容易使用您的API,并减少了出错的可能性。

PHP Fractal是如何工作的?

PHP Fractal的工作原理是获取复杂的数据结构并将其转换为更易于使用的格式。它通过两个主要组件来实现:Transformer和Serializer。Transformer负责将复杂的数据转换为更简单的格式,而Serializer则格式化最终输出。

PHP Fractal中的Transformer是什么?

PHP Fractal中的Transformer是定义应用程序数据应如何在API响应中输出的类。它们获取复杂的数据结构并将它们转换为更简单、更易于使用的格式。这允许您精确控制API响应中包含哪些数据以及数据的结构。

PHP Fractal中的Serializer是什么?

PHP Fractal中的Serializer负责格式化API的最终输出。它们获取已由Transformer转换的数据,并将其格式化为特定的结构。这允许您确保API的输出一致且易于理解。

我如何在项目中实现PHP Fractal?

在项目中实现PHP Fractal包括通过Composer安装Fractal库,为数据创建Transformer,然后使用Fractal类使用Transformer转换数据。然后,您可以使用Fractal的Serializer之一输出转换后的数据。

我可以将PHP Fractal与任何PHP项目一起使用吗?

是的,PHP Fractal是一个独立的库,可以与任何PHP项目一起使用。它不依赖于任何特定的框架或平台,这使得它成为任何PHP开发人员的通用工具。

使用PHP Fractal的好处是什么?

使用PHP Fractal提供了许多好处。它确保API的输出一致且结构良好,使开发人员更容易使用。它还提供了一种标准化的方法来转换复杂的数据结构,减少了出错的可能性,并使代码更容易维护。

PHP Fractal与其他数据转换工具相比如何?

PHP Fractal以其简单性和灵活性而脱颖而出。它提供了一种直接的方法来转换复杂的数据结构,并且它使用Transformer和Serializer允许高度定制。这使得它成为任何使用API的开发人员的强大工具。

我可以自定义PHP Fractal的输出吗?

是的,PHP Fractal是高度可定制的。您可以创建自定义Transformer来精确控制数据的转换方式,并且您可以使用不同的Serializer以不同的方式格式化输出。这允许您根据您的特定需求调整API的输出。

我在哪里可以了解更多关于PHP Fractal的信息?

有很多资源可以帮助您了解更多关于PHP Fractal的信息。官方PHP League网站提供了全面的文档,并且网上有许多教程和博文。此外,PHP Fractal GitHub存储库是一个探索代码并查看其使用方法示例的好地方。

以上是PHP Fractal-使您的API json变得漂亮,永远!的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn