| @@ -17,4 +17,5 @@ yarn-error.log | |||
| /.fleet | |||
| /.idea | |||
| /.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\Validation\ValidationException; | |||
| use LogicException; | |||
| use Slack; | |||
| use Symfony\Component\HttpFoundation\BinaryFileResponse; | |||
| use Symfony\Component\HttpKernel\Exception\HttpException; | |||
| @@ -172,25 +171,20 @@ abstract class WebController extends BaseController | |||
| $ret = $this->run($request); | |||
| $this->transaction->commit(); | |||
| Slack::commit(); | |||
| return $ret; | |||
| } catch (GeneralErrorMessageException $e) { | |||
| $this->transaction->rollBack(); | |||
| Slack::commit(); | |||
| return $this->failedResponse([], $e->getMessage()); | |||
| } catch (AppCommonException $e) { | |||
| $this->transaction->rollBack(); | |||
| Slack::commit(); | |||
| logs()->error(sprintf("Appエラー:%s File:%s Line:%d", $e->getMessage(), $e->getFile(), $e->getLine())); | |||
| return $this->failedResponse(); | |||
| } catch (ExclusiveException $e) { | |||
| $this->transaction->rollBack(); | |||
| Slack::commit(); | |||
| logs()->error(sprintf("排他エラー:%s", $e->getMessage())); | |||
| return $this->exclusiveErrorResponse(); | |||
| } catch (LogicException $e) { | |||
| $this->transaction->rollBack(); | |||
| Slack::commit(); | |||
| logs()->error([ | |||
| sprintf("実装エラー:%s", $e->getMessage()), | |||
| get_class($e), | |||
| @@ -204,18 +198,15 @@ abstract class WebController extends BaseController | |||
| return $this->failedResponse(); | |||
| } catch (ValidationException $e) { | |||
| $this->transaction->rollBack(); | |||
| Slack::commit(); | |||
| return $this->validateErrorResponse($e); | |||
| } catch (HttpException $e) { | |||
| $this->transaction->rollBack(); | |||
| Slack::commit(); | |||
| if ($e->getStatusCode() === 401) { | |||
| return $this->unAuthorizedResponse(); | |||
| } | |||
| throw e; | |||
| } catch (Exception $e) { | |||
| $this->transaction->rollBack(); | |||
| Slack::commit(); | |||
| logs()->error([ | |||
| sprintf("例外エラー:%s", $e->getMessage()), | |||
| get_class($e), | |||
| @@ -319,7 +310,7 @@ abstract class WebController extends BaseController | |||
| $header = []; | |||
| $user = Auth::user(); | |||
| if ($user) { | |||
| $header["App-User-Auth"] = sprintf("%d,%d", $user->id, $user->role->value); | |||
| $header["App-User-Auth"] = sprintf("%s", $user->id); | |||
| } else { | |||
| $header["App-User-Auth"] = 'none'; | |||
| } | |||
| @@ -346,31 +337,6 @@ abstract class WebController extends BaseController | |||
| if (!Auth::check()) { | |||
| 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 const DEFAULT_FIELDS = [ | |||
| "レコード番号", | |||
| 'RecordNo', | |||
| "作成日時", | |||
| "更新日時", | |||
| '$id', | |||
| '$revision', | |||
| ]; | |||
| private array $fields = []; | |||
| @@ -83,10 +83,9 @@ class KintoneAccess | |||
| /** | |||
| * @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([ | |||
| @@ -104,9 +103,10 @@ class KintoneAccess | |||
| throw $e; | |||
| } | |||
| } | |||
| $result = new $this->appName(); | |||
| $result->setDataFromRecordResponse($response['record']); | |||
| return $response; | |||
| return $result; | |||
| } | |||
| /** | |||
| @@ -28,7 +28,7 @@ class KintoneRecordQuery | |||
| $ret .= " "; | |||
| } | |||
| $ret .= $this->order; | |||
| logger(sprintf("QUERY[%s]:%s", $this->appName, $ret)); | |||
| // logger(sprintf("QUERY[%s]:%s", $this->appName, $ret)); | |||
| return $ret; | |||
| } | |||
| @@ -63,7 +63,7 @@ abstract class KintoneModel | |||
| { | |||
| return new KintoneRecordQuery(static::class); | |||
| } | |||
| protected ?int $recordId = null; | |||
| protected ?string $recordId = null; | |||
| protected ?int $revision = null; | |||
| @@ -142,7 +142,7 @@ abstract class KintoneModel | |||
| foreach ($data as $fieldCode => $ele) { | |||
| $type = data_get($ele, "type"); | |||
| $value = data_get($ele, "value"); | |||
| if ($type === "RECORD_NUMBER") { | |||
| if ($type === "__ID__") { | |||
| $this->recordId = $value; | |||
| continue; | |||
| } | |||
| @@ -259,25 +259,16 @@ abstract class KintoneModel | |||
| return $this->get($key); | |||
| } | |||
| public function getRecordId(): ?int | |||
| public function getRecordId(): ?string | |||
| { | |||
| return $this->recordId; | |||
| } | |||
| public function setRecordId(int $recordId): void | |||
| { | |||
| $this->recordId = $recordId; | |||
| } | |||
| public function getRevision(): ?int | |||
| { | |||
| return $this->revision; | |||
| } | |||
| public function setRevision(int $revision): void | |||
| { | |||
| $this->revision = $revision; | |||
| } | |||
| public function getUpdatedAt(): ?Carbon | |||
| { | |||
| return $this->updatedAt; | |||
| @@ -287,7 +278,7 @@ abstract class KintoneModel | |||
| return $this->createdAt; | |||
| } | |||
| public function toArray(): array | |||
| public function toArray($column = ['*']): array | |||
| { | |||
| if ($this->recordId === null) { | |||
| throw new LogicException("保存前モデルのシリアライズ検知"); | |||
| @@ -298,11 +289,17 @@ abstract class KintoneModel | |||
| '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, $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; | |||
| use App\Events\Model\UpdatingEvent; | |||
| use App\Models\User; | |||
| use Illuminate\Support\Facades\Auth; | |||
| use Illuminate\Support\Facades\Hash; | |||
| 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); | |||
| } | |||
| } | |||
| @@ -2,39 +2,41 @@ | |||
| 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\Model; | |||
| use Illuminate\Database\Query\Builder; | |||
| 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; | |||
| 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 = [ | |||
| 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 | |||
| @@ -42,26 +44,49 @@ abstract class BaseModel extends Model | |||
| 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(); | |||
| foreach ($data as $key) { | |||
| if ($key === ColumnName::ID && $this instanceof BaseHistory) { | |||
| continue; | |||
| } | |||
| $this->$key = $from->$key; | |||
| } | |||
| return $this; | |||
| } | |||
| public function getAttributeKeys() | |||
| public function getAttributeKeys(): array | |||
| { | |||
| return array_values(array_unique(array_merge(array_keys($this->attributesToArray()), $this->hidden))); | |||
| } | |||
| 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; | |||
| use App\Codes\UserRole; | |||
| 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; | |||
| @@ -21,7 +21,6 @@ class User extends Authenticatable implements IModelFeature | |||
| use HasApiTokens, HasFactory, Notifiable, HasUuids, SoftDeletes; | |||
| const COL_NAME_ID = 'id'; | |||
| const COL_NAME_ROLE = 'role'; | |||
| const COL_NAME_EMAIL = 'email'; | |||
| const COL_NAME_NAME = 'name'; | |||
| const COL_NAME_PASSWORD = 'password'; | |||
| @@ -50,11 +49,9 @@ class User extends Authenticatable implements IModelFeature | |||
| self::COL_NAME_DELETED_AT, | |||
| ]; | |||
| protected $casts = [ | |||
| self::COL_NAME_ROLE => UserRole::class, | |||
| ]; | |||
| protected $dispatchesEvents = [ | |||
| 'creating' => CreatingEvent::class, | |||
| 'created' => CreatedEvent::class, | |||
| 'updating' => UpdatingEvent::class, | |||
| 'deleted' => DeletedEvent::class, | |||
| @@ -33,6 +33,6 @@ class EventServiceProvider extends ServiceProvider | |||
| */ | |||
| public function shouldDiscoverEvents(): bool | |||
| { | |||
| return false; | |||
| return true; | |||
| } | |||
| } | |||
| @@ -27,7 +27,7 @@ class RouteServiceProvider extends ServiceProvider | |||
| $this->configureRateLimiting(); | |||
| $this->routes(function () { | |||
| Route::middleware('api') | |||
| Route::middleware('web') | |||
| ->prefix('api') | |||
| ->group(base_path('routes/api.php')); | |||
| @@ -44,5 +44,8 @@ class RouteServiceProvider extends ServiceProvider | |||
| RateLimiter::for('api', function (Request $request) { | |||
| 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_origins' => ['*'], | |||
| 'allowed_origins' => ['http://localhost:*', config('app.url')], | |||
| 'allowed_origins_patterns' => [], | |||
| @@ -29,6 +29,6 @@ return [ | |||
| 'max_age' => 0, | |||
| 'supports_credentials' => false, | |||
| 'supports_credentials' => true, | |||
| ]; | |||
| @@ -85,7 +85,7 @@ return [ | |||
| 'handler_with' => [ | |||
| 'host' => env('PAPERTRAIL_URL'), | |||
| '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' => [ | |||
| '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('password')->comment('ログインパスワード'); | |||
| $table->string('kintone_id')->comment('KintoneID'); | |||
| $helper->index(1, ['email']); | |||
| $helper->index(2, ['kintone_id']); | |||
| }); | |||
| Schema::create('user_histories', function (Blueprint $table) { | |||
| $helper = new MigrationHelper($table, true); | |||
| @@ -26,6 +29,9 @@ return new class extends Migration | |||
| $table->string('email')->comment('Email'); | |||
| $table->string('password')->comment('ログインパスワード'); | |||
| $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 | |||
| 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 | |||
| 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 | |||
| { | |||
| $model = new SeasonTicketContract(); | |||
| $access = SeasonTicketContract::getAccess(); | |||
| $access->find(505, $model); | |||
| /** | |||
| * @var SeasonTicketContract | |||
| */ | |||
| $model = $access->find(505); | |||
| $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("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("shi.yy16@gmail.com", $array['email']); | |||
| } | |||