Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

366 lines
9.3KB

  1. <?php
  2. namespace App\Kintone\Models;
  3. use App\Exceptions\AppCommonException;
  4. use App\Exceptions\ConfigException;
  5. use App\Kintone\File;
  6. use App\Kintone\KintoneAccess;
  7. use App\Kintone\KintoneRecordQuery;
  8. use App\Util\DateUtil;
  9. use Illuminate\Support\Arr;
  10. use Illuminate\Support\Carbon;
  11. use LogicException;
  12. use stdClass;
  13. abstract class KintoneModel
  14. {
  15. const CONFIG_KEY = "";
  16. static public function configKey(): string
  17. {
  18. if (!static::CONFIG_KEY) {
  19. throw new LogicException(sprintf("Kintone 設定キー 未設定:%s", static::class));
  20. }
  21. return static::CONFIG_KEY;
  22. }
  23. static public function setConfig(): array
  24. {
  25. $configStr = env(static::configKey());
  26. if (!$configStr) {
  27. throw new ConfigException(static::configKey(), $configStr);
  28. }
  29. $params = explode(",", $configStr);
  30. if (count($params) !== 2) {
  31. throw new ConfigException(static::configKey(), $configStr);
  32. }
  33. [$apiToken, $appId] = $params;
  34. return [static::class => [
  35. 'apiToken' => $apiToken,
  36. 'appId' => (int)$appId,
  37. ]];
  38. }
  39. /**
  40. * @return KintoneAccess<static>
  41. */
  42. static public function getAccess(): KintoneAccess
  43. {
  44. $access = new KintoneAccess(static::class);
  45. $access->setFields(array_keys(static::FIELDS));
  46. return $access;
  47. }
  48. /**
  49. * @return KintoneRecordQuery<static>
  50. */
  51. static public function getQuery(): KintoneRecordQuery
  52. {
  53. return new KintoneRecordQuery(static::class);
  54. }
  55. protected ?string $recordId = null;
  56. protected ?int $revision = null;
  57. protected ?Carbon $createdAt = null;
  58. protected ?Carbon $updatedAt = null;
  59. protected ?stdClass $dataOrigin = null;
  60. protected stdClass $data;
  61. protected const FIELDS = [];
  62. protected const FIELD_NAMES = [];
  63. private array $changed = [];
  64. public function __construct()
  65. {
  66. $this->data = new stdClass();
  67. }
  68. public function set(string $fieldCode, $value)
  69. {
  70. $field = Arr::get(static::FIELDS, $fieldCode);
  71. if ($field instanceof FieldType) {
  72. $this->setData($fieldCode, $field, $value);
  73. } else if (is_array($field)) {
  74. $this->setTable($fieldCode, $value);
  75. }
  76. data_set($this->changed, $fieldCode, true);
  77. return $this;
  78. }
  79. private function setTable(string $fieldCode, array $table)
  80. {
  81. foreach ($table as $index => $row) {
  82. $row = data_get($row, "value");
  83. $this->setTableRow($fieldCode, $index, $row);
  84. }
  85. }
  86. private function setTableRow(string $fieldCode, int $index, array $row)
  87. {
  88. foreach ($row as $columnFieldCode => $column) {
  89. $value = $column["value"];
  90. $type = static::FIELDS[$fieldCode][$columnFieldCode];
  91. $insertKey = sprintf("%s.%d.%s", $fieldCode, $index, $columnFieldCode);
  92. $this->setData($insertKey, $type, $value);
  93. }
  94. }
  95. private function setData(string $path, FieldType $type, $value)
  96. {
  97. return $this;
  98. // if (
  99. // $type === FieldType::STRING ||
  100. // $type === FieldType::ARRAY
  101. // ) {
  102. // data_set($this->data, $path, $value);
  103. // // logger([$path, $value]);
  104. // return $this;
  105. // }
  106. // if ($type === FieldType::DATETIME) {
  107. // data_set($this->data, $path, DateUtil::parse($value));
  108. // return $this;
  109. // }
  110. }
  111. public function setDataFromRecordResponse(array $data): bool
  112. {
  113. $ret = true;
  114. foreach ($data as $fieldCode => $ele) {
  115. $type = data_get($ele, "type");
  116. $value = data_get($ele, "value");
  117. if ($type === "__ID__") {
  118. $this->recordId = $value;
  119. continue;
  120. }
  121. if ($type === "__REVISION__") {
  122. $this->revision = $value;
  123. continue;
  124. }
  125. if ($type === "CREATED_TIME") {
  126. $this->createdAt = DateUtil::parse($value);
  127. continue;
  128. }
  129. if ($type === "UPDATED_TIME") {
  130. $this->updatedAt = DateUtil::parse($value);
  131. continue;
  132. }
  133. $type = FieldType::tryFrom($type);
  134. if ($type === null) continue;
  135. if (in_array($type, [FieldType::DATETIME, FieldType::DATE])) {
  136. if ($value) {
  137. data_set($this->data, $fieldCode, DateUtil::parse($value));
  138. } else {
  139. data_set($this->data, $fieldCode, null);
  140. }
  141. continue;
  142. }
  143. if ($type === FieldType::FILE) {
  144. $ret = [];
  145. foreach ($value as $f) {
  146. $ret[] = new File($f);
  147. }
  148. data_set($this->data, $fieldCode, $ret);
  149. continue;
  150. }
  151. if ($type === FieldType::SUBTABLE) {
  152. continue;
  153. }
  154. // 以外はそのまま格納
  155. data_set($this->data, $fieldCode, $value);
  156. }
  157. if ($this->recordId === null) {
  158. throw new LogicException(sprintf("レコード番号取得失敗 :%s", static::class));
  159. }
  160. $ret = $this->setDataCustom($data);
  161. if ($ret) {
  162. $this->clean();
  163. }
  164. return $ret;
  165. }
  166. /**
  167. * 変更前データを現在データで上書きする
  168. *
  169. * @return void
  170. */
  171. public function clean(?int $revision = null)
  172. {
  173. $this->dataOrigin = clone $this->data;
  174. if ($revision !== null) {
  175. $this->revision = $revision;
  176. }
  177. $this->changed = [];
  178. }
  179. public function getApiLayout(): array
  180. {
  181. $ret = [];
  182. foreach (static::FIELDS as $fieldCode => $type) {
  183. // 変更があった項目のみレイアウトへ出力する
  184. if (!Arr::has($this->changed, $fieldCode)) {
  185. continue;
  186. }
  187. $path = sprintf("%s.value", $fieldCode);
  188. if ($type === FieldType::DATETIME) {
  189. data_set($ret, $path, $this->getDate($fieldCode)->toIso8601ZuluString());
  190. continue;
  191. }
  192. data_set($ret, $path, data_get($this->data, $fieldCode));
  193. }
  194. return array_merge($ret, $this->getApiLayoutCustom());
  195. }
  196. public function get(string $key)
  197. {
  198. $a = json_decode(json_encode($this->data), true);
  199. return data_get($this->data, $key);
  200. }
  201. public function getStr(string $key): ?string
  202. {
  203. return $this->get($key);
  204. }
  205. public function getNumber(string $key): ?int
  206. {
  207. return $this->get($key);
  208. }
  209. public function getDate(string $key): ?Carbon
  210. {
  211. return $this->get($key);
  212. }
  213. public function getTable(string $key): ?array
  214. {
  215. return $this->get($key);
  216. }
  217. public function getRecordId(): ?string
  218. {
  219. return $this->recordId;
  220. }
  221. public function getRevision(): ?int
  222. {
  223. return $this->revision;
  224. }
  225. public function getUpdatedAt(): ?Carbon
  226. {
  227. return $this->updatedAt;
  228. }
  229. public function getCreatedAt(): ?Carbon
  230. {
  231. return $this->createdAt;
  232. }
  233. public function toArray($column = ['*']): array
  234. {
  235. if ($this->recordId === null) {
  236. throw new LogicException("保存前モデルのシリアライズ検知");
  237. }
  238. $ret = [
  239. 'record_no' => $this->recordId,
  240. 'revision' => $this->revision,
  241. ];
  242. $columnAll = data_get($column, 0) === '*';
  243. /**
  244. * @var string $fieldCode
  245. */
  246. foreach ($this->data as $fieldCode => $value) {
  247. if (!$columnAll && !in_array($fieldCode, $column)) {
  248. continue;
  249. }
  250. $type = data_get(static::FIELDS, $fieldCode);
  251. $columnName = data_get(static::FIELD_NAMES, $fieldCode, null);
  252. if ($columnName === null) {
  253. continue;
  254. }
  255. if ($type === null) {
  256. $ret[$columnName] = $value;
  257. continue;
  258. }
  259. if ($type === FieldType::DATETIME) {
  260. if ($value instanceof Carbon) {
  261. $ret[$columnName] = $value->format('Y/m/d H:i:s');
  262. } else {
  263. $ret[$columnName] = $value;
  264. }
  265. continue;
  266. }
  267. if ($type === FieldType::DATE) {
  268. if ($value instanceof Carbon) {
  269. $ret[$columnName] = $value->format('Y/m/d');
  270. } else {
  271. $ret[$columnName] = $value;
  272. }
  273. continue;
  274. }
  275. $ret[$columnName] = $value;
  276. }
  277. $ret = array_merge($ret, $this->toArrayCustom());
  278. return $ret;
  279. }
  280. protected function toArrayCustom(): array
  281. {
  282. return [];
  283. }
  284. /**
  285. * オーバーライドを期待
  286. *
  287. * @param array $data
  288. * @return boolean
  289. */
  290. protected function setDataCustom(array $data): bool
  291. {
  292. return true;
  293. }
  294. /**
  295. * オーバーライドを期待
  296. *
  297. * @return array
  298. */
  299. protected function getApiLayoutCustom(): array
  300. {
  301. return [];
  302. }
  303. }