From f794d1f08319c9353f82d0f6c25ac289b42e91e2 Mon Sep 17 00:00:00 2001 From: cyberali Date: Sat, 20 Jul 2024 17:59:03 +0330 Subject: [PATCH 1/3] feat: Implement home page API endpoint - Add HomeController with index method to provide home page data - Fetch and include menu with items, limiting selected fields for optimization - Fetch and include latest 6 sliders using SliderResource - Fetch and include top 8 parent categories with their products using CategoryResource - Fetch and include active advertisements with available clicks using AdvResource - Fetch and include latest 8 posts using PostResource - Return all collected data as a successful JSON response --- .../Controllers/Api/Web/HomeController.php | 38 ++++++++++++++++++ app/Http/Resources/AdvResource.php | 28 +++++++++++++ app/Http/Resources/CategoryResource.php | 39 +++++++++++++++++++ app/Http/Resources/PostResource.php | 38 ++++++++++++++++++ app/Http/Resources/ProductResource.php | 39 +++++++++++++++++++ app/Http/Resources/SliderResource.php | 32 +++++++++++++++ app/Models/Category.php | 2 +- routes/api.php | 16 ++++---- 8 files changed, 224 insertions(+), 8 deletions(-) create mode 100644 app/Http/Controllers/Api/Web/HomeController.php create mode 100644 app/Http/Resources/AdvResource.php create mode 100644 app/Http/Resources/CategoryResource.php create mode 100644 app/Http/Resources/PostResource.php create mode 100644 app/Http/Resources/ProductResource.php create mode 100644 app/Http/Resources/SliderResource.php diff --git a/app/Http/Controllers/Api/Web/HomeController.php b/app/Http/Controllers/Api/Web/HomeController.php new file mode 100644 index 0000000..1bfdada --- /dev/null +++ b/app/Http/Controllers/Api/Web/HomeController.php @@ -0,0 +1,38 @@ + function ($query) { + $query->select(['id', 'title', 'menuable_id', 'menuable_type', 'kind', 'meta', 'parent', 'sort', 'user_id', 'menu_id']); + }])->first(['id', 'name']); + $data['slider'] = SliderResource::collection(Slider::take(6)->get()); + $data['categories'] = CategoryResource::collection(Category::with('products')->whereNull('parent_id')->orderBy('sort')->take(8)->get()); + $data['adv'] = AdvResource::collection( + Adv::query() + ->where('status', true) + ->whereColumn('click', '<', 'max_click') + ->get() + ); + $data['post'] = PostResource::collection(Post::orderByDesc('created_at')->take(8)->get()); + return success($data); + } +} diff --git a/app/Http/Resources/AdvResource.php b/app/Http/Resources/AdvResource.php new file mode 100644 index 0000000..5cb703a --- /dev/null +++ b/app/Http/Resources/AdvResource.php @@ -0,0 +1,28 @@ + + */ + public function toArray(Request $request): array + { + /** + * @var $this Adv + */ + return [ + 'id' => $this->id, + 'image' => $this->imgUrl, + 'title' => $this->title, + 'link' => $this->link, + ]; + } +} diff --git a/app/Http/Resources/CategoryResource.php b/app/Http/Resources/CategoryResource.php new file mode 100644 index 0000000..5129d19 --- /dev/null +++ b/app/Http/Resources/CategoryResource.php @@ -0,0 +1,39 @@ + + */ + public function toArray(Request $request, $data = null): array + { + /** + * @var $this Category + */ + $request->merge([ + 'loadCategory' => false + ]); + return [ + 'id' => $this->id, + 'name' => $this->name, + 'slug' => $this->slug, + 'subtitle' => $this->subtitle, + 'description' => $this->description, + 'sort' => $this->sort, + 'image' => $this->image, + 'bg' => $this->bg, + 'products' => $this->when($request->input('loadProduct' , true) , ProductResource::collection($this->products)->additional(['request' => $request['loadCategory']]) ) + ]; + } +} diff --git a/app/Http/Resources/PostResource.php b/app/Http/Resources/PostResource.php new file mode 100644 index 0000000..74a5795 --- /dev/null +++ b/app/Http/Resources/PostResource.php @@ -0,0 +1,38 @@ + + */ + public function toArray(Request $request): array + { + /** + * @var $this Post + */ + return [ + 'id' => $this->id, + 'slug' => $this->slug, + 'subtitle' => $this->subtitle, + 'body' => $this->body, + 'group' => $this->load('groups'), + 'author' => $this->load('author'), + 'view' => $this->view, + 'is_pinned' => $this->is_pinned, + 'hash' => $this->hash, + 'like' => $this->like, + 'dislike' => $this->dislike, + 'icon' => $this->icon, + 'created_at' => $this->created_at, + + ]; + } +} diff --git a/app/Http/Resources/ProductResource.php b/app/Http/Resources/ProductResource.php new file mode 100644 index 0000000..85b50b7 --- /dev/null +++ b/app/Http/Resources/ProductResource.php @@ -0,0 +1,39 @@ + + */ + public function toArray(Request $request): array + { + /** + * @var $this Product + */ + $request->merge([ + 'loadProduct' => false + ]); + return [ + 'id' => $this->id, + 'slug' => $this->slug, + 'description' => $this->description, + 'table' => $this->table, + 'sku' => $this->sku, + 'virtual' => $this->virtual, + 'downloadable' => $this->downloadable, + 'price' => $this->price, + 'buy_price' => $this->buy_price, + 'average_rating' => $this->average_rating, + 'view' => $this->view, + 'category' => $this->when($request->input('loadCategory', true), new CategoryResource($this->category)) + ]; + } +} diff --git a/app/Http/Resources/SliderResource.php b/app/Http/Resources/SliderResource.php new file mode 100644 index 0000000..cebf359 --- /dev/null +++ b/app/Http/Resources/SliderResource.php @@ -0,0 +1,32 @@ + + */ + public function toArray(Request $request): array + { + /** + * @var $this Slider + */ + return [ + 'id' => $this->id, + 'body' => $this->body, + 'image' => $this->imgUrl(), + 'tag' => $this->tag, + 'user_id' => $this->user_id, + 'status' => $this->status, + 'data' => $this->data, + 'user' => $this->load('author') + ]; + } +} diff --git a/app/Models/Category.php b/app/Models/Category.php index c8cdebc..7493554 100644 --- a/app/Models/Category.php +++ b/app/Models/Category.php @@ -83,7 +83,7 @@ class Category extends Model public function products() { - return $this->belongsToMany(Product::class); + return $this->hasMany(Product::class); } diff --git a/routes/api.php b/routes/api.php index cdaa0f3..c99478e 100644 --- a/routes/api.php +++ b/routes/api.php @@ -9,12 +9,12 @@ Route::get('/user', function (Request $request) { Route::get('', function () { - return 'xshop api:'.config('app.name'); + return 'xshop api:' . config('app.name'); }); Route::get('/clear', function () { - if (!auth()->check()){ + if (!auth()->check()) { return abort(403); } Artisan::call('cache:clear'); @@ -33,9 +33,11 @@ Route::prefix('v1')->name('v1.')->group( return 'xShop api v1'; }); - Route::get('states', [\App\Http\Controllers\Api\StateController::class,'index'])->name('state.index'); - Route::get('state/{state}', [\App\Http\Controllers\Api\StateController::class,'show'])->name('state.show'); - Route::get('category/props/{category}', [\App\Http\Controllers\Api\CategoryController::class,'props'])->name('category.prop'); - Route::post('morph/search', [\App\Http\Controllers\Api\MorphController::class,'search'])->name('morph.search'); - Route::post('visitor/display', [\App\Http\Controllers\Api\VisitorController::class,'display'])->name('visitor.display'); + Route::get('states', [\App\Http\Controllers\Api\StateController::class, 'index'])->name('state.index'); + Route::get('state/{state}', [\App\Http\Controllers\Api\StateController::class, 'show'])->name('state.show'); + Route::get('category/props/{category}', [\App\Http\Controllers\Api\CategoryController::class, 'props'])->name('category.prop'); + Route::post('morph/search', [\App\Http\Controllers\Api\MorphController::class, 'search'])->name('morph.search'); + Route::post('visitor/display', [\App\Http\Controllers\Api\VisitorController::class, 'display'])->name('visitor.display'); + + Route::apiResource('web', \App\Http\Controllers\Api\Web\HomeController::class)->only('index'); }); From a8099343ad269a9049711eca6db68de128b57920 Mon Sep 17 00:00:00 2001 From: cyberali Date: Sat, 20 Jul 2024 18:09:43 +0330 Subject: [PATCH 2/3] Roll back Category model --- app/Models/Category.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Models/Category.php b/app/Models/Category.php index 7493554..c8cdebc 100644 --- a/app/Models/Category.php +++ b/app/Models/Category.php @@ -83,7 +83,7 @@ class Category extends Model public function products() { - return $this->hasMany(Product::class); + return $this->belongsToMany(Product::class); } From dcead2a8655653de7a05a22519c65989271c7931 Mon Sep 17 00:00:00 2001 From: cyberali Date: Sun, 21 Jul 2024 01:44:03 +0330 Subject: [PATCH 3/3] feat: Implement product listing API with caching , sorting , filters and search - Add ProductController with index method to provide product listings - Implement caching for product listings based on request URI - Add sorting functionality for products by various criteria (new, old, most_view, less_view, most_buy, less_buy) - Implement filtering by category using slug - Add search functionality to filter products by name - Implement price range filtering using min_price and max_price parameters - Include related category data in the product resource response - Set default pagination to 20 items per page with optional customization via per_page parameter --- .../Controllers/Api/Web/ProductController.php | 63 +++++++++++++++++++ app/Http/Resources/CategoryResource.php | 10 +-- app/Http/Resources/ProductResource.php | 16 +++-- routes/api.php | 1 + 4 files changed, 80 insertions(+), 10 deletions(-) create mode 100644 app/Http/Controllers/Api/Web/ProductController.php diff --git a/app/Http/Controllers/Api/Web/ProductController.php b/app/Http/Controllers/Api/Web/ProductController.php new file mode 100644 index 0000000..c6dd798 --- /dev/null +++ b/app/Http/Controllers/Api/Web/ProductController.php @@ -0,0 +1,63 @@ +getUri()); + + $data = Cache::remember($cacheKey, now()->addMinutes(env('CACHE_LIFE_TIME', 10)), function () use ($request) { + $product = Product::query(); + /** + * Product Sort by keys + */ + if (isset($request['sort']) && !is_null($request['sort'])) { + if ($request['sort'] === 'new') + $product = $product->orderByDesc('created_at'); + if ($request['sort'] === 'old') + $product = $product->orderBy('created_at'); + if ($request['sort'] === 'most_view') + $product = $product->orderByDesc('view'); + if ($request['sort'] === 'less_view') + $product = $product->orderBy('view'); + if ($request['sort'] === 'most_buy') + $product = $product->orderByDesc('sell'); + if ($request['sort'] === 'less_buy') + $product = $product->orderBy('sell'); + } + if (isset($request['category']) && !is_null($request['category'])) + $product = $product->where('category_id', Category::firstWhere('slug', $request['category'])->id); + + if (isset($request['search']) && !is_null($request['search'])) + $product = $product->where('name', 'LIKE', "%$request->search%"); + + if (isset($request['min_price']) && + isset($request['max_price']) && + !is_null($request['min_price']) && + !is_null($request['max_price']) + ) { + $product = $product->whereBetween('buy_price', [ + intval($request->min_price), + intval($request->max_price) + ]); + } + $request->merge([ + 'loadCategory' => true + ]); + return [ + 'products' => ProductResource::collection($product->paginate($request->input('per_page', 20)))->resource->toArray(), + ]; + }); + return success($data); + } +} diff --git a/app/Http/Resources/CategoryResource.php b/app/Http/Resources/CategoryResource.php index 5129d19..64c347f 100644 --- a/app/Http/Resources/CategoryResource.php +++ b/app/Http/Resources/CategoryResource.php @@ -21,9 +21,11 @@ class CategoryResource extends JsonResource /** * @var $this Category */ - $request->merge([ - 'loadCategory' => false - ]); + + if (!$request['loadCategory']) + $request->merge([ + 'loadCategory' => false + ]); return [ 'id' => $this->id, 'name' => $this->name, @@ -33,7 +35,7 @@ class CategoryResource extends JsonResource 'sort' => $this->sort, 'image' => $this->image, 'bg' => $this->bg, - 'products' => $this->when($request->input('loadProduct' , true) , ProductResource::collection($this->products)->additional(['request' => $request['loadCategory']]) ) + 'products' => $this->when($request->input('loadProduct', true), ProductResource::collection($this->products)->additional(['request' => $request['loadCategory']])) ]; } } diff --git a/app/Http/Resources/ProductResource.php b/app/Http/Resources/ProductResource.php index 85b50b7..3b0ea55 100644 --- a/app/Http/Resources/ProductResource.php +++ b/app/Http/Resources/ProductResource.php @@ -18,22 +18,26 @@ class ProductResource extends JsonResource /** * @var $this Product */ - $request->merge([ - 'loadProduct' => false - ]); + if (!$request['loadProduct']) + $request->merge([ + 'loadProduct' => false + ]); + return [ 'id' => $this->id, + 'name' => $this->name, 'slug' => $this->slug, 'description' => $this->description, 'table' => $this->table, 'sku' => $this->sku, 'virtual' => $this->virtual, 'downloadable' => $this->downloadable, - 'price' => $this->price, + 'price' => intval($this->price), 'buy_price' => $this->buy_price, - 'average_rating' => $this->average_rating, + 'average_rating' => floatval($this->average_rating), 'view' => $this->view, - 'category' => $this->when($request->input('loadCategory', true), new CategoryResource($this->category)) + 'category' => $this->when($request->input('loadCategory', true), new CategoryResource($this->category)), + 'image' => $this->imgUrl() ]; } } diff --git a/routes/api.php b/routes/api.php index c99478e..9e288ef 100644 --- a/routes/api.php +++ b/routes/api.php @@ -40,4 +40,5 @@ Route::prefix('v1')->name('v1.')->group( Route::post('visitor/display', [\App\Http\Controllers\Api\VisitorController::class, 'display'])->name('visitor.display'); Route::apiResource('web', \App\Http\Controllers\Api\Web\HomeController::class)->only('index'); + Route::apiResource('products' , \App\Http\Controllers\Api\Web\ProductController::class)->only('index'); });