@fleetbase/storefront-engine 0.1.7 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/.php-cs-fixer.php +29 -0
  2. package/LICENSE.md +16 -4
  3. package/README.md +106 -13
  4. package/composer.json +88 -0
  5. package/extension.json +10 -0
  6. package/package.json +6 -6
  7. package/phpstan.neon.dist +8 -0
  8. package/phpunit.xml.dist +16 -0
  9. package/server/.gitattributes +14 -0
  10. package/server/README.md +40 -0
  11. package/server/config/api.php +101 -0
  12. package/server/config/database.connections.php +57 -0
  13. package/server/config/storefront.php +19 -0
  14. package/server/config/twilio-notification-channel.php +36 -0
  15. package/server/migrations/2023_05_03_025307_create_carts_table.php +44 -0
  16. package/server/migrations/2023_05_03_025307_create_checkouts_table.php +51 -0
  17. package/server/migrations/2023_05_03_025307_create_gateways_table.php +48 -0
  18. package/server/migrations/2023_05_03_025307_create_network_stores_table.php +36 -0
  19. package/server/migrations/2023_05_03_025307_create_networks_table.php +56 -0
  20. package/server/migrations/2023_05_03_025307_create_notification_channels_table.php +43 -0
  21. package/server/migrations/2023_05_03_025307_create_payment_methods_table.php +44 -0
  22. package/server/migrations/2023_05_03_025307_create_product_addon_categories_table.php +38 -0
  23. package/server/migrations/2023_05_03_025307_create_product_addons_table.php +43 -0
  24. package/server/migrations/2023_05_03_025307_create_product_hours_table.php +37 -0
  25. package/server/migrations/2023_05_03_025307_create_product_store_locations_table.php +34 -0
  26. package/server/migrations/2023_05_03_025307_create_product_variant_options_table.php +40 -0
  27. package/server/migrations/2023_05_03_025307_create_product_variants_table.php +44 -0
  28. package/server/migrations/2023_05_03_025307_create_products_table.php +59 -0
  29. package/server/migrations/2023_05_03_025307_create_reviews_table.php +41 -0
  30. package/server/migrations/2023_05_03_025307_create_store_hours_table.php +37 -0
  31. package/server/migrations/2023_05_03_025307_create_store_locations_table.php +38 -0
  32. package/server/migrations/2023_05_03_025307_create_stores_table.php +57 -0
  33. package/server/migrations/2023_05_03_025307_create_votes_table.php +39 -0
  34. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_carts_table.php +40 -0
  35. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_checkouts_table.php +48 -0
  36. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_gateways_table.php +40 -0
  37. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_network_stores_table.php +40 -0
  38. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_networks_table.php +42 -0
  39. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_notification_channels_table.php +40 -0
  40. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_payment_methods_table.php +40 -0
  41. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_addon_categories_table.php +38 -0
  42. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_addons_table.php +38 -0
  43. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_hours_table.php +32 -0
  44. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_store_locations_table.php +34 -0
  45. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_variant_options_table.php +32 -0
  46. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_variants_table.php +32 -0
  47. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_products_table.php +44 -0
  48. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_reviews_table.php +38 -0
  49. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_store_hours_table.php +32 -0
  50. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_store_locations_table.php +40 -0
  51. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_stores_table.php +42 -0
  52. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_votes_table.php +38 -0
  53. package/server/src/Auth/Schemas/Storefront.php +95 -0
  54. package/server/src/Console/Commands/NotifyStorefrontOrderNearby.php +86 -0
  55. package/server/src/Expansions/EntityExpansion.php +44 -0
  56. package/server/src/Http/Controllers/ActionController.php +119 -0
  57. package/server/src/Http/Controllers/AddonCategoryController.php +13 -0
  58. package/server/src/Http/Controllers/CustomerController.php +13 -0
  59. package/server/src/Http/Controllers/GatewayController.php +13 -0
  60. package/server/src/Http/Controllers/MetricsController.php +71 -0
  61. package/server/src/Http/Controllers/NetworkController.php +170 -0
  62. package/server/src/Http/Controllers/NotificationChannelController.php +13 -0
  63. package/server/src/Http/Controllers/OrderController.php +154 -0
  64. package/server/src/Http/Controllers/ProductAddonCategoryController.php +14 -0
  65. package/server/src/Http/Controllers/ProductAddonController.php +14 -0
  66. package/server/src/Http/Controllers/ProductController.php +123 -0
  67. package/server/src/Http/Controllers/ProductHourController.php +13 -0
  68. package/server/src/Http/Controllers/ProductVariantController.php +13 -0
  69. package/server/src/Http/Controllers/ProductVariantOptionController.php +13 -0
  70. package/server/src/Http/Controllers/ReviewController.php +13 -0
  71. package/server/src/Http/Controllers/StoreController.php +26 -0
  72. package/server/src/Http/Controllers/StoreHourController.php +13 -0
  73. package/server/src/Http/Controllers/StoreLocationController.php +13 -0
  74. package/server/src/Http/Controllers/StorefrontController.php +15 -0
  75. package/server/src/Http/Controllers/VoteController.php +13 -0
  76. package/server/src/Http/Controllers/v1/CartController.php +161 -0
  77. package/server/src/Http/Controllers/v1/CategoryController.php +138 -0
  78. package/server/src/Http/Controllers/v1/CheckoutController.php +957 -0
  79. package/server/src/Http/Controllers/v1/CustomerController.php +482 -0
  80. package/server/src/Http/Controllers/v1/GatewayControllerController.php +11 -0
  81. package/server/src/Http/Controllers/v1/NetworkController.php +281 -0
  82. package/server/src/Http/Controllers/v1/PaymentMethodController.php +11 -0
  83. package/server/src/Http/Controllers/v1/ProductController.php +94 -0
  84. package/server/src/Http/Controllers/v1/ReviewController.php +270 -0
  85. package/server/src/Http/Controllers/v1/ServiceQuoteController.php +402 -0
  86. package/server/src/Http/Controllers/v1/StoreController.php +176 -0
  87. package/server/src/Http/Filter/AddonCategoryFilter.php +19 -0
  88. package/server/src/Http/Filter/CustomerFilter.php +18 -0
  89. package/server/src/Http/Filter/GatewayFilter.php +13 -0
  90. package/server/src/Http/Filter/NetworkFilter.php +18 -0
  91. package/server/src/Http/Filter/NotificationChannelFilter.php +13 -0
  92. package/server/src/Http/Filter/OrderFilter.php +46 -0
  93. package/server/src/Http/Filter/ProductFilter.php +28 -0
  94. package/server/src/Http/Filter/StoreFilter.php +42 -0
  95. package/server/src/Http/Filter/StoreLocationFilter.php +23 -0
  96. package/server/src/Http/Middleware/SetStorefrontSession.php +130 -0
  97. package/server/src/Http/Requests/AddStoreToNetworkCategory.php +45 -0
  98. package/server/src/Http/Requests/CaptureOrderRequest.php +30 -0
  99. package/server/src/Http/Requests/CreateCustomerRequest.php +44 -0
  100. package/server/src/Http/Requests/CreateReviewRequest.php +34 -0
  101. package/server/src/Http/Requests/GetServiceQuoteFromCart.php +40 -0
  102. package/server/src/Http/Requests/InitializeCheckoutRequest.php +38 -0
  103. package/server/src/Http/Requests/NetworkActionRequest.php +43 -0
  104. package/server/src/Http/Requests/VerifyCreateCustomerRequest.php +31 -0
  105. package/server/src/Http/Resources/Cart.php +31 -0
  106. package/server/src/Http/Resources/Category.php +48 -0
  107. package/server/src/Http/Resources/Customer.php +36 -0
  108. package/server/src/Http/Resources/Gateway.php +32 -0
  109. package/server/src/Http/Resources/Media.php +29 -0
  110. package/server/src/Http/Resources/Network.php +48 -0
  111. package/server/src/Http/Resources/Product.php +209 -0
  112. package/server/src/Http/Resources/Review.php +45 -0
  113. package/server/src/Http/Resources/ReviewCustomer.php +60 -0
  114. package/server/src/Http/Resources/Store.php +76 -0
  115. package/server/src/Http/Resources/StoreHour.php +29 -0
  116. package/server/src/Http/Resources/StoreLocation.php +33 -0
  117. package/server/src/Imports/ProductsImport.php +20 -0
  118. package/server/src/Jobs/DownloadProductImageUrl.php +60 -0
  119. package/server/src/Listeners/HandleOrderCompleted.php +31 -0
  120. package/server/src/Listeners/HandleOrderDispatched.php +34 -0
  121. package/server/src/Listeners/HandleOrderDriverAssigned.php +37 -0
  122. package/server/src/Listeners/HandleOrderStarted.php +27 -0
  123. package/server/src/Mail/StorefrontNetworkInvite.php +48 -0
  124. package/server/src/Models/AddonCategory.php +30 -0
  125. package/server/src/Models/Cart.php +691 -0
  126. package/server/src/Models/Checkout.php +166 -0
  127. package/server/src/Models/Customer.php +88 -0
  128. package/server/src/Models/Gateway.php +165 -0
  129. package/server/src/Models/Network.php +300 -0
  130. package/server/src/Models/NetworkStore.php +86 -0
  131. package/server/src/Models/NotificationChannel.php +147 -0
  132. package/server/src/Models/PaymentMethod.php +99 -0
  133. package/server/src/Models/Product.php +315 -0
  134. package/server/src/Models/ProductAddon.php +128 -0
  135. package/server/src/Models/ProductAddonCategory.php +90 -0
  136. package/server/src/Models/ProductHour.php +59 -0
  137. package/server/src/Models/ProductStoreLocation.php +77 -0
  138. package/server/src/Models/ProductVariant.php +125 -0
  139. package/server/src/Models/ProductVariantOption.php +86 -0
  140. package/server/src/Models/Review.php +127 -0
  141. package/server/src/Models/Store.php +478 -0
  142. package/server/src/Models/StoreHour.php +59 -0
  143. package/server/src/Models/StoreLocation.php +126 -0
  144. package/server/src/Models/StorefrontModel.php +22 -0
  145. package/server/src/Models/Vote.php +84 -0
  146. package/server/src/Notifications/StorefrontOrderCanceled.php +196 -0
  147. package/server/src/Notifications/StorefrontOrderCompleted.php +201 -0
  148. package/server/src/Notifications/StorefrontOrderCreated.php +157 -0
  149. package/server/src/Notifications/StorefrontOrderDriverAssigned.php +200 -0
  150. package/server/src/Notifications/StorefrontOrderEnroute.php +199 -0
  151. package/server/src/Notifications/StorefrontOrderNearby.php +201 -0
  152. package/server/src/Notifications/StorefrontOrderPreparing.php +202 -0
  153. package/server/src/Notifications/StorefrontOrderReadyForPickup.php +202 -0
  154. package/server/src/Observers/NetworkObserver.php +40 -0
  155. package/server/src/Observers/ProductObserver.php +118 -0
  156. package/server/src/Providers/EventServiceProvider.php +23 -0
  157. package/server/src/Providers/StorefrontServiceProvider.php +103 -0
  158. package/server/src/Support/Metrics.php +193 -0
  159. package/server/src/Support/OrderConfig.php +13 -0
  160. package/server/src/Support/QPay.php +208 -0
  161. package/server/src/Support/Storefront.php +201 -0
  162. package/server/src/routes.php +180 -0
  163. package/server/tests/Feature.php +5 -0
@@ -0,0 +1,281 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\Storefront\Http\Controllers\v1;
4
+
5
+ use Fleetbase\Http\Controllers\Controller;
6
+ use Fleetbase\Storefront\Http\Resources\Store as StorefrontStore;
7
+ use Fleetbase\Storefront\Http\Resources\StoreLocation as StorefrontStoreLocation;
8
+ use Fleetbase\Storefront\Models\Store;
9
+ use Fleetbase\Storefront\Models\StoreLocation;
10
+ use Fleetbase\Models\Category;
11
+ use Fleetbase\FleetOps\Support\Utils;
12
+ use Grimzy\LaravelMysqlSpatial\Types\Point;
13
+ use Illuminate\Http\Request;
14
+
15
+ class NetworkController extends Controller
16
+ {
17
+ /**
18
+ * Returns all stores within the network.
19
+ *
20
+ * @param \Illuminate\Http\Request $request
21
+ * @return \Illuminate\Http\Response
22
+ */
23
+ public function stores(Request $request)
24
+ {
25
+ if (session('storefront_store')) {
26
+ return response()->error('Stores cannot have stores!');
27
+ }
28
+
29
+ $sort = $request->input('sort', false);
30
+ $limit = $request->input('limit', false);
31
+ $offset = $request->input('offset', false);
32
+ $ids = $request->input('ids', []);
33
+ $tagged = $request->input('tagged', []);
34
+ $query = $request->input('query', false);
35
+ $location = $request->input('location');
36
+ $maxDistance = $request->input('maximum_distance', null);
37
+ $exclude = $request->input('exclude', []);
38
+
39
+ if (is_string($tagged)) {
40
+ $tagged = explode(',', $tagged);
41
+ }
42
+
43
+ if (is_string($exclude)) {
44
+ $exclude = explode(',', $exclude);
45
+ }
46
+
47
+ if (is_string($ids)) {
48
+ $ids = explode(',', $ids);
49
+ }
50
+
51
+ /** @var \Illuminate\Database\Query\Builder $query */
52
+ $query = Store::select('*')
53
+ ->where('company_uuid', session('company'))
54
+ ->with(['logo', 'backdrop', 'media'])
55
+ ->whereHas('locations')
56
+ ->whereHas('networks', function ($q) use ($request) {
57
+ $q->where('network_uuid', session('storefront_network'));
58
+
59
+ // Query stores without a category
60
+ if ($request->filled('without_category')) {
61
+ $q->whereNull('category_uuid');
62
+ }
63
+
64
+ // Query stores by category
65
+ if ($request->filled('category')) {
66
+ $category = Category::select('uuid')->where('public_id', $request->input('category'))->first();
67
+ $q->where('category_uuid', $category->uuid);
68
+ // $q->whereHas('category', function ())
69
+ }
70
+ });
71
+
72
+ // query stores using tags provided
73
+ if (!empty($tagged)) {
74
+ $query->where(function ($q) use ($tagged) {
75
+ foreach ($tagged as $tag) {
76
+ $q->orWhereJsonContains('tags', $tag);
77
+ }
78
+ });
79
+ }
80
+
81
+ // if we need to exclude specific stores
82
+ if (!empty($exclude)) {
83
+ $query->where(function ($q) use ($exclude) {
84
+ $q->whereNotIn('public_id', $exclude);
85
+ });
86
+ }
87
+
88
+ // if query for specific ids
89
+ if (!empty($ids)) {
90
+ $query->where(function ($q) use ($ids) {
91
+ $q->whereIn('public_id', $ids);
92
+ });
93
+ }
94
+
95
+ // only get stores with `StoreLocation` within $maxDistance (meters)
96
+ // need to move place->location as alias column to storeLocation->location
97
+ // when place gets updated/changed it also updates corresponding storeLocation in observer
98
+ // if ($location && $maxDistance) {
99
+ // $query->whereHas('locations', function ($q) use ($maxDistance) {
100
+ // });
101
+ // }
102
+
103
+ switch ($sort) {
104
+ case 'highest_rated':
105
+ $query->withAvg('reviews', 'rating')->orderByDesc('reviews_avg_rating');
106
+ case 'lowest_rated':
107
+ $query->withAvg('reviews', 'rating')->orderBy('reviews_avg_rating');
108
+ case 'newest':
109
+ $query->orderByDesc('created_at');
110
+ case 'oldest':
111
+ $query->orderBy('created_at');
112
+ case 'popular':
113
+ $query->withCount('checkouts')->orderByDesc('checkouts_count');
114
+ }
115
+
116
+ if ($limit) {
117
+ $query->limit($limit);
118
+ }
119
+
120
+ if ($offset) {
121
+ $query->offset($offset);
122
+ }
123
+
124
+ $stores = $query->get();
125
+
126
+ // handle nearest sort special case due to location depth
127
+ if ($sort === 'nearest' && $location) {
128
+ $coordinates = Utils::getPointFromCoordinates($location);
129
+
130
+ $stores = $stores->sort(function ($storeA, $storeB) use ($coordinates) {
131
+ $distanceA = $storeA->locations->sort(function ($locationA, $locationB) use ($coordinates) {
132
+ $distanceA = Utils::vincentyGreatCircleDistance($coordinates, $locationA->place->location);
133
+ $distanceB = Utils::vincentyGreatCircleDistance($coordinates, $locationB->place->location);
134
+
135
+ return $distanceA - $distanceB;
136
+ })->map(function ($location) use ($coordinates) {
137
+ $location->distance = Utils::vincentyGreatCircleDistance($coordinates, $location->place->location);
138
+ return $location;
139
+ })->first()->distance;
140
+
141
+ $distanceB = $storeB->locations->sort(function ($locationA, $locationB) use ($coordinates) {
142
+ $distanceA = Utils::vincentyGreatCircleDistance($coordinates, $locationA->place->location);
143
+ $distanceB = Utils::vincentyGreatCircleDistance($coordinates, $locationB->place->location);
144
+
145
+ return $distanceA - $distanceB;
146
+ })->map(function ($location) use ($coordinates) {
147
+ $location->distance = Utils::vincentyGreatCircleDistance($coordinates, $location->place->location);
148
+ return $location;
149
+ })->first()->distance;
150
+
151
+ return $distanceA - $distanceB;
152
+ });
153
+ }
154
+
155
+ // sort trending ( most checkouts within 24h )
156
+ if ($sort === 'trending') {
157
+ $stores = $stores->sortByDesc('24h_checkouts_count');
158
+ }
159
+
160
+ return StorefrontStore::collection($stores);
161
+ }
162
+
163
+ /**
164
+ * Returns all store locations within the network.
165
+ *
166
+ * @param \Illuminate\Http\Request $request
167
+ * @return \Illuminate\Http\Response
168
+ */
169
+ public function storeLocations(Request $request)
170
+ {
171
+ $limit = $request->input('limit', 30);
172
+ $ids = $request->input('ids', []);
173
+ $exclude = $request->input('exclude', []);
174
+ $offset = $request->input('offset');
175
+ $location = $request->input('location');
176
+ $tagged = $request->input('tagged', []);
177
+ $searchQuery = $request->input('query', false);
178
+ $shouldIncludeStore = $request->input('with_store', false);
179
+ $coordinates = Utils::getPointFromCoordinates($location);
180
+
181
+ if (is_string($tagged)) {
182
+ $tagged = explode(',', $tagged);
183
+ }
184
+
185
+ if (is_string($exclude)) {
186
+ $exclude = explode(',', $exclude);
187
+ }
188
+
189
+ if (is_string($ids)) {
190
+ $ids = explode(',', $ids);
191
+ }
192
+
193
+ $databaseName = config('database.connections.mysql.database');
194
+ $placesTableName = $databaseName . '.places';
195
+
196
+ $query = StoreLocation::select(['store_locations.*', $placesTableName . '.location', $placesTableName . '.uuid'])
197
+ ->join($placesTableName, $placesTableName . '.uuid', '=', 'store_locations.place_uuid')
198
+ ->whereHas('store', function ($q) use ($tagged, $searchQuery) {
199
+ $q->whereHas('networks', function ($q) {
200
+ $q->where('network_uuid', session('storefront_network'));
201
+ });
202
+
203
+ if (!empty($tagged)) {
204
+ $q->where(function ($q) use ($tagged) {
205
+ foreach ($tagged as $tag) {
206
+ $q->orWhereJsonContains('tags', $tag);
207
+ }
208
+ });
209
+ }
210
+
211
+ if ($searchQuery) {
212
+ $q->search($searchQuery);
213
+ }
214
+ });
215
+
216
+ if ($shouldIncludeStore) {
217
+ $query->with('store');
218
+ }
219
+
220
+ // if we need to exclude specific stores
221
+ if (!empty($exclude)) {
222
+ $query->where(function ($q) use ($exclude) {
223
+ $q->whereNotIn('store_locations.public_id', $exclude);
224
+ });
225
+ }
226
+
227
+ // if query for specific ids
228
+ if (!empty($ids)) {
229
+ $query->where(function ($q) use ($ids) {
230
+ $q->whereIn('store_locations.public_id', $ids);
231
+ });
232
+ }
233
+
234
+ if ($limit) {
235
+ $query->limit($limit);
236
+ }
237
+
238
+ if ($offset) {
239
+ $query->offset($offset);
240
+ }
241
+
242
+ if ($coordinates instanceof Point) {
243
+ $query->orderByDistance('location', $coordinates);
244
+ }
245
+
246
+ $locations = $query->get();
247
+
248
+ return StorefrontStoreLocation::collection($locations);
249
+ }
250
+
251
+ /**
252
+ * Returns all tags from stores/categories throughout the network
253
+ *
254
+ * @param \Illuminate\Http\Request $request
255
+ * @return \Illuminate\Http\Response
256
+ */
257
+ public function tags(Request $request)
258
+ {
259
+ $tags = [];
260
+
261
+ $stores = Store::select(['tags'])
262
+ ->whereHas('locations')
263
+ ->whereHas('networks', function ($q) {
264
+ $q->where('network_uuid', session('storefront_network'));
265
+ })->get();
266
+
267
+ foreach ($stores as $store) {
268
+ $tags = array_merge($tags, $store->tags ?? []);
269
+ }
270
+
271
+ // $categories = Category::select(['tags'])->where(['owner_uuid' => session('storefront_network'), 'for' => 'storefront_network']);
272
+ // foreach ($categories as $category) {
273
+ // $tags = array_merge($tags, $category->tags ?? []);
274
+ // }
275
+
276
+ // unique only
277
+ $tags = array_values(array_unique($tags));
278
+
279
+ return response()->json($tags);
280
+ }
281
+ }
@@ -0,0 +1,11 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\Storefront\Http\Controllers\v1;
4
+
5
+ use Fleetbase\Http\Controllers\Controller;
6
+ use Illuminate\Http\Request;
7
+
8
+ class PaymentMethodController extends Controller
9
+ {
10
+ //
11
+ }
@@ -0,0 +1,94 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\Storefront\Http\Controllers\v1;
4
+
5
+ use Fleetbase\Http\Controllers\Controller;
6
+ use Fleetbase\Storefront\Http\Resources\Product as StorefrontProduct;
7
+ use Fleetbase\Http\Resources\v1\DeletedResource;
8
+ use Fleetbase\Models\Category;
9
+ use Fleetbase\Storefront\Models\Product;
10
+ use Illuminate\Database\Eloquent\ModelNotFoundException;
11
+ use Illuminate\Http\Request;
12
+
13
+ class ProductController extends Controller
14
+ {
15
+ /**
16
+ * Query for Storefront Product resources.
17
+ *
18
+ * @param \Illuminate\Http\Request $request
19
+ * @return \Fleetbase\Http\Resources\DriverCollection
20
+ */
21
+ public function query(Request $request)
22
+ {
23
+ $results = Product::queryWithRequest($request, function (&$query, $request) {
24
+ // for stores
25
+ if (session('storefront_store')) {
26
+ $query->where(['store_uuid' => session('storefront_store')]);
27
+ }
28
+
29
+ // for networks
30
+ if (session('storefront_network')) {
31
+ $query->whereHas('store', function ($sq) {
32
+ $sq->whereHas('networks', function ($nq) {
33
+ $nq->where('network_uuid', session('storefront_network'));
34
+ });
35
+ });
36
+ }
37
+
38
+ // @todo When done dev is completed make sure status is published - also add status field to product view
39
+ $query->where('is_available', 1);
40
+ $query->with(['addonCategories.category', 'variants.options', 'files']);
41
+
42
+ if ($request->filled('category')) {
43
+ $category = Category::where(['public_id' => $request->input('category'), 'for' => 'storefront_product'])->first();
44
+
45
+ if ($category) {
46
+ $query->where('category_uuid', $category->uuid);
47
+ }
48
+ }
49
+ });
50
+
51
+ return StorefrontProduct::collection($results);
52
+ }
53
+
54
+ /**
55
+ * Finds a single Storefront Product resources.
56
+ *
57
+ * @param \Illuminate\Http\Request $request
58
+ * @return \Fleetbase\Http\Resources\EntityCollection
59
+ */
60
+ public function find($id)
61
+ {
62
+ // find for the product
63
+ try {
64
+ $product = Product::findRecordOrFail($id);
65
+ } catch (ModelNotFoundException $exception) {
66
+ return response()->error('Product resource not found.');
67
+ }
68
+
69
+ // response the product resource
70
+ return new StorefrontProduct($product);
71
+ }
72
+
73
+ /**
74
+ * Deletes a Storefront Product resources.
75
+ *
76
+ * @param \Illuminate\Http\Request $request
77
+ * @return \Fleetbase\Http\Resources\v1\DeletedResource
78
+ */
79
+ public function delete($id)
80
+ {
81
+ // find for the product
82
+ try {
83
+ $product = Product::findRecordOrFail($id);
84
+ } catch (ModelNotFoundException $exception) {
85
+ return response()->error('Product resource not found.');
86
+ }
87
+
88
+ // delete the product
89
+ $product->delete();
90
+
91
+ // response the product resource
92
+ return new DeletedResource($product);
93
+ }
94
+ }
@@ -0,0 +1,270 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\Storefront\Http\Controllers\v1;
4
+
5
+ use Fleetbase\Http\Controllers\Controller;
6
+ use Fleetbase\Storefront\Http\Requests\CreateReviewRequest;
7
+ use Fleetbase\Storefront\Http\Resources\Review as StorefrontReview;
8
+ use Fleetbase\FleetOps\Http\Resources\v1\DeletedResource;
9
+ use Fleetbase\Storefront\Models\Store;
10
+ use Fleetbase\Storefront\Models\Review;
11
+ use Fleetbase\Storefront\Support\Storefront;
12
+ use Fleetbase\FleetOps\Support\Utils;
13
+ use Fleetbase\Models\File;
14
+ use Illuminate\Database\Eloquent\ModelNotFoundException;
15
+ use Illuminate\Http\Request;
16
+ use Illuminate\Support\Facades\Storage;
17
+
18
+ class ReviewController extends Controller
19
+ {
20
+ /**
21
+ * Query for Storefront Review resources.
22
+ *
23
+ * @param \Illuminate\Http\Request $request
24
+ * @return \Illuminate\Http\Response
25
+ */
26
+ public function query(Request $request)
27
+ {
28
+ $limit = $request->input('limit', false);
29
+ $offset = $request->input('offset', false);
30
+ $sort = $request->input('sort');
31
+
32
+ if (session('storefront_store')) {
33
+ $results = Review::queryWithRequest($request, function (&$query) use ($limit, $offset, $sort) {
34
+ $query->where('subject_uuid', session('storefront_store'));
35
+
36
+ if ($limit) {
37
+ $query->limit($limit);
38
+ }
39
+
40
+ if ($offset) {
41
+ $query->limit($offset);
42
+ }
43
+
44
+ if ($sort) {
45
+ switch ($sort) {
46
+ case 'highest':
47
+ case 'highest rated':
48
+ $query->orderByDesc('rating');
49
+ break;
50
+
51
+ case 'lowest':
52
+ case 'lowest rated':
53
+ $query->orderBy('rating');
54
+ break;
55
+
56
+ case 'newest':
57
+ case 'newest first':
58
+ $query->orderByDesc('created_at');
59
+ break;
60
+
61
+ case 'oldest':
62
+ case 'oldest first':
63
+ $query->orderBy('created_at');
64
+ break;
65
+ }
66
+ }
67
+ });
68
+ }
69
+
70
+ if (session('storefront_network')) {
71
+ if ($request->filled('store')) {
72
+ $store = Store::where([
73
+ 'company_uuid' => session('company'),
74
+ 'public_id' => $request->input('store')
75
+ ])->whereHas('networks', function ($q) {
76
+ $q->where('network_uuid', session('storefront_network'));
77
+ })->first();
78
+
79
+ if (!$store) {
80
+ return response()->json(['error' => 'Cannot find reviews for store'], 400);
81
+ }
82
+
83
+ $results = Review::queryWithRequest($request, function (&$query) use ($store, $sort, $limit, $offset) {
84
+ $query->where('subject_uuid', $store->uuid);
85
+
86
+ if ($limit) {
87
+ $query->limit($limit);
88
+ }
89
+
90
+ if ($offset) {
91
+ $query->limit($offset);
92
+ }
93
+
94
+ if ($sort) {
95
+ switch ($sort) {
96
+ case 'highest':
97
+ case 'highest rated':
98
+ $query->orderByDesc('rating');
99
+ break;
100
+
101
+ case 'lowest':
102
+ case 'lowest rated':
103
+ $query->orderBy('rating');
104
+ break;
105
+
106
+ case 'newest':
107
+ case 'newest first':
108
+ $query->orderByDesc('created_at');
109
+ break;
110
+
111
+ case 'oldest':
112
+ case 'oldest first':
113
+ $query->orderBy('created_at');
114
+ break;
115
+ }
116
+ }
117
+ });
118
+ }
119
+ }
120
+
121
+ return StorefrontReview::collection($results);
122
+ }
123
+
124
+ /**
125
+ * Coutns the number of ratings between 1-5 for a store.
126
+ *
127
+ * @param \Illuminate\Http\Request $request
128
+ * @return \Illuminate\Http\Response
129
+ */
130
+ public function count(Request $request)
131
+ {
132
+ $counts = [];
133
+ $range = range(1, 5);
134
+
135
+ if (session('storefront_store')) {
136
+ foreach ($range as $rating) {
137
+ $counts[$rating] = Review::where(['subject_uuid' => session('storefront_store'), 'rating' => $rating])->count();
138
+ }
139
+ }
140
+
141
+ if (session('storefront_network')) {
142
+ if ($request->filled('store')) {
143
+ $store = Store::where([
144
+ 'company_uuid' => session('company'),
145
+ 'public_id' => $request->input('store')
146
+ ])->whereHas('networks', function ($q) {
147
+ $q->where('network_uuid', session('storefront_network'));
148
+ })->first();
149
+
150
+ if (!$store) {
151
+ return response()->json(['error' => 'Cannot count reviews for store'], 400);
152
+ }
153
+
154
+ foreach ($range as $rating) {
155
+ $counts[$rating] = Review::where(['subject_uuid' => $store->uuid, 'rating' => $rating])->count();
156
+ }
157
+ }
158
+ }
159
+
160
+ return response()->json($counts);
161
+ }
162
+
163
+ /**
164
+ * Finds a single Storefront Review resources.
165
+ *
166
+ * @param string $id
167
+ * @return \Fleetbase\Http\Response
168
+ */
169
+ public function find($id)
170
+ {
171
+ // find for the review
172
+ try {
173
+ $review = Review::findRecordOrFail($id);
174
+ } catch (ModelNotFoundException $exception) {
175
+ return response()->error('Review resource not found.');
176
+ }
177
+
178
+ // response the review resource
179
+ return new StorefrontReview($review);
180
+ }
181
+
182
+ /**
183
+ * Create a review.
184
+ *
185
+ * @param \Fleetbase\Storefront\Http\Requests\CreateReviewRequest $request
186
+ * @return \Fleetbase\Http\Response
187
+ */
188
+ public function create(CreateReviewRequest $request)
189
+ {
190
+ $customer = Storefront::getCustomerFromToken();
191
+ $about = Storefront::about();
192
+
193
+ if (!$customer) {
194
+ return response()->error('Not authorized to create reviews');
195
+ }
196
+
197
+ $subject = Utils::resolveSubject($request->input('subject'));
198
+
199
+ if (!$subject) {
200
+ return response()->error('Invalid subject for review');
201
+ }
202
+
203
+ $review = Review::create([
204
+ 'created_by_uuid' => $customer->user_uuid,
205
+ 'customer_uuid' => $customer->uuid,
206
+ 'subject_uuid' => $subject->uuid,
207
+ 'subject_type' => Utils::getMutationType($subject),
208
+ 'rating' => $request->input('rating'),
209
+ 'content' => $request->input('content')
210
+ ]);
211
+
212
+ // if files provided
213
+ if ($request->filled('files')) {
214
+ $files = $request->input('files');
215
+
216
+ foreach ($files as $upload) {
217
+
218
+ $data = Utils::get($upload, 'data');
219
+ $mimeType = Utils::get($upload, 'type');
220
+ $extension = File::getExtensionFromMimeType($mimeType);
221
+ $bucketPath = 'hyperstore/' . $about->public_id . '/review-photos/' . $review->uuid . '/' . File::randomFileName($extension);
222
+
223
+ // upload file to path
224
+ $upload = Storage::disk('s3')->put($bucketPath, base64_decode($data), 'public');
225
+
226
+ // create the file
227
+ $file = File::create([
228
+ 'company_uuid' => session('company'),
229
+ 'uploader_uuid' => $customer->user_uuid,
230
+ 'subject_uuid' => $review->uuid,
231
+ 'subject_type' => Utils::getMutationType($review),
232
+ 'name' => basename($bucketPath),
233
+ 'original_filename' => basename($bucketPath),
234
+ 'extension' => $extension,
235
+ 'content_type' => $mimeType,
236
+ 'path' => $bucketPath,
237
+ 'bucket' => config('filesystems.disks.s3.bucket'),
238
+ 'type' => 'storefront_review_upload',
239
+ 'size' => Utils::getBase64ImageSize($data)
240
+ ]);
241
+
242
+ $review->files->push($file);
243
+ }
244
+ }
245
+
246
+ return new StorefrontReview($review);
247
+ }
248
+
249
+ /**
250
+ * Deletes a Storefront Review resources.
251
+ *
252
+ * @param \Illuminate\Http\Request $request
253
+ * @return \Fleetbase\Http\Resources\v1\DeletedResource
254
+ */
255
+ public function delete($id)
256
+ {
257
+ // find for the product
258
+ try {
259
+ $review = Review::findRecordOrFail($id);
260
+ } catch (ModelNotFoundException $exception) {
261
+ return response()->error('Review resource not found.');
262
+ }
263
+
264
+ // delete the review
265
+ $review->delete();
266
+
267
+ // response the review resource
268
+ return new DeletedResource($review);
269
+ }
270
+ }