From e44397d0fec80bd1901713314b9e534857d5012e Mon Sep 17 00:00:00 2001 From: Caknoooo Date: Wed, 6 Dec 2023 22:16:29 +0700 Subject: [PATCH] feat: create onion architectue with successfully testing register --- app/Core/Application/Image/ImageUpload.php | 106 ++++++++++++++++++ .../User/RegisterUser/RegisterUserRequest.php | 57 ++++++++++ .../User/RegisterUser/RegisterUserService.php | 49 ++++++++ .../Interfaces/UserRepositoryInterface.php | 20 ++++ .../Repository/SqlUserRepository.php | 95 ++++++++++++++++ .../Domain/Infrastructure/dependencies.php | 7 ++ .../Domain/Models/Auth}/User.php | 19 +--- app/Core/Domain/Models/Email.php | 36 ++++++ app/Core/Domain/Models/User/User.php | 91 +++++++++++++++ app/Core/Domain/Models/User/UserId.php | 10 ++ app/Core/Domain/Models/UuidTrait.php | 37 ++++++ app/Exceptions/UserException.php | 34 ++++++ app/Http/Controllers/UserController.php | 42 +++++++ app/Providers/DependencyInjectionProvider.php | 31 +++++ config/app.php | 3 + .../2014_10_12_000000_create_users_table.php | 10 +- routes/api.php | 3 + 17 files changed, 628 insertions(+), 22 deletions(-) create mode 100644 app/Core/Application/Image/ImageUpload.php create mode 100644 app/Core/Application/Service/User/RegisterUser/RegisterUserRequest.php create mode 100644 app/Core/Application/Service/User/RegisterUser/RegisterUserService.php create mode 100644 app/Core/Domain/Infrastructure/Interfaces/UserRepositoryInterface.php create mode 100644 app/Core/Domain/Infrastructure/Repository/SqlUserRepository.php create mode 100644 app/Core/Domain/Infrastructure/dependencies.php rename app/{Models => Core/Domain/Models/Auth}/User.php (60%) create mode 100644 app/Core/Domain/Models/Email.php create mode 100644 app/Core/Domain/Models/User/User.php create mode 100644 app/Core/Domain/Models/User/UserId.php create mode 100644 app/Core/Domain/Models/UuidTrait.php create mode 100644 app/Exceptions/UserException.php create mode 100644 app/Http/Controllers/UserController.php create mode 100644 app/Providers/DependencyInjectionProvider.php diff --git a/app/Core/Application/Image/ImageUpload.php b/app/Core/Application/Image/ImageUpload.php new file mode 100644 index 0000000..054c9b2 --- /dev/null +++ b/app/Core/Application/Image/ImageUpload.php @@ -0,0 +1,106 @@ +uploaded_file = $uploaded_file; + + $this->available_type = [ + 'jpg', + 'jpeg', + 'png', + ]; + + $this->available_mime_type = [ + 'image/jpeg', + 'image/png', + 'image/jpg', + ]; + + $this->max_size = 1024 * 1024 * 2; + $this->path = $path; + $this->seed = $seed; + $this->file_name = trim($file_name); + + $this->check(); + } + + /** + * @throws Exception + */ + public static function create( + UploadedFile $uploaded_file, + string $path, + string $seed, + string $file_name, + ): self { + return new self($uploaded_file, $path, $seed, $file_name); + } + + /** + * @throws Exception + */ + public function check(): void + { + if(!in_array($this->uploaded_file->getClientOriginalExtension(), $this->available_type)) { + throw new UserException('file type not allowed', 400); + } + + if(!in_array($this->uploaded_file->getMimeType(), $this->available_mime_type)) { + throw new UserException('file mime type not allowed', 400); + } + + if($this->uploaded_file->getSize() > $this->max_size) { + throw new UserException('file size greather than 2Mb', 400); + } + } + + /** + * @return string + */ + public function upload(): string + { + $file_front = str_replace(" ", "_", strtolower($this->file_name)); + $encrypted_seed = base64_encode($this->seed); + $uploaded = Storage::putFileAs( + "public/" . $this->path, + $this->uploaded_file, + $file_front . "_" . $encrypted_seed . "." . $this->uploaded_file->getClientOriginalExtension() + ); + + if(!$uploaded) { + throw new UserException('file upload failed', 500); + } + + $full_path = "/storage/" . $this->path . "/" . $file_front . "_" . $encrypted_seed . "." . $this->uploaded_file->getClientOriginalExtension(); + + return $full_path; + } +} diff --git a/app/Core/Application/Service/User/RegisterUser/RegisterUserRequest.php b/app/Core/Application/Service/User/RegisterUser/RegisterUserRequest.php new file mode 100644 index 0000000..012f558 --- /dev/null +++ b/app/Core/Application/Service/User/RegisterUser/RegisterUserRequest.php @@ -0,0 +1,57 @@ +name = $name; + $this->email = $email; + $this->image = $image; + $this->password = $password; + } + + /** + * @return string + */ + public function getName(): string + { + return $this->name; + } + + /** + * @return string + */ + public function getEmail(): string + { + return $this->email; + } + + /** + * @return UploadedFile + */ + public function getImage(): UploadedFile + { + return $this->image; + } + + /** + * @return string + */ + public function getPassword(): string + { + return $this->password; + } +} diff --git a/app/Core/Application/Service/User/RegisterUser/RegisterUserService.php b/app/Core/Application/Service/User/RegisterUser/RegisterUserService.php new file mode 100644 index 0000000..a8156b2 --- /dev/null +++ b/app/Core/Application/Service/User/RegisterUser/RegisterUserService.php @@ -0,0 +1,49 @@ +user_repository = $user_repository; + } + + /** + * @throws UserException + */ + public function execute(RegisterUserRequest $request) + { + $registeredUser = $this->user_repository->getUserByEmail($request->getEmail()); + if($registeredUser) { + throw new UserException('email telah terdaftar', 1002, 404); + } + + $image_url = ImageUpload::create( + $request->getImage(), + 'images', + $request->getEmail(), + 'profile' + )->upload(); + + $user = User::create( + $request->getName(), + new Email($request->getEmail()), + $image_url, + $request->getPassword() + ); + + $this->user_repository->persist($user); + } +} diff --git a/app/Core/Domain/Infrastructure/Interfaces/UserRepositoryInterface.php b/app/Core/Domain/Infrastructure/Interfaces/UserRepositoryInterface.php new file mode 100644 index 0000000..d92ab6c --- /dev/null +++ b/app/Core/Domain/Infrastructure/Interfaces/UserRepositoryInterface.php @@ -0,0 +1,20 @@ +upsert([ + 'id' => $user->getId()->toString(), + 'name' => $user->getName(), + 'email' => $user->getEmail()->toString(), + 'image_url' => $user->getImageUrl(), + 'password' => $user->getHashPassword(), + ], 'id'); + } + + /** + * @throws Exception + */ + public function find(string $id): ?User + { + $row = DB::table('users')->where('id', $id)->first(); + if ($row === null) { + return null; + } + + return $this->constructFromRows([$row])[0]; + } + + /** + * @throws Exception + */ + public function getAll(): ?array + { + $rows = DB::table('users')->get(); + if ($rows === null) { + return null; + } + + return $this->constructFromRows($rows->all()); + } + + /** + * @throws Exception + */ + public function getUserByEmail(string $email): ?User + { + $row = DB::table('users')->where('email', $email)->first(); + if ($row === null) { + return null; + } + + return $this->constructFromRows([$row])[0]; + } + + /** + * @throws Exception + */ + public function updateUser(User $user): void + { + DB::table('users')->where('id', $user->getId()->toString())->update([ + 'name' => $user->getName(), + 'email' => $user->getEmail(), + 'image_url' => $user->getImageUrl(), + 'password' => $user->getHashPassword(), + ]); + } + + /** + * @throws Exception + */ + public function constructFromRows(array $rows): ?array + { + $users = []; + foreach ($rows as $row) { + $users[] = new User( + new UserId($row->id), + $row->name, + new Email($row->email), + $row->image_url, + $row->password + ); + } + + return $users; + } +} diff --git a/app/Core/Domain/Infrastructure/dependencies.php b/app/Core/Domain/Infrastructure/dependencies.php new file mode 100644 index 0000000..a75c34e --- /dev/null +++ b/app/Core/Domain/Infrastructure/dependencies.php @@ -0,0 +1,7 @@ +singleton(UserRepositoryInterface::class, SqlUserRepository::class); diff --git a/app/Models/User.php b/app/Core/Domain/Models/Auth/User.php similarity index 60% rename from app/Models/User.php rename to app/Core/Domain/Models/Auth/User.php index 4d7f70f..6a1c21f 100644 --- a/app/Models/User.php +++ b/app/Core/Domain/Models/Auth/User.php @@ -1,6 +1,6 @@ - */ protected $hidden = [ 'password', - 'remember_token', - ]; - - /** - * The attributes that should be cast. - * - * @var array - */ - protected $casts = [ - 'email_verified_at' => 'datetime', - 'password' => 'hashed', ]; } diff --git a/app/Core/Domain/Models/Email.php b/app/Core/Domain/Models/Email.php new file mode 100644 index 0000000..99777c3 --- /dev/null +++ b/app/Core/Domain/Models/Email.php @@ -0,0 +1,36 @@ + $value + ], + [ + 'email' => 'email|email' + ] + )->validate(); + $this->value = $value; + } + + /** + * @return string + */ + public function toString(): string + { + return $this->value; + } +} diff --git a/app/Core/Domain/Models/User/User.php b/app/Core/Domain/Models/User/User.php new file mode 100644 index 0000000..4a1b7bb --- /dev/null +++ b/app/Core/Domain/Models/User/User.php @@ -0,0 +1,91 @@ +id = $id; + $this->name = $name; + $this->email = $email; + $this->image_url = $image_url; + $this->hash_pasword = $hash_pasword; + } + + /** + * @throws Exception + */ + public static function create( + string $name, + Email $email, + string $image_url, + string $hash_pasword + ): self { + return new self(UserId::generate(), $name, $email, $image_url, Hash::make($hash_pasword)); + } + + /** + * @return UserId + */ + public function getId(): UserId + { + return $this->id; + } + + /** + * @return string + */ + public function getName(): string + { + return $this->name; + } + + /** + * @return Email + */ + public function getEmail(): Email + { + return $this->email; + } + + /** + * @return string + */ + public function getImageUrl(): string + { + return $this->image_url; + } + + /** + * @return string + */ + public function getHashPassword(): string + { + return $this->hash_pasword; + } + + // Relationship +} diff --git a/app/Core/Domain/Models/User/UserId.php b/app/Core/Domain/Models/User/UserId.php new file mode 100644 index 0000000..1703e02 --- /dev/null +++ b/app/Core/Domain/Models/User/UserId.php @@ -0,0 +1,10 @@ +uuid = $uuid ?? Uuid::uuid4()->toString(); + } + + public function toString(): ?string + { + return $this->uuid; + } + + /** + * @throws Exception + */ + public static function generate(): self + { + return new self(Uuid::uuid4()); + } +} diff --git a/app/Exceptions/UserException.php b/app/Exceptions/UserException.php new file mode 100644 index 0000000..3061e81 --- /dev/null +++ b/app/Exceptions/UserException.php @@ -0,0 +1,34 @@ +json([ + 'success' => false, + 'message' => $this->message, + 'code' => $this->code, + ], self::$status); + } +} diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php new file mode 100644 index 0000000..3ce353b --- /dev/null +++ b/app/Http/Controllers/UserController.php @@ -0,0 +1,42 @@ +all()); + $request->validate( + [ + 'name' => 'required', + 'email' => 'required|email', + 'password' => 'required', + ] + ); + + $req = new RegisterUserRequest( + $request->input('name'), + $request->input('email'), + $request->file('image'), + $request->input('password'), + ); + + DB::beginTransaction(); + try { + $service->execute($req); + } catch (Throwable $e) { + DB::rollBack(); + throw $e; + } + DB::commit(); + + return response()->json(['message' => 'success']); + } +} diff --git a/app/Providers/DependencyInjectionProvider.php b/app/Providers/DependencyInjectionProvider.php new file mode 100644 index 0000000..0498989 --- /dev/null +++ b/app/Providers/DependencyInjectionProvider.php @@ -0,0 +1,31 @@ +app; + if (file_exists($dependencies = app_path() . '/Core/Domain/Infrastructure/dependencies.php')) { + require $dependencies; + } + } + + /** + * Bootstrap services. + * + * @return void + */ + public function boot() + { + // + } +} diff --git a/config/app.php b/config/app.php index 9207160..7a7d66a 100644 --- a/config/app.php +++ b/config/app.php @@ -1,5 +1,6 @@ toArray(), /* diff --git a/database/migrations/2014_10_12_000000_create_users_table.php b/database/migrations/2014_10_12_000000_create_users_table.php index 814b11a..9b5546d 100644 --- a/database/migrations/2014_10_12_000000_create_users_table.php +++ b/database/migrations/2014_10_12_000000_create_users_table.php @@ -11,13 +11,13 @@ public function up(): void { Schema::create('users', function (Blueprint $table) { - $table->id(); + $table->uuid('id')->primary(); $table->string('name'); $table->string('email')->unique(); - $table->timestamp('email_verified_at')->nullable(); - $table->string('password'); - $table->rememberToken(); - $table->timestamps(); + $table->string('image_url', 128); + $table->string('password', 64); + $table->timestamp('created_at')->useCurrent(); + $table->timestamp('updated_at')->useCurrent()->useCurrentOnUpdate(); }); } diff --git a/routes/api.php b/routes/api.php index 889937e..86cff5d 100644 --- a/routes/api.php +++ b/routes/api.php @@ -1,5 +1,6 @@ get('/user', function (Request $request) { return $request->user(); }); + +Route::post('/register', [UserController::class, 'create']);