Skip to content

Commit 71b3e6a

Browse files
committed
Implement assigning categories to products
1 parent 89991c5 commit 71b3e6a

File tree

13 files changed

+159
-8
lines changed

13 files changed

+159
-8
lines changed

app/Http/Controllers/Api/CategoryController.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use App\Http\Requests\StoreCategoryRequest;
77
use App\Http\Requests\UpdateCategoryRequest;
88
use App\Http\Resources\CategoryResource;
9+
use App\Http\Resources\CategoryTreeResource;
910
use App\Models\Category;
1011

1112
class CategoryController extends Controller
@@ -26,6 +27,11 @@ public function index()
2627
return CategoryResource::collection($categories);
2728
}
2829

30+
public function getAsTree()
31+
{
32+
return Category::getActiveAsTree(CategoryTreeResource::class);
33+
}
34+
2935
/**
3036
* Store a newly created resource in storage.
3137
*/

app/Http/Controllers/Api/ProductController.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use App\Http\Resources\ProductListResource;
88
use App\Http\Resources\ProductResource;
99
use App\Models\Api\Product;
10+
use App\Models\ProductCategory;
1011
use App\Models\ProductImage;
1112
use Illuminate\Http\Request;
1213
use Illuminate\Http\UploadedFile;
@@ -51,9 +52,11 @@ public function store(ProductRequest $request)
5152
/** @var \Illuminate\Http\UploadedFile[] $images */
5253
$images = $data['images'] ?? [];
5354
$imagePositions = $data['image_positions'] ?? [];
55+
$categories = $data['categories'] ?? [];
5456

5557
$product = Product::create($data);
5658

59+
$this->saveCategories($categories, $product);
5760
$this->saveImages($images, $imagePositions, $product);
5861

5962
return new ProductResource($product);
@@ -86,7 +89,9 @@ public function update(ProductRequest $request, Product $product)
8689
$images = $data['images'] ?? [];
8790
$deletedImages = $data['deleted_images'] ?? [];
8891
$imagePositions = $data['image_positions'] ?? [];
92+
$categories = $data['categories'] ?? [];
8993

94+
$this->saveCategories($categories, $product);
9095
$this->saveImages($images, $imagePositions, $product);
9196
if (count($deletedImages) > 0) {
9297
$this->deleteImages($deletedImages, $product);
@@ -110,6 +115,14 @@ public function destroy(Product $product)
110115
return response()->noContent();
111116
}
112117

118+
private function saveCategories($categoryIds, Product $product)
119+
{
120+
ProductCategory::where('product_id', $product->id)->delete();
121+
$data = array_map(fn($id) => (['category_id' => $id, 'product_id' => $product->id]), $categoryIds);
122+
123+
ProductCategory::insert($data);
124+
}
125+
113126
/**
114127
*
115128
*

app/Http/Requests/ProductRequest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public function rules()
2828
'images.*' => ['nullable', 'image'],
2929
'deleted_images.*' => ['nullable', 'int'],
3030
'image_positions.*' => ['nullable', 'int'],
31+
'categories.*' => ['nullable', 'int', 'exists:categories,id'],
3132
'price' => ['required', 'numeric'],
3233
'quantity' => ['nullable', 'numeric'],
3334
'description' => ['nullable', 'string'],
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace App\Http\Resources;
4+
5+
use Illuminate\Http\Request;
6+
use Illuminate\Http\Resources\Json\JsonResource;
7+
8+
class CategoryTreeResource extends JsonResource
9+
{
10+
/**
11+
* Transform the resource into an array.
12+
*
13+
* @return array<string, mixed>
14+
*/
15+
public function toArray(Request $request): array
16+
{
17+
$data = [
18+
'id' => $this->id,
19+
'label' => $this->name,
20+
];
21+
22+
if ($this->children ?? false) {
23+
$data['children'] = $this->children;
24+
}
25+
26+
return $data;
27+
}
28+
}

app/Http/Resources/ProductResource.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public function toArray($request)
2828
'price' => $this->price,
2929
'quantity' => $this->quantity,
3030
'published' => (bool)$this->published,
31+
'categories' => $this->categories->map(fn($c) => $c->id),
3132
'created_at' => (new \DateTime($this->created_at))->format('Y-m-d H:i:s'),
3233
'updated_at' => (new \DateTime($this->updated_at))->format('Y-m-d H:i:s'),
3334
];

app/Models/Category.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,32 @@ public function parent()
2727
{
2828
return $this->belongsTo(Category::class);
2929
}
30+
31+
public function products()
32+
{
33+
return $this->belongsToMany(Product::class); // product_category
34+
}
35+
36+
public static function getActiveAsTree($resourceClassName = null)
37+
{
38+
$categories = Category::where('active', true)->orderBy('parent_id')->get();
39+
return self::buildCategoryTree($categories, null, $resourceClassName);
40+
}
41+
42+
private static function buildCategoryTree($categories, $parentId = null, $resourceClassName = null)
43+
{
44+
$categoryTree = [];
45+
46+
foreach ($categories as $category) {
47+
if ($category->parent_id === $parentId) {
48+
$children = self::buildCategoryTree($categories, $category->id, $resourceClassName);
49+
if ($children) {
50+
$category->setAttribute('children', $children);
51+
}
52+
$categoryTree[] = $resourceClassName ? new $resourceClassName($category) : $category;
53+
}
54+
}
55+
56+
return $categoryTree;
57+
}
3058
}

app/Models/Product.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,9 @@ public function getImageAttribute()
4040
{
4141
return $this->images->count() > 0 ? $this->images->get(0)->url : null;
4242
}
43+
44+
public function categories()
45+
{
46+
return $this->belongsToMany(Category::class, 'product_categories');
47+
}
4348
}

app/Models/ProductCategory.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
namespace App\Models;
4+
5+
use Illuminate\Database\Eloquent\Factories\HasFactory;
6+
use Illuminate\Database\Eloquent\Model;
7+
8+
class ProductCategory extends Model
9+
{
10+
use HasFactory;
11+
}

backend/package-lock.json

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"vue": "^3.2.25",
1919
"vue-chartjs": "^4.1.2",
2020
"vue-router": "^4.0.13",
21+
"vue3-treeselect": "^0.1.10",
2122
"vuex": "^4.0.2"
2223
},
2324
"devDependencies": {

0 commit comments

Comments
 (0)