You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

353 line
8.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. const FIELD_RECORD_NUMBER = "レコード番号";
  17. const FIELD_CREATED_TIME = "作成日時";
  18. const FIELD_UPDATED_TIME = "更新日時";
  19. static public function configKey(): string
  20. {
  21. if (!static::CONFIG_KEY) {
  22. throw new LogicException(sprintf("Kintone 設定キー 未設定:%s", static::class));
  23. }
  24. return static::CONFIG_KEY;
  25. }
  26. static public function setConfig(): array
  27. {
  28. $configStr = env(static::configKey());
  29. if (!$configStr) {
  30. throw new ConfigException(static::configKey(), $configStr);
  31. }
  32. $params = explode(",", $configStr);
  33. if (count($params) !== 2) {
  34. throw new ConfigException(static::configKey(), $configStr);
  35. }
  36. [$apiToken, $appId] = $params;
  37. return [static::class => [
  38. 'apiToken' => $apiToken,
  39. 'appId' => (int)$appId,
  40. ]];
  41. }
  42. /**
  43. * @return KintoneAccess<static>
  44. */
  45. static public function getAccess(): KintoneAccess
  46. {
  47. $access = new KintoneAccess(static::class);
  48. $access->setFields(array_keys(static::FIELDS));
  49. return $access;
  50. }
  51. /**
  52. * @return KintoneRecordQuery<static>
  53. */
  54. static public function getQuery(): KintoneRecordQuery
  55. {
  56. return new KintoneRecordQuery(static::class);
  57. }
  58. protected ?int $recordId = null;
  59. protected ?int $revision = null;
  60. protected ?Carbon $createdAt = null;
  61. protected ?Carbon $updatedAt = null;
  62. protected ?stdClass $dataOrigin = null;
  63. protected stdClass $data;
  64. protected const FIELDS = [];
  65. protected const FIELD_NAMES = [];
  66. private array $changed = [];
  67. public function __construct()
  68. {
  69. $this->data = new stdClass();
  70. }
  71. public function set(string $fieldCode, $value)
  72. {
  73. $field = Arr::get(static::FIELDS, $fieldCode);
  74. if ($field instanceof FieldType) {
  75. $this->setData($fieldCode, $field, $value);
  76. } else if (is_array($field)) {
  77. $this->setTable($fieldCode, $value);
  78. }
  79. data_set($this->changed, $fieldCode, true);
  80. return $this;
  81. }
  82. private function setTable(string $fieldCode, array $table)
  83. {
  84. foreach ($table as $index => $row) {
  85. $row = data_get($row, "value");
  86. $this->setTableRow($fieldCode, $index, $row);
  87. }
  88. }
  89. private function setTableRow(string $fieldCode, int $index, array $row)
  90. {
  91. foreach ($row as $columnFieldCode => $column) {
  92. $value = $column["value"];
  93. $type = static::FIELDS[$fieldCode][$columnFieldCode];
  94. $insertKey = sprintf("%s.%d.%s", $fieldCode, $index, $columnFieldCode);
  95. $this->setData($insertKey, $type, $value);
  96. }
  97. }
  98. private function setData(string $path, FieldType $type, $value)
  99. {
  100. return $this;
  101. // if (
  102. // $type === FieldType::STRING ||
  103. // $type === FieldType::ARRAY
  104. // ) {
  105. // data_set($this->data, $path, $value);
  106. // // logger([$path, $value]);
  107. // return $this;
  108. // }
  109. // if ($type === FieldType::DATETIME) {
  110. // data_set($this->data, $path, DateUtil::parse($value));
  111. // return $this;
  112. // }
  113. }
  114. public function setDataFromRecordResponse(array $data): bool
  115. {
  116. $ret = true;
  117. foreach ($data as $fieldCode => $ele) {
  118. $type = data_get($ele, "type");
  119. $value = data_get($ele, "value");
  120. if ($type === "RECORD_NUMBER") {
  121. $this->recordId = $value;
  122. continue;
  123. }
  124. if ($type === "__REVISION__") {
  125. $this->revision = $value;
  126. continue;
  127. }
  128. if ($type === "CREATED_TIME") {
  129. $this->createdAt = DateUtil::parse($value);
  130. continue;
  131. }
  132. if ($type === "UPDATED_TIME") {
  133. $this->updatedAt = DateUtil::parse($value);
  134. continue;
  135. }
  136. $type = FieldType::tryFrom($type);
  137. if ($type === null) continue;
  138. if ($type === FieldType::DATETIME) {
  139. if ($value) {
  140. data_set($this->data, $fieldCode, DateUtil::parse($value));
  141. } else {
  142. data_set($this->data, $fieldCode, null);
  143. }
  144. continue;
  145. }
  146. if ($type === FieldType::FILE) {
  147. $ret = [];
  148. foreach ($value as $f) {
  149. $ret[] = new File($f);
  150. }
  151. data_set($this->data, $fieldCode, $ret);
  152. continue;
  153. }
  154. if ($type === FieldType::SUBTABLE) {
  155. continue;
  156. }
  157. // 以外はそのまま格納
  158. data_set($this->data, $fieldCode, $value);
  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(): ?int
  218. {
  219. return $this->recordId;
  220. }
  221. public function setRecordId(int $recordId): void
  222. {
  223. $this->recordId = $recordId;
  224. }
  225. public function getRevision(): ?int
  226. {
  227. return $this->revision;
  228. }
  229. public function setRevision(int $revision): void
  230. {
  231. $this->revision = $revision;
  232. }
  233. public function getUpdatedAt(): ?Carbon
  234. {
  235. return $this->updatedAt;
  236. }
  237. public function getCreatedAt(): ?Carbon
  238. {
  239. return $this->createdAt;
  240. }
  241. public function toArray(): array
  242. {
  243. if ($this->recordId === null) {
  244. throw new LogicException("保存前モデルのシリアライズ検知");
  245. }
  246. $ret = [
  247. 'record_no' => $this->recordId,
  248. 'revision' => $this->revision,
  249. ];
  250. /**
  251. * @var string $fieldCode
  252. */
  253. foreach ($this->data as $fieldCode => $value) {
  254. $type = data_get(static::FIELDS, $fieldCode);
  255. $columnName = data_get(static::FIELD_NAMES, $fieldCode, $fieldCode);
  256. if ($type === null) {
  257. $ret[$columnName] = $value;
  258. continue;
  259. }
  260. if ($type === FieldType::DATETIME) {
  261. if ($value instanceof Carbon) {
  262. $ret[$columnName] = $value->format('Y/m/d H:i:s');
  263. } else {
  264. $ret[$columnName] = $value;
  265. }
  266. continue;
  267. }
  268. $ret[$columnName] = $value;
  269. }
  270. return $ret;
  271. }
  272. /**
  273. * オーバーライドを期待
  274. *
  275. * @param array $data
  276. * @return boolean
  277. */
  278. protected function setDataCustom(array $data): bool
  279. {
  280. return true;
  281. }
  282. /**
  283. * オーバーライドを期待
  284. *
  285. * @return array
  286. */
  287. protected function getApiLayoutCustom(): array
  288. {
  289. return [];
  290. }
  291. }