From 684b7585bbbe83d96451cbf462e9fde78f23a0ff Mon Sep 17 00:00:00 2001 From: kusowl Date: Thu, 26 Feb 2026 19:03:41 +0530 Subject: [PATCH] feature: product creation and image upload create image upload endpoint create product creation endpoint create get product categories endpoint --- backend/app/Actions/CreateProductAction.php | 16 +++++++++ backend/app/Actions/GetAllProductCategory.php | 19 ++++++++++ backend/app/Actions/UploadImageAction.php | 16 +++++++++ backend/app/Data/ProductCategoryDTO.php | 36 +++++++++++++++++++ backend/app/Data/ProductImageDTO.php | 26 ++++++++++++++ backend/app/Data/UploadImageDTO.php | 22 ++++++++++++ .../Controllers/ProductCategoryController.php | 13 +++++++ .../Http/Controllers/ProductController.php | 23 ++++++++++++ .../Controllers/ProductImagesController.php | 30 ++++++++++++++++ .../Http/Requests/CreateProductRequest.php | 24 +++++++++++++ .../app/Http/Requests/UploadImageRequest.php | 29 +++++++++++++++ backend/app/Models/Product.php | 28 +++++++++++++++ backend/app/Models/ProductCategory.php | 10 ++++++ backend/app/Models/ProductImage.php | 19 ++++++++++ ...073616_create_product_categories_table.php | 29 +++++++++++++++ ...026_02_26_073626_create_products_table.php | 33 +++++++++++++++++ ..._26_080304_create_product_images_table.php | 24 +++++++++++++ .../seeders/ProductCategorySeeder.php | 28 +++++++++++++++ backend/routes/api.php | 6 ++++ 19 files changed, 431 insertions(+) create mode 100644 backend/app/Actions/CreateProductAction.php create mode 100644 backend/app/Actions/GetAllProductCategory.php create mode 100644 backend/app/Actions/UploadImageAction.php create mode 100644 backend/app/Data/ProductCategoryDTO.php create mode 100644 backend/app/Data/ProductImageDTO.php create mode 100644 backend/app/Data/UploadImageDTO.php create mode 100644 backend/app/Http/Controllers/ProductCategoryController.php create mode 100644 backend/app/Http/Controllers/ProductController.php create mode 100644 backend/app/Http/Controllers/ProductImagesController.php create mode 100644 backend/app/Http/Requests/CreateProductRequest.php create mode 100644 backend/app/Http/Requests/UploadImageRequest.php create mode 100644 backend/app/Models/Product.php create mode 100644 backend/app/Models/ProductCategory.php create mode 100644 backend/app/Models/ProductImage.php create mode 100644 backend/database/migrations/2026_02_26_073616_create_product_categories_table.php create mode 100644 backend/database/migrations/2026_02_26_073626_create_products_table.php create mode 100644 backend/database/migrations/2026_02_26_080304_create_product_images_table.php create mode 100644 backend/database/seeders/ProductCategorySeeder.php diff --git a/backend/app/Actions/CreateProductAction.php b/backend/app/Actions/CreateProductAction.php new file mode 100644 index 0000000..e9ec199 --- /dev/null +++ b/backend/app/Actions/CreateProductAction.php @@ -0,0 +1,16 @@ +image->store('public/images'); + $uploadImageDTO->product->images()->create([ + 'path' => $path, + ]); + } +} diff --git a/backend/app/Actions/GetAllProductCategory.php b/backend/app/Actions/GetAllProductCategory.php new file mode 100644 index 0000000..24d12c3 --- /dev/null +++ b/backend/app/Actions/GetAllProductCategory.php @@ -0,0 +1,19 @@ +map(fn ($category) => ProductCategoryDTO::fromModel($category)) + ->toArray(); + } +} diff --git a/backend/app/Actions/UploadImageAction.php b/backend/app/Actions/UploadImageAction.php new file mode 100644 index 0000000..cd11381 --- /dev/null +++ b/backend/app/Actions/UploadImageAction.php @@ -0,0 +1,16 @@ +image->store('public/images'); + $uploadImageDTO->product->images()->create([ + 'path' => $path, + ]); + } +} diff --git a/backend/app/Data/ProductCategoryDTO.php b/backend/app/Data/ProductCategoryDTO.php new file mode 100644 index 0000000..734a535 --- /dev/null +++ b/backend/app/Data/ProductCategoryDTO.php @@ -0,0 +1,36 @@ + + */ + public function toArray(): array + { + return [ + 'id' => $this->id, + 'name' => $this->name, + 'slug' => $this->slug, + ]; + } + + public static function fromModel(ProductCategory $category): self + { + return new self( + id: $category->id, + name: $category->name, + slug: $category->slug, + ); + } +} diff --git a/backend/app/Data/ProductImageDTO.php b/backend/app/Data/ProductImageDTO.php new file mode 100644 index 0000000..ac40a4e --- /dev/null +++ b/backend/app/Data/ProductImageDTO.php @@ -0,0 +1,26 @@ + + */ + public function toArray(): array + { + return [ + 'id' => $this->id, + 'path' => $this->path, + 'productId' => $this->productId, + ]; + } +} diff --git a/backend/app/Data/UploadImageDTO.php b/backend/app/Data/UploadImageDTO.php new file mode 100644 index 0000000..7a17477 --- /dev/null +++ b/backend/app/Data/UploadImageDTO.php @@ -0,0 +1,22 @@ +execute(); + } +} diff --git a/backend/app/Http/Controllers/ProductController.php b/backend/app/Http/Controllers/ProductController.php new file mode 100644 index 0000000..0693776 --- /dev/null +++ b/backend/app/Http/Controllers/ProductController.php @@ -0,0 +1,23 @@ +validated()); + } + + public function show(Product $product) {} + + public function update(Request $request, Product $product) {} + + public function destroy(Product $product) {} +} diff --git a/backend/app/Http/Controllers/ProductImagesController.php b/backend/app/Http/Controllers/ProductImagesController.php new file mode 100644 index 0000000..0ecd954 --- /dev/null +++ b/backend/app/Http/Controllers/ProductImagesController.php @@ -0,0 +1,30 @@ +execute(UploadImageDTO::fromRequest($request->validated())); + + return response()->json(['message' => 'Image uploaded successfully']); + } + + public function show(ProductImages $productImages) + { + return $productImages; + } + + public function destroy(ProductImages $productImages) + { + $productImages->delete(); + + return response()->json(); + } +} diff --git a/backend/app/Http/Requests/CreateProductRequest.php b/backend/app/Http/Requests/CreateProductRequest.php new file mode 100644 index 0000000..3b7d11c --- /dev/null +++ b/backend/app/Http/Requests/CreateProductRequest.php @@ -0,0 +1,24 @@ + 'required|string|max:255', + 'description' => 'required|string', + 'product_category_id' => 'required|exists:product_categories,id', + 'actual_price' => 'required|numeric|min:0', + 'list_price' => 'required|numeric|min:0', + ]; + } + + public function authorize(): bool + { + return true; + } +} diff --git a/backend/app/Http/Requests/UploadImageRequest.php b/backend/app/Http/Requests/UploadImageRequest.php new file mode 100644 index 0000000..c5b523a --- /dev/null +++ b/backend/app/Http/Requests/UploadImageRequest.php @@ -0,0 +1,29 @@ +|string> + */ + public function rules(): array + { + return [ + 'image' => 'required|image|mimes:jpeg,png,jpg,gif,svg|max:2048', + 'product_id' => 'required|exists:products,id', + ]; + } +} diff --git a/backend/app/Models/Product.php b/backend/app/Models/Product.php new file mode 100644 index 0000000..cf431ca --- /dev/null +++ b/backend/app/Models/Product.php @@ -0,0 +1,28 @@ +belongsTo(ProductCategory::class); + } + + public function images(): HasMany + { + return $this->hasMany(ProductImage::class, 'product_id', 'id'); + } +} diff --git a/backend/app/Models/ProductCategory.php b/backend/app/Models/ProductCategory.php new file mode 100644 index 0000000..badd25d --- /dev/null +++ b/backend/app/Models/ProductCategory.php @@ -0,0 +1,10 @@ +belongsTo(Product::class); + } +} diff --git a/backend/database/migrations/2026_02_26_073616_create_product_categories_table.php b/backend/database/migrations/2026_02_26_073616_create_product_categories_table.php new file mode 100644 index 0000000..e6d3d22 --- /dev/null +++ b/backend/database/migrations/2026_02_26_073616_create_product_categories_table.php @@ -0,0 +1,29 @@ +id()->index(); + $table->string('name')->unique(); + $table->string('slug')->unique(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('product_categories'); + } +}; diff --git a/backend/database/migrations/2026_02_26_073626_create_products_table.php b/backend/database/migrations/2026_02_26_073626_create_products_table.php new file mode 100644 index 0000000..6dc7a2f --- /dev/null +++ b/backend/database/migrations/2026_02_26_073626_create_products_table.php @@ -0,0 +1,33 @@ +id(); + $table->string('title'); + $table->text('description'); + $table->decimal('actual_price', 10, 2); + $table->decimal('list_price', 10, 2); + $table->foreignIdFor(ProductCategory::class)->index(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('products'); + } +}; diff --git a/backend/database/migrations/2026_02_26_080304_create_product_images_table.php b/backend/database/migrations/2026_02_26_080304_create_product_images_table.php new file mode 100644 index 0000000..42940e1 --- /dev/null +++ b/backend/database/migrations/2026_02_26_080304_create_product_images_table.php @@ -0,0 +1,24 @@ +id()->index(); + $table->string('path'); + $table->foreignIdFor(Product::class)->index(); + $table->timestamps(); + }); + } + + public function down(): void + { + Schema::dropIfExists('product_images'); + } +}; diff --git a/backend/database/seeders/ProductCategorySeeder.php b/backend/database/seeders/ProductCategorySeeder.php new file mode 100644 index 0000000..822835b --- /dev/null +++ b/backend/database/seeders/ProductCategorySeeder.php @@ -0,0 +1,28 @@ +map(function ($name) { + return [ + 'name' => $name, + 'slug' => Str::slug($name), + ]; + })->toArray(); + + ProductCategory::upsert($categories, ['name'], ['slug']); + } +} diff --git a/backend/routes/api.php b/backend/routes/api.php index 626d647..eec46f1 100644 --- a/backend/routes/api.php +++ b/backend/routes/api.php @@ -1,6 +1,9 @@ group(function () { Route::get('/user', [AuthenticatedUserController::class, 'show']); Route::post('/logout', [AuthenticatedUserController::class, 'destroy']); + Route::post('/upload/images', action: [ProductImagesController::class, 'store']); + Route::apiResource('products', ProductController::class); }); +Route::get('/categories', [ProductCategoryController::class, 'index']);