solvire/php-hypermedia-api
Composer 安装命令:
composer require solvire/php-hypermedia-api
包简介
PHP Hypermedia Enabled API Wrapper for HTTP REST
README 文档
README
Repo: php-hypermedia-api
NO REST 4 U!
Let's just say it. Your API is probably not RESTful. I don't even like using the term anymore.
Sadly I do not maintain this anymore. The PHP community did not seem to feel the need for such structure.
Installation
composer require solvire/php-hypermedia-api
Compatibility
Runs on:
- php 5.5+
- php 7
- HHVM
Frameworks
- Laravel
History
This work originally started trying to bring the LeadFerret front-end user API into compliance. It was something I started a few years ago, but was never given the chance to finish. Late nights and coffee pushed it through.
Philosophy
Why?
"Knowing the questions to ask is sometimes more important than knowing the answer." - Smart A. Dude
When you land on a random webpage the authors care enough to provide you a way to navigate the server. Most APIs will only return exactly what you are asking for. You have to read the documentation to know where to go next and what that record actually means.
Hypermedia API
The / root of the application should provide enough information that a machine may transverse the server on it's own.
This library intends to wrap an agnostic framework and provide utilities to produce hypermedia context more easily. This context is meaning behind the returned data.
For REST to be valid it needs to provide context in the form of hypermedia. Again see Roy T. Fielding on the topic.
HATEOAS - Hypermedia as the Engine of Application State
Architecture
JSON-Schema
Taking a queue from google developers discoverability service, we opted for using the JSON Schema definition structure. Specifically JSON Schema draft 04.
There are obvious benefits with having a schema in place. Validation, client generation, etc.
In order for JSON Schema to work properly the types of responses are strongly defined. There is no guessing.
Some of the features were loosely modeled on the Django Rest Framework (DRF). Primarily how they treat their views which we call renderers.
Schemas
When creating a web service the default schema return from the root level of the resources. This is part of a somewhat separate JSON-Schema controller. It shows some of the application level items.
{
"$schema": "http://json-schema.org/draft-04/schema#",
"basePath": "/public/api",
"baseUrl": "https://api.yourdomain.com",
"description": "API Cool End-User API",
"documentationUrl": "http://docs.domain.apiary.io/#",
"id": "appname:v1",
"name": "appname",
"version": "v1.0"
}
Class Structure
Representation Controllers
This object will be the authority to respond back to the control with all data and context related to the resource. Attached to it are the renderers which can present and handle interactions with the data.
Renderers
This is the gateway in and out of the database. It takes the data in and moves it through the serializer into the DB. Also in reverse. Some can also just parse an array for static content. They should be output agnostic. The serializers are attached.
Serializers
Define your resource structure here. Each field has an object with very specific types.
Data Fields
There is an object for each one of your favorite data types. Below is the list.
- BooleanField
- CharField
- DataField
- DataFieldCollection
- DateField
- DateTimeField
- DoubleField
- EmailField
- FileField
- FloatField
- IPAddressField
- ImageField
- IntegerField
- ListField
- PointField
- ReadOnlyField
- RegexField
- SplitPointField
- TimeField
- URLField
- UUIDField
- PhoneField
- GenderField (normalized)
- TransformerField (fractal)
- SerializerField (recursive)
Data Field Condition Attributes
For each data field there are a set of attributes which will affect the functionality of your serializer. Most of the attribute refer to transformations that might occur, while some are validators or filters.
Read Only
readOnly
A field that is marked as 'readOnly'=>true may not be written to. The serializer should ignore this field in determining data writes.
Examples of this are date fields or account status fields.
Default: false
Write Only
writeOnly
A field marked as 'writeOnly'=>true will not be displayed to the end user. They are written to and may have filters or validators applied to them.
Examples are passwords or point increment
Default: false
Required Field
required
If a field is marked with 'required'=>true causes the system to throw an error if the field is not present during the update. Setting this to false is unnecessary since that is the default setting.
Default: false
Allow Null Values
allowNull
If the field is marked with 'allowNull'=>true the serializer will allow you to pass a null value with the field. The recording agent should also respect this request and set the data field to null as well.
Default: false
Allow Empty Strings
allowEmpty
If the field is marked with 'allowEmpty'=>true the serializer will allow you to pass an empty string ''. The recording agent should also respect this request and set the data field to blank as well. Note that null and empty strings are not the same. They should be treated differently.
Default: false
Default Value
defaultValue
Many times you may want to set a default value so that if the data isn't in the db or passed in that it is set already. 'defaultValue'=>'something'
Default: null
Column Name - Alias
One of the biggest problems with an API response value is setting the keys and translating back and forth. Setting a column name will help readability and usability. 'columnName'=>'mapped_column'. For instance your users doesn't need to know the primary column is CompanyNameID. Just 'id' will be fine.
Examples: Primary keys, sensitive names, ugly names
Default: null - uses your DB column name
Validators
This is not implemented yet, but you can assume that we will be allowing the ability to pass in chains of validators. We may standardize but that hasn't happened yet. Suggestions are welcome.
Output Specific Attributes
Styling
This is not implemented yet but you may pass back output related styling.
Help Text
For form fields and UI components it may be helpful to deploy information about it for the human interface. Not implemented yet.
Serializer Example
Here is an example of a serializer for a company record. It extends the LaravelModelSerializer which can interface with Laravel models explicitly. At some point we will integrate with symfony but not at this time.
use Solvire\API\Serializers\DataFields\CharField; use Solvire\API\Serializers\DataFields\BooleanField; use Solvire\API\Serializers\DataFields\IPAddressField; use Solvire\API\Serializers\DataFields\DateTimeField; use Solvire\API\Serializers\DataFields\EmailField; use Solvire\API\Serializers\DataFields\IntegerField; use Solvire\API\Serializers\LaravelModelSerializer; use Solvire\API\Serializers\DataFields\SplitPointField; /** * * @author solvire <info@scotttactical.com> * @package Serializers * @namespace App\Http\Controllers\API\Serializers */ class CompanySerializer extends LaravelModelSerializer { public function initDataFields() { $this->addField('id', new IntegerField(['readOnly'=>true, 'columnName'=>'ComapnyID'])) ->addField('name', new CharField(['columnName'=>'Company_name'])) ->addField('address', new CharField(['columnName'=>'Address'])) ->addField('city', new CharField(['columnName'=>'City'])) ->addField('state', new CharField(['columnName'=>'State'])) ->addField('zip', new CharField(['columnName'=>'Zip'])) ->addField('county', new CharField()) ->addField('area_code', new IntegerField(['columnName'=>'AreaCode'])) ->addField('phone', new CharField(['columnName'=>'Comp_phone'])) ->addField('domain', new CharField()) ->addField('sic', new CharField(['columnName'=>'SIC'])) ->addField('revenue', new IntegerField(['columnName'=>'Rev'])) ->addField('employees', new IntegerField(['columnName'=>'Emps'])) ->addField('exp_id', new IntegerField(['writeOnly'=>true,'columnName'=>'ExpID'])) ->addField('naics', new IntegerField(['columnName'=>'NAICS'])) ->addField('year_founded', new IntegerField()) ->addField('f1000', new IntegerField()) ->addField('alexa', new IntegerField()) ->addField('fbpage', new CharField()) ->addField('opt_out', new BooleanField(['writeOnly'=>true])) ->addField('location', new SplitPointField(['latitudeColumn' => 'latitude', 'longitudeColumn' => 'longitude', 'allowNull' => true])); } }
Here we have an example of a health check serializer. Since this is just a heartbeat response we don't really need to interface with the database. It doesn't need data binding. For this case we just extended the ArraySerializer. Basically only output although we might be able to find some input use cases for it.
use Solvire\API\Serializers\ArraySerializer; use Solvire\API\Serializers\DataFields\CharField; use Solvire\API\Serializers\DataFields\BooleanField; use Solvire\API\Serializers\DataFields\IPAddressField; use Solvire\API\Serializers\DataFields\DateTimeField; /** * * @author solvire <info@scotttactical.com> * @package Serializers * @namespace LeadFerret\Http\Controllers\API\Serializers */ class HealthSerializer extends ArraySerializer { public function initDataFields() { $this->addField('status', new CharField()); $this->addField('server_data', new CharField()); $this->addField('ip', new IPAddressField()); $this->addField('you', new CharField()); $this->addField('stage', new CharField()); $this->addField('alive', new BooleanField()); $this->addField('timestamp', new DateTimeField()); } }
The server response will look like this. It should format the fields into their proper data types. More flexibility will be added for the various fields in the future. Things such as setting timezones and adding translations.
{
"alive": true,
"ip": "192.168.56.1",
"server_data": "LF API",
"stage": "development",
"status": "OK",
"timestamp": {
"date": "2015-09-25 22:23:51.000000",
"timezone": "UTC",
"timezone_type": 3
},
"you": "HTTPie/0.9.2"
}
I've included a class here with some examples of two data field types: TransformerField and SerializerField. As you expect these perform complex nested serializations. Below this class is an example of a sample output.
use Solvire\API\Serializers\DataFields\CharField; use Solvire\API\Serializers\DataFields\BooleanField; use Solvire\API\Serializers\DataFields\DateTimeField; use Solvire\API\Serializers\DataFields\IntegerField; use Solvire\API\Serializers\DataFields\PhoneField; use Solvire\API\Serializers\LaravelModelSerializer; use Solvire\API\Serializers\DataFields\SerializerField; use Solvire\API\Serializers\DataFields\TransformerField; use Solvire\API\Serializers\DataFields\SplitPointField; use LeadFerret\Models\Transformers\API\DepartmentToCategoryTransformer; use Solvire\API\Serializers\DataFields\GenderField; /** * Serialize a contact object * * @author solvire <info@scotttactical.com> * @package Serializers * @namespace LeadFerret\Http\Controllers\API\Serializers */ class ContactSerializer extends LaravelModelSerializer { /** * (non-PHPdoc) * @see \Solvire\API\Serializers\BaseSerializer::initDataFields() */ public function initDataFields() { // the serializer field takes a closure $company = function ($model) { return $model->company; }; $getModel = function ($model){ return $model; }; $this->addField('id', new IntegerField(['readOnly'=>true, 'columnName'=>'ContactID'])) ->addField('first_name', new CharField(['columnName'=>'first_name'])) ->addField('last_name', new CharField(['columnName'=>'last_name'])) ->addField('title', new CharField(['columnName'=>'user_title'])) ->addField('phone', new PhoneField(['columnName'=>'contact_phone'])) ->addField('created', new DateTimeField(['columnName'=>'created_on','format'=>'Y-m-d H:i:s'])) ->addField('score', new IntegerField()) ->addField('crowd_score', new IntegerField()) ->addField('title_level', new CharField(['columnName'=>'title_levels'])) ->addField('gender', new GenderField(['maleValue'=>'Male','femaleValue'=>'Female'])) // normalized gender values ->addField('ads_status', new IntegerField(['writeOnly'=>true])) ->addField('useragent', new CharField()) ->addField('device', new CharField(['writeOnly'=>true,'columnName'=>'uadevice'])) ->addField('opt_out', new BooleanField(['writeOnly'=>true])) ->addField('company', new SerializerField( ['serializer'=>new CompanySerializer(), 'callback' => $company] )) ->addField('professional_categories', new TransformerField(['transformer'=> (new DepartmentToCategoryTransformer()), 'callback' => $getModel ])); } }
The serializer field is basically a tested data field for bringing in already defined serializers.
The transformer can transform your data in any format you need. Literally. I had a unique case I had to combine a lot of fields to create the data as it was so I passed the model in the closure.
And the output:
{
"currentItemCount": 1,
"items": [
{
"company": {
"address": "4209 Technology Drive",
"alexa": 0,
"area_code": 510,
"city": "Fremont",
"county": "Alameda",
"domain": "123.com",
"employees": 15,
"f1000": 0,
"fbpage": "",
"id": 38252,
"location": {
"latitude": 37.520000457764,
"longitude": -121.95999908447
},
"naics": 0,
"name": "3PARdata, Inc.",
"phone": "510-413-5999",
"revenue": 35,
"sic": "3572",
"state": "CA",
"year_founded": 1999,
"zip": "94538"
},
"created": {
"date": "2015-09-30 06:06:55.000000",
"timezone": "UTC",
"timezone_type": 3
},
"crowd_score": 0,
"first_name": "Jeff",
"gender": "male",
"id": 12756,
"last_name": "Song",
"phone": "510-413-1234",
"professional_categories": [
"engineering",
"photographer"
],
"score": 38,
"title": "Vice President of Engineering and Founder",
"title_level": "VP",
"useragent": "Chrome"
}
],
"itemsPerPage": "50",
"pageIndex": 1,
"totalItems": 1,
"totalPages": 1
}
solvire/php-hypermedia-api 适用场景与选型建议
solvire/php-hypermedia-api 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 61 次下载、GitHub Stars 达 14, 最近一次更新时间为 2015 年 09 月 04 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「php」 「http」 「rest」 「api」 「wrapper」 「hypermedia」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 solvire/php-hypermedia-api 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 solvire/php-hypermedia-api 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 solvire/php-hypermedia-api 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
repository php library
A PSR-7 compatible library for making CRUD API endpoints
Api bundle
http客户端
Easily add Excepct-CT header to your project
Build mini web servers in PHP for testing
统计信息
- 总下载量: 61
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 14
- 点击次数: 19
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2015-09-04
