Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

487 Zeilen
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. $response = Http::withHeaders([
  165. "X-Cybozu-API-Token" => $this->apiToken,
  166. ])->put($this->getRecordUrl(), $sendData);
  167. if ($response->failed()) {
  168. $e = $response->toException();
  169. if ($e instanceof Exception) {
  170. Log::error($e->getMessage());
  171. throw $e;
  172. }
  173. }
  174. $model->clean($response["revision"]);
  175. return $response;
  176. }
  177. /**
  178. * @param TValue $model
  179. * @return void
  180. */
  181. public function create(KintoneModel &$model)
  182. {
  183. $sendData = [
  184. "app" => $this->appId,
  185. "record" => $model->getApiLayout(),
  186. ];
  187. $response = Http::withHeaders([
  188. "X-Cybozu-API-Token" => $this->apiToken,
  189. ])->post($this->getRecordUrl(), $sendData);
  190. if ($response->failed()) {
  191. $e = $response->toException();
  192. if ($e instanceof Exception) {
  193. Log::error($e->getMessage());
  194. Log::error($response->body());
  195. Log::error($sendData);
  196. throw $e;
  197. }
  198. }
  199. $model->clean($response["revision"]);
  200. $model->setRecordId($response["id"]);
  201. return $response;
  202. }
  203. public function createCursor(KintoneRecordQuery|null $query = null)
  204. {
  205. $data = [
  206. "app" => $this->appId,
  207. 'fields' => $this->fields,
  208. ];
  209. if ($query !== null) {
  210. $data["query"] = $query->toQuery();
  211. }
  212. $response = Http::withHeaders([
  213. "X-Cybozu-API-Token" => $this->apiToken,
  214. ])->post($this->getCursorUrl(), $data);
  215. if ($response->failed()) {
  216. $e = $response->toException();
  217. if ($e instanceof Exception) {
  218. Log::error($e->getMessage(), ['data' => $data]);
  219. Log::error($response->body());
  220. throw $e;
  221. }
  222. }
  223. $cursor = $response["id"];
  224. $totalCount = $response["totalCount"];
  225. if (0 < $totalCount) {
  226. $this->hasNext = true;
  227. $this->cursor = $cursor;
  228. $this->cursorDataCount = $totalCount;
  229. } else {
  230. $this->cursor = null;
  231. $this->hasNext = false;
  232. $this->cursorDataCount = 0;
  233. }
  234. return $response;
  235. }
  236. /**
  237. * @return Collection<int,TValue>
  238. */
  239. public function next()
  240. {
  241. if (!$this->hasNext) {
  242. return collect();
  243. }
  244. $response = Http::withHeaders([
  245. "X-Cybozu-API-Token" => $this->apiToken,
  246. ])->get($this->getCursorUrl(), [
  247. "id" => $this->cursor,
  248. ]);
  249. if ($response->failed()) {
  250. $e = $response->toException();
  251. if ($e instanceof Exception) {
  252. Log::error($e->getMessage());
  253. throw $e;
  254. }
  255. }
  256. $ret = collect();
  257. $hasNext = $response["next"];
  258. if (!$hasNext) {
  259. $this->cursor = null;
  260. $this->hasNext = false;
  261. $this->cursorDataCount = 0;
  262. }
  263. foreach ($response["records"] as $data) {
  264. /**
  265. * @var KintoneModel $model
  266. */
  267. $model = new $this->appName();
  268. $model->setDataFromRecordResponse($data);
  269. $ret->push($model);
  270. }
  271. return $ret;
  272. }
  273. /**
  274. * @return Collection<int,TValue>
  275. */
  276. public function all(KintoneRecordQuery|null $query = null)
  277. {
  278. if ($this->cursor !== null) {
  279. $this->deleteCursor();
  280. }
  281. $this->createCursor($query);
  282. $list = collect();
  283. while (true) {
  284. $ret = $this->next();
  285. foreach ($ret as $ele) {
  286. $list->push($ele);
  287. }
  288. if ($this->cursor === null) {
  289. break;
  290. }
  291. }
  292. return $list;
  293. }
  294. public function deleteCursor()
  295. {
  296. if ($this->cursor === null) {
  297. return;
  298. }
  299. $response = Http::withHeaders([
  300. "X-Cybozu-API-Token" => $this->apiToken,
  301. ])->delete($this->getCursorUrl(), [
  302. "id" => $this->cursor,
  303. ]);
  304. if ($response->failed()) {
  305. $e = $response->toException();
  306. if ($e instanceof Exception) {
  307. Log::error($e->getMessage());
  308. throw $e;
  309. }
  310. }
  311. $this->cursor = null;
  312. $this->hasNext = false;
  313. $this->cursorDataCount = 0;
  314. }
  315. public function fileGet(string $fileKey)
  316. {
  317. $response = Http::withHeaders([
  318. "X-Cybozu-API-Token" => $this->apiToken,
  319. ])->get($this->getFileUrl(), [
  320. "fileKey" => $fileKey,
  321. ]);
  322. if ($response->failed()) {
  323. $e = $response->toException();
  324. if ($e instanceof Exception) {
  325. Log::error($e->getMessage());
  326. Log::error($response->body());
  327. throw $e;
  328. }
  329. }
  330. return $response;
  331. }
  332. /**
  333. * ファイルアップロード
  334. *
  335. * @param UploadedFile|TmpFile $file
  336. * @return string fileKey
  337. */
  338. public function filePut(UploadedFile|TmpFile $file): string
  339. {
  340. $content = "";
  341. $sendFileName = "";
  342. if ($file instanceof UploadedFile) {
  343. $content = file_get_contents($file);
  344. $sendFileName = sprintf("file.%s", $file->extension());
  345. } else if ($file instanceof TmpFile) {
  346. $content = $file->get();
  347. $sendFileName = $file->getAppFileName();
  348. }
  349. $response = Http::withHeaders([
  350. "X-Cybozu-API-Token" => $this->apiToken,
  351. ])
  352. ->attach("file", $content, $sendFileName)
  353. ->post($this->getFileUrl());
  354. if ($response->failed()) {
  355. $e = $response->toException();
  356. if ($e instanceof Exception) {
  357. Log::error($e->getMessage());
  358. Log::error($response->body());
  359. throw $e;
  360. }
  361. }
  362. return $response['fileKey'];
  363. }
  364. public function getAppFormFields(): array
  365. {
  366. $response = Http::withHeaders([
  367. "X-Cybozu-API-Token" => $this->apiToken,
  368. ])->get($this->getAppFormFieldsUrl(), [
  369. "app" => $this->appId,
  370. ]);
  371. if ($response->failed()) {
  372. $e = $response->toException();
  373. if ($e instanceof Exception) {
  374. Log::error($e->getMessage());
  375. throw $e;
  376. }
  377. }
  378. return $response['properties'];
  379. }
  380. private function getRecordUrl()
  381. {
  382. return $this->getUrl(self::URL_RECORD);
  383. }
  384. private function getRecordsUrl()
  385. {
  386. return $this->getUrl(self::URL_RECORDS);
  387. }
  388. private function getCursorUrl()
  389. {
  390. return $this->getUrl(self::URL_CURSOL);
  391. }
  392. private function getFileUrl()
  393. {
  394. return $this->getUrl(self::URL_FILE);
  395. }
  396. private function getAppFormFieldsUrl()
  397. {
  398. return $this->getUrl(self::URL_APP_FORM_FIELDS);
  399. }
  400. private function getUrl(string $path)
  401. {
  402. return $this->host . $path;
  403. }
  404. }