Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

495 rindas
12KB

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