[ 'apiToken' => $apiToken, 'appId' => (int)$appId, ]]; } /** * @return KintoneAccess */ static public function getAccess(): KintoneAccess { $target = static::BASE_MODEL ?? static::class; $store = KintoneAccessStore::instance(); if ($store->has($target)) { return $store->get($target); } $access = new KintoneAccess($target, static::class); $access->setFields(array_keys(static::FIELDS)); foreach (static::RELATIONS as $relation) { $access->addAppToken($relation); } $store->set($target, $access); return $access; } /** * @return KintoneRecordQuery */ static public function getQuery(): KintoneRecordQuery { $target = static::BASE_MODEL ?? static::class; return new KintoneRecordQuery($target); } /** * @return static */ public static function find(string $recordId) { return static::getAccess()->find($recordId); } public static function getDropDownOptions(string $fieldCode): array { $ret = []; $properties = static::getAccess()->getAppFormFields(); $options = Arr::get($properties, sprintf("%s.options", $fieldCode)); $ret = Arr::pluck(Arr::sort($options, function ($option) { return data_get($option, "index"); }), "label"); return $ret; } protected ?string $recordId = null; protected ?int $revision = null; protected ?Carbon $createdAt = null; protected ?Carbon $updatedAt = null; protected ?stdClass $dataOrigin = null; protected stdClass $data; protected const FIELDS = []; protected const FIELD_NAMES = []; protected const RELATIONS = []; private array $changed = []; public function __construct() { $this->data = new stdClass(); } public function __get($name) { $field = "FIELD_" . Str::of($name)->snake()->upper()->toString(); $constantPath = sprintf("%s::%s", static::class, $field); if (defined($constantPath)) { return $this->get(constant($constantPath)); } else { throw new LogicException(sprintf("未定義のメンバアクセス %s %s", static::class, $field)); } } public function __set($name, $value) { $field = "FIELD_" . Str::of($name)->snake()->upper()->toString(); $constantPath = sprintf("%s::%s", static::class, $field); if (defined($constantPath)) { return $this->set(constant($constantPath), $value); } else { throw new LogicException(sprintf("未定義のメンバアクセス %s %s", static::class, $field)); } } public function set(string $fieldCode, $value) { $field = Arr::get(static::FIELDS, $fieldCode); if ($field instanceof FieldType) { $this->setData($fieldCode, $field, $value); } else if (is_array($field)) { $this->setTable($fieldCode, $value); } data_set($this->changed, $fieldCode, true); return $this; } private function setTable(string $fieldCode, array $table) { foreach ($table as $index => $row) { $row = data_get($row, "value"); $this->setTableRow($fieldCode, $index, $row); } } private function setTableRow(string $fieldCode, int $index, array $row) { foreach ($row as $columnFieldCode => $column) { $value = $column["value"]; $type = static::FIELDS[$fieldCode][$columnFieldCode]; $insertKey = sprintf("%s.%d.%s", $fieldCode, $index, $columnFieldCode); $this->setData($insertKey, $type, $value); } } private function setData(string $path, FieldType $type, $value) { data_set($this->data, $path, $value); return $this; // if ( // $type === FieldType::STRING || // $type === FieldType::ARRAY // ) { // data_set($this->data, $path, $value); // // logger([$path, $value]); // return $this; // } // if ($type === FieldType::DATETIME) { // data_set($this->data, $path, DateUtil::parse($value)); // return $this; // } } public function setDataFromRecordResponse(array $data): bool { $ret = true; foreach ($data as $fieldCode => $ele) { $type = data_get($ele, "type"); $value = data_get($ele, "value"); if ($type === "__ID__") { $this->recordId = $value; continue; } if ($type === "__REVISION__") { $this->revision = $value; continue; } if ($type === "CREATED_TIME") { $this->createdAt = DateUtil::parse($value); continue; } if ($type === "UPDATED_TIME") { $this->updatedAt = DateUtil::parse($value); continue; } $type = FieldType::tryFrom($type); if ($type === null) continue; if (in_array($type, [FieldType::DATETIME, FieldType::DATE])) { if ($value) { data_set($this->data, $fieldCode, DateUtil::parse($value)); } else { data_set($this->data, $fieldCode, null); } continue; } if ($type === FieldType::FILE) { $ret = []; foreach ($value as $f) { $ret[] = new File($f); } data_set($this->data, $fieldCode, $ret); continue; } if ($type === FieldType::SUBTABLE) { continue; } // 以外はそのまま格納 data_set($this->data, $fieldCode, $value); } if ($this->recordId === null) { throw new LogicException(sprintf("レコード番号取得失敗 :%s", static::class)); } $ret = $this->setDataCustom($data); if ($ret) { $this->clean(); } return $ret; } /** * 変更前データを現在データで上書きする * * @return void */ public function clean(?int $revision = null) { $this->dataOrigin = clone $this->data; if ($revision !== null) { $this->revision = $revision; } $this->changed = []; } public function getApiLayout(): array { $ret = []; foreach (static::FIELDS as $fieldCode => $type) { // 変更があった項目のみレイアウトへ出力する if (!Arr::has($this->changed, $fieldCode)) { continue; } $path = sprintf("%s.value", $fieldCode); if ($type === FieldType::DATETIME) { $data = $this->getDate($fieldCode); if ($data) { data_set($ret, $path, $data->toIso8601ZuluString()); } continue; } if ($type === FieldType::DATE) { $data = $this->getDate($fieldCode); if ($data) { data_set($ret, $path, $data->format("Y-m-d")); data_set($ret, $path, $data->toDateString()); } continue; } data_set($ret, $path, data_get($this->data, $fieldCode)); } return array_merge($ret, $this->getApiLayoutCustom()); } public function get(string $key) { return data_get($this->data, $key); } public function getStr(string $key): ?string { return $this->get($key); } public function getNumber(string $key): ?int { return $this->get($key); } public function getDate(string $key): ?Carbon { return $this->get($key); } public function getTable(string $key): ?array { return $this->get($key); } public function setRecordId(string $id): static { $this->recordId = $id; return $this; } public function getRecordId(): ?string { return $this->recordId; } public function getRevision(): ?int { return $this->revision; } public function getUpdatedAt(): ?Carbon { return $this->updatedAt; } public function getCreatedAt(): ?Carbon { return $this->createdAt; } public function save() { $access = static::getAccess(); if ($this->recordId === null) { $access->create($this); } else { $access->update($this); } } public function toArray($column = ['*']): array { if ($this->recordId === null) { throw new LogicException("保存前モデルのシリアライズ検知"); } $ret = [ 'record_no' => $this->recordId, 'revision' => $this->revision, ]; $columnAll = data_get($column, 0) === '*'; /** * @var string $fieldCode */ foreach ($this->data as $fieldCode => $value) { if (!$columnAll && !in_array($fieldCode, $column)) { continue; } $type = data_get(static::FIELDS, $fieldCode); $columnName = data_get(static::FIELD_NAMES, $fieldCode, null); if ($columnName === null) { continue; } if ($type === null) { $ret[$columnName] = $value; continue; } if ($type === FieldType::DATETIME) { if ($value instanceof Carbon) { $ret[$columnName] = $value->format('Y/m/d H:i'); } else { $ret[$columnName] = $value; } continue; } if ($type === FieldType::DATE) { if ($value instanceof Carbon) { $ret[$columnName] = $value->format('Y/m/d'); } else { $ret[$columnName] = $value; } continue; } $ret[$columnName] = $value; } $ret = array_merge($ret, $this->toArrayCustom()); return $ret; } protected function toArrayCustom(): array { return []; } /** * オーバーライドを期待 * * @param array $data * @return boolean */ protected function setDataCustom(array $data): bool { return true; } /** * オーバーライドを期待 * * @return array */ protected function getApiLayoutCustom(): array { return []; } }