Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

380 lines
9.8KB

  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. data_set($this->data, $path, $value);
  98. return $this;
  99. // if (
  100. // $type === FieldType::STRING ||
  101. // $type === FieldType::ARRAY
  102. // ) {
  103. // data_set($this->data, $path, $value);
  104. // // logger([$path, $value]);
  105. // return $this;
  106. // }
  107. // if ($type === FieldType::DATETIME) {
  108. // data_set($this->data, $path, DateUtil::parse($value));
  109. // return $this;
  110. // }
  111. }
  112. public function setDataFromRecordResponse(array $data): bool
  113. {
  114. $ret = true;
  115. foreach ($data as $fieldCode => $ele) {
  116. $type = data_get($ele, "type");
  117. $value = data_get($ele, "value");
  118. if ($type === "__ID__") {
  119. $this->recordId = $value;
  120. continue;
  121. }
  122. if ($type === "__REVISION__") {
  123. $this->revision = $value;
  124. continue;
  125. }
  126. if ($type === "CREATED_TIME") {
  127. $this->createdAt = DateUtil::parse($value);
  128. continue;
  129. }
  130. if ($type === "UPDATED_TIME") {
  131. $this->updatedAt = DateUtil::parse($value);
  132. continue;
  133. }
  134. $type = FieldType::tryFrom($type);
  135. if ($type === null) continue;
  136. if (in_array($type, [FieldType::DATETIME, FieldType::DATE])) {
  137. if ($value) {
  138. data_set($this->data, $fieldCode, DateUtil::parse($value));
  139. } else {
  140. data_set($this->data, $fieldCode, null);
  141. }
  142. continue;
  143. }
  144. if ($type === FieldType::FILE) {
  145. $ret = [];
  146. foreach ($value as $f) {
  147. $ret[] = new File($f);
  148. }
  149. data_set($this->data, $fieldCode, $ret);
  150. continue;
  151. }
  152. if ($type === FieldType::SUBTABLE) {
  153. continue;
  154. }
  155. // 以外はそのまま格納
  156. data_set($this->data, $fieldCode, $value);
  157. }
  158. if ($this->recordId === null) {
  159. throw new LogicException(sprintf("レコード番号取得失敗 :%s", static::class));
  160. }
  161. $ret = $this->setDataCustom($data);
  162. if ($ret) {
  163. $this->clean();
  164. }
  165. return $ret;
  166. }
  167. /**
  168. * 変更前データを現在データで上書きする
  169. *
  170. * @return void
  171. */
  172. public function clean(?int $revision = null)
  173. {
  174. $this->dataOrigin = clone $this->data;
  175. if ($revision !== null) {
  176. $this->revision = $revision;
  177. }
  178. $this->changed = [];
  179. }
  180. public function getApiLayout(): array
  181. {
  182. $ret = [];
  183. foreach (static::FIELDS as $fieldCode => $type) {
  184. // 変更があった項目のみレイアウトへ出力する
  185. if (!Arr::has($this->changed, $fieldCode)) {
  186. continue;
  187. }
  188. $path = sprintf("%s.value", $fieldCode);
  189. if ($type === FieldType::DATETIME) {
  190. $data = $this->getDate($fieldCode);
  191. if ($data) {
  192. data_set($ret, $path, $data->toIso8601ZuluString());
  193. }
  194. continue;
  195. }
  196. if ($type === FieldType::DATE) {
  197. $data = $this->getDate($fieldCode);
  198. if ($data) {
  199. data_set($ret, $path, $data->format("Y-m-d"));
  200. data_set($ret, $path, $data->toDateString());
  201. }
  202. continue;
  203. }
  204. data_set($ret, $path, data_get($this->data, $fieldCode));
  205. }
  206. return array_merge($ret, $this->getApiLayoutCustom());
  207. }
  208. public function get(string $key)
  209. {
  210. return data_get($this->data, $key);
  211. }
  212. public function getStr(string $key): ?string
  213. {
  214. return $this->get($key);
  215. }
  216. public function getNumber(string $key): ?int
  217. {
  218. return $this->get($key);
  219. }
  220. public function getDate(string $key): ?Carbon
  221. {
  222. return $this->get($key);
  223. }
  224. public function getTable(string $key): ?array
  225. {
  226. return $this->get($key);
  227. }
  228. public function setRecordId(string $id): static
  229. {
  230. $this->recordId = $id;
  231. return $this;
  232. }
  233. public function getRecordId(): ?string
  234. {
  235. return $this->recordId;
  236. }
  237. public function getRevision(): ?int
  238. {
  239. return $this->revision;
  240. }
  241. public function getUpdatedAt(): ?Carbon
  242. {
  243. return $this->updatedAt;
  244. }
  245. public function getCreatedAt(): ?Carbon
  246. {
  247. return $this->createdAt;
  248. }
  249. public function toArray($column = ['*']): array
  250. {
  251. if ($this->recordId === null) {
  252. throw new LogicException("保存前モデルのシリアライズ検知");
  253. }
  254. $ret = [
  255. 'record_no' => $this->recordId,
  256. 'revision' => $this->revision,
  257. ];
  258. $columnAll = data_get($column, 0) === '*';
  259. /**
  260. * @var string $fieldCode
  261. */
  262. foreach ($this->data as $fieldCode => $value) {
  263. if (!$columnAll && !in_array($fieldCode, $column)) {
  264. continue;
  265. }
  266. $type = data_get(static::FIELDS, $fieldCode);
  267. $columnName = data_get(static::FIELD_NAMES, $fieldCode, null);
  268. if ($columnName === null) {
  269. continue;
  270. }
  271. if ($type === null) {
  272. $ret[$columnName] = $value;
  273. continue;
  274. }
  275. if ($type === FieldType::DATETIME) {
  276. if ($value instanceof Carbon) {
  277. $ret[$columnName] = $value->format('Y/m/d H:i:s');
  278. } else {
  279. $ret[$columnName] = $value;
  280. }
  281. continue;
  282. }
  283. if ($type === FieldType::DATE) {
  284. if ($value instanceof Carbon) {
  285. $ret[$columnName] = $value->format('Y/m/d');
  286. } else {
  287. $ret[$columnName] = $value;
  288. }
  289. continue;
  290. }
  291. $ret[$columnName] = $value;
  292. }
  293. $ret = array_merge($ret, $this->toArrayCustom());
  294. return $ret;
  295. }
  296. protected function toArrayCustom(): array
  297. {
  298. return [];
  299. }
  300. /**
  301. * オーバーライドを期待
  302. *
  303. * @param array $data
  304. * @return boolean
  305. */
  306. protected function setDataCustom(array $data): bool
  307. {
  308. return true;
  309. }
  310. /**
  311. * オーバーライドを期待
  312. *
  313. * @return array
  314. */
  315. protected function getApiLayoutCustom(): array
  316. {
  317. return [];
  318. }
  319. }