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.

474 rindas
12KB

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