ioncurly / vetmanager-api-gateway
兽医管理系统 API 网关
Requires
- php: >=8.1
- ext-mbstring: *
- guzzlehttp/guzzle: ^7.5
- otis22/vetmanager-rest-api: ^0.1.11
- phpdocumentor/reflection-docblock: ^5.3
- symfony/property-access: ^6.3
- symfony/serializer: ^6.3
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.16
- phpunit/phpunit: ^10
- vimeo/psalm: ^5.9
This package is auto-updated.
Last update: 2024-09-20 11:37:14 UTC
README
兽医管理系统 API 工具。负责以下功能:
- 认证;
- 获取数据并以对象和对象的类型属性的形式展示;
- 方便通过获取到的对象(模型)的方法获取相关模型;
- 发送新模型、编辑、删除;
- 实现缓存的便利性:可以以数组的形式提供模型,并从数组创建模型;
- 统一不同获取方式下对模型的工作方式。
该库基于 Vetmanager API 请求的 Query 构建库 - 这里还有复杂 API 请求的类(Builder 和 PagedQuery)的使用文档
为什么使用?
通过这个库,可以方便地从兽医管理系统 API 获取数据。数据以 Active Record 的形式到来。每个 Active Record 都与一个或多个其他 Active Record 相关联。代码示例
$apiGateway = VetmanagerApiGateway\ApiGateway::fromSubdomainAndApiKey('subDomain', 'apiKey', true); $invoice = $apiGateway->getInvoice()->getById(7); // Получение модели Счета по ID 7 $invoiceDiscount = $invoice->getDiscount(); // Получение скидки из Счета $petBreedTitle = $invoice->getPetBreed()->getTitle(); // Получение названия типа питомца из счета $pet = $invoice->getPet(); // Получение модели Питомца из Счета $petAlias = $pet->getAlias(); // Получение клички Питомца из Счета
安装
composer require ioncurly/vetmanager-api-gateway
简短概述
详细使用说明
开始使用/配置连接
简单来说,ApiGateway 对象是与兽医管理系统 API 交互的桥梁。不需要再创建其他东西
- 使用子域名和 API 密钥
$subDomain = 'kras-best'; // субдомен клиники в ветменеджер $apiKey = 'xXdfxfsfsdffsf'; // АПИ ключ к домену в ветменеджер $isProduction = true; // рабочий или тестовый сервер будет использоваться $apiGateway = VetmanagerApiGateway\ApiGateway::fromSubdomainAndApiKey($subDomain, $apiKey, $isProduction);
- 使用服务器完整路径和 API 密钥
$apiGateway = VetmanagerApiGateway\ApiGateway::fromFullUrlAndApiKey('https://xxx', 'apiKey', true);
- 使用子域名、API 服务名称和 API 密钥(用于特殊内部服务)
$apiGateway = VetmanagerApiGateway\ApiGateway::fromSubdomainAndServiceNameAndApiKey('subDomain', 'serviceName', 'apiKey', true);
- 使用服务器完整路径、API 服务名称和 API 密钥(用于特殊内部服务)
$apiGateway = VetmanagerApiGateway\ApiGateway::fromFullUrlAndServiceNameAndApiKey('https://xxx', 'serviceName', 'apiKey', true);
初始对象获取
获取 Active Record 的所有逻辑都封装在相应的 Facade 中。以下是通过 ID 获取客户的示例
$apiGateway = VetmanagerApiGateway\ApiGateway::fromSubdomainAndApiKey('subDomain', 'apiKey', true); $clientFacade = $apiGateway->getClient(); // выделение фасада в переменную лишь для наглядности $client = $clientFacade->getById(33);
也就是说,在这个库中,对于每种类型的 Active Record,都存在一个对应的 Facade。在 Facade 中,包含了 API 请求的逻辑和从响应中获取 Active Record 的逻辑。例如,在 Client 的 Facade 中,包含了通过 ID 获取相应 Active Record 的方法。同样,在 Facade 中还包含了其他获取方法(包括通过更复杂的查询 - 例如,通过过滤)。
有些 Active Records 只能通过特定的 API 请求获取。例如,MedicalCardByClient 只能通过客户 ID 获取。在相应的外观中没有提供获取方法,这些方法就不存在
$apiGateway = VetmanagerApiGateway\ApiGateway::fromSubdomainAndApiKey('subDomain', 'apiKey', true); $medicalCards = $apiGateway->getMedicalCardByClient()->getByClientId(33);
通过 ID 获取对象
$apiGateway = VetmanagerApiGateway\ApiGateway::fromSubdomainAndApiKey('subDomain', 'apiKey', true); $client = $apiGateway->getClient()->getById(33);
获取所有对象
总是返回模型数组。即使只获取一个对象,也要通过数组来访问。
$apiGateway = VetmanagerApiGateway\ApiGateway::fromSubdomainAndApiKey('subDomain', 'apiKey', true); $invoices = $apiGateway->getInvoice()->getAll(maxLimitOfReturnedModels: 20); // В параметре можем дописать лимит возвращаемых моделей (иначе 100 по умолчанию) if (!empty($invoices)) { $invoiceDescription = $invoices[0]->getDescription(); }
通过查询获取对象
与获取所有模型一样,查询请求总是返回对象数组。也就是说,即使返回一个对象,也要通过数组来访问。
以下是同一查询的3种变体
-
使用 查询构建器
$apiGateway = VetmanagerApiGateway\ApiGateway::fromSubdomainAndApiKey('subDomain', 'apiKey', true); $comboManualItems = $apiGateway->getComboManualItem()->getByQueryBuilder( (new Otis22\VetmanagerRestApi\Query\Builder()) ->where('value', '7') ->where('combo_manual_id', '1'), 1 // Опциональный параметр - лимит возвращаемых моделей );
-
使用 分页查询
使用此对象更方便地处理分页。
$apiGateway = VetmanagerApiGateway\ApiGateway::fromSubdomainAndApiKey('subDomain', 'apiKey', true); $comboManualItems = $apiGateway->getComboManualItem()->getByPagedQuery( (new Otis22\VetmanagerRestApi\Query\Builder()) ->where('value', '7') ->where('combo_manual_id', '1') ->top(1) // Лимит возвращаемых моделей );
-
以字符串形式使用 获取参数
可以传递所有与Postman集合中使用的相同Get参数。有关过滤器、排序等更详细的信息,请参阅 - Vetmanager REST API文档
$apiGateway = VetmanagerApiGateway\ApiGateway::fromSubdomainAndApiKey('subDomain', 'apiKey', true); $comboManualItems = $apiGateway->getComboManualItem()->getByGetParametersAsString( "filter=[{'property':'combo_manual_id', 'value':'1'},{'property':'value', 'value':'7'}]&limit=1" );
获取特定模型的替代方法
use VetmanagerApiGateway\DTO\ComboManualName\NameEnum; $apiGateway = VetmanagerApiGateway\ApiGateway::fromSubdomainAndApiKey('subDomain', 'apiKey', true); $clinicLanguage = $apiGateway->getProperty()->getByClinicIdAndPropertyName($clinicId = 13, $property = 'lang')->getTitle(); $clientMedicalCards = $apiGateway->getMedicalCardByClient()->getByClientId(77); $petVaccinations = $apiGateway->getMedicalCardAsVaccination()->getByPetId(11); $admissionType = $apiGateway->getComboManualItem()->getByAdmissionTypeId(11); $admissionTypeTitle = $admissionType->getTitle(); // Пример использования содержимого модели $result = $apiGateway->getComboManualItem()->getByAdmissionResultId(11); $petColor = $apiGateway->getComboManualItem()->getByPetColorId(11); $vaccineType = $apiGateway->getComboManualItem()->getByVaccineTypeId(11); $admissionResult13 = $apiGateway->getComboManualItem()->getOneByValueAndComboManualName(13, NameEnum::AdmissionResult); $admissionResultManual = $apiGateway->getComboManualName()->getByNameAsString('admission_result'); $admissionResultManualId = $apiGateway->getComboManualName()->getIdByNameAsString('admission_result'); $admissionResultManual = $apiGateway->getComboManualName()->getByNameAsEnum(NameEnum::AdmissionResult); $admissionResultManualId = $apiGateway->getComboManualName()->getIdByNameAsEnum(NameEnum::AdmissionResult); $clientAdmissions = $apiGateway->getAdmission()->getByClientId(40); $petAdmissions = $apiGateway->getAdmission()->getByPetId(88);
创建新模型
使用外观和数组
$apiGateway = VetmanagerApiGateway\ApiGateway::fromSubdomainAndApiKey('subDomain', 'apiKey', true); // Отправляет новый город и возвращает объект (соответствующий Active Record) с созданной моделью от АПИ $newCity = $apiGateway->getCity()->createNewUsingArray(["title" => "New City", "type_id" => "1"]); echo $newCity->getId(); // Получим новый присвоенный ID. Можно и другие свойства так же получить
使用Active Record和setter
$apiGateway = VetmanagerApiGateway\ApiGateway::fromSubdomainAndApiKey('subDomain', 'apiKey', true); $newEmptyCity = $apiGateway->getCity()->getNewEmpty(); $cityWithSetValues = $newEmptyCity->setTitle("New City")->setTypeId(1); $newCity = $cityWithSetValues->create(); echo $newCity->getId(); // Получим новый присвоенный ID. Можно и другие свойства так же получить
编辑模型
使用外观和数组
$apiGateway = VetmanagerApiGateway\ApiGateway::fromSubdomainAndApiKey('subDomain', 'apiKey', true); // Отправляет массив данных для изменения модели и возвращает объект (соответствующий Active Record) с созданной моделью от АПИ $updatedCity = $apiGateway->getCity()->updateUsingIdAndArray(13, ["title" => "New City", "type_id" => "1"]); // В $updatedCity будет модель полученная из ответа по АПИ echo $updatedCity->getTitle(); // Получим "New City". Можно и другие свойства так же получить
使用Active Record和setter
$apiGateway = VetmanagerApiGateway\ApiGateway::fromSubdomainAndApiKey('subDomain', 'apiKey', true); $city = $apiGateway->getCity()->getById(13); $updatedCity = $city->setTitle("New City")->update(); // Без вызова update - модель не отправится // В $updatedCity будет модель полученная из ответа по АПИ echo $updatedCity->getTitle(); // Получим "New City". Можно и другие свойства так же получить
删除模型
使用外观
$apiGateway = VetmanagerApiGateway\ApiGateway::fromSubdomainAndApiKey('subDomain', 'apiKey', true); $apiGateway->getCity()->delete(13);
使用Active Record
$apiGateway = VetmanagerApiGateway\ApiGateway::fromSubdomainAndApiKey('subDomain', 'apiKey', true); $city = $apiGateway->getCity()->getById(13); $city->delete();
模型数据表示示例
通过get方法获取每个属性。每个方法都有类型化
$apiGateway = VetmanagerApiGateway\ApiGateway::fromSubdomainAndApiKey('subDomain', 'apiKey', true); $client = $apiGateway->getClient()->getById(13); $clientEmail = $client->getEmail(); // Объявлено, что только строка, возможно пустая $clientCityId = $client->getCityId(); // Объявлено, что только int или null может прийти $clientDateRegister = $client->getDateRegisterAsDateTime()?->format('Y-m-d H:i:s'); //dateRegister содержит DateTime (или null, если отсутствует дата) $clientName = $client->getFullName()->getInitials(); // FullName - вспомогательный объект для удобного форматирования имени $clientStatus = $client->getStatusAsEnum(); // Возвращается одно из возможных значений соответствующего Enum
相关查询示例
对象的某些属性,而不是返回标量数据,返回其他对象或对象数组。
$apiGateway = VetmanagerApiGateway\ApiGateway::fromSubdomainAndApiKey('subDomain', 'apiKey', true); $client = $apiGateway->getClient()->getById(13); $clientStreet = $client->getStreet(); // В переменной будет null или Модель Улицы $streetName = $clientStreet?->getTitle() ?? ''; // Название улицы или пустая строка $clientPets = $client->getPetsAlive(); // Массив с Моделями Питомцев $firstPet = (!empty($client->petsAlive)) ? $clientPets[0] : null; // Будет Питомец или null $firstPetName = $firstPet?->getColor()?->getTitle() : ''; // Получение названия цвета питомца или пустая строка, если нет
更复杂的调用示例
$apiGateway = VetmanagerApiGateway\ApiGateway::fromSubdomainAndApiKey('subDomain', 'apiKey', true)->getClient()->getById(13)->getMedicalCards(); $firstMedicalCardOfClient = !empty($clientMedicalCards) ? $clientMedicalCards[0] : null; $middleNameOfFirstMedicalCardDoctor = $firstMedicalCardOfClient?->getUser()?->getMiddleName();
相同的记录,但添加了额外的变量以供理解
$apiGateway = VetmanagerApiGateway\ApiGateway::fromSubdomainAndApiKey('subDomain', 'apiKey', true); // Получение объекта ApiGateway $clientFacade = $apiGateway->getClient(); // Получение фасада для моделей Клиента с разными методами для работы с ними $client = $clientFacade->getById(13); // Получение через АПИ-запрос используя ID модель Клиента (1ый АПИ запрос) $clientMedicalCards = $client->getMedicalCards(); // Получение всех карт Клиента (2ой АПИ запрос) $firstMedicalCardOfClient = !empty($clientMedicalCards) ? $clientMedicalCards[0] : null; // Получим первую карту Клиента или null, если нет карт $firstMedicalCardDoctor = $firstMedicalCardOfClient?->getUser(); // Получение модели Доктора из медицинской карты (3ий запрос) $middleNameOfFirstMedicalCardDoctor = $firstMedicalCardDoctor?->getMiddleName(); // Получение отчества доктора из первой Карты Клиента
对象规范化(转换为数组)
使用这些方法可以轻松实现模型的 缓存。
每次需要执行重复的相同查询时
1 - 尝试获取已缓存的对象作为数组。
-
如果找到了缓存
2 - 从此缓存创建对象。
-
如果未找到缓存
2 - 执行API请求以获取对象
3 - 将模型作为数组放入缓存以供下一个PHP会话使用
获取模型数组
返回的数据与通过API获得的数据相同
$apiGateway = VetmanagerApiGateway\ApiGateway::fromSubdomainAndApiKey('subDomain', 'apiKey', true); $clientAsArray = $apiGateway->getClient()->getById(13)->getAsArray(); // Получим массив модели вида: ["id" => "1", "address" => "", "home_phone" => "3322122", ... ]
从数组数据创建对象
- 从缓存数组模型(例如 ['id' => '12', '...' => ...])中获取一个对象。
$apiGateway = VetmanagerApiGateway\ApiGateway::fromSubdomainAndApiKey('subDomain', 'apiKey', true); $client = $apiGateway->getClient()->fromSingleModelAsArray($cachedClientAsArray);
- 从这样的数组数组中获取对象数组
$apiGateway = VetmanagerApiGateway\ApiGateway::fromSubdomainAndApiKey('subDomain', 'apiKey', true); $clients = $apiGateway->getClient()->fromMultipleModelsAsArrays($cachedClientAsArray); // Дальше продолжаем использовать будто получили массив моделей как обычно $firstClientId = $clients[0]->getId();
其他功能
辅助对象FullName
例如,User和Client模型具有FullName获取器。FullName对象具有返回不同格式的完整姓名的方法
$clientFullName = VetmanagerApiGateway\ApiGateway::fromSubdomainAndApiKey('subDomain', 'apiKey', true)->getClient()->getById(9)->getFullName(); echo $clientFullName->getFullStartingWithFirst();// Возвращает: "Имя Отчество Фамилия" echo $clientFullName->getFullStartingWithLast(); // Возвращает: "Фамилия Имя Отчество" echo $clientFullName->getLastPlusInitials(); // Возвращает: "Фамилия И. О." echo $clientFullName->getInitials(); // Возвращает: "Ф. И. О."
如果假设没有姓氏,则每个方法将简单地跳过单词而不会创建多余的空格、点等。
辅助对象FullPhone
例如,诊所模型具有返回FullPhone的方法。FullPhone对象具有返回带有国家代码和所选掩码的电话号码的方法,例如:+7(918)-277-21-21
$clinicFullPhone = VetmanagerApiGateway\ApiGateway::fromSubdomainAndApiKey('subDomain', 'apiKey', true)->getClinic()->getById(9)->getFullPhone(); echo $clinicFullPhone->getAsMaskedWithCountryCode(); // Выведет телефона в виде +7(918)-277-21-21 echo $clinicFullPhone; // То же самое, что и прошлая строка - _toString() вызывает тот же метод echo $clinicFullPhone->mask; // Подобные маски могут вернуться: '(___)-__-__-__', '(__)___-____' или '____-____' echo $clinicFullPhone->countryCode; // +7 или +38 и т.д.
了解诊所的在线预约能力
有多种选择。返回bool
$apiGateway = VetmanagerApiGateway\ApiGateway::fromSubdomainAndApiKey('subDomain', 'apiKey', true) $bool1 = $apiGateway->getProperty()->getIsOnlineSigningUpAvailableForClinic(13); // Один АПИ-запрос $bool2 = $apiGateway->getClinic()->getIsOnlineSigningUpAvailable(13); // Один АПИ-запрос $bool3 = $apiGateway->getClinic()->getById(13)->getIsOnlineSigningUpAvailable(); // Два АПИ-запроса
关于库结构的简要介绍
使用的库
-
Symfony 序列化器 - 用于从数组数据(反序列化的 JSON)创建 DTO 及其反向过程(规范化)
-
Guzzle HTTP 客户端 - 用于所有使用的请求
-
vetmanager-rest-api - 用于查询 Vetmanager API 的助手
仅限开发时使用
-
PSALM
-
PHPUnit
内部特性
对于 Vetmanager API 的大多数模型,除了请求的模型外,还会返回其他相关联的模型的内容。这也取决于请求的类型 - 例如,在按 ID 请求时,通常会返回最多的相关联模型。对于使用这个库的用户来说,这是隐藏的:无论如何,无论获得哪种类型的 Active Record,都可以访问所有方法和与其他模型的关系。至于调用方法时如何获取数据:是从已获取的数据中获取还是通过额外的请求获取 - 这也是库负责的。
以下是理解结构的示例。但使用时这并不重要。在使用中,每个获取到的 Active Record 都是相同的(因为它们具有相同的方法集,只是实现方式不同)
$apiGateway = VetmanagerApiGateway\ApiGateway::fromSubdomainAndApiKey('subDomain', 'apiKey', true); /** @var VetmanagerApiGateway\Facade\Client $clientFacade Содержит методы связанные с моделью для осуществления АПИ-запросов и созданию Active Records */ $clientFacade = $apiGateway->getClient(); /** @var VetmanagerApiGateway\ActiveRecord\Client\ClientOnly[] $clients Содержит в себе DTO Client и связи со связанным Active Records*/ $clients = $clientFacade->getAll(); /** @var VetmanagerApiGateway\ActiveRecord\Client\ClientPlusTypeAndCity $client Содержит в себе DTO для моделей Client, Client Type, City */ $client = $clientFacade->getById(33);
开发
- 从示例中复制环境文件
cp .env.example .env
- 填写
.env
- 安装库
docker compose run php-fpm composer install
- 运行单元测试
docker compose run php-fpm composer test-unit
可以运行其他命令,示例可以在 composer.json
的 scripts
部分查看。例如
docker compose run php-fpm composer style-check