您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

458 行
11KB

  1. <?php
  2. namespace App\Kintone;
  3. use App\Exceptions\ConfigException;
  4. use App\Kintone\Models\KintoneModel;
  5. use Exception;
  6. use Illuminate\Http\UploadedFile;
  7. use Illuminate\Support\Collection;
  8. use Illuminate\Support\Facades\Http;
  9. use Log;
  10. /**
  11. * @template TValue of KintoneModel
  12. */
  13. class KintoneAccess
  14. {
  15. private string $appName;
  16. private string $host;
  17. private string $apiToken = "";
  18. private int $appId;
  19. private const DEFAULT_FIELDS = [
  20. "作成日時",
  21. "更新日時",
  22. '$id',
  23. '$revision',
  24. ];
  25. private array $fields = [];
  26. private string|null $cursor = null;
  27. private int $cursorDataCount = 0;
  28. private bool $hasNext = false;
  29. private const URL_RECORD = "/k/v1/record.json";
  30. private const URL_RECORDS = "/k/v1/records.json";
  31. private const URL_CURSOL = "/k/v1/records/cursor.json";
  32. private const URL_FILE = "/k/v1/file.json";
  33. private const URL_APP_FORM_FIELDS = "/k/v1/app/form/fields.json";
  34. public function __construct(string $appName)
  35. {
  36. $this->appName = $appName;
  37. $key = "kintone.host";
  38. $host = config($key);
  39. if (!$host) {
  40. throw new ConfigException("kintone.host", $host);
  41. }
  42. $key = "kintone.applications." . $appName . ".apiToken";
  43. $apiToken = config($key);
  44. if (!$apiToken) {
  45. throw new ConfigException($key, $apiToken);
  46. }
  47. $key = "kintone.applications." . $appName . ".appId";
  48. $appId = config($key);
  49. if (!$appId) {
  50. throw new ConfigException($key, $appId);
  51. }
  52. $this->addAppToken($appName);
  53. $this->host = $host;
  54. $this->appId = $appId;
  55. }
  56. public function addAppToken(string $appName): static
  57. {
  58. $key = "kintone.applications." . $appName . ".apiToken";
  59. $apiToken = config($key);
  60. if (!$apiToken) {
  61. throw new ConfigException($key, $apiToken);
  62. }
  63. $this->apiToken = $this->apiToken ? $this->apiToken . "," . $apiToken : $apiToken;
  64. return $this;
  65. }
  66. public function setFields(array $fields): static
  67. {
  68. $this->fields = [
  69. ...$fields,
  70. ...self::DEFAULT_FIELDS,
  71. ];
  72. return $this;
  73. }
  74. public function __destruct()
  75. {
  76. $this->deleteCursor();
  77. }
  78. /**
  79. * @param integer $id
  80. * @return TValue
  81. */
  82. public function find(int $id)
  83. {
  84. $response = Http::withHeaders([
  85. "X-Cybozu-API-Token" => $this->apiToken,
  86. ])->get($this->getRecordUrl(), [
  87. "app" => $this->appId,
  88. "id" => $id,
  89. 'fields' => $this->fields,
  90. ]);
  91. if ($response->failed()) {
  92. $e = $response->toException();
  93. if ($e instanceof Exception) {
  94. Log::error($e->getMessage());
  95. throw $e;
  96. }
  97. }
  98. $result = new $this->appName();
  99. $result->setDataFromRecordResponse($response['record']);
  100. return $result;
  101. }
  102. /**
  103. * @param KintoneRecordQuery|null|null $query
  104. * @return Collection<string,TValue>
  105. */
  106. public function some(KintoneRecordQuery|null $query = null)
  107. {
  108. $data = [
  109. "app" => $this->appId,
  110. 'fields' => $this->fields,
  111. ];
  112. if ($query !== null) {
  113. $data["query"] = $query->toQuery();
  114. }
  115. $response = Http::withHeaders([
  116. "X-Cybozu-API-Token" => $this->apiToken,
  117. ])->get($this->getRecordsUrl(), $data);
  118. if ($response->failed()) {
  119. $e = $response->toException();
  120. if ($e instanceof Exception) {
  121. Log::error($e->getMessage());
  122. throw $e;
  123. }
  124. }
  125. $ret = collect();
  126. foreach ($response["records"] as $data) {
  127. /**
  128. * @var KintoneModel $model
  129. */
  130. $model = new $this->appName();
  131. $model->setDataFromRecordResponse($data);
  132. $ret->put($model->getRecordId(), $model);
  133. }
  134. return $ret;
  135. }
  136. /**
  137. * @param TValue $model
  138. * @return void
  139. */
  140. public function update(KintoneModel &$model)
  141. {
  142. $sendData = [
  143. "app" => $this->appId,
  144. "id" => $model->getRecordId(),
  145. "record" => $model->getApiLayout(),
  146. "revision" => $model->getRevision(),
  147. ];
  148. $response = Http::withHeaders([
  149. "X-Cybozu-API-Token" => $this->apiToken,
  150. ])->put($this->getRecordUrl(), $sendData);
  151. if ($response->failed()) {
  152. $e = $response->toException();
  153. if ($e instanceof Exception) {
  154. Log::error($e->getMessage());
  155. throw $e;
  156. }
  157. }
  158. $model->clean($response["revision"]);
  159. return $response;
  160. }
  161. /**
  162. * @param TValue $model
  163. * @return void
  164. */
  165. public function create(KintoneModel &$model)
  166. {
  167. $sendData = [
  168. "app" => $this->appId,
  169. "record" => $model->getApiLayout(),
  170. ];
  171. $response = Http::withHeaders([
  172. "X-Cybozu-API-Token" => $this->apiToken,
  173. ])->post($this->getRecordUrl(), $sendData);
  174. if ($response->failed()) {
  175. $e = $response->toException();
  176. if ($e instanceof Exception) {
  177. Log::error($e->getMessage());
  178. Log::error($response->body());
  179. Log::error($sendData);
  180. throw $e;
  181. }
  182. }
  183. $model->clean($response["revision"]);
  184. $model->setRecordId($response["id"]);
  185. return $response;
  186. }
  187. public function createCursor(KintoneRecordQuery|null $query = null)
  188. {
  189. $data = [
  190. "app" => $this->appId,
  191. 'fields' => $this->fields,
  192. ];
  193. if ($query !== null) {
  194. $data["query"] = $query->toQuery();
  195. }
  196. $response = Http::withHeaders([
  197. "X-Cybozu-API-Token" => $this->apiToken,
  198. ])->post($this->getCursorUrl(), $data);
  199. if ($response->failed()) {
  200. $e = $response->toException();
  201. if ($e instanceof Exception) {
  202. Log::error($e->getMessage(), ['data' => $data]);
  203. throw $e;
  204. }
  205. }
  206. $cursor = $response["id"];
  207. $totalCount = $response["totalCount"];
  208. if (0 < $totalCount) {
  209. $this->hasNext = true;
  210. $this->cursor = $cursor;
  211. $this->cursorDataCount = $totalCount;
  212. } else {
  213. $this->cursor = null;
  214. $this->hasNext = false;
  215. $this->cursorDataCount = 0;
  216. }
  217. return $response;
  218. }
  219. /**
  220. * @return Collection<int,TValue>
  221. */
  222. public function next()
  223. {
  224. if (!$this->hasNext) {
  225. return collect();
  226. }
  227. $response = Http::withHeaders([
  228. "X-Cybozu-API-Token" => $this->apiToken,
  229. ])->get($this->getCursorUrl(), [
  230. "id" => $this->cursor,
  231. ]);
  232. if ($response->failed()) {
  233. $e = $response->toException();
  234. if ($e instanceof Exception) {
  235. Log::error($e->getMessage());
  236. throw $e;
  237. }
  238. }
  239. $ret = collect();
  240. $hasNext = $response["next"];
  241. if (!$hasNext) {
  242. $this->cursor = null;
  243. $this->hasNext = false;
  244. $this->cursorDataCount = 0;
  245. }
  246. foreach ($response["records"] as $data) {
  247. /**
  248. * @var KintoneModel $model
  249. */
  250. $model = new $this->appName();
  251. $model->setDataFromRecordResponse($data);
  252. $ret->push($model);
  253. }
  254. return $ret;
  255. }
  256. /**
  257. * @return Collection<int,TValue>
  258. */
  259. public function all(KintoneRecordQuery|null $query = null)
  260. {
  261. if ($this->cursor !== null) {
  262. $this->deleteCursor();
  263. }
  264. $this->createCursor($query);
  265. $list = collect();
  266. while (true) {
  267. $ret = $this->next();
  268. foreach ($ret as $ele) {
  269. $list->push($ele);
  270. }
  271. if ($this->cursor === null) {
  272. break;
  273. }
  274. }
  275. return $list;
  276. }
  277. public function deleteCursor()
  278. {
  279. if ($this->cursor === null) {
  280. return;
  281. }
  282. $response = Http::withHeaders([
  283. "X-Cybozu-API-Token" => $this->apiToken,
  284. ])->delete($this->getCursorUrl(), [
  285. "id" => $this->cursor,
  286. ]);
  287. if ($response->failed()) {
  288. $e = $response->toException();
  289. if ($e instanceof Exception) {
  290. Log::error($e->getMessage());
  291. throw $e;
  292. }
  293. }
  294. $this->cursor = null;
  295. $this->hasNext = false;
  296. $this->cursorDataCount = 0;
  297. }
  298. public function fileGet(string $fileKey)
  299. {
  300. $response = Http::withHeaders([
  301. "X-Cybozu-API-Token" => $this->apiToken,
  302. "Content-Type" => "application/json",
  303. ])->get($this->getCursorUrl(), [
  304. "fileKey" => $fileKey,
  305. ]);
  306. if ($response->failed()) {
  307. $e = $response->toException();
  308. if ($e instanceof Exception) {
  309. Log::error($e->getMessage());
  310. throw $e;
  311. }
  312. }
  313. return $response;
  314. }
  315. /**
  316. * ファイルアップロード
  317. *
  318. * @param UploadedFile $file
  319. * @return string fileKey
  320. */
  321. public function filePut(UploadedFile $file): string
  322. {
  323. $content = file_get_contents($file);
  324. $response = Http::withHeaders([
  325. "X-Cybozu-API-Token" => $this->apiToken,
  326. ])
  327. ->attach("file", $content, sprintf("file.%s", $file->extension()))
  328. ->post($this->getFileUrl());
  329. if ($response->failed()) {
  330. $e = $response->toException();
  331. if ($e instanceof Exception) {
  332. Log::error($e->getMessage());
  333. Log::error($response->body());
  334. throw $e;
  335. }
  336. }
  337. return $response['fileKey'];
  338. }
  339. public function getAppFormFields(): array
  340. {
  341. $response = Http::withHeaders([
  342. "X-Cybozu-API-Token" => $this->apiToken,
  343. ])->get($this->getAppFormFieldsUrl(), [
  344. "app" => $this->appId,
  345. ]);
  346. if ($response->failed()) {
  347. $e = $response->toException();
  348. if ($e instanceof Exception) {
  349. Log::error($e->getMessage());
  350. throw $e;
  351. }
  352. }
  353. return $response['properties'];
  354. }
  355. private function getRecordUrl()
  356. {
  357. return $this->getUrl(self::URL_RECORD);
  358. }
  359. private function getRecordsUrl()
  360. {
  361. return $this->getUrl(self::URL_RECORDS);
  362. }
  363. private function getCursorUrl()
  364. {
  365. return $this->getUrl(self::URL_CURSOL);
  366. }
  367. private function getFileUrl()
  368. {
  369. return $this->getUrl(self::URL_FILE);
  370. }
  371. private function getAppFormFieldsUrl()
  372. {
  373. return $this->getUrl(self::URL_APP_FORM_FIELDS);
  374. }
  375. private function getUrl(string $path)
  376. {
  377. return $this->host . $path;
  378. }
  379. }