| @@ -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\AppCommonException; | ||||
| use App\Exceptions\ExclusiveException; | use App\Exceptions\ExclusiveException; | ||||
| use App\Exceptions\GeneralErrorMessageException; | use App\Exceptions\GeneralErrorMessageException; | ||||
| use App\Util\DBUtil; | |||||
| use Exception; | use Exception; | ||||
| use Illuminate\Foundation\Auth\Access\AuthorizesRequests; | use Illuminate\Foundation\Auth\Access\AuthorizesRequests; | ||||
| use Illuminate\Foundation\Bus\DispatchesJobs; | use Illuminate\Foundation\Bus\DispatchesJobs; | ||||
| @@ -74,6 +75,8 @@ abstract class WebController extends BaseController | |||||
| */ | */ | ||||
| private $data = null; | private $data = null; | ||||
| protected DBUtil $transaction; | |||||
| /** | /** | ||||
| * 返却する結果コード | * 返却する結果コード | ||||
| * | * | ||||
| @@ -83,6 +86,7 @@ abstract class WebController extends BaseController | |||||
| public function __construct() | 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_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_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. | * The attributes that should be hidden for serialization. | ||||
| @@ -31,7 +38,16 @@ class User extends Authenticatable implements IModelFeature | |||||
| * @var array<int, string> | * @var array<int, string> | ||||
| */ | */ | ||||
| protected $hidden = [ | 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 = [ | 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 | 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'); | 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() | $helper->baseColumn() | ||||
| ->contractId(); | ->contractId(); | ||||
| @@ -41,7 +41,12 @@ return new class extends Migration | |||||
| $helper->index(1, [ColumnName::CONTRACT_ID]); | $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 . "太郎", | 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('/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 | // Custom for HelloTechno | ||||
| RouteHelper::get('/custom/hello-techno/customers', App\Http\Controllers\Web\Custom\HelloTechno\CustomersController::class); | 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); | RouteHelper::get('/custom/hello-techno/parkings', App\Http\Controllers\Web\Custom\HelloTechno\ParkingsController::class); | ||||