| @@ -0,0 +1,22 @@ | |||||
| <?php | |||||
| namespace App\Kintone; | |||||
| class File | |||||
| { | |||||
| public string $contentType; | |||||
| public string $fileKey; | |||||
| public string $name; | |||||
| public int $size; | |||||
| public function __construct($param = null) | |||||
| { | |||||
| if ($param !== null) { | |||||
| $this->contentType = data_get($param, "contentType", ""); | |||||
| $this->fileKey = data_get($param, "fileKey", ""); | |||||
| $this->name = data_get($param, "name", ""); | |||||
| $this->size = (int)data_get($param, "size", 0); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -10,6 +10,9 @@ use Illuminate\Support\Collection; | |||||
| use Illuminate\Support\Facades\Http; | use Illuminate\Support\Facades\Http; | ||||
| use Log; | use Log; | ||||
| /** | |||||
| * @template TValue of KintoneModel | |||||
| */ | |||||
| class KintoneAccess | class KintoneAccess | ||||
| { | { | ||||
| @@ -18,12 +21,21 @@ class KintoneAccess | |||||
| private string $apiToken; | private string $apiToken; | ||||
| private int $appId; | private int $appId; | ||||
| private const DEFAULT_FIELDS = [ | |||||
| KintoneModel::FIELD_RECORD_NUMBER, | |||||
| KintoneModel::FIELD_UPDATED_TIME, | |||||
| KintoneModel::FIELD_RECORD_NUMBER, | |||||
| ]; | |||||
| private array $fields = []; | |||||
| private string|null $cursor = null; | private string|null $cursor = null; | ||||
| private int $cursorDataCount = 0; | private int $cursorDataCount = 0; | ||||
| private bool $hasNext = false; | private bool $hasNext = false; | ||||
| private const URL_RECORD = "/k/v1/record.json"; | private const URL_RECORD = "/k/v1/record.json"; | ||||
| private const URL_CURSOL = "/k/v1/records/cursor.json"; | private const URL_CURSOL = "/k/v1/records/cursor.json"; | ||||
| private const URL_FILE = "/k/v1/file.json"; | |||||
| public function __construct(string $appName) | public function __construct(string $appName) | ||||
| @@ -53,12 +65,26 @@ class KintoneAccess | |||||
| $this->appId = $appId; | $this->appId = $appId; | ||||
| } | } | ||||
| public function setFields(array $fields): static | |||||
| { | |||||
| $this->fields = [ | |||||
| ...$fields, | |||||
| ...self::DEFAULT_FIELDS, | |||||
| ]; | |||||
| return $this; | |||||
| } | |||||
| public function __destruct() | public function __destruct() | ||||
| { | { | ||||
| $this->deleteCursor(); | $this->deleteCursor(); | ||||
| } | } | ||||
| /** | |||||
| * @param integer $id | |||||
| * @param TValue $result | |||||
| * @return Response | |||||
| */ | |||||
| public function find(int $id, KintoneModel &$result): Response | public function find(int $id, KintoneModel &$result): Response | ||||
| { | { | ||||
| @@ -67,11 +93,13 @@ class KintoneAccess | |||||
| ])->get($this->getRecordUrl(), [ | ])->get($this->getRecordUrl(), [ | ||||
| "app" => $this->appId, | "app" => $this->appId, | ||||
| "id" => $id, | "id" => $id, | ||||
| 'fields' => $this->fields, | |||||
| ]); | ]); | ||||
| if ($response->failed()) { | if ($response->failed()) { | ||||
| $e = $response->toException(); | $e = $response->toException(); | ||||
| if ($e instanceof Exception) { | if ($e instanceof Exception) { | ||||
| Log::error($e->getMessage()); | |||||
| throw $e; | throw $e; | ||||
| } | } | ||||
| } | } | ||||
| @@ -80,6 +108,10 @@ class KintoneAccess | |||||
| return $response; | return $response; | ||||
| } | } | ||||
| /** | |||||
| * @param TValue $model | |||||
| * @return void | |||||
| */ | |||||
| public function update(KintoneModel &$model) | public function update(KintoneModel &$model) | ||||
| { | { | ||||
| $response = Http::withHeaders([ | $response = Http::withHeaders([ | ||||
| @@ -94,6 +126,7 @@ class KintoneAccess | |||||
| if ($response->failed()) { | if ($response->failed()) { | ||||
| $e = $response->toException(); | $e = $response->toException(); | ||||
| if ($e instanceof Exception) { | if ($e instanceof Exception) { | ||||
| Log::error($e->getMessage()); | |||||
| throw $e; | throw $e; | ||||
| } | } | ||||
| } | } | ||||
| @@ -102,6 +135,10 @@ class KintoneAccess | |||||
| return $response; | return $response; | ||||
| } | } | ||||
| /** | |||||
| * @param TValue $model | |||||
| * @return void | |||||
| */ | |||||
| public function create(KintoneModel &$model) | public function create(KintoneModel &$model) | ||||
| { | { | ||||
| $response = Http::withHeaders([ | $response = Http::withHeaders([ | ||||
| @@ -124,17 +161,15 @@ class KintoneAccess | |||||
| return $response; | return $response; | ||||
| } | } | ||||
| public function createCursor(KintoneRecordQuery|null $query = null, array|null $fields = null) | |||||
| public function createCursor(KintoneRecordQuery|null $query = null) | |||||
| { | { | ||||
| $data = [ | $data = [ | ||||
| "app" => $this->appId, | "app" => $this->appId, | ||||
| 'fields' => $this->fields, | |||||
| ]; | ]; | ||||
| if ($query !== null) { | if ($query !== null) { | ||||
| $data["query"] = $query->toQuery(); | $data["query"] = $query->toQuery(); | ||||
| } | } | ||||
| if ($fields !== null) { | |||||
| $data["fields"] = $fields; | |||||
| } | |||||
| $response = Http::withHeaders([ | $response = Http::withHeaders([ | ||||
| "X-Cybozu-API-Token" => $this->apiToken, | "X-Cybozu-API-Token" => $this->apiToken, | ||||
| @@ -143,16 +178,23 @@ class KintoneAccess | |||||
| if ($response->failed()) { | if ($response->failed()) { | ||||
| $e = $response->toException(); | $e = $response->toException(); | ||||
| if ($e instanceof Exception) { | if ($e instanceof Exception) { | ||||
| Log::error($e->getMessage()); | |||||
| Log::error($e->getMessage(), ['data' => $data]); | |||||
| throw $e; | throw $e; | ||||
| } | } | ||||
| } | } | ||||
| $this->cursor = $response["id"]; | |||||
| $this->cursorDataCount = $response["totalCount"]; | |||||
| $cursor = $response["id"]; | |||||
| $totalCount = $response["totalCount"]; | |||||
| if (0 < $this->cursorDataCount) { | |||||
| if (0 < $totalCount) { | |||||
| $this->hasNext = true; | $this->hasNext = true; | ||||
| $this->cursor = $cursor; | |||||
| $this->cursorDataCount = $totalCount; | |||||
| } else { | |||||
| $this->cursor = null; | |||||
| $this->hasNext = false; | |||||
| $this->cursorDataCount = 0; | |||||
| } | } | ||||
| return $response; | return $response; | ||||
| @@ -160,7 +202,7 @@ class KintoneAccess | |||||
| /** | /** | ||||
| * @return Collection<KintoneModel> | |||||
| * @return Collection<int,TValue> | |||||
| */ | */ | ||||
| public function next() | public function next() | ||||
| { | { | ||||
| @@ -207,7 +249,7 @@ class KintoneAccess | |||||
| } | } | ||||
| /** | /** | ||||
| * @return Collection<KintoneModel> | |||||
| * @return Collection<int,TValue> | |||||
| */ | */ | ||||
| public function all(KintoneRecordQuery|null $query = null, array|null $fields = null) | public function all(KintoneRecordQuery|null $query = null, array|null $fields = null) | ||||
| { | { | ||||
| @@ -257,6 +299,42 @@ class KintoneAccess | |||||
| $this->cursorDataCount = 0; | $this->cursorDataCount = 0; | ||||
| } | } | ||||
| public function fileGet(string $fileKey) | |||||
| { | |||||
| $response = Http::withHeaders([ | |||||
| "X-Cybozu-API-Token" => $this->apiToken, | |||||
| "Content-Type" => "application/json", | |||||
| ])->get($this->getCursorUrl(), [ | |||||
| "fileKey" => $fileKey, | |||||
| ]); | |||||
| if ($response->failed()) { | |||||
| $e = $response->toException(); | |||||
| if ($e instanceof Exception) { | |||||
| Log::error($e->getMessage()); | |||||
| throw $e; | |||||
| } | |||||
| } | |||||
| return $response; | |||||
| } | |||||
| public function filePut($file): string | |||||
| { | |||||
| $response = Http::withHeaders([ | |||||
| "X-Cybozu-API-Token" => $this->apiToken, | |||||
| "Content-Type" => "multipart/form-data", | |||||
| ])->post($this->getCursorUrl(), $file); | |||||
| if ($response->failed()) { | |||||
| $e = $response->toException(); | |||||
| if ($e instanceof Exception) { | |||||
| Log::error($e->getMessage()); | |||||
| throw $e; | |||||
| } | |||||
| } | |||||
| return $response['']; | |||||
| } | |||||
| private function getRecordUrl() | private function getRecordUrl() | ||||
| { | { | ||||
| return $this->getUrl(self::URL_RECORD); | return $this->getUrl(self::URL_RECORD); | ||||
| @@ -267,6 +345,11 @@ class KintoneAccess | |||||
| return $this->getUrl(self::URL_CURSOL); | return $this->getUrl(self::URL_CURSOL); | ||||
| } | } | ||||
| private function getFileUrl() | |||||
| { | |||||
| return $this->getUrl(self::URL_FILE); | |||||
| } | |||||
| private function getUrl(string $path) | private function getUrl(string $path) | ||||
| { | { | ||||
| return $this->host . $path; | return $this->host . $path; | ||||
| @@ -1,42 +0,0 @@ | |||||
| <?php | |||||
| namespace App\Kintone\Models; | |||||
| class CarRoom extends KintoneModel | |||||
| { | |||||
| const FIELD_PARK_NAME = "駐車場名"; | |||||
| const FIELD_ROOM_NO = "車室番号"; | |||||
| const FIELD_ROOM_NAME = "車室名"; | |||||
| const FIELD_FULL = "満空"; | |||||
| const FIELD_CAN_USE_VEHICLE_TYPE = "利用可能車種"; | |||||
| const FIELD_CAN_DEVIDE_PER_DAY = "日割り"; | |||||
| const FIELD_TABLE_FEE = "料金表"; | |||||
| const FIELD_TABLE_FEE_VEHICLE_TYPE = "車種"; | |||||
| const FIELD_TABLE_FEE_AMOUNT_PER_MONTH = "金額_1ヶ月"; | |||||
| protected array $fields = [ | |||||
| self::FIELD_PARK_NAME => FieldType::STRING, | |||||
| self::FIELD_ROOM_NO => FieldType::STRING, | |||||
| self::FIELD_ROOM_NAME => FieldType::STRING, | |||||
| self::FIELD_FULL => FieldType::STRING, | |||||
| self::FIELD_CAN_USE_VEHICLE_TYPE => FieldType::ARRAY, | |||||
| self::FIELD_TABLE_FEE => [ | |||||
| self::FIELD_TABLE_FEE_VEHICLE_TYPE => FieldType::STRING, | |||||
| self::FIELD_TABLE_FEE_AMOUNT_PER_MONTH => FieldType::STRING, | |||||
| ], | |||||
| ]; | |||||
| protected function setModelData(array $data): bool | |||||
| { | |||||
| return true; | |||||
| } | |||||
| protected function toArrayModel(): array | |||||
| { | |||||
| return []; | |||||
| } | |||||
| } | |||||
| @@ -1,15 +0,0 @@ | |||||
| <?php | |||||
| namespace App\Kintone\Models; | |||||
| class Customer extends KintoneModel | |||||
| { | |||||
| const FIELD_CUSTOMER_NAME = "顧客名"; | |||||
| const FIELD_CUSTOMER_MEMO = "備考"; | |||||
| protected array $fields = [ | |||||
| self::FIELD_CUSTOMER_NAME => FieldType::STRING, | |||||
| self::FIELD_CUSTOMER_MEMO => FieldType::STRING, | |||||
| ]; | |||||
| } | |||||
| @@ -2,11 +2,27 @@ | |||||
| namespace App\Kintone\Models; | namespace App\Kintone\Models; | ||||
| enum FieldType | |||||
| enum FieldType: string | |||||
| { | { | ||||
| case STRING; | |||||
| case ARRAY; | |||||
| case DATETIME; | |||||
| case USERS; | |||||
| case TABLE; | |||||
| case SINGLE_LINE_TEXT = "SINGLE_LINE_TEXT"; | |||||
| case MULTI_LINE_TEXT = "MULTI_LINE_TEXT"; | |||||
| case RICH_TEXT = "RICH_TEXT"; | |||||
| case NUMBER = "NUMBER"; | |||||
| case CALC = "CALC"; | |||||
| case CHECK_BOX = "CHECK_BOX"; | |||||
| case RADIO_BUTTON = "RADIO_BUTTON"; | |||||
| case MULTI_SELECT = "MULTI_SELECT"; | |||||
| case DROP_DOWN = "DROP_DOWN"; | |||||
| case USER_SELECT = "USER_SELECT"; | |||||
| case ORGANIZATION_SELECT = "ORGANIZATION_SELECT"; | |||||
| case GROUP_SELECT = "GROUP_SELECT"; | |||||
| case DATE = "DATE"; | |||||
| case TIME = "TIME"; | |||||
| case DATETIME = "DATETIME"; | |||||
| case LINK = "LINK"; | |||||
| case FILE = "FILE"; | |||||
| case SUBTABLE = "SUBTABLE"; | |||||
| case CATEGORY = "CATEGORY"; | |||||
| case STATUS = "STATUS"; | |||||
| case STATUS_ASSIGNEE = "STATUS_ASSIGNEE"; | |||||
| } | } | ||||
| @@ -2,19 +2,71 @@ | |||||
| namespace App\Kintone\Models; | namespace App\Kintone\Models; | ||||
| use App\Exceptions\AppCommonException; | |||||
| use App\Exceptions\ConfigException; | |||||
| use App\Kintone\File; | |||||
| use App\Kintone\KintoneAccess; | |||||
| use App\Kintone\KintoneRecordQuery; | use App\Kintone\KintoneRecordQuery; | ||||
| use App\Util\DateUtil; | use App\Util\DateUtil; | ||||
| use Illuminate\Support\Arr; | use Illuminate\Support\Arr; | ||||
| use Illuminate\Support\Carbon; | use Illuminate\Support\Carbon; | ||||
| use LogicException; | |||||
| use stdClass; | use stdClass; | ||||
| abstract class KintoneModel | abstract class KintoneModel | ||||
| { | { | ||||
| const CONFIG_KEY = ""; | |||||
| const FIELD_RECORD_NUMBER = "レコード番号"; | const FIELD_RECORD_NUMBER = "レコード番号"; | ||||
| const FIELD_CREATED_TIME = "作成日時"; | const FIELD_CREATED_TIME = "作成日時"; | ||||
| const FIELD_UPDATED_TIME = "更新日時"; | const FIELD_UPDATED_TIME = "更新日時"; | ||||
| static public function configKey(): string | |||||
| { | |||||
| if (!static::CONFIG_KEY) { | |||||
| throw new LogicException(sprintf("Kintone 設定キー 未設定:%s", static::class)); | |||||
| } | |||||
| return static::CONFIG_KEY; | |||||
| } | |||||
| static public function setConfig(): array | |||||
| { | |||||
| $configStr = env(static::configKey()); | |||||
| if (!$configStr) { | |||||
| throw new ConfigException(static::configKey(), $configStr); | |||||
| } | |||||
| $params = explode(",", $configStr); | |||||
| if (count($params) !== 2) { | |||||
| throw new ConfigException(static::configKey(), $configStr); | |||||
| } | |||||
| [$apiToken, $appId] = $params; | |||||
| return [static::class => [ | |||||
| 'apiToken' => $apiToken, | |||||
| 'appId' => (int)$appId, | |||||
| ]]; | |||||
| } | |||||
| /** | |||||
| * @return KintoneAccess<static> | |||||
| */ | |||||
| static public function getAccess(): KintoneAccess | |||||
| { | |||||
| $access = new KintoneAccess(static::class); | |||||
| $access->setFields(array_keys(static::FIELDS)); | |||||
| return $access; | |||||
| } | |||||
| /** | |||||
| * @return KintoneRecordQuery<static> | |||||
| */ | |||||
| static public function getQuery(): KintoneRecordQuery | |||||
| { | |||||
| return new KintoneRecordQuery(static::class); | |||||
| } | |||||
| protected ?int $recordId = null; | protected ?int $recordId = null; | ||||
| protected ?int $revision = null; | protected ?int $revision = null; | ||||
| @@ -26,7 +78,9 @@ abstract class KintoneModel | |||||
| protected ?stdClass $dataOrigin = null; | protected ?stdClass $dataOrigin = null; | ||||
| protected stdClass $data; | protected stdClass $data; | ||||
| protected array $fields = []; | |||||
| protected const FIELDS = []; | |||||
| protected const FIELD_NAMES = []; | |||||
| private array $changed = []; | private array $changed = []; | ||||
| @@ -37,7 +91,7 @@ abstract class KintoneModel | |||||
| public function set(string $fieldCode, $value) | public function set(string $fieldCode, $value) | ||||
| { | { | ||||
| $field = Arr::get($this->fields, $fieldCode); | |||||
| $field = Arr::get(static::FIELDS, $fieldCode); | |||||
| if ($field instanceof FieldType) { | if ($field instanceof FieldType) { | ||||
| $this->setData($fieldCode, $field, $value); | $this->setData($fieldCode, $field, $value); | ||||
| } else if (is_array($field)) { | } else if (is_array($field)) { | ||||
| @@ -60,7 +114,7 @@ abstract class KintoneModel | |||||
| { | { | ||||
| foreach ($row as $columnFieldCode => $column) { | foreach ($row as $columnFieldCode => $column) { | ||||
| $value = $column["value"]; | $value = $column["value"]; | ||||
| $type = $this->fields[$fieldCode][$columnFieldCode]; | |||||
| $type = static::FIELDS[$fieldCode][$columnFieldCode]; | |||||
| $insertKey = sprintf("%s.%d.%s", $fieldCode, $index, $columnFieldCode); | $insertKey = sprintf("%s.%d.%s", $fieldCode, $index, $columnFieldCode); | ||||
| $this->setData($insertKey, $type, $value); | $this->setData($insertKey, $type, $value); | ||||
| } | } | ||||
| @@ -68,18 +122,22 @@ abstract class KintoneModel | |||||
| private function setData(string $path, FieldType $type, $value) | private function setData(string $path, FieldType $type, $value) | ||||
| { | { | ||||
| 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; | |||||
| } | |||||
| 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 | public function setDataFromRecordResponse(array $data): bool | ||||
| @@ -105,7 +163,32 @@ abstract class KintoneModel | |||||
| continue; | continue; | ||||
| } | } | ||||
| $this->set($fieldCode, $value); | |||||
| $type = FieldType::tryFrom($type); | |||||
| if ($type === null) continue; | |||||
| if ($type === FieldType::DATETIME) { | |||||
| 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); | |||||
| } | } | ||||
| $ret = $this->setDataCustom($data); | $ret = $this->setDataCustom($data); | ||||
| @@ -132,7 +215,7 @@ abstract class KintoneModel | |||||
| public function getApiLayout(): array | public function getApiLayout(): array | ||||
| { | { | ||||
| $ret = []; | $ret = []; | ||||
| foreach ($this->fields as $fieldCode => $type) { | |||||
| foreach (static::FIELDS as $fieldCode => $type) { | |||||
| // 変更があった項目のみレイアウトへ出力する | // 変更があった項目のみレイアウトへ出力する | ||||
| if (!Arr::has($this->changed, $fieldCode)) { | if (!Arr::has($this->changed, $fieldCode)) { | ||||
| @@ -140,18 +223,11 @@ abstract class KintoneModel | |||||
| } | } | ||||
| $path = sprintf("%s.value", $fieldCode); | $path = sprintf("%s.value", $fieldCode); | ||||
| if ($type === FieldType::STRING) { | |||||
| data_set($ret, $path, data_get($this->data, $fieldCode)); | |||||
| continue; | |||||
| } | |||||
| if ($type === FieldType::ARRAY) { | |||||
| data_set($ret, $path, data_get($this->data, $fieldCode)); | |||||
| continue; | |||||
| } | |||||
| if ($type === FieldType::DATETIME) { | if ($type === FieldType::DATETIME) { | ||||
| data_set($ret, $path, $this->getDate($fieldCode)->toIso8601ZuluString()); | data_set($ret, $path, $this->getDate($fieldCode)->toIso8601ZuluString()); | ||||
| continue; | continue; | ||||
| } | } | ||||
| data_set($ret, $path, data_get($this->data, $fieldCode)); | |||||
| } | } | ||||
| return array_merge($ret, $this->getApiLayoutCustom()); | return array_merge($ret, $this->getApiLayoutCustom()); | ||||
| @@ -159,6 +235,7 @@ abstract class KintoneModel | |||||
| public function get(string $key) | public function get(string $key) | ||||
| { | { | ||||
| $a = json_decode(json_encode($this->data), true); | |||||
| return data_get($this->data, $key); | return data_get($this->data, $key); | ||||
| } | } | ||||
| @@ -210,11 +287,49 @@ abstract class KintoneModel | |||||
| return $this->createdAt; | return $this->createdAt; | ||||
| } | } | ||||
| static public function query() | |||||
| public function toArray(): array | |||||
| { | { | ||||
| return new KintoneRecordQuery(static::class); | |||||
| if ($this->recordId === null) { | |||||
| throw new LogicException("保存前モデルのシリアライズ検知"); | |||||
| } | |||||
| $ret = [ | |||||
| 'record_no' => $this->recordId, | |||||
| 'revision' => $this->revision, | |||||
| ]; | |||||
| /** | |||||
| * @var string $fieldCode | |||||
| */ | |||||
| foreach ($this->data as $fieldCode => $value) { | |||||
| $type = data_get(static::FIELDS, $fieldCode); | |||||
| $columnName = data_get(static::FIELD_NAMES, $fieldCode, $fieldCode); | |||||
| if ($type === null) { | |||||
| $ret[$columnName] = $value; | |||||
| continue; | |||||
| } | |||||
| if ($type === FieldType::DATETIME) { | |||||
| if ($value instanceof Carbon) { | |||||
| $ret[$columnName] = $value->format('Y/m/d H:i:s'); | |||||
| } else { | |||||
| $ret[$columnName] = $value; | |||||
| } | |||||
| continue; | |||||
| } | |||||
| $ret[$columnName] = $value; | |||||
| } | |||||
| return $ret; | |||||
| } | } | ||||
| /** | /** | ||||
| * オーバーライドを期待 | * オーバーライドを期待 | ||||
| * | * | ||||
| @@ -4,12 +4,22 @@ namespace App\Kintone\Models; | |||||
| class SeasonTicketContract extends KintoneModel | class SeasonTicketContract extends KintoneModel | ||||
| { | { | ||||
| const CONFIG_KEY = "KINTONE_APP_SEASON_TICKET_CONTRACT"; | |||||
| const FIELD_CUSTOMER_NAME = "顧客名"; | const FIELD_CUSTOMER_NAME = "顧客名"; | ||||
| const FIELD_CUSTOMER_MEMO = "備考"; | |||||
| protected array $fields = [ | |||||
| self::FIELD_CUSTOMER_NAME => FieldType::STRING, | |||||
| self::FIELD_CUSTOMER_MEMO => FieldType::STRING, | |||||
| protected const FIELDS = [ | |||||
| ...parent::FIELDS, | |||||
| self::FIELD_CUSTOMER_NAME => FieldType::SINGLE_LINE_TEXT, | |||||
| ]; | ]; | ||||
| protected const FIELD_NAMES = [ | |||||
| ...parent::FIELD_NAMES, | |||||
| self::FIELD_CUSTOMER_NAME => 'customer_name', | |||||
| ]; | |||||
| protected function setDataCustom(array $data): bool | |||||
| { | |||||
| return true; | |||||
| } | |||||
| } | } | ||||
| @@ -0,0 +1,19 @@ | |||||
| <?php | |||||
| namespace App\Models; | |||||
| abstract class ColumnName | |||||
| { | |||||
| // 共通 | |||||
| const ID = 'id'; | |||||
| const UPDATED_AT = 'updated_at'; | |||||
| const UPDATED_BY = 'updated_by'; | |||||
| const CREATED_AT = 'created_at'; | |||||
| const CREATED_BY = 'created_by'; | |||||
| const DELETED_AT = 'deleted_at'; | |||||
| const HISTORY_ID = 'history_id'; | |||||
| // 業務 | |||||
| const USER_ID = 'user_id'; | |||||
| const EMAIL_ID = "email_id"; | |||||
| } | |||||
| @@ -18,7 +18,7 @@ use Laravel\Sanctum\HasApiTokens; | |||||
| class User extends Authenticatable implements IModelFeature | class User extends Authenticatable implements IModelFeature | ||||
| { | { | ||||
| use HasApiTokens, HasFactory, Notifiable, HasUuids, SoftDeletes, ContractFeature; | |||||
| use HasApiTokens, HasFactory, Notifiable, HasUuids, SoftDeletes; | |||||
| const COL_NAME_ID = 'id'; | const COL_NAME_ID = 'id'; | ||||
| const COL_NAME_ROLE = 'role'; | const COL_NAME_ROLE = 'role'; | ||||
| @@ -26,8 +26,6 @@ class User extends Authenticatable implements IModelFeature | |||||
| const COL_NAME_NAME = 'name'; | const COL_NAME_NAME = 'name'; | ||||
| const COL_NAME_PASSWORD = 'password'; | const COL_NAME_PASSWORD = 'password'; | ||||
| const COL_NAME_CONTRACT_ID = ColumnName::CONTRACT_ID; | |||||
| const COL_NAME_CREATED_BY = ColumnName::CREATED_BY; | const COL_NAME_CREATED_BY = ColumnName::CREATED_BY; | ||||
| const COL_NAME_UPDATED_BY = ColumnName::UPDATED_BY; | const COL_NAME_UPDATED_BY = ColumnName::UPDATED_BY; | ||||
| const COL_NAME_CREATED_AT = ColumnName::CREATED_AT; | const COL_NAME_CREATED_AT = ColumnName::CREATED_AT; | ||||
| @@ -123,43 +123,12 @@ class MigrationHelper | |||||
| return sprintf("%s_uq_%02d", $this->table->getTable(), $number); | return sprintf("%s_uq_%02d", $this->table->getTable(), $number); | ||||
| } | } | ||||
| public function contractId(bool $nullable = false) | |||||
| { | |||||
| $this->table->uuid(ColumnName::CONTRACT_ID)->comment("契約ID")->nullable($nullable); | |||||
| // $this->table->foreign(ColumnName::CONTRACT_ID)->references(ColumnName::ID)->on(Contract::getTableName()); | |||||
| return $this; | |||||
| } | |||||
| public function receiptIssuingOrderId(bool $nullable = false) | |||||
| { | |||||
| $this->table->uuid(ColumnName::RECEIPT_ISSUING_ORDER_ID)->comment("領収証発行依頼ID")->nullable($nullable); | |||||
| // $this->table->foreign(ColumnName::RECEIPT_ISSUING_ORDER_ID)->references(ColumnName::ID)->on(ReceiptIssuingOrder::getTableName()); | |||||
| return $this; | |||||
| } | |||||
| public function smsSendOrderId(bool $nullable = false) | |||||
| { | |||||
| $this->table->uuid(ColumnName::SMS_SEND_ORDER_ID)->comment("SMS送信依頼ID")->nullable($nullable); | |||||
| // $this->table->foreign(ColumnName::SMS_SEND_ORDER_ID)->references(ColumnName::ID)->on(SMSSendOrder::getTableName()); | |||||
| return $this; | |||||
| } | |||||
| public function userId(bool $nullable = false, ?string $columnName = null, ?string $comment = null) | public function userId(bool $nullable = false, ?string $columnName = null, ?string $comment = null) | ||||
| { | { | ||||
| $columnName = $columnName ?? ColumnName::USER_ID; | $columnName = $columnName ?? ColumnName::USER_ID; | ||||
| $comment = $comment ?? "ユーザーID"; | $comment = $comment ?? "ユーザーID"; | ||||
| $this->table->uuid($columnName)->comment($comment)->nullable($nullable); | $this->table->uuid($columnName)->comment($comment)->nullable($nullable); | ||||
| // $this->table->foreign($columnName)->references(ColumnName::ID)->on(User::getTableName()); | |||||
| return $this; | |||||
| } | |||||
| public function smsProviderId(bool $nullable = false) | |||||
| { | |||||
| $this->table->uuid(ColumnName::SMS_PROVIDER_ID)->comment("SMSプロバイダーID")->nullable($nullable); | |||||
| // $this->table->foreign(ColumnName::SMS_PROVIDER_ID)->references(ColumnName::ID)->on(SMSProvider::getTableName()); | |||||
| return $this; | return $this; | ||||
| } | } | ||||
| @@ -1,5 +1,7 @@ | |||||
| <?php | <?php | ||||
| use App\Kintone\KintoneConfiguration; | |||||
| return [ | return [ | ||||
| @@ -24,18 +26,7 @@ return [ | |||||
| */ | */ | ||||
| 'applications' => [ | 'applications' => [ | ||||
| App\Kintone\Models\Customer::class => [ | |||||
| 'apiToken' => (string)explode(",", env("KINTONE_APP_CUSTOMER", ","))[0], | |||||
| 'appId' => (int)explode(",", env("KINTONE_APP_CUSTOMER", ","))[1], | |||||
| ], | |||||
| App\Kintone\Models\CarRoom::class => [ | |||||
| 'apiToken' => (string)explode(",", env("KINTONE_APP_CARROOM", ","))[0], | |||||
| 'appId' => (int)explode(",", env("KINTONE_APP_CARROOM", ","))[1], | |||||
| ], | |||||
| App\Kintone\Models\Small::class => [ | |||||
| 'apiToken' => (string)explode(",", env("KINTONE_APP_SMALL", ","))[0], | |||||
| 'appId' => (int)explode(",", env("KINTONE_APP_SMALL", ","))[1], | |||||
| ], | |||||
| ...App\Kintone\Models\SeasonTicketContract::setConfig(), | |||||
| ], | ], | ||||
| ]; | ]; | ||||
| @@ -0,0 +1,40 @@ | |||||
| <?php | |||||
| use App\Util\MigrationHelper; | |||||
| use Illuminate\Database\Migrations\Migration; | |||||
| use Illuminate\Database\Schema\Blueprint; | |||||
| use Illuminate\Support\Facades\Schema; | |||||
| return new class extends Migration | |||||
| { | |||||
| /** | |||||
| * Run the migrations. | |||||
| */ | |||||
| public function up(): void | |||||
| { | |||||
| Schema::create('users', function (Blueprint $table) { | |||||
| $helper = new MigrationHelper($table); | |||||
| $helper->baseColumn(); | |||||
| $table->string('email')->unique()->comment('Email'); | |||||
| $table->string('password')->comment('ログインパスワード'); | |||||
| $table->string('kintone_id')->comment('KintoneID'); | |||||
| }); | |||||
| Schema::create('user_histories', function (Blueprint $table) { | |||||
| $helper = new MigrationHelper($table, true); | |||||
| $helper->baseColumn(); | |||||
| $table->string('email')->comment('Email'); | |||||
| $table->string('password')->comment('ログインパスワード'); | |||||
| $table->string('kintone_id')->comment('KintoneID'); | |||||
| }); | |||||
| } | |||||
| /** | |||||
| * Reverse the migrations. | |||||
| */ | |||||
| public function down(): void | |||||
| { | |||||
| Schema::dropIfExists('users'); | |||||
| Schema::dropIfExists('user_histories'); | |||||
| } | |||||
| }; | |||||
| @@ -0,0 +1,32 @@ | |||||
| <?php | |||||
| use Illuminate\Database\Migrations\Migration; | |||||
| use Illuminate\Database\Schema\Blueprint; | |||||
| use Illuminate\Support\Facades\Schema; | |||||
| return new class extends Migration | |||||
| { | |||||
| /** | |||||
| * Run the migrations. | |||||
| */ | |||||
| public function up(): void | |||||
| { | |||||
| Schema::create('failed_jobs', function (Blueprint $table) { | |||||
| $table->id(); | |||||
| $table->string('uuid')->unique(); | |||||
| $table->text('connection'); | |||||
| $table->text('queue'); | |||||
| $table->longText('payload'); | |||||
| $table->longText('exception'); | |||||
| $table->timestamp('failed_at')->useCurrent(); | |||||
| }); | |||||
| } | |||||
| /** | |||||
| * Reverse the migrations. | |||||
| */ | |||||
| public function down(): void | |||||
| { | |||||
| Schema::dropIfExists('failed_jobs'); | |||||
| } | |||||
| }; | |||||
| @@ -0,0 +1,28 @@ | |||||
| #!/bin/bash | |||||
| cd $(dirname $0) | |||||
| SAIL="" | |||||
| COMMANDS=() | |||||
| if [ $# -eq 0 ]; then | |||||
| SAIL="" | |||||
| COMMANDS+=("composer install --optimize-autoloader --no-dev") | |||||
| elif [ $# -eq 1 ] && [ $1 == "sail" ]; then | |||||
| SAIL="./sail" | |||||
| NO_DEV="--no-dev" | |||||
| else | |||||
| echo "引数不正" | |||||
| exit 1 | |||||
| fi | |||||
| COMMANDS+=("${SAIL} php artisan config:cache") | |||||
| COMMANDS+=("${SAIL} php artisan route:cache") | |||||
| COMMANDS+=("${SAIL} php artisan view:cache") | |||||
| COMMANDS+=("${SAIL} php artisan event:cache") | |||||
| COMMANDS+=("${SAIL} php artisan queue:restart") | |||||
| for COMMAND in "${COMMANDS[@]}"; do | |||||
| echo ${COMMAND} | |||||
| ${COMMAND} | |||||
| done | |||||
| @@ -0,0 +1,23 @@ | |||||
| #!/bin/bash | |||||
| cd $(dirname $0) | |||||
| SAIL="" | |||||
| if [ $# -eq 0 ]; then | |||||
| SAIL="" | |||||
| elif [ $# -eq 1 ] && [ $1 == "sail" ]; then | |||||
| SAIL="./sail" | |||||
| else | |||||
| echo "引数不正" | |||||
| exit 1 | |||||
| fi | |||||
| COMMANDS=() | |||||
| # COMMANDS+=("${SAIL} php artisan ide-helper:generate") | |||||
| COMMANDS+=("${SAIL} php artisan ide-helper:models -N") | |||||
| for COMMAND in "${COMMANDS[@]}"; do | |||||
| echo ${COMMAND} | |||||
| ${COMMAND} | |||||
| done | |||||
| @@ -5,116 +5,118 @@ namespace Tests\Feature; | |||||
| use App\Kintone\KintoneAccess; | use App\Kintone\KintoneAccess; | ||||
| use App\Kintone\KintoneRecordQuery; | use App\Kintone\KintoneRecordQuery; | ||||
| use App\Kintone\KintoneRecordQueryOperator; | use App\Kintone\KintoneRecordQueryOperator; | ||||
| use App\Kintone\Models\CarRoom; | |||||
| use App\Kintone\Models\Customer; | |||||
| use App\Kintone\Models\Small; | |||||
| use App\Kintone\Models\SeasonTicketContract; | |||||
| use App\Middlewares\Now; | use App\Middlewares\Now; | ||||
| use Illuminate\Support\Collection; | use Illuminate\Support\Collection; | ||||
| use Tests\TestCase; | use Tests\TestCase; | ||||
| class KintoneAccessTest extends TestCase | class KintoneAccessTest extends TestCase | ||||
| { | { | ||||
| /** | |||||
| * A basic feature test example. | |||||
| */ | |||||
| public function test_access(): void | |||||
| public function test_find(): void | |||||
| { | { | ||||
| $model = new Customer(); | |||||
| $model = new SeasonTicketContract(); | |||||
| $access = new KintoneAccess(Customer::class); | |||||
| $access = SeasonTicketContract::getAccess(); | |||||
| $access->find(1, $model); | |||||
| $access->find(505, $model); | |||||
| $this->assertEquals("林田商会", $model->getStr(Customer::FIELD_CUSTOMER_NAME)); | |||||
| $memo = Now::get()->format("YmdHis"); | |||||
| $model->set(Customer::FIELD_CUSTOMER_MEMO, $memo); | |||||
| $beforeRevision = $model->getRevision(); | |||||
| $access->update($model); | |||||
| $afterRevision = $model->getRevision(); | |||||
| $this->assertEquals($beforeRevision + 1, $afterRevision); | |||||
| // 再度データを取得して項目が更新されていることを確認 | |||||
| $model = new Customer(); | |||||
| $access->find(1, $model); | |||||
| $this->assertEquals($memo, $model->getStr(Customer::FIELD_CUSTOMER_MEMO)); | |||||
| $this->assertEquals("塩山兼司", $model->getStr(SeasonTicketContract::FIELD_CUSTOMER_NAME)); | |||||
| } | } | ||||
| public function test_CarRoom(): void | |||||
| public function test_all(): void | |||||
| { | { | ||||
| $model = new SeasonTicketContract(); | |||||
| $model = new CarRoom(); | |||||
| $access = SeasonTicketContract::getAccess(); | |||||
| $access = new KintoneAccess(CarRoom::class); | |||||
| $query = SeasonTicketContract::getQuery(); | |||||
| $query->where(SeasonTicketContract::FIELD_CUSTOMER_NAME, "井出侑加"); | |||||
| $ret = $access->all($query); | |||||
| $access->find(1, $model); | |||||
| $this->assertEquals(1, $ret->count()); | |||||
| $this->assertEquals("岩渕パーク", $model->getStr(CarRoom::FIELD_PARK_NAME)); | |||||
| $this->assertEquals("5000", $model->getTable(CarRoom::FIELD_TABLE_FEE)[0][CarRoom::FIELD_TABLE_FEE_AMOUNT_PER_MONTH]); | |||||
| $this->assertEquals("自転車", $model->get(CarRoom::FIELD_CAN_USE_VEHICLE_TYPE)[0]); | |||||
| $this->assertEquals("普通自動車", $model->get(CarRoom::FIELD_CAN_USE_VEHICLE_TYPE)[1]); | |||||
| } | |||||
| public function test_small() | |||||
| { | |||||
| $model = new Small(); | |||||
| $access = new KintoneAccess(Small::class); | |||||
| /** | |||||
| * @var SeasonTicketContract | |||||
| */ | |||||
| $model = $ret[0]; | |||||
| $model->set(Small::FIELD_NAME, "iwabuchi"); | |||||
| $model->set(Small::FIELD_AGE, "32"); | |||||
| $this->assertEquals("井出侑加", $model->getStr(SeasonTicketContract::FIELD_CUSTOMER_NAME)); | |||||
| $res = $access->create($model); | |||||
| $this->assertTrue($res->ok()); | |||||
| $array = $model->toArray(); | |||||
| $this->assertEquals("井出侑加", $array['customer_name']); | |||||
| } | } | ||||
| /** | |||||
| * @group cursor | |||||
| */ | |||||
| public function test_cursor() | |||||
| { | |||||
| $access = new KintoneAccess(Small::class); | |||||
| $now = Now::get(); | |||||
| $query = Small::query() | |||||
| ->whereIn(Small::FIELD_NAME, ["iwabuchi", "aa"]) | |||||
| ->where(Small::FIELD_AGE, 32) | |||||
| ->whereDateTime(Small::FIELD_CREATED_TIME, $now, KintoneRecordQueryOperator::LT); | |||||
| $access->createCursor($query); | |||||
| $ret = $access->next(); | |||||
| $this->assertTrue($ret instanceof Collection); | |||||
| $this->assertSame(1, $ret->count()); | |||||
| $this->assertSame("iwabuchi", $ret->first()->getStr(Small::FIELD_NAME)); | |||||
| } | |||||
| /** | |||||
| * @group cursor | |||||
| */ | |||||
| public function test_cursor_all() | |||||
| { | |||||
| $access = new KintoneAccess(Small::class); | |||||
| $now = Now::get(); | |||||
| $query = Small::query() | |||||
| ->orderByAsc(Small::FIELD_NAME); | |||||
| $ret = $access->all($query, [ | |||||
| Small::FIELD_NAME, | |||||
| Small::FIELD_AGE, | |||||
| ]); | |||||
| $this->assertTrue($ret instanceof Collection); | |||||
| $this->assertSame(2, $ret->count()); | |||||
| $first = $ret->first(); | |||||
| $this->assertInstanceOf(Small::class, $first); | |||||
| $this->assertSame("iwabuchi", $ret->first()->getStr(Small::FIELD_NAME)); | |||||
| $this->assertSame(32, $ret->first()->getNumber(Small::FIELD_AGE)); | |||||
| } | |||||
| // public function test_CarRoom(): void | |||||
| // { | |||||
| // $model = new CarRoom(); | |||||
| // $access = new KintoneAccess(CarRoom::class); | |||||
| // $access->find(1, $model); | |||||
| // $this->assertEquals("岩渕パーク", $model->getStr(CarRoom::FIELD_PARK_NAME)); | |||||
| // $this->assertEquals("5000", $model->getTable(CarRoom::FIELD_TABLE_FEE)[0][CarRoom::FIELD_TABLE_FEE_AMOUNT_PER_MONTH]); | |||||
| // $this->assertEquals("自転車", $model->get(CarRoom::FIELD_CAN_USE_VEHICLE_TYPE)[0]); | |||||
| // $this->assertEquals("普通自動車", $model->get(CarRoom::FIELD_CAN_USE_VEHICLE_TYPE)[1]); | |||||
| // } | |||||
| // public function test_small() | |||||
| // { | |||||
| // $model = new Small(); | |||||
| // $access = new KintoneAccess(Small::class); | |||||
| // $model->set(Small::FIELD_NAME, "iwabuchi"); | |||||
| // $model->set(Small::FIELD_AGE, "32"); | |||||
| // $res = $access->create($model); | |||||
| // $this->assertTrue($res->ok()); | |||||
| // } | |||||
| // /** | |||||
| // * @group cursor | |||||
| // */ | |||||
| // public function test_cursor() | |||||
| // { | |||||
| // $access = new KintoneAccess(Small::class); | |||||
| // $now = Now::get(); | |||||
| // $query = Small::query() | |||||
| // ->whereIn(Small::FIELD_NAME, ["iwabuchi", "aa"]) | |||||
| // ->where(Small::FIELD_AGE, 32) | |||||
| // ->whereDateTime(Small::FIELD_CREATED_TIME, $now, KintoneRecordQueryOperator::LT); | |||||
| // $access->createCursor($query); | |||||
| // $ret = $access->next(); | |||||
| // $this->assertTrue($ret instanceof Collection); | |||||
| // $this->assertSame(1, $ret->count()); | |||||
| // $this->assertSame("iwabuchi", $ret->first()->getStr(Small::FIELD_NAME)); | |||||
| // } | |||||
| // /** | |||||
| // * @group cursor | |||||
| // */ | |||||
| // public function test_cursor_all() | |||||
| // { | |||||
| // $access = new KintoneAccess(Small::class); | |||||
| // $now = Now::get(); | |||||
| // $query = Small::query() | |||||
| // ->orderByAsc(Small::FIELD_NAME); | |||||
| // $ret = $access->all($query, [ | |||||
| // Small::FIELD_NAME, | |||||
| // Small::FIELD_AGE, | |||||
| // ]); | |||||
| // $this->assertTrue($ret instanceof Collection); | |||||
| // $this->assertSame(2, $ret->count()); | |||||
| // $first = $ret->first(); | |||||
| // $this->assertInstanceOf(Small::class, $first); | |||||
| // $this->assertSame("iwabuchi", $ret->first()->getStr(Small::FIELD_NAME)); | |||||
| // $this->assertSame(32, $ret->first()->getNumber(Small::FIELD_AGE)); | |||||
| // } | |||||
| } | } | ||||