jaspr/mapper 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

jaspr/mapper

Composer 安装命令:

composer require jaspr/mapper

包简介

JSON API implementation, by annotations or schemas.

README 文档

README

Implementation of JSON API Standard Specification

This project goal is to create easy-to-use library to implement JSON API specification.

Whole project is at the beginning of development. So don't hesitate to help. I'm open to some good ideas how make this more customizable and friendly.

Library only provides wrappers to create valid JSON API document. Controllers and Response is on you.

Issues

You can write email or create issue in gitlab

Installation

Install library via Composer

composer require jaspr/mapper

Basic Usage

For simplicity, we use $container as some dependency provider (Dependency Injection).

Describing your objects

You can choose which way you want to describe your object metadata.

With Annotations

Note: If you want to use annotations you have to use AnnotationDriver in MetadataFactory

Example

How you can see, setting up resource object is quiet easy. Just annotate your getter with `#[Attribute] or #[Relationship]` annotation.

Schema

The important part is to implement Resource interface. Then fill up static method getSchema.

Note: If you want to use schema you have to use SchemaDriver in MetadataFactory

Example

Polymorphic relationships (abstract targets)

A relationship target may be an abstract base class, letting a single relationship resolve to any of its concrete subtypes. Annotate the abstract base as a resource and each concrete subtype as its own resource:

#[API\Resource(type: 'payment-method')]
abstract class PaymentMethod
{
    #[API\Id] public string $id;
    #[API\Attribute] public string $status;   // common fields live on the base
}

#[API\Resource(type: 'card-payment')]
class CardPayment extends PaymentMethod { #[API\Attribute] public string $pan; }

#[API\Resource(type: 'wire-payment')]
class WirePayment extends PaymentMethod { #[API\Attribute] public string $iban; }

class Order
{
    #[API\Id] public string $id;
    // target is the abstract base; the related resource may be any concrete subtype
    #[API\Relationship(PaymentMethod::class)] public PaymentMethod $method;
}

Requirements and behavior:

  • The abstract base must declare a common #[Id]; any #[Attribute]/#[Relationship] declared on it are the common fields shared by all subtypes.
  • Concrete subtypes are discovered by inheritance, so they must live under the paths scanned by MetadataFactory. An abstract base with no registered concrete subtype logs a warning at build time.
  • Encoding/decoding is polymorphic: the concrete type is used. Writing a relationship whose type is not one of the allowed concrete subtypes is rejected with a validation error.
  • Filtering through a polymorphic relationship is restricted to the common fields declared on the abstract base (subtype-only fields are not addressable).
  • OpenAPI: the relationship data identifier type is an enum of the concrete subtypes, and related/include schemas expand to the concrete subtypes. No standalone CRUD path is generated for the abstract base — it is a relationship target only.
  • TypeScript: the relationship is emitted as a union of the concrete subtype interfaces (e.g. ToOneRelationship<CardPayment | WirePayment>); the abstract base is not emitted as an interface and its common fields are inlined into each subtype.
  • Route registration: MetadataRepository::getAll() still returns the abstract base. Consumers registering CRUD routes should skip entries where isAbstract() is true (abstracts are not instantiable). Use getSubTypes(string $type) / getConcreteByClass(string $className) to resolve the concrete subtypes.

Note: interfaces as targets are not supported yet — use an abstract class.

MetadataRepository

To create MetadataRepository we must use MetadataFactory.

Usage

<?php
/** @var $container Psr\Container\ContainerInterface */
// This is cache instance implements PSR SimpleCache
$cache = $container->get( Psr\SimpleCache\CacheInterface::class);
// This is AnnotationDriver or SchemaDriver, depends on your preferences
$driver = $container->get(\JSONAPI\Driver\Driver::class);
// Paths to your object representing resources
$paths = ['paths/to/your/resources','another/path'];
// Factory returns instance of MetadataRepository
$repository = JSONAPI\Factory\MetadataFactory::create(
            $paths,
            $cache,
            $driver
        );

Encoder

Options

ParamDefaultDescription
repositoryInstance of MetadataRepository.

Usage

<?php

// First we need DocumentBuilderFactory
// Let's get MetadataRepository from DI
/** @var $container Psr\Container\ContainerInterface */
$metadataRepository = $container->get(JSONAPI\Metadata\MetadataRepository::class);
$encoder = \JSONAPI\Mapper\Encoding\EncoderFactory::createDefaultEncoder($metadataRepository)
$data = new \JSONAPI\Mapper\Test\Resources\Valid\GettersExample('id');
/** @var \JSONAPI\Mapper\Components\Membership\ResourceObjectIdentifier $identifier */
$identifier = $encoder->identify($data);
/** @var \JSONAPI\Mapper\Components\Membership\ResourceObject $resource */
$resource = $encoder->encode($data);
/** @var \JSONAPI\Mapper\Components\Membership\Document $document */
$document = $encoder->compose($data);


Request Parser

This object works with url, and parse required keywords as described at JSON API Standard

Options

ParamDefaultDescription
baseUrlURL where you API lays. Must end with / to work properly with relative links.
repositoryInstance of MetadataRepository.
pathParserPathParserInstance of PathParserInterface. Provides information about path, like resource type, resource ID, relation type, is it collection or is it relationship.
paginationParserOffsetStrategyParserInstance of PaginationParserInterface. Pagination.
sortParserSortParserInstance of SortParserInterface. Sort.
inclusionParserInclusionParserInstance of InclusionParserInterface. Inclusion.
fieldsetParserFieldsetParserInstance of FieldsetParserInterface. Sparse Fields
filterParserExpressionFilterParserFilterParserInterface instance, which is responsible for parsing filter. Filter
bodyParserBodyParserInstance of BodyParserInterface.
loggerNullLoggerLoggerInterface instance, PSR compliant logger instance.

Filter

As described, specification is agnostic about filter implementation. So I created, more like borrowed, expression filter from OData. So now you can use something like this:

?filter=stringProperty eq 'string' and contains(stringProperty,'asdf') and intProperty in (1,2,3) or boolProperty ne true and relation.property eq null

Or if you have simpler use cases you can try QuatrodotFilter:

?filter=stringProperty::contains::Bonus|boolProperty::eq::true

Pagination

I implement two of three pagination technics

  • LimitOffsetPagination
  • PagePagination

Includes

https://jsonapi.org/format/#fetching-includes

Sort

https://jsonapi.org/format/#fetching-sorting

Index Page

If you want use JASPR SDK to its full potential, consider expose index page.

<?php
$doc      = new JSONAPI\Mapper\IndexDocument(self::$mr, self::$url);
$response = json_encode($doc);

which returns something like this:

{
    "jsonapi": {
        "version": "1.0"
    },
    "links": {
        "relation": "https:\/\/unit.test.org\/relation",
        "getter": "https:\/\/unit.test.org\/getter",
        "meta": "https:\/\/unit.test.org\/meta",
        "prop": "https:\/\/unit.test.org\/prop",
        "third": "https:\/\/unit.test.org\/third"
    },
    "meta": {
        "title": "JSON:API Index Page",
        "baseUrl": "https:\/\/unit.test.org/"
    }
}

And if your front-end use jaspr/client-js library, then you can use useJsonApiWithIndex factory to enjoy RESTful experience.

Open API Schema

This library provides lightweight wrapper around OAS. It can generate OAS v3.0.3 schema in json, so you can provide doc for your api easily.

Basic Example

    $factory = new OpenAPISpecificationBuilder(
        $metadataRepository,
        'https://your.api.url'
    );

    $info = new Info('JSON:API OAS', '1.0.0');
    $info->setDescription('Test specification');
    $info->setContact(
        (new Contact())
            ->setName('Tomas Benedikt')
            ->setEmail('tomas.benedikt@gmail.com')
            ->setUrl('https://gitlab.com/jaspr')
    );
    $info->setLicense(
        (new License('MIT'))
            ->setUrl('https://gitlab.com/jaspr/mapper/-/blob/5.x/LICENSE')
    );
    $info->setTermsOfService('https://gitlab.com/jaspr/mapper/-/blob/5.x/CONTRIBUTING.md');

    $oas = $factory->create($info);
    $oas->setExternalDocs(new ExternalDocumentation('https://gitlab.com/jaspr/mapper/-/wikis/home'));

    $json = json_encode($oas);

For more examples, try look at tests

jaspr/mapper 适用场景与选型建议

jaspr/mapper 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 15.77k 次下载、GitHub Stars 达 0, 最近一次更新时间为 2022 年 06 月 16 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「annotations」 「php」 「json」 「rest」 「serialization」 「api」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 jaspr/mapper 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 jaspr/mapper 我们能提供哪些服务?
定制开发 / 二次开发

基于 jaspr/mapper 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 15.77k
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 19
  • 依赖项目数: 1
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 1
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2022-06-16