Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 198
0.00% covered (danger)
0.00%
0 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
ShortcutCategoryController
0.00% covered (danger)
0.00%
0 / 198
0.00% covered (danger)
0.00%
0 / 7
930
0.00% covered (danger)
0.00%
0 / 1
 create
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 1
30
 update
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
30
 list
0.00% covered (danger)
0.00%
0 / 92
0.00% covered (danger)
0.00%
0 / 1
2
 details
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 delete
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
42
 dependency
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
20
 nestCategories
0.00% covered (danger)
0.00%
0 / 47
0.00% covered (danger)
0.00%
0 / 1
72
1<?php
2
3namespace App\Http\Controllers\v1;
4
5use App\Http\Controllers\Controller;
6use App\Http\Helpers\ChangeManagement\ShortcutCategory\Move;
7use App\Http\Helpers\ChangeManagement\ShortcutCategory\Remove;
8use App\Http\Models\Shortcut;
9use App\Http\Models\ShortcutCategory;
10use App\Http\Models\ShortcutSubCategoryLv1;
11use App\Http\Models\ShortcutSubCategoryLv1Position;
12use App\Http\Models\ShortcutSubCategoryLv2;
13use App\Http\Models\ShortcutSubCategoryLv2Position;
14use App\Http\Requests\ShortcutCategoryFormRequest;
15use App\Http\Services\ShortcutCategoryPositionService;
16use App\Http\Services\ShortcutCategoryPositionServiceLv1;
17use App\Http\Services\ShortcutCategoryPositionServiceLv2;
18use App\Http\Services\ShortcutResolveService;
19use Illuminate\Http\JsonResponse;
20use Illuminate\Http\Request;
21use Illuminate\Support\Facades\DB;
22use Illuminate\Support\Facades\Log;
23
24class ShortcutCategoryController extends Controller
25{
26    public function create(ShortcutCategoryFormRequest $request): JsonResponse
27    {
28        $data = $request->validated();
29        $userId = $request->user()->getKey();
30        $data['user_id'] = $userId;
31        $data['is_default'] = false;
32
33        // If a subcategory and containing the parent category
34        if ($request['category_id'] && $request['category_id'] != '') {
35            $data['category_id'] = $request['category_id'];
36
37            // if sub_categories_lv1 is included too will create sub_categories_lv2
38            if ($request['sub_categories_lv1'] && $request['sub_categories_lv1'] != '') {
39                $data['sub_categories_lv1'] = $request['sub_categories_lv1'];
40                $result = ShortcutSubCategoryLv2::create($data);
41                $updateArray = ['sub_categories_lv2' => $result['_id']];
42                ShortcutSubCategoryLv1::where('_id', $request['sub_categories_lv1'])->push($updateArray);
43                ShortcutSubCategoryLv2Position::updateOrCreate(['user_id' => $userId], ['user_id' => $userId]);
44                $obj = new ShortcutCategoryPositionServiceLv2;
45                $obj->updateCategoryPosition($userId, $result['_id']);
46            } else {
47                $result = ShortcutSubCategoryLv1::create($data);
48                $updateArray = ['sub_categories_lv1' => $result['_id']];
49                ShortcutCategory::where('_id', $request['category_id'])->push($updateArray);
50                ShortcutSubCategoryLv1Position::updateOrCreate(['user_id' => $userId], ['user_id' => $userId]);
51                $obj = new ShortcutCategoryPositionServiceLv1;
52                $obj->updateCategoryPosition($userId, $result['_id']);
53            }
54        } else {
55            $result = ShortcutCategory::create($data);
56            $obj = new ShortcutCategoryPositionService;
57            $obj->updateCategoryPosition($userId, $result['_id']);
58        }
59
60        return response()->json($result['_id']);
61    }
62
63    public function update(ShortcutCategoryFormRequest $request, $categoryId): JsonResponse
64    {
65        $data = $request->validated();
66        $userId = $request->user()->getKey();
67        $data['user_id'] = $userId;
68        $data['is_default'] = false;
69
70        // If there's a nesting request retrieve the data and update relations.
71        if (filled($request->category_id) || filled($request->sub_categories_lv1)) {
72
73            $sourceCategoryType = $data['source_category_type'];
74            unset($data['source_category_type']);
75
76            if (filled($request->sub_categories_lv1)) {
77
78                $result = $this->nestCategories($categoryId, $sourceCategoryType, $request->sub_categories_lv1, 'sub_categories_lv1', $data);
79            } else {
80
81                $result = $this->nestCategories($categoryId, $sourceCategoryType, $request->category_id, 'category_id', $data);
82            }
83        } else {
84
85            // If there's no nesting request, just update the name of the category
86
87            $result = match ($data['source_category_type']) {
88                'category' => ShortcutCategory::find($categoryId),
89                'sub_categories_lv1' => ShortcutSubCategoryLv1::find($categoryId),
90                'sub_categories_lv2' => ShortcutSubCategoryLv2::find($categoryId)
91            };
92
93            if ($result) {
94                $result->update($data);
95            } else {
96                $result = false;
97            }
98        }
99
100        return response()->json((bool) $result);
101    }
102
103    public function list(Request $request)
104    {
105        $userId = $request->user()->getKey();
106
107        $result = ShortcutCategory::raw(function ($collection) use ($userId) {
108            return $collection->aggregate([
109                ['$match' => ['user_id' => $userId]], // Filter by logged in user
110                ['$lookup' => [
111                    'from' => 'shortcut_sub_categories_lv1', // Perform a left outer join on the shortcut_sub_categories_lv1 collection
112                    'let' => ['id' => ['$toString' => '$_id']], // Convert mongo id to string to allow for join comparison
113                    'pipeline' => [
114                        ['$match' => [
115                            '$expr' => [
116                                '$eq' => ['$$id', '$category_id'], // Join comparison
117                            ],
118                        ]],
119                        ['$lookup' => [ // Repeat the last three comments for the shortcut_sub_categories_lv2
120                            'from' => 'shortcut_sub_categories_lv2',
121                            'let' => ['subId' => ['$toString' => '$_id']],
122                            'pipeline' => [
123                                [
124                                    '$match' => [
125                                        '$expr' => [
126                                            '$eq' => ['$$subId', '$sub_categories_lv1'],
127                                        ],
128                                    ],
129                                ],
130                                ['$lookup' => [
131                                    'from' => 'shortcuts',
132                                    'let' => ['subSubCatId' => ['$toString' => '$_id']],
133                                    'pipeline' => [
134                                        ['$match' => [
135                                            '$expr' => ['$eq' => ['$$subSubCatId', '$sub_categories_lv2_id']],
136                                        ]],
137                                    ],
138                                    'as' => 'shortcuts', // Store the matching shortcuts in an array named 'shortcuts'
139                                ]],
140                                [
141                                    '$addFields' => [
142                                        'id' => ['$toString' => '$_id'], // Convert mongo object id to string
143                                        'updated_at' => [
144                                            '$dateToString' => [
145                                                'format' => '%Y-%m-%dT%H:%M:%S.%LZ',
146                                                'date' => '$updated_at',
147                                            ],
148                                        ],
149                                        'created_at' => [
150                                            '$dateToString' => [
151                                                'format' => '%Y-%m-%dT%H:%M:%S.%LZ',
152                                                'date' => '$created_at',
153                                            ],
154                                        ], // Make the updated_at and created_at fields in carbon form for frontend utilisation
155                                        'shortcuts_count' => ['$size' => '$shortcuts'], // Calculate the size of the 'shortcuts' array
156                                    ],
157                                ],
158                                ['$unset' => 'shortcuts'], // Remove the 'shortcuts' array as it's no longer needed
159                            ],
160                            'as' => 'sub_category_lv2',
161                        ]],
162                        ['$lookup' => [
163                            'from' => 'shortcuts',
164                            'let' => ['subCatId' => ['$toString' => '$_id']],
165                            'pipeline' => [
166                                ['$match' => [
167                                    '$expr' => ['$eq' => ['$$subCatId', '$sub_categories_lv1_id']],
168                                ]],
169                            ],
170                            'as' => 'shortcuts', // Store the matching shortcuts in an array named 'shortcuts'
171                        ]],
172                        [
173                            '$addFields' => [
174                                'id' => ['$toString' => '$_id'],
175                                'updated_at' => [
176                                    '$dateToString' => [
177                                        'format' => '%Y-%m-%dT%H:%M:%S.%LZ',
178                                        'date' => '$updated_at',
179                                    ],
180                                ],
181                                'created_at' => [
182                                    '$dateToString' => [
183                                        'format' => '%Y-%m-%dT%H:%M:%S.%LZ',
184                                        'date' => '$created_at',
185                                    ],
186                                ], // Make the updated_at and created_at fields in carbon form for frontend utilisation
187                                'shortcuts_count' => ['$size' => '$shortcuts'], // Calculate the size of the 'shortcuts' array
188                            ],
189                        ],
190                        ['$unset' => 'shortcuts'], // Remove the 'shortcuts' array as it's no longer needed
191                    ],
192                    'as' => 'sub_category_lv1',
193                ]],
194                ['$sort' => ['is_default' => -1, 'seq_id' => 1]], // sort the result
195            ]);
196        });
197
198        return response()->json($result);
199    }
200
201    public function details(Request $request, $categoryId)
202    {
203        $searchArray = ['_id' => $categoryId];
204        $category = ShortcutCategory::where($searchArray)->first();
205        $category['shortcuts_count'] = $category->shortcuts()->count();
206
207        return response()->json($category);
208    }
209
210    public function delete(ShortcutCategoryFormRequest $request, ShortcutResolveService $shortcut_service, $id): JsonResponse
211    {
212        $categoryId = $request->has('type') && $request->type === 'move' ? $request->category_id : ShortcutCategory::where('is_default', true)->value('_id');
213
214        if (! ($request->has('type') && $request->type === 'move') && ! $categoryId) {
215            abort(422, 'You do not have default category');
216        }
217
218        $shortcut_service->{'resolve_'.$request->category_type}($id, $categoryId);
219
220        return response()->json('Deleted successfully', 202);
221    }
222
223    public function dependency(Request $request, $categoryId, $type)
224    {
225        switch ($type) {
226            case 'remove':
227                $obj = new Remove;
228
229                return response()->json($obj->dependency($categoryId));
230            case 'move':
231                $obj = new Move;
232
233                return response()->json($obj->dependency($categoryId));
234            default:
235                return response()->json([]);
236        }
237    }
238
239    private function nestCategories($sourceCategoryId, $sourceType, $destinationCategoryId, $destinationType, $data)
240    {
241        $sourceCategory = match ($sourceType) {
242            'category' => ShortcutCategory::find($sourceCategoryId),
243            'sub_categories_lv1' => ShortcutSubCategoryLv1::find($sourceCategoryId),
244            'sub_categories_lv2' => ShortcutSubCategoryLv2::find($sourceCategoryId)
245        };
246
247        $destinationCategory = match ($destinationType) {
248            'category_id' => ShortcutCategory::find($destinationCategoryId),
249            'sub_categories_lv1' => ShortcutSubCategoryLv1::find($destinationCategoryId),
250        };
251
252        // sub_category_lv2 nested under sub_category_lv1 | sub_category_lv1 nested under category
253
254        if (
255            ($sourceType == 'sub_categories_lv2' && $destinationType == 'sub_categories_lv1') ||
256            (($sourceType == 'sub_categories_lv1' && $destinationType == 'category_id'))
257        ) {
258            $sourceCategory->update($data);
259
260            $searchArray = match ($sourceType) {
261                // 'category' => ['category_id' => $sourceCategoryId],
262                'sub_categories_lv1' => ['sub_categories_lv1_id' => $sourceCategoryId],
263                'sub_categories_lv2' => ['sub_categories_lv2_id' => $sourceCategoryId]
264            };
265
266            $updateArray = match ($destinationType) {
267                'category_id' => [
268                    'category_id' => $destinationCategory->id,
269                ],
270                'sub_categories_lv1' => [
271                    'category_id' => $destinationCategory->category_id,
272                    'sub_categories_lv1_id' => $destinationCategory->id,
273                ]
274            };
275
276            Shortcut::where($searchArray)->update($updateArray);
277
278            return true;
279            // sub_category_lv2 nested under category
280        } elseif (($sourceType == 'sub_categories_lv2' && $destinationType == 'category_id')) {
281
282            $session = DB::getMongoClient()->startSession();
283            $session->startTransaction();
284
285            try {
286                // extract the content of the sub_cat_lv2 and create it as a sub_cat_lv1. preserve the id so that references can maintain connnection
287                $data = array_merge($sourceCategory->getAttributes(), $data);
288                $shortcutSubCategoryLv1 = ShortcutSubCategoryLv1::create($data);
289
290                ShortcutSubCategoryLv1Position::updateOrCreate(['user_id' => $data['user_id']], ['user_id' => $data['user_id']]);
291                (new ShortcutCategoryPositionServiceLv1)->updateCategoryPosition($data['user_id'], $shortcutSubCategoryLv1->id);
292
293                // update all the shortcuts that were previously connected to the sub_cat_lv2 so they can become connected to the sub_cat_lv1
294                Shortcut::where('sub_categories_lv2_id', $sourceCategoryId)->update([
295                    'category_id' => $shortcutSubCategoryLv1->category_id,
296                    'sub_categories_lv1_id' => $shortcutSubCategoryLv1->id,
297                    'sub_categories_lv2_id' => '',
298                ]);
299
300                // delete the sub_cat_lv2
301                $sourceCategory->delete();
302
303                $session->commitTransaction();
304
305                return true;
306            } catch (\Exception $e) {
307                Log::error($e->getMessage());
308                $session->abortTransaction();
309
310                return false;
311            }
312        } else {
313            // Don't permit other kinds of nesting
314            // abort(422, 'Invalid nesting');
315            return false;
316        }
317    }
318}