No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

396 líneas
10KB

  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. public static function getDropDownOptions(string $fieldCode): array
  168. {
  169. $ret = [];
  170. $properties = static::getAccess()->getAppFormFields();
  171. $options = Arr::get($properties, sprintf("%s.options", $fieldCode));
  172. $ret = Arr::pluck(Arr::sort($options, function ($option) {
  173. return data_get($option, "index");
  174. }), "label");
  175. // foreach ($options as $option) {
  176. // $index = data_get($option, "index");
  177. // $label = data_get($option, "label");
  178. // $ret[$index] = $label;
  179. // }
  180. return $ret;
  181. }
  182. /**
  183. * 変更前データを現在データで上書きする
  184. *
  185. * @return void
  186. */
  187. public function clean(?int $revision = null)
  188. {
  189. $this->dataOrigin = clone $this->data;
  190. if ($revision !== null) {
  191. $this->revision = $revision;
  192. }
  193. $this->changed = [];
  194. }
  195. public function getApiLayout(): array
  196. {
  197. $ret = [];
  198. foreach (static::FIELDS as $fieldCode => $type) {
  199. // 変更があった項目のみレイアウトへ出力する
  200. if (!Arr::has($this->changed, $fieldCode)) {
  201. continue;
  202. }
  203. $path = sprintf("%s.value", $fieldCode);
  204. if ($type === FieldType::DATETIME) {
  205. $data = $this->getDate($fieldCode);
  206. if ($data) {
  207. data_set($ret, $path, $data->toIso8601ZuluString());
  208. }
  209. continue;
  210. }
  211. if ($type === FieldType::DATE) {
  212. $data = $this->getDate($fieldCode);
  213. if ($data) {
  214. data_set($ret, $path, $data->format("Y-m-d"));
  215. data_set($ret, $path, $data->toDateString());
  216. }
  217. continue;
  218. }
  219. data_set($ret, $path, data_get($this->data, $fieldCode));
  220. }
  221. return array_merge($ret, $this->getApiLayoutCustom());
  222. }
  223. public function get(string $key)
  224. {
  225. return data_get($this->data, $key);
  226. }
  227. public function getStr(string $key): ?string
  228. {
  229. return $this->get($key);
  230. }
  231. public function getNumber(string $key): ?int
  232. {
  233. return $this->get($key);
  234. }
  235. public function getDate(string $key): ?Carbon
  236. {
  237. return $this->get($key);
  238. }
  239. public function getTable(string $key): ?array
  240. {
  241. return $this->get($key);
  242. }
  243. public function setRecordId(string $id): static
  244. {
  245. $this->recordId = $id;
  246. return $this;
  247. }
  248. public function getRecordId(): ?string
  249. {
  250. return $this->recordId;
  251. }
  252. public function getRevision(): ?int
  253. {
  254. return $this->revision;
  255. }
  256. public function getUpdatedAt(): ?Carbon
  257. {
  258. return $this->updatedAt;
  259. }
  260. public function getCreatedAt(): ?Carbon
  261. {
  262. return $this->createdAt;
  263. }
  264. public function toArray($column = ['*']): array
  265. {
  266. if ($this->recordId === null) {
  267. throw new LogicException("保存前モデルのシリアライズ検知");
  268. }
  269. $ret = [
  270. 'record_no' => $this->recordId,
  271. 'revision' => $this->revision,
  272. ];
  273. $columnAll = data_get($column, 0) === '*';
  274. /**
  275. * @var string $fieldCode
  276. */
  277. foreach ($this->data as $fieldCode => $value) {
  278. if (!$columnAll && !in_array($fieldCode, $column)) {
  279. continue;
  280. }
  281. $type = data_get(static::FIELDS, $fieldCode);
  282. $columnName = data_get(static::FIELD_NAMES, $fieldCode, null);
  283. if ($columnName === null) {
  284. continue;
  285. }
  286. if ($type === null) {
  287. $ret[$columnName] = $value;
  288. continue;
  289. }
  290. if ($type === FieldType::DATETIME) {
  291. if ($value instanceof Carbon) {
  292. $ret[$columnName] = $value->format('Y/m/d H:i:s');
  293. } else {
  294. $ret[$columnName] = $value;
  295. }
  296. continue;
  297. }
  298. if ($type === FieldType::DATE) {
  299. if ($value instanceof Carbon) {
  300. $ret[$columnName] = $value->format('Y/m/d');
  301. } else {
  302. $ret[$columnName] = $value;
  303. }
  304. continue;
  305. }
  306. $ret[$columnName] = $value;
  307. }
  308. $ret = array_merge($ret, $this->toArrayCustom());
  309. return $ret;
  310. }
  311. protected function toArrayCustom(): array
  312. {
  313. return [];
  314. }
  315. /**
  316. * オーバーライドを期待
  317. *
  318. * @param array $data
  319. * @return boolean
  320. */
  321. protected function setDataCustom(array $data): bool
  322. {
  323. return true;
  324. }
  325. /**
  326. * オーバーライドを期待
  327. *
  328. * @return array
  329. */
  330. protected function getApiLayoutCustom(): array
  331. {
  332. return [];
  333. }
  334. }