| @@ -17,4 +17,5 @@ yarn-error.log | |||||
| /.fleet | /.fleet | ||||
| /.idea | /.idea | ||||
| /.vscode | /.vscode | ||||
| _* | |||||
| _* | |||||
| sail | |||||
| @@ -0,0 +1,8 @@ | |||||
| <?php | |||||
| namespace App\Events\Model; | |||||
| class CreatingEvent extends ModelChangeEvent | |||||
| { | |||||
| } | |||||
| @@ -0,0 +1,88 @@ | |||||
| <?php | |||||
| namespace App\Http\Controllers\Web\Auth; | |||||
| use App\Http\Controllers\Web\WebController; | |||||
| use App\Kintone\Models\Customer; | |||||
| use App\Models\User; | |||||
| use Exception; | |||||
| use Illuminate\Http\JsonResponse; | |||||
| use Illuminate\Http\Request; | |||||
| use Illuminate\Support\Facades\Auth; | |||||
| class LoginController extends WebController | |||||
| { | |||||
| public function name(): string | |||||
| { | |||||
| return "ログイン"; | |||||
| } | |||||
| public function description(): string | |||||
| { | |||||
| return "ログインを行う"; | |||||
| } | |||||
| public function __construct(protected LoginParam $param) | |||||
| { | |||||
| parent::__construct(); | |||||
| } | |||||
| protected function run(Request $request): JsonResponse | |||||
| { | |||||
| // 取得したユーザ情報を登録しログインを行う | |||||
| $param = $this->param; | |||||
| $access = Customer::getAccess(); | |||||
| $query = Customer::getQuery()->where(Customer::FIELD_EMAIL, $param->email); | |||||
| $customer = $access->all($query); | |||||
| if ($customer->count() !== 1) { | |||||
| return $this->failedResponse(); | |||||
| } | |||||
| /** | |||||
| * @var Customer | |||||
| */ | |||||
| $customer = $customer->first(); | |||||
| $kintoneId = $customer->getRecordId(); | |||||
| $user = User::whereKintoneId($kintoneId) | |||||
| ->first(); | |||||
| if ($user instanceof User) { | |||||
| // すでにユーザー登録されているケース | |||||
| if ($user->email !== $param->email) { | |||||
| $user->email = $param->email; | |||||
| $user->save(); | |||||
| } | |||||
| if (Auth::attempt([ | |||||
| 'email' => $param->email, | |||||
| 'password' => $param->password, | |||||
| ])) { | |||||
| return $this->successResponse($customer->toArray()); | |||||
| } else { | |||||
| return $this->failedResponse(); | |||||
| } | |||||
| } else { | |||||
| // 初回ログインのケース | |||||
| $password = "testtest"; | |||||
| $user = new User(); | |||||
| $user->email = $param->email; | |||||
| $user->kintone_id = $customer->getRecordId(); | |||||
| $user->password = $password; | |||||
| $user->save(); | |||||
| if (Auth::attempt([ | |||||
| 'email' => $param->email, | |||||
| 'password' => $password, | |||||
| ])) { | |||||
| return $this->successResponse($customer->toArray()); | |||||
| } else { | |||||
| return $this->failedResponse(); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,20 @@ | |||||
| <?php | |||||
| namespace App\Http\Controllers\Web\Auth; | |||||
| use App\Http\Controllers\Web\BaseParam; | |||||
| /** | |||||
| * @property string $email | |||||
| * @property string $password | |||||
| */ | |||||
| class LoginParam extends BaseParam | |||||
| { | |||||
| public function rules(): array | |||||
| { | |||||
| return [ | |||||
| 'email' => $this->str(), | |||||
| 'password' => $this->str(), | |||||
| ]; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,35 @@ | |||||
| <?php | |||||
| namespace App\Http\Controllers\Web\Auth; | |||||
| use App\Http\Controllers\Web\WebController; | |||||
| use Illuminate\Http\JsonResponse; | |||||
| use Illuminate\Http\Request; | |||||
| use Illuminate\Support\Facades\Auth; | |||||
| class LogoutController extends WebController | |||||
| { | |||||
| public function name(): string | |||||
| { | |||||
| return "ログアウト"; | |||||
| } | |||||
| public function description(): string | |||||
| { | |||||
| return "ログアウトを行う"; | |||||
| } | |||||
| public function __construct(protected LogoutParam $param) | |||||
| { | |||||
| parent::__construct(); | |||||
| } | |||||
| protected function run(Request $request): JsonResponse | |||||
| { | |||||
| Auth::logout(); | |||||
| return $this->successResponse(); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,13 @@ | |||||
| <?php | |||||
| namespace App\Http\Controllers\Web\Auth; | |||||
| use App\Http\Controllers\Web\BaseParam; | |||||
| class LogoutParam extends BaseParam | |||||
| { | |||||
| public function rules(): array | |||||
| { | |||||
| return []; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,26 @@ | |||||
| <?php | |||||
| namespace App\Http\Controllers\Web\Auth; | |||||
| use App\Exceptions\AppCommonException; | |||||
| use App\Kintone\Models\Customer; | |||||
| use Illuminate\Support\Facades\Auth; | |||||
| trait Me | |||||
| { | |||||
| public function me(): array | |||||
| { | |||||
| if (!Auth::check()) { | |||||
| throw new AppCommonException("Me失敗"); | |||||
| } | |||||
| $user = Auth::user(); | |||||
| $access = Customer::getAccess(); | |||||
| /** | |||||
| * @var Customer | |||||
| */ | |||||
| $customer = $access->find($user->kintone_id); | |||||
| return $customer->toArray(); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,41 @@ | |||||
| <?php | |||||
| namespace App\Http\Controllers\Web\Auth; | |||||
| use App\Exceptions\AppCommonException; | |||||
| use App\Http\Controllers\Web\WebController; | |||||
| use App\Kintone\Models\Customer; | |||||
| use Illuminate\Http\JsonResponse; | |||||
| use Illuminate\Http\Request; | |||||
| use Illuminate\Support\Facades\Auth; | |||||
| class MeController extends WebController | |||||
| { | |||||
| use Me; | |||||
| public function name(): string | |||||
| { | |||||
| return "ログアウト"; | |||||
| } | |||||
| public function description(): string | |||||
| { | |||||
| return "ログアウトを行う"; | |||||
| } | |||||
| public function __construct(protected MeParam $param) | |||||
| { | |||||
| parent::__construct(); | |||||
| // $this->middleware('auth:sanctum'); | |||||
| } | |||||
| protected function run(Request $request): JsonResponse | |||||
| { | |||||
| try { | |||||
| return $this->successResponse($this->me()); | |||||
| } catch (AppCommonException) { | |||||
| return $this->failedResponse(); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,13 @@ | |||||
| <?php | |||||
| namespace App\Http\Controllers\Web\Auth; | |||||
| use App\Http\Controllers\Web\BaseParam; | |||||
| class MeParam extends BaseParam | |||||
| { | |||||
| public function rules(): array | |||||
| { | |||||
| return []; | |||||
| } | |||||
| } | |||||
| @@ -23,7 +23,6 @@ use Illuminate\Support\Facades\Validator; | |||||
| use Illuminate\Support\Str; | use Illuminate\Support\Str; | ||||
| use Illuminate\Validation\ValidationException; | use Illuminate\Validation\ValidationException; | ||||
| use LogicException; | use LogicException; | ||||
| use Slack; | |||||
| use Symfony\Component\HttpFoundation\BinaryFileResponse; | use Symfony\Component\HttpFoundation\BinaryFileResponse; | ||||
| use Symfony\Component\HttpKernel\Exception\HttpException; | use Symfony\Component\HttpKernel\Exception\HttpException; | ||||
| @@ -172,25 +171,20 @@ abstract class WebController extends BaseController | |||||
| $ret = $this->run($request); | $ret = $this->run($request); | ||||
| $this->transaction->commit(); | $this->transaction->commit(); | ||||
| Slack::commit(); | |||||
| return $ret; | return $ret; | ||||
| } catch (GeneralErrorMessageException $e) { | } catch (GeneralErrorMessageException $e) { | ||||
| $this->transaction->rollBack(); | $this->transaction->rollBack(); | ||||
| Slack::commit(); | |||||
| return $this->failedResponse([], $e->getMessage()); | return $this->failedResponse([], $e->getMessage()); | ||||
| } catch (AppCommonException $e) { | } catch (AppCommonException $e) { | ||||
| $this->transaction->rollBack(); | $this->transaction->rollBack(); | ||||
| Slack::commit(); | |||||
| logs()->error(sprintf("Appエラー:%s File:%s Line:%d", $e->getMessage(), $e->getFile(), $e->getLine())); | logs()->error(sprintf("Appエラー:%s File:%s Line:%d", $e->getMessage(), $e->getFile(), $e->getLine())); | ||||
| return $this->failedResponse(); | return $this->failedResponse(); | ||||
| } catch (ExclusiveException $e) { | } catch (ExclusiveException $e) { | ||||
| $this->transaction->rollBack(); | $this->transaction->rollBack(); | ||||
| Slack::commit(); | |||||
| logs()->error(sprintf("排他エラー:%s", $e->getMessage())); | logs()->error(sprintf("排他エラー:%s", $e->getMessage())); | ||||
| return $this->exclusiveErrorResponse(); | return $this->exclusiveErrorResponse(); | ||||
| } catch (LogicException $e) { | } catch (LogicException $e) { | ||||
| $this->transaction->rollBack(); | $this->transaction->rollBack(); | ||||
| Slack::commit(); | |||||
| logs()->error([ | logs()->error([ | ||||
| sprintf("実装エラー:%s", $e->getMessage()), | sprintf("実装エラー:%s", $e->getMessage()), | ||||
| get_class($e), | get_class($e), | ||||
| @@ -204,18 +198,15 @@ abstract class WebController extends BaseController | |||||
| return $this->failedResponse(); | return $this->failedResponse(); | ||||
| } catch (ValidationException $e) { | } catch (ValidationException $e) { | ||||
| $this->transaction->rollBack(); | $this->transaction->rollBack(); | ||||
| Slack::commit(); | |||||
| return $this->validateErrorResponse($e); | return $this->validateErrorResponse($e); | ||||
| } catch (HttpException $e) { | } catch (HttpException $e) { | ||||
| $this->transaction->rollBack(); | $this->transaction->rollBack(); | ||||
| Slack::commit(); | |||||
| if ($e->getStatusCode() === 401) { | if ($e->getStatusCode() === 401) { | ||||
| return $this->unAuthorizedResponse(); | return $this->unAuthorizedResponse(); | ||||
| } | } | ||||
| throw e; | throw e; | ||||
| } catch (Exception $e) { | } catch (Exception $e) { | ||||
| $this->transaction->rollBack(); | $this->transaction->rollBack(); | ||||
| Slack::commit(); | |||||
| logs()->error([ | logs()->error([ | ||||
| sprintf("例外エラー:%s", $e->getMessage()), | sprintf("例外エラー:%s", $e->getMessage()), | ||||
| get_class($e), | get_class($e), | ||||
| @@ -319,7 +310,7 @@ abstract class WebController extends BaseController | |||||
| $header = []; | $header = []; | ||||
| $user = Auth::user(); | $user = Auth::user(); | ||||
| if ($user) { | if ($user) { | ||||
| $header["App-User-Auth"] = sprintf("%d,%d", $user->id, $user->role->value); | |||||
| $header["App-User-Auth"] = sprintf("%s", $user->id); | |||||
| } else { | } else { | ||||
| $header["App-User-Auth"] = 'none'; | $header["App-User-Auth"] = 'none'; | ||||
| } | } | ||||
| @@ -346,31 +337,6 @@ abstract class WebController extends BaseController | |||||
| if (!Auth::check()) { | if (!Auth::check()) { | ||||
| return; | return; | ||||
| } | } | ||||
| $role = Auth::user()->role; | |||||
| if (!$this->canAccess($role)) { | |||||
| abort(401); | |||||
| } | |||||
| } | |||||
| public function canAccess(UserRole $role) | |||||
| { | |||||
| if (is_array($this->roleAllow) && !in_array($role, $this->roleAllow)) { | |||||
| return false; | |||||
| } | |||||
| if (is_array($this->roleDisallow) && in_array($role, $this->roleDisallow)) { | |||||
| return false; | |||||
| } | |||||
| return $this->canCustomAccess(); | |||||
| } | |||||
| public function canCustomAccess(): bool | |||||
| { | |||||
| return true; | |||||
| } | } | ||||
| // 返却用データの登録 | // 返却用データの登録 | ||||
| @@ -22,10 +22,10 @@ class KintoneAccess | |||||
| private int $appId; | private int $appId; | ||||
| private const DEFAULT_FIELDS = [ | private const DEFAULT_FIELDS = [ | ||||
| "レコード番号", | |||||
| 'RecordNo', | |||||
| "作成日時", | "作成日時", | ||||
| "更新日時", | "更新日時", | ||||
| '$id', | |||||
| '$revision', | |||||
| ]; | ]; | ||||
| private array $fields = []; | private array $fields = []; | ||||
| @@ -83,10 +83,9 @@ class KintoneAccess | |||||
| /** | /** | ||||
| * @param integer $id | * @param integer $id | ||||
| * @param TValue $result | |||||
| * @return Response | |||||
| * @return TValue | |||||
| */ | */ | ||||
| public function find(int $id, KintoneModel &$result): Response | |||||
| public function find(int $id) | |||||
| { | { | ||||
| $response = Http::withHeaders([ | $response = Http::withHeaders([ | ||||
| @@ -104,9 +103,10 @@ class KintoneAccess | |||||
| throw $e; | throw $e; | ||||
| } | } | ||||
| } | } | ||||
| $result = new $this->appName(); | |||||
| $result->setDataFromRecordResponse($response['record']); | $result->setDataFromRecordResponse($response['record']); | ||||
| return $response; | |||||
| return $result; | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -28,7 +28,7 @@ class KintoneRecordQuery | |||||
| $ret .= " "; | $ret .= " "; | ||||
| } | } | ||||
| $ret .= $this->order; | $ret .= $this->order; | ||||
| logger(sprintf("QUERY[%s]:%s", $this->appName, $ret)); | |||||
| // logger(sprintf("QUERY[%s]:%s", $this->appName, $ret)); | |||||
| return $ret; | return $ret; | ||||
| } | } | ||||
| @@ -63,7 +63,7 @@ abstract class KintoneModel | |||||
| { | { | ||||
| return new KintoneRecordQuery(static::class); | return new KintoneRecordQuery(static::class); | ||||
| } | } | ||||
| protected ?int $recordId = null; | |||||
| protected ?string $recordId = null; | |||||
| protected ?int $revision = null; | protected ?int $revision = null; | ||||
| @@ -142,7 +142,7 @@ abstract class KintoneModel | |||||
| foreach ($data as $fieldCode => $ele) { | foreach ($data as $fieldCode => $ele) { | ||||
| $type = data_get($ele, "type"); | $type = data_get($ele, "type"); | ||||
| $value = data_get($ele, "value"); | $value = data_get($ele, "value"); | ||||
| if ($type === "RECORD_NUMBER") { | |||||
| if ($type === "__ID__") { | |||||
| $this->recordId = $value; | $this->recordId = $value; | ||||
| continue; | continue; | ||||
| } | } | ||||
| @@ -259,25 +259,16 @@ abstract class KintoneModel | |||||
| return $this->get($key); | return $this->get($key); | ||||
| } | } | ||||
| public function getRecordId(): ?int | |||||
| public function getRecordId(): ?string | |||||
| { | { | ||||
| return $this->recordId; | return $this->recordId; | ||||
| } | } | ||||
| public function setRecordId(int $recordId): void | |||||
| { | |||||
| $this->recordId = $recordId; | |||||
| } | |||||
| public function getRevision(): ?int | public function getRevision(): ?int | ||||
| { | { | ||||
| return $this->revision; | return $this->revision; | ||||
| } | } | ||||
| public function setRevision(int $revision): void | |||||
| { | |||||
| $this->revision = $revision; | |||||
| } | |||||
| public function getUpdatedAt(): ?Carbon | public function getUpdatedAt(): ?Carbon | ||||
| { | { | ||||
| return $this->updatedAt; | return $this->updatedAt; | ||||
| @@ -287,7 +278,7 @@ abstract class KintoneModel | |||||
| return $this->createdAt; | return $this->createdAt; | ||||
| } | } | ||||
| public function toArray(): array | |||||
| public function toArray($column = ['*']): array | |||||
| { | { | ||||
| if ($this->recordId === null) { | if ($this->recordId === null) { | ||||
| throw new LogicException("保存前モデルのシリアライズ検知"); | throw new LogicException("保存前モデルのシリアライズ検知"); | ||||
| @@ -298,11 +289,17 @@ abstract class KintoneModel | |||||
| 'revision' => $this->revision, | 'revision' => $this->revision, | ||||
| ]; | ]; | ||||
| $columnAll = data_get($column, 0) === '*'; | |||||
| /** | /** | ||||
| * @var string $fieldCode | * @var string $fieldCode | ||||
| */ | */ | ||||
| foreach ($this->data as $fieldCode => $value) { | foreach ($this->data as $fieldCode => $value) { | ||||
| if (!$columnAll && !in_array($fieldCode, $column)) { | |||||
| continue; | |||||
| } | |||||
| $type = data_get(static::FIELDS, $fieldCode); | $type = data_get(static::FIELDS, $fieldCode); | ||||
| $columnName = data_get(static::FIELD_NAMES, $fieldCode, $fieldCode); | $columnName = data_get(static::FIELD_NAMES, $fieldCode, $fieldCode); | ||||
| @@ -0,0 +1,20 @@ | |||||
| <?php | |||||
| namespace App\Listeners\Model; | |||||
| use App\Events\Model\CreatingEvent; | |||||
| use App\Models\User; | |||||
| use Illuminate\Support\Facades\Hash; | |||||
| class CreatingListener extends ModelListener | |||||
| { | |||||
| protected const ACTION = '作成中'; | |||||
| public function handle(CreatingEvent $event): void | |||||
| { | |||||
| // ログインパスワードのハッシュ化 | |||||
| if ($event->model instanceof User) { | |||||
| $event->model->password = Hash::make($event->model->password); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -3,7 +3,9 @@ | |||||
| namespace App\Listeners\Model; | namespace App\Listeners\Model; | ||||
| use App\Events\Model\UpdatingEvent; | use App\Events\Model\UpdatingEvent; | ||||
| use App\Models\User; | |||||
| use Illuminate\Support\Facades\Auth; | use Illuminate\Support\Facades\Auth; | ||||
| use Illuminate\Support\Facades\Hash; | |||||
| class UpdatingListener extends ModelListener | class UpdatingListener extends ModelListener | ||||
| { | { | ||||
| @@ -20,6 +22,14 @@ class UpdatingListener extends ModelListener | |||||
| } | } | ||||
| } | } | ||||
| // ログインパスワードのハッシュ化 | |||||
| if ($event->model instanceof User) { | |||||
| if ($event->model->isDirty(User::COL_NAME_PASSWORD)) { | |||||
| $event->model->password = Hash::make($event->model->password); | |||||
| } | |||||
| } | |||||
| $this->handleEvent($event->model); | $this->handleEvent($event->model); | ||||
| } | } | ||||
| } | } | ||||
| @@ -2,39 +2,41 @@ | |||||
| namespace App\Models; | namespace App\Models; | ||||
| use Auth; | |||||
| use App\Events\Model\CreatedEvent; | |||||
| use App\Events\Model\CreatingEvent; | |||||
| use App\Events\Model\DeletedEvent; | |||||
| use App\Events\Model\UpdatingEvent; | |||||
| use App\Models\Feature\IModelFeature; | |||||
| use Illuminate\Database\Eloquent\Factories\HasFactory; | use Illuminate\Database\Eloquent\Factories\HasFactory; | ||||
| use Illuminate\Database\Eloquent\Model; | use Illuminate\Database\Eloquent\Model; | ||||
| use Illuminate\Database\Query\Builder; | |||||
| use Illuminate\Support\Facades\DB; | use Illuminate\Support\Facades\DB; | ||||
| use Illuminate\Support\Str; | |||||
| /** | |||||
| * @property int|null $created_by | |||||
| */ | |||||
| abstract class BaseModel extends Model | |||||
| abstract class BaseModel extends Model implements IModelFeature | |||||
| { | { | ||||
| use HasFactory; | use HasFactory; | ||||
| const COL_NAME_ID = ColumnName::ID; | const COL_NAME_ID = ColumnName::ID; | ||||
| const COL_NAME_CREATED_BY = 'created_by'; | |||||
| const COL_NAME_CREATED_AT = self::CREATED_AT; | |||||
| const COL_NAME_UPDATED_AT = self::UPDATED_AT; | |||||
| public function __construct(array $attr = []) | |||||
| { | |||||
| if (Auth::check()) { | |||||
| $this->created_by = Auth::id(); | |||||
| } | |||||
| parent::__construct($attr); | |||||
| } | |||||
| const COL_NAME_CREATED_BY = ColumnName::CREATED_BY; | |||||
| const COL_NAME_UPDATED_BY = ColumnName::UPDATED_BY; | |||||
| const COL_NAME_CREATED_AT = ColumnName::CREATED_AT; | |||||
| const COL_NAME_UPDATED_AT = ColumnName::UPDATED_AT; | |||||
| const COL_NAME_DELETED_AT = ColumnName::DELETED_AT; | |||||
| protected $guarded = [ | protected $guarded = [ | ||||
| self::COL_NAME_ID, | self::COL_NAME_ID, | ||||
| self::COL_NAME_CREATED_BY, | |||||
| self::COL_NAME_UPDATED_BY, | |||||
| self::COL_NAME_CREATED_AT, | |||||
| self::COL_NAME_UPDATED_AT, | |||||
| self::COL_NAME_DELETED_AT, | |||||
| ]; | ]; | ||||
| public static function getBuilder(string $name = 'main') | |||||
| public static function getBuilder(string $name = 'main'): Builder | |||||
| { | { | ||||
| return DB::table(static::getTableName(), $name); | |||||
| return DB::table(static::getTableName(), $name) | |||||
| ->whereNull($name . "." . static::COL_NAME_DELETED_AT); | |||||
| } | } | ||||
| public static function getTableName(): string | public static function getTableName(): string | ||||
| @@ -42,26 +44,49 @@ abstract class BaseModel extends Model | |||||
| return (new static)->getTable(); | return (new static)->getTable(); | ||||
| } | } | ||||
| public function copy(BaseModel|User $from) | |||||
| public static function hasColumn(string $columnName): bool | |||||
| { | |||||
| $target = sprintf("%s::COL_NAME_%s", static::class, Str::upper($columnName)); | |||||
| $ret = defined($target); | |||||
| return $ret; | |||||
| } | |||||
| public function copy(IModelFeature $from): static | |||||
| { | { | ||||
| $data = $from->getAttributeKeys(); | $data = $from->getAttributeKeys(); | ||||
| foreach ($data as $key) { | foreach ($data as $key) { | ||||
| if ($key === ColumnName::ID && $this instanceof BaseHistory) { | |||||
| continue; | |||||
| } | |||||
| $this->$key = $from->$key; | $this->$key = $from->$key; | ||||
| } | } | ||||
| return $this; | return $this; | ||||
| } | } | ||||
| public function getAttributeKeys() | |||||
| public function getAttributeKeys(): array | |||||
| { | { | ||||
| return array_values(array_unique(array_merge(array_keys($this->attributesToArray()), $this->hidden))); | return array_values(array_unique(array_merge(array_keys($this->attributesToArray()), $this->hidden))); | ||||
| } | } | ||||
| public function isNotSavedModel(): bool | public function isNotSavedModel(): bool | ||||
| { | { | ||||
| return data_get($this, self::COL_NAME_ID) === null; | |||||
| return data_get($this, ColumnName::ID) === null; | |||||
| } | |||||
| protected $dispatchesEvents = [ | |||||
| 'creating' => CreatingEvent::class, | |||||
| 'created' => CreatedEvent::class, | |||||
| 'updating' => UpdatingEvent::class, | |||||
| 'deleted' => DeletedEvent::class, | |||||
| ]; | |||||
| // カラムが存在する項目のみfillするようオーバーライド | |||||
| public function fill(array $atr) | |||||
| { | |||||
| $filterd = array_filter($atr, function ($value, $key) { | |||||
| return static::hasColumn($key); | |||||
| }, ARRAY_FILTER_USE_BOTH); | |||||
| return parent::fill($filterd); | |||||
| } | } | ||||
| } | } | ||||
| @@ -2,8 +2,8 @@ | |||||
| namespace App\Models; | namespace App\Models; | ||||
| use App\Codes\UserRole; | |||||
| use App\Events\Model\CreatedEvent; | use App\Events\Model\CreatedEvent; | ||||
| use App\Events\Model\CreatingEvent; | |||||
| use App\Events\Model\DeletedEvent; | use App\Events\Model\DeletedEvent; | ||||
| use App\Events\Model\UpdatingEvent; | use App\Events\Model\UpdatingEvent; | ||||
| use App\Models\Feature\IModelFeature; | use App\Models\Feature\IModelFeature; | ||||
| @@ -21,7 +21,6 @@ class User extends Authenticatable implements IModelFeature | |||||
| use HasApiTokens, HasFactory, Notifiable, HasUuids, SoftDeletes; | use HasApiTokens, HasFactory, Notifiable, HasUuids, SoftDeletes; | ||||
| const COL_NAME_ID = 'id'; | const COL_NAME_ID = 'id'; | ||||
| const COL_NAME_ROLE = 'role'; | |||||
| const COL_NAME_EMAIL = 'email'; | const COL_NAME_EMAIL = 'email'; | ||||
| const COL_NAME_NAME = 'name'; | const COL_NAME_NAME = 'name'; | ||||
| const COL_NAME_PASSWORD = 'password'; | const COL_NAME_PASSWORD = 'password'; | ||||
| @@ -50,11 +49,9 @@ class User extends Authenticatable implements IModelFeature | |||||
| self::COL_NAME_DELETED_AT, | self::COL_NAME_DELETED_AT, | ||||
| ]; | ]; | ||||
| protected $casts = [ | |||||
| self::COL_NAME_ROLE => UserRole::class, | |||||
| ]; | |||||
| protected $dispatchesEvents = [ | protected $dispatchesEvents = [ | ||||
| 'creating' => CreatingEvent::class, | |||||
| 'created' => CreatedEvent::class, | 'created' => CreatedEvent::class, | ||||
| 'updating' => UpdatingEvent::class, | 'updating' => UpdatingEvent::class, | ||||
| 'deleted' => DeletedEvent::class, | 'deleted' => DeletedEvent::class, | ||||
| @@ -33,6 +33,6 @@ class EventServiceProvider extends ServiceProvider | |||||
| */ | */ | ||||
| public function shouldDiscoverEvents(): bool | public function shouldDiscoverEvents(): bool | ||||
| { | { | ||||
| return false; | |||||
| return true; | |||||
| } | } | ||||
| } | } | ||||
| @@ -27,7 +27,7 @@ class RouteServiceProvider extends ServiceProvider | |||||
| $this->configureRateLimiting(); | $this->configureRateLimiting(); | ||||
| $this->routes(function () { | $this->routes(function () { | ||||
| Route::middleware('api') | |||||
| Route::middleware('web') | |||||
| ->prefix('api') | ->prefix('api') | ||||
| ->group(base_path('routes/api.php')); | ->group(base_path('routes/api.php')); | ||||
| @@ -44,5 +44,8 @@ class RouteServiceProvider extends ServiceProvider | |||||
| RateLimiter::for('api', function (Request $request) { | RateLimiter::for('api', function (Request $request) { | ||||
| return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip()); | return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip()); | ||||
| }); | }); | ||||
| RateLimiter::for('web', function (Request $request) { | |||||
| return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip()); | |||||
| }); | |||||
| } | } | ||||
| } | } | ||||
| @@ -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:clear") | |||||
| COMMANDS+=("${SAIL} php artisan route:clear") | |||||
| COMMANDS+=("${SAIL} php artisan view:clear") | |||||
| COMMANDS+=("${SAIL} php artisan event:clear") | |||||
| COMMANDS+=("${SAIL} php artisan queue:restart") | |||||
| for COMMAND in "${COMMANDS[@]}"; do | |||||
| echo ${COMMAND} | |||||
| ${COMMAND} | |||||
| done | |||||
| @@ -19,7 +19,7 @@ return [ | |||||
| 'allowed_methods' => ['*'], | 'allowed_methods' => ['*'], | ||||
| 'allowed_origins' => ['*'], | |||||
| 'allowed_origins' => ['http://localhost:*', config('app.url')], | |||||
| 'allowed_origins_patterns' => [], | 'allowed_origins_patterns' => [], | ||||
| @@ -29,6 +29,6 @@ return [ | |||||
| 'max_age' => 0, | 'max_age' => 0, | ||||
| 'supports_credentials' => false, | |||||
| 'supports_credentials' => true, | |||||
| ]; | ]; | ||||
| @@ -85,7 +85,7 @@ return [ | |||||
| 'handler_with' => [ | 'handler_with' => [ | ||||
| 'host' => env('PAPERTRAIL_URL'), | 'host' => env('PAPERTRAIL_URL'), | ||||
| 'port' => env('PAPERTRAIL_PORT'), | 'port' => env('PAPERTRAIL_PORT'), | ||||
| 'connectionString' => 'tls://'.env('PAPERTRAIL_URL').':'.env('PAPERTRAIL_PORT'), | |||||
| 'connectionString' => 'tls://' . env('PAPERTRAIL_URL') . ':' . env('PAPERTRAIL_PORT'), | |||||
| ], | ], | ||||
| ], | ], | ||||
| @@ -117,6 +117,39 @@ return [ | |||||
| 'emergency' => [ | 'emergency' => [ | ||||
| 'path' => storage_path('logs/laravel.log'), | 'path' => storage_path('logs/laravel.log'), | ||||
| ], | ], | ||||
| 'web' => [ | |||||
| 'driver' => 'daily', | |||||
| 'path' => storage_path('logs/web.log'), | |||||
| 'level' => env('LOG_LEVEL', 'debug'), | |||||
| 'days' => 14, | |||||
| // 'replace_placeholders' => true, | |||||
| 'permission' => 0666, | |||||
| ], | |||||
| 'batch' => [ | |||||
| 'driver' => 'daily', | |||||
| 'path' => storage_path('logs/batch.log'), | |||||
| 'level' => env('LOG_LEVEL', 'debug'), | |||||
| 'days' => 14, | |||||
| // 'replace_placeholders' => true, | |||||
| 'permission' => 0666, | |||||
| ], | |||||
| 'queue-email' => [ | |||||
| 'driver' => 'daily', | |||||
| 'path' => storage_path('logs/email.log'), | |||||
| 'level' => env('LOG_LEVEL', 'debug'), | |||||
| 'days' => 14, | |||||
| // 'replace_placeholders' => true, | |||||
| 'permission' => 0666, | |||||
| ], | |||||
| 'queue-job' => [ | |||||
| 'driver' => 'daily', | |||||
| 'path' => storage_path('logs/job.log'), | |||||
| 'level' => env('LOG_LEVEL', 'debug'), | |||||
| 'days' => 14, | |||||
| // 'replace_placeholders' => true, | |||||
| 'permission' => 0666, | |||||
| ], | |||||
| ], | ], | ||||
| ]; | ]; | ||||
| @@ -0,0 +1 @@ | |||||
| *.sqlite* | |||||
| @@ -0,0 +1,38 @@ | |||||
| <?php | |||||
| namespace Database\Factories; | |||||
| use Illuminate\Database\Eloquent\Factories\Factory; | |||||
| use Illuminate\Support\Str; | |||||
| /** | |||||
| * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User> | |||||
| */ | |||||
| class UserFactory extends Factory | |||||
| { | |||||
| /** | |||||
| * Define the model's default state. | |||||
| * | |||||
| * @return array<string, mixed> | |||||
| */ | |||||
| public function definition(): array | |||||
| { | |||||
| return [ | |||||
| 'name' => fake()->name(), | |||||
| 'email' => fake()->unique()->safeEmail(), | |||||
| 'email_verified_at' => now(), | |||||
| 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password | |||||
| 'remember_token' => Str::random(10), | |||||
| ]; | |||||
| } | |||||
| /** | |||||
| * Indicate that the model's email address should be unverified. | |||||
| */ | |||||
| public function unverified(): static | |||||
| { | |||||
| return $this->state(fn (array $attributes) => [ | |||||
| 'email_verified_at' => null, | |||||
| ]); | |||||
| } | |||||
| } | |||||
| @@ -18,6 +18,9 @@ return new class extends Migration | |||||
| $table->string('email')->unique()->comment('Email'); | $table->string('email')->unique()->comment('Email'); | ||||
| $table->string('password')->comment('ログインパスワード'); | $table->string('password')->comment('ログインパスワード'); | ||||
| $table->string('kintone_id')->comment('KintoneID'); | $table->string('kintone_id')->comment('KintoneID'); | ||||
| $helper->index(1, ['email']); | |||||
| $helper->index(2, ['kintone_id']); | |||||
| }); | }); | ||||
| Schema::create('user_histories', function (Blueprint $table) { | Schema::create('user_histories', function (Blueprint $table) { | ||||
| $helper = new MigrationHelper($table, true); | $helper = new MigrationHelper($table, true); | ||||
| @@ -26,6 +29,9 @@ return new class extends Migration | |||||
| $table->string('email')->comment('Email'); | $table->string('email')->comment('Email'); | ||||
| $table->string('password')->comment('ログインパスワード'); | $table->string('password')->comment('ログインパスワード'); | ||||
| $table->string('kintone_id')->comment('KintoneID'); | $table->string('kintone_id')->comment('KintoneID'); | ||||
| $helper->index(1, ['email']); | |||||
| $helper->index(2, ['kintone_id']); | |||||
| }); | }); | ||||
| } | } | ||||
| @@ -0,0 +1,22 @@ | |||||
| <?php | |||||
| namespace Database\Seeders; | |||||
| // use Illuminate\Database\Console\Seeds\WithoutModelEvents; | |||||
| use Illuminate\Database\Seeder; | |||||
| class DatabaseSeeder extends Seeder | |||||
| { | |||||
| /** | |||||
| * Seed the application's database. | |||||
| */ | |||||
| public function run(): void | |||||
| { | |||||
| // \App\Models\User::factory(10)->create(); | |||||
| // \App\Models\User::factory()->create([ | |||||
| // 'name' => 'Test User', | |||||
| // 'email' => 'test@example.com', | |||||
| // ]); | |||||
| } | |||||
| } | |||||
| @@ -1,7 +1,6 @@ | |||||
| <?php | <?php | ||||
| use Illuminate\Http\Request; | |||||
| use Illuminate\Support\Facades\Route; | |||||
| use App\Util\RouteHelper; | |||||
| /* | /* | ||||
| |-------------------------------------------------------------------------- | |-------------------------------------------------------------------------- | ||||
| @@ -14,6 +13,7 @@ use Illuminate\Support\Facades\Route; | |||||
| | | | | ||||
| */ | */ | ||||
| Route::middleware('auth:sanctum')->get('/user', function (Request $request) { | |||||
| return $request->user(); | |||||
| }); | |||||
| RouteHelper::post('/login', App\Http\Controllers\Web\Auth\LoginController::class); | |||||
| RouteHelper::get('/logout', App\Http\Controllers\Web\Auth\LogoutController::class); | |||||
| RouteHelper::get('/me', App\Http\Controllers\Web\Auth\MeController::class); | |||||
| @@ -1,6 +1,6 @@ | |||||
| <?php | <?php | ||||
| use Illuminate\Support\Facades\Route; | |||||
| use App\Util\RouteHelper; | |||||
| /* | /* | ||||
| |-------------------------------------------------------------------------- | |-------------------------------------------------------------------------- | ||||
| @@ -13,6 +13,5 @@ use Illuminate\Support\Facades\Route; | |||||
| | | | | ||||
| */ | */ | ||||
| Route::get('/', function () { | |||||
| return view('welcome'); | |||||
| }); | |||||
| // ルーティングで適合しない場合はフロント側のRoutingにゆだねる | |||||
| RouteHelper::get('/{any?}', App\Http\Controllers\Web\IndexController::class)->where('any', '.*'); | |||||
| @@ -10,11 +10,12 @@ class KintoneAccessTest extends TestCase | |||||
| { | { | ||||
| public function test_find(): void | public function test_find(): void | ||||
| { | { | ||||
| $model = new SeasonTicketContract(); | |||||
| $access = SeasonTicketContract::getAccess(); | $access = SeasonTicketContract::getAccess(); | ||||
| $access->find(505, $model); | |||||
| /** | |||||
| * @var SeasonTicketContract | |||||
| */ | |||||
| $model = $access->find(505); | |||||
| $this->assertEquals("塩山兼司", $model->getStr(SeasonTicketContract::FIELD_CUSTOMER_NAME)); | $this->assertEquals("塩山兼司", $model->getStr(SeasonTicketContract::FIELD_CUSTOMER_NAME)); | ||||
| } | } | ||||
| @@ -63,7 +64,7 @@ class KintoneAccessTest extends TestCase | |||||
| $this->assertEquals("山下千晶", $model->getStr(Customer::FIELD_CUSTOMER_NAME)); | $this->assertEquals("山下千晶", $model->getStr(Customer::FIELD_CUSTOMER_NAME)); | ||||
| $this->assertEquals("shi.yy16@gmail.com", $model->getStr(Customer::FIELD_EMAIL)); | $this->assertEquals("shi.yy16@gmail.com", $model->getStr(Customer::FIELD_EMAIL)); | ||||
| $array = $model->toArray(); | |||||
| $array = $model->toArray([Customer::FIELD_CUSTOMER_NAME, Customer::FIELD_EMAIL]); | |||||
| $this->assertEquals("山下千晶", $array['customer_name']); | $this->assertEquals("山下千晶", $array['customer_name']); | ||||
| $this->assertEquals("shi.yy16@gmail.com", $array['email']); | $this->assertEquals("shi.yy16@gmail.com", $array['email']); | ||||
| } | } | ||||