[ '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(int $recordId) { return static::getAccess()->find($recordId); } /** * @return static */ public static function first(?KintoneRecordQuery $query = null) { return static::getAccess()->first($query); } 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; const FIELD_RECORD_NO = "レコード番号"; protected const FIELDS = []; protected const SUB_TABLES = []; protected const FIELD_NAMES = []; protected const RELATIONS = []; private array $changed = []; public function __construct() { $this->data = new stdClass(); foreach (static::FIELDS as $fileCode => $type) { if ($type === FieldType::SUBTABLE) { $this->set($fileCode, collect()); } } } 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) { $type = $this->getFieldType($fieldCode); if ($type) { $this->setData($fieldCode, $value); data_set($this->changed, $fieldCode, true); } return $this; } private function setData(string $path, $value) { data_set($this->data, $path, $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; } data_set($this->data, $fieldCode, $this->readData($ele, $fieldCode)); } if ($this->recordId === null) { throw new LogicException(sprintf("レコード番号取得失敗 :%s", static::class)); } $ret = $this->setDataCustom($data); if ($ret) { $this->clean(); } return $ret; } private function readData(array $data, string $fieldCode = "") { $type = data_get($data, "type"); $value = data_get($data, "value"); if ($type === null || $value === null) { return null; } $type = FieldType::tryFrom($type); if ($type !== null) { if (in_array($type, [FieldType::DATETIME, FieldType::DATE])) { if ($value) { return DateUtil::parse($value); } else { return null; } } if ($type === FieldType::NUMBER) { return intval($value); } if ($type === FieldType::FILE) { $ret = []; foreach ($value as $f) { $ret[] = new File($f); } return $ret; } if ($type === FieldType::SUBTABLE && $this->getFieldType($fieldCode)) { $ret = collect(); foreach ($value as $v) { $rowRet = []; $rowArray = data_get($v, 'value'); $isEmptyRow = true; foreach ($rowArray as $subFieldCode => $ele) { $rowRet[$subFieldCode] = $this->readData($ele); if (!!$rowRet[$subFieldCode]) { $isEmptyRow = false; } } if (!$isEmptyRow) { $ret->push($this->getSubTableData($fieldCode, $rowRet)); } } return $ret; } return $value; } return null; } private function getSubTableData(string $fieldCode, array $data): SubTableData { $className = data_get(static::SUB_TABLES, $fieldCode, ""); if (class_exists($className)) { return new $className($data); } throw new LogicException("キントーン設定不備"); } /** * 変更前データを現在データで上書きする * * @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); data_set($ret, $path, $this->getApiLayoutValue($fieldCode)); } return array_merge($ret, $this->getApiLayoutCustom()); } private function getApiLayoutValue(string $fieldCode) { $ret = []; $data = $this->get($fieldCode); $type = $this->getFieldType($fieldCode); if ($type === FieldType::DATETIME) { $data = $this->getDate($fieldCode); if ($data) { return $data->toIso8601ZuluString(); } else { return ""; } } if ($type === FieldType::DATE) { $data = $this->getDate($fieldCode); if ($data) { return $data->toDateString(); } else { return ""; } } if ($data instanceof Collection) { $ret = []; foreach ($data as $ele) { if ($ele instanceof SubTableData) { $obj = []; $values = $ele->toArray(); foreach ($values as $subFieldCode => $value) { $obj['value'][$subFieldCode]['value'] = $value; } $ret[] = $obj; } } return $ret; } return data_get($this->data, $fieldCode); } private function getFieldType(string $fieldCode): ?FieldType { return data_get(static::FIELDS, $fieldCode); } 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; } /** * @param string $fieldCode * @param Collection $files * @param bool $override * @return static */ public function setFiles(string $fieldCode, Collection $files, bool $override = true): static { if ($override) { $this->set($fieldCode, []); } foreach ($files as $file) { $this->addFile($fieldCode, $file); } return $this; } public function addFile(string $fieldCode, UploadedFile|TmpFile $file) { if ($file instanceof UploadedFile) { $name = sprintf("image_%d.%s", Str::uuid(), $file->extension()); $contentType = $file->getClientMimeType(); } else { $name = $file->getAppFileName(); $contentType = $file->getMimeType(); } $access = $this->getAccess(); $field = $this->get($fieldCode); array_push($field, [ 'fileKey' => $access->filePut($file), 'name' => $name, 'contentType' => $contentType, ]); $this->set($fieldCode, $field); return $this; } 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 []; } }