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        return response()->json($category);
207    }
208
209    public function delete(ShortcutCategoryFormRequest $request, ShortcutResolveService $shortcut_service, $id): JsonResponse
210    {
211        $categoryId = $request->has('type') && $request->type === 'move' ? $request->category_id : ShortcutCategory::where('is_default', true)->value('_id');
212
213        if (! ($request->has('type') && $request->type === 'move') && ! $categoryId) {
214            abort(422, 'You do not have default category');
215        }
216
217        $shortcut_service->{'resolve_' . $request->category_type}($id, $categoryId);
218
219        return response()->json('Deleted successfully', 202);
220    }
221
222    public function dependency(Request $request, $categoryId, $type)
223    {
224        switch ($type) {
225            case 'remove':
226                $obj = new Remove();
227                return response()->json($obj->dependency($categoryId));
228            case 'move':
229                $obj = new Move();
230                return response()->json($obj->dependency($categoryId));
231            default:
232                return response()->json([]);
233        }
234    }
235
236    private function nestCategories($sourceCategoryId, $sourceType, $destinationCategoryId, $destinationType, $data)
237    {
238        $sourceCategory = match ($sourceType) {
239            'category' => ShortcutCategory::find($sourceCategoryId),
240            'sub_categories_lv1' => ShortcutSubCategoryLv1::find($sourceCategoryId),
241            'sub_categories_lv2' => ShortcutSubCategoryLv2::find($sourceCategoryId)
242        };
243
244        $destinationCategory = match ($destinationType) {
245            'category_id' => ShortcutCategory::find($destinationCategoryId),
246            'sub_categories_lv1' => ShortcutSubCategoryLv1::find($destinationCategoryId),
247        };
248
249        // sub_category_lv2 nested under sub_category_lv1 | sub_category_lv1 nested under category
250
251        if (
252            ($sourceType == 'sub_categories_lv2' && $destinationType == 'sub_categories_lv1') ||
253            (($sourceType == 'sub_categories_lv1' && $destinationType == 'category_id'))
254        ) {
255            $sourceCategory->update($data);
256
257            $searchArray = match ($sourceType) {
258                //'category' => ['category_id' => $sourceCategoryId],
259                'sub_categories_lv1' => ['sub_categories_lv1_id' => $sourceCategoryId],
260                'sub_categories_lv2' => ['sub_categories_lv2_id' => $sourceCategoryId]
261            };
262
263            $updateArray = match ($destinationType) {
264                'category_id' => [
265                    'category_id' => $destinationCategory->id,
266                ],
267                'sub_categories_lv1' => [
268                    'category_id' => $destinationCategory->category_id,
269                    'sub_categories_lv1_id' => $destinationCategory->id,
270                ]
271            };
272
273            Shortcut::where($searchArray)->update($updateArray);
274            return true;
275            // sub_category_lv2 nested under category
276        } else if (($sourceType == 'sub_categories_lv2' && $destinationType == 'category_id')) {
277
278            $session = DB::getMongoClient()->startSession();
279            $session->startTransaction();
280
281            try {
282                // 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
283                $data = array_merge($sourceCategory->getAttributes(), $data);
284                $shortcutSubCategoryLv1 = ShortcutSubCategoryLv1::create($data);
285
286                ShortcutSubCategoryLv1Position::updateOrCreate(['user_id' => $data['user_id']], ['user_id' => $data['user_id']]);
287                (new ShortcutCategoryPositionServiceLv1())->updateCategoryPosition($data['user_id'], $shortcutSubCategoryLv1->id);
288
289                // update all the shortcuts that were previously connected to the sub_cat_lv2 so they can become connected to the sub_cat_lv1
290                Shortcut::where('sub_categories_lv2_id', $sourceCategoryId)->update([
291                    'category_id' => $shortcutSubCategoryLv1->category_id,
292                    'sub_categories_lv1_id' => $shortcutSubCategoryLv1->id,
293                    'sub_categories_lv2_id' => ''
294                ]);
295
296                // delete the sub_cat_lv2
297                $sourceCategory->delete();
298
299                $session->commitTransaction();
300
301                return true;
302            } catch (\Exception $e) {
303                Log::error($e->getMessage());
304                $session->abortTransaction();
305
306                return false;
307            }
308        } else {
309            // Don't permit other kinds of nesting
310            // abort(422, 'Invalid nesting');
311            return false;
312        }
313    }
314}