Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
78.05% covered (warning)
78.05%
32 / 41
71.43% covered (warning)
71.43%
5 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
ProductCollectionController
78.05% covered (warning)
78.05%
32 / 41
71.43% covered (warning)
71.43%
5 / 7
14.79
0.00% covered (danger)
0.00%
0 / 1
 index
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 store
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 update
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 destroy
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 searchProducts
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
6
 validated
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
2
 syncProducts
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace App\Http\Controllers\admin;
4
5use App\Http\Controllers\Controller;
6use App\Models\admin\Product;
7use App\Models\ProductCollection;
8use Illuminate\Http\Request;
9use Illuminate\Support\Facades\DB;
10use Illuminate\Support\Str;
11use Illuminate\Validation\Rule;
12
13class ProductCollectionController extends Controller
14{
15    public function index()
16    {
17        $collections = ProductCollection::with(['products:id,name,sku_no,product_code,thumbnail,price,selling_price'])
18            ->withCount('products')->orderBy('sort_order')->orderBy('name')->get();
19
20        return view('admin.collections.index', compact('collections'));
21    }
22
23    public function store(Request $request)
24    {
25        $data = $this->validated($request);
26        DB::transaction(function () use ($data) {
27            $products = $data['product_ids']; unset($data['product_ids']);
28            $collection = ProductCollection::create($data);
29            $this->syncProducts($collection, $products);
30        });
31        return back()->with('success', 'Collection created successfully.');
32    }
33
34    public function update(Request $request, ProductCollection $collection)
35    {
36        $data = $this->validated($request, $collection);
37        DB::transaction(function () use ($collection, $data) {
38            $products = $data['product_ids']; unset($data['product_ids']);
39            $collection->update($data);
40            $this->syncProducts($collection, $products);
41        });
42        return back()->with('success', 'Collection updated successfully.');
43    }
44
45    public function destroy(ProductCollection $collection)
46    {
47        $collection->delete();
48        return back()->with('success', 'Collection deleted.');
49    }
50
51    public function searchProducts(Request $request)
52    {
53        $term = trim((string) $request->query('q'));
54        if (mb_strlen($term) < 1 || mb_strlen($term) > 100) return response()->json(['results' => [], 'count' => 0]);
55        $like = '%' . $term . '%';
56        $results = Product::query()->select('id','name','sku_no','product_code','thumbnail','price','selling_price')
57            ->where(fn ($q) => $q->where('name','like',$like)->orWhere('sku_no','like',$like)->orWhere('product_code','like',$like)->orWhere('barcode','like',$like))
58            ->orderBy('name')->limit(20)->get()->map(fn ($p) => [
59                'id'=>$p->id,'name'=>$p->name,'sku'=>$p->sku_no ?: $p->product_code,
60                'image'=>$p->thumbnail ? asset($p->thumbnail) : asset('dist/img/default-150x150.png'),
61                'price'=>(float)($p->selling_price ?: $p->price),
62            ])->values();
63        return response()->json(['results'=>$results,'count'=>$results->count()]);
64    }
65
66    private function validated(Request $request, ?ProductCollection $collection = null): array
67    {
68        $request->merge(['slug'=>Str::slug($request->input('slug') ?: $request->input('name'))]);
69        $data = $request->validate([
70            'name'=>['required','string','max:180'], 'slug'=>['required','string','max:180',Rule::unique('product_collections')->ignore($collection?->id)],
71            'description'=>['nullable','string'],'image'=>['nullable','string','max:500'],'meta_title'=>['nullable','string','max:180'],
72            'meta_description'=>['nullable','string','max:500'],'sort_order'=>['nullable','integer','min:0'],
73            'is_active'=>['nullable','boolean'],'product_ids'=>['array'],'product_ids.*'=>['integer',Rule::exists('products','id')],
74        ]);
75        $data['is_active']=$request->boolean('is_active'); $data['sort_order']=(int)($data['sort_order']??0);
76        $data['product_ids']=array_values(array_unique($data['product_ids']??[]));
77        return $data;
78    }
79
80    private function syncProducts(ProductCollection $collection, array $ids): void
81    {
82        $collection->products()->sync(collect($ids)->mapWithKeys(fn ($id,$i)=>[$id=>['sort_order'=>$i]])->all());
83    }
84}