| @@ -0,0 +1,74 @@ | |||
| <?php | |||
| namespace App\Http\Controllers\Web\LoginUser; | |||
| use App\Codes\UserRole; | |||
| use App\Exceptions\AppCommonException; | |||
| use App\Features\LoginUser; | |||
| use App\Http\Controllers\Web\IParam; | |||
| use App\Http\Controllers\Web\WebController; | |||
| use App\Logic\User\LoginUserManager; | |||
| use App\Repositories\LoginUserRepository; | |||
| use Illuminate\Http\JsonResponse; | |||
| use Illuminate\Http\Request; | |||
| class CreateController extends WebController | |||
| { | |||
| use LoginUser; | |||
| public function name(): string | |||
| { | |||
| return "ログインユーザー一覧取得"; | |||
| } | |||
| public function description(): string | |||
| { | |||
| return "ログインユーザー一覧を取得する"; | |||
| } | |||
| public function __construct( | |||
| protected CreateParam $param, | |||
| private LoginUserManager $manager | |||
| ) { | |||
| parent::__construct(); | |||
| $this->roleAllow(UserRole::CONTRACT_ADMIN); | |||
| } | |||
| protected function getParam(): IParam | |||
| { | |||
| return $this->param; | |||
| } | |||
| protected function run(Request $request): JsonResponse | |||
| { | |||
| $param = $this->param; | |||
| try { | |||
| $this->transaction->beginTransaction(); | |||
| $currentContract = $this->loginUser()->getCurrentContract(); | |||
| if (!$currentContract) { | |||
| throw new AppCommonException("認証不正"); | |||
| } | |||
| $messages = $this->manager->initForCreate($currentContract) | |||
| ->fill($param->toArray()) | |||
| ->create(); | |||
| if (count($messages) !== 0) { | |||
| $this->transaction->rollBack(); | |||
| return $this->validateErrorResponse($messages); | |||
| } | |||
| $this->transaction->commit(); | |||
| } catch (Exception $e) { | |||
| $this->transaction->rollBack(); | |||
| throw $e; | |||
| } | |||
| return $this->successResponse(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,26 @@ | |||
| <?php | |||
| namespace App\Http\Controllers\Web\LoginUser; | |||
| use App\Http\Controllers\Web\BaseParam; | |||
| use App\Models\User; | |||
| use App\Rules\LoginPassword; | |||
| /** | |||
| * @property string $email | |||
| * @property string $name | |||
| * @property string $password | |||
| */ | |||
| class CreateParam extends BaseParam | |||
| { | |||
| public function rules(): array | |||
| { | |||
| return | |||
| [ | |||
| User::COL_NAME_EMAIL => $this->str(['email:dns']), | |||
| User::COL_NAME_NAME => $this->str(), | |||
| User::COL_NAME_PASSWORD => $this->str([new LoginPassword()]), | |||
| ]; | |||
| } | |||
| } | |||
| @@ -0,0 +1,63 @@ | |||
| <?php | |||
| namespace App\Http\Controllers\Web\LoginUser; | |||
| use App\Codes\UserRole; | |||
| use App\Exceptions\AppCommonException; | |||
| use App\Features\LoginUser; | |||
| use App\Http\Controllers\Web\IParam; | |||
| use App\Http\Controllers\Web\WebController; | |||
| use App\Repositories\LoginUserRepository; | |||
| use Illuminate\Http\JsonResponse; | |||
| use Illuminate\Http\Request; | |||
| class LoginUsersController extends WebController | |||
| { | |||
| use LoginUser; | |||
| public function name(): string | |||
| { | |||
| return "ログインユーザー一覧取得"; | |||
| } | |||
| public function description(): string | |||
| { | |||
| return "ログインユーザー一覧を取得する"; | |||
| } | |||
| public function __construct( | |||
| protected LoginUsersParam $param, | |||
| private LoginUserRepository $repository | |||
| ) { | |||
| parent::__construct(); | |||
| $this->roleAllow(UserRole::CONTRACT_ADMIN); | |||
| } | |||
| protected function getParam(): IParam | |||
| { | |||
| return $this->param; | |||
| } | |||
| protected function run(Request $request): JsonResponse | |||
| { | |||
| $param = $this->param; | |||
| $currentContractId = $this->loginUser()->getCurrentContractId(); | |||
| if (!$currentContractId) { | |||
| throw new AppCommonException("認証不正"); | |||
| } | |||
| $condition = [ | |||
| ...$param->toArray(), | |||
| LoginUserRepository::CONDITION_CONTRACT_ID => $currentContractId, | |||
| ]; | |||
| $list = $this->repository->get($condition); | |||
| return $this->successResponse([ | |||
| 'records' => $list | |||
| ]); | |||
| } | |||
| } | |||
| @@ -0,0 +1,28 @@ | |||
| <?php | |||
| namespace App\Http\Controllers\Web\LoginUser; | |||
| use App\Http\Controllers\Web\BaseParam; | |||
| use App\Repositories\LoginUserRepository as Repository; | |||
| /** | |||
| * @property ?string $id | |||
| * @property ?string $name | |||
| * @property ?string $email | |||
| */ | |||
| class LoginUsersParam extends BaseParam | |||
| { | |||
| public function rules(): array | |||
| { | |||
| return | |||
| array_merge( | |||
| [ | |||
| Repository::CONDITION_ID => $this->str(true), | |||
| Repository::CONDITION_NAME => $this->str(true), | |||
| Repository::CONDITION_EMAIL => $this->str(true), | |||
| ], | |||
| $this->sortableRules(), | |||
| ); | |||
| } | |||
| } | |||
| @@ -7,6 +7,7 @@ use App\Codes\UserRole; | |||
| use App\Exceptions\AppCommonException; | |||
| use App\Exceptions\ExclusiveException; | |||
| use App\Exceptions\GeneralErrorMessageException; | |||
| use App\Util\DBUtil; | |||
| use Exception; | |||
| use Illuminate\Foundation\Auth\Access\AuthorizesRequests; | |||
| use Illuminate\Foundation\Bus\DispatchesJobs; | |||
| @@ -74,6 +75,8 @@ abstract class WebController extends BaseController | |||
| */ | |||
| private $data = null; | |||
| protected DBUtil $transaction; | |||
| /** | |||
| * 返却する結果コード | |||
| * | |||
| @@ -83,6 +86,7 @@ abstract class WebController extends BaseController | |||
| public function __construct() | |||
| { | |||
| $this->transaction = DBUtil::instance(); | |||
| } | |||
| @@ -0,0 +1,146 @@ | |||
| <?php | |||
| namespace App\Logic\User; | |||
| use App\Codes\UserRole; | |||
| use App\Models\Contract; | |||
| use App\Models\User; | |||
| use Illuminate\Support\Arr; | |||
| use Illuminate\Support\Carbon; | |||
| use Illuminate\Support\Facades\Hash; | |||
| use Illuminate\Support\Facades\Validator; | |||
| use LogicException; | |||
| class LoginUserManager | |||
| { | |||
| private bool $initialized = false; | |||
| private User $user; | |||
| private Contract $contract; | |||
| public function __construct() | |||
| { | |||
| } | |||
| public function initForCreate(string|Contract $contractId) | |||
| { | |||
| $this->setContract($contractId); | |||
| $this->setUser(null); | |||
| $this->initialized = true; | |||
| return $this; | |||
| } | |||
| public function initForModify(string|Contract $contractId, string|User $userId) | |||
| { | |||
| $this->setContract($contractId); | |||
| $this->setUser($userId); | |||
| $this->initialized = true; | |||
| return $this; | |||
| } | |||
| public function getTimestamp(): Carbon | |||
| { | |||
| if (!$this->initialized) { | |||
| throw new LogicException("初期化不正"); | |||
| } | |||
| return $this->user->updated_at < $this->contract->updated_at ? $this->contract->updated_at : $this->user->updated_at; | |||
| } | |||
| public function fill(array $attr) | |||
| { | |||
| if (!$this->initialized) { | |||
| throw new LogicException("初期化不正"); | |||
| } | |||
| $this->user->fill($attr); | |||
| return $this; | |||
| } | |||
| public function create(): array | |||
| { | |||
| $messages = $this->checkParam(); | |||
| if (count($messages) !== 0) { | |||
| return $messages; | |||
| } | |||
| $this->user->save(); | |||
| return []; | |||
| } | |||
| public function update(): array | |||
| { | |||
| $messages = $this->checkParam(); | |||
| if (count($messages) !== 0) { | |||
| return $messages; | |||
| } | |||
| $this->user->save(); | |||
| return []; | |||
| } | |||
| private function setContract(string|Contract $contractId) | |||
| { | |||
| if ($contractId instanceof Contract) { | |||
| $this->contract = $contractId; | |||
| $this->initialized = true; | |||
| return; | |||
| } | |||
| $this->contract = Contract::findOrFail($contractId); | |||
| $this->initialized = true; | |||
| return; | |||
| } | |||
| private function setUser(string|User|null $userId) | |||
| { | |||
| if ($userId instanceof User) { | |||
| $this->user = $userId; | |||
| return; | |||
| } else if (is_string($userId)) { | |||
| $this->user = User::findOrFail($userId); | |||
| return; | |||
| } | |||
| $this->user = new User(); | |||
| $this->user->setContract($this->contract); | |||
| $this->user->role = UserRole::NORMAL_ADMIN; | |||
| } | |||
| private function checkParam() | |||
| { | |||
| $validator = Validator::make($this->user->toArray(), []); | |||
| if ($validator->failed()) { | |||
| throw new LogicException("バリデートエラー"); | |||
| } | |||
| $messages = []; | |||
| $this->checkEmailUnique($messages); | |||
| $this->passwordEncrypto($messages); | |||
| return $messages; | |||
| } | |||
| private function passwordEncrypto(array &$messages) | |||
| { | |||
| if ($this->user->isDirty(User::COL_NAME_PASSWORD)) { | |||
| $this->user->password = Hash::make($this->user->password); | |||
| } | |||
| } | |||
| private function checkEmailUnique(array &$messages) | |||
| { | |||
| if ($this->user->isDirty(User::COL_NAME_EMAIL)) { | |||
| $exists = User::whereEmail($this->user->email) | |||
| ->where(User::COL_NAME_ID, '<>', $this->user->id) | |||
| ->exists(); | |||
| if ($exists) { | |||
| $messages[User::COL_NAME_EMAIL] = trans('validation.unique'); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -24,6 +24,13 @@ class User extends Authenticatable implements IModelFeature | |||
| const COL_NAME_ROLE = 'role'; | |||
| const COL_NAME_EMAIL = 'email'; | |||
| const COL_NAME_NAME = 'name'; | |||
| const COL_NAME_PASSWORD = 'password'; | |||
| 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; | |||
| /** | |||
| * The attributes that should be hidden for serialization. | |||
| @@ -31,7 +38,16 @@ class User extends Authenticatable implements IModelFeature | |||
| * @var array<int, string> | |||
| */ | |||
| protected $hidden = [ | |||
| 'password', | |||
| self::COL_NAME_PASSWORD, | |||
| ]; | |||
| 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, | |||
| ]; | |||
| protected $casts = [ | |||
| @@ -0,0 +1,81 @@ | |||
| <?php | |||
| namespace App\Repositories; | |||
| use App\Models\User; | |||
| use App\Repositories\BaseRepository; | |||
| use Illuminate\Support\Collection; | |||
| use Illuminate\Support\Facades\DB; | |||
| class LoginUserRepository extends BaseRepository | |||
| { | |||
| const CONDITION_ID = 'id'; | |||
| const CONDITION_NAME = 'name'; | |||
| const CONDITION_ROLE = 'role'; | |||
| const CONDITION_EMAIL = 'email'; | |||
| const CONDITION_CONTRACT_ID = 'contract_id'; | |||
| const TABLE_USER = "user"; | |||
| /** | |||
| * コレクションを取得する | |||
| * | |||
| * @param array $condition | |||
| * @return Collection<LoginUserRepositoryData> | |||
| */ | |||
| public function get(array $condition): Collection | |||
| { | |||
| $table = User::getBuilder(static::TABLE_USER); | |||
| // -----検索条件 | |||
| // ID | |||
| $this->where($table, $condition, static::CONDITION_ID, $this->makeColumnName([static::TABLE_USER, User::COL_NAME_ID])); | |||
| // 名前 | |||
| $name = data_get($condition, static::CONDITION_NAME); | |||
| if ($name) { | |||
| $table->where($this->makeColumnName([static::TABLE_USER, User::COL_NAME_NAME]), 'like', "%{$name}%"); | |||
| } | |||
| $email = data_get($condition, static::CONDITION_EMAIL); | |||
| if ($email) { | |||
| $table->where($this->makeColumnName([static::TABLE_USER, User::COL_NAME_EMAIL]), 'like', "%{$email}%"); | |||
| } | |||
| // 契約ID | |||
| $this->where($table, $condition, static::CONDITION_CONTRACT_ID, $this->makeColumnName([static::TABLE_USER, User::COL_NAME_CONTRACT_ID])); | |||
| $table->select($this->columns()); | |||
| $main = DB::table($table, "main"); | |||
| // ソート | |||
| $this->sort($main, $condition); | |||
| $main->orderBy(static::CONDITION_ID); | |||
| // リミット | |||
| $this->limit($main, $condition); | |||
| return LoginUserRepositoryData::makeList($main->get()); | |||
| } | |||
| private function columns() | |||
| { | |||
| $user = static::TABLE_USER; | |||
| $columns = [ | |||
| $this->makeColumnNameForSelect([$user, User::COL_NAME_ID]), | |||
| $this->makeColumnNameForSelect([$user, User::COL_NAME_NAME]), | |||
| $this->makeColumnNameForSelect([$user, User::COL_NAME_ROLE]), | |||
| $this->makeColumnNameForSelect([$user, User::COL_NAME_EMAIL]), | |||
| $this->makeColumnNameForSelect([$user, User::COL_NAME_UPDATED_AT]), | |||
| ]; | |||
| return $columns; | |||
| } | |||
| } | |||
| @@ -0,0 +1,10 @@ | |||
| <?php | |||
| namespace App\Repositories; | |||
| /** | |||
| */ | |||
| class LoginUserRepositoryData extends BaseRepositoryData | |||
| { | |||
| } | |||
| @@ -14,8 +14,8 @@ return new class extends Migration | |||
| public function up(): void | |||
| { | |||
| MigrationHelper::createTable('users', $this->schema()); | |||
| MigrationHelper::createTable('user_histories', $this->schema()); | |||
| MigrationHelper::createTable('users', $this->schema(false)); | |||
| MigrationHelper::createTable('user_histories', $this->schema(true)); | |||
| } | |||
| /** | |||
| @@ -27,10 +27,10 @@ return new class extends Migration | |||
| Schema::dropIfExists('user_histories'); | |||
| } | |||
| private function schema() | |||
| private function schema(bool $forHistory) | |||
| { | |||
| return function (Blueprint $table, MigrationHelper $helper) { | |||
| return function (Blueprint $table, MigrationHelper $helper) use ($forHistory) { | |||
| $helper->baseColumn() | |||
| ->contractId(); | |||
| @@ -41,7 +41,12 @@ return new class extends Migration | |||
| $helper->index(1, [ColumnName::CONTRACT_ID]); | |||
| $helper->index(2, ['email']); | |||
| if ($forHistory) { | |||
| $helper->index(2, ['email']); | |||
| } else { | |||
| $helper->unique(1, ['email']); | |||
| } | |||
| }; | |||
| } | |||
| }; | |||
| @@ -52,5 +52,13 @@ class TestUserSeeder extends Seeder | |||
| User::COL_NAME_NAME => $email . "太郎", | |||
| ]); | |||
| } | |||
| $email = 'hello-admin@aa.com'; | |||
| if (!User::whereEmail($email)->exists()) { | |||
| User::factory()->for($contract)->create([ | |||
| User::COL_NAME_EMAIL => $email, | |||
| User::COL_NAME_ROLE => UserRole::CONTRACT_ADMIN, | |||
| User::COL_NAME_NAME => $email . "太郎", | |||
| ]); | |||
| } | |||
| } | |||
| } | |||
| @@ -30,6 +30,10 @@ RouteHelper::get('/receipt/download', App\Http\Controllers\Web\ReceiptIssuingOrd | |||
| RouteHelper::get('/contracts', App\Http\Controllers\Web\Contract\ContractsController::class); | |||
| RouteHelper::get('/users', App\Http\Controllers\Web\LoginUser\LoginUsersController::class); | |||
| RouteHelper::post('/user/create', App\Http\Controllers\Web\LoginUser\CreateController::class); | |||
| // Custom for HelloTechno | |||
| RouteHelper::get('/custom/hello-techno/customers', App\Http\Controllers\Web\Custom\HelloTechno\CustomersController::class); | |||
| RouteHelper::get('/custom/hello-techno/parkings', App\Http\Controllers\Web\Custom\HelloTechno\ParkingsController::class); | |||