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.

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