@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.
- package/.php-cs-fixer.php +29 -0
- package/LICENSE.md +16 -4
- package/README.md +106 -13
- package/composer.json +88 -0
- package/extension.json +10 -0
- package/package.json +6 -6
- package/phpstan.neon.dist +8 -0
- package/phpunit.xml.dist +16 -0
- package/server/.gitattributes +14 -0
- package/server/README.md +40 -0
- package/server/config/api.php +101 -0
- package/server/config/database.connections.php +57 -0
- package/server/config/storefront.php +19 -0
- package/server/config/twilio-notification-channel.php +36 -0
- package/server/migrations/2023_05_03_025307_create_carts_table.php +44 -0
- package/server/migrations/2023_05_03_025307_create_checkouts_table.php +51 -0
- package/server/migrations/2023_05_03_025307_create_gateways_table.php +48 -0
- package/server/migrations/2023_05_03_025307_create_network_stores_table.php +36 -0
- package/server/migrations/2023_05_03_025307_create_networks_table.php +56 -0
- package/server/migrations/2023_05_03_025307_create_notification_channels_table.php +43 -0
- package/server/migrations/2023_05_03_025307_create_payment_methods_table.php +44 -0
- package/server/migrations/2023_05_03_025307_create_product_addon_categories_table.php +38 -0
- package/server/migrations/2023_05_03_025307_create_product_addons_table.php +43 -0
- package/server/migrations/2023_05_03_025307_create_product_hours_table.php +37 -0
- package/server/migrations/2023_05_03_025307_create_product_store_locations_table.php +34 -0
- package/server/migrations/2023_05_03_025307_create_product_variant_options_table.php +40 -0
- package/server/migrations/2023_05_03_025307_create_product_variants_table.php +44 -0
- package/server/migrations/2023_05_03_025307_create_products_table.php +59 -0
- package/server/migrations/2023_05_03_025307_create_reviews_table.php +41 -0
- package/server/migrations/2023_05_03_025307_create_store_hours_table.php +37 -0
- package/server/migrations/2023_05_03_025307_create_store_locations_table.php +38 -0
- package/server/migrations/2023_05_03_025307_create_stores_table.php +57 -0
- package/server/migrations/2023_05_03_025307_create_votes_table.php +39 -0
- package/server/migrations/2023_05_03_025310_add_foreign_keys_to_carts_table.php +40 -0
- package/server/migrations/2023_05_03_025310_add_foreign_keys_to_checkouts_table.php +48 -0
- package/server/migrations/2023_05_03_025310_add_foreign_keys_to_gateways_table.php +40 -0
- package/server/migrations/2023_05_03_025310_add_foreign_keys_to_network_stores_table.php +40 -0
- package/server/migrations/2023_05_03_025310_add_foreign_keys_to_networks_table.php +42 -0
- package/server/migrations/2023_05_03_025310_add_foreign_keys_to_notification_channels_table.php +40 -0
- package/server/migrations/2023_05_03_025310_add_foreign_keys_to_payment_methods_table.php +40 -0
- package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_addon_categories_table.php +38 -0
- package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_addons_table.php +38 -0
- package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_hours_table.php +32 -0
- package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_store_locations_table.php +34 -0
- package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_variant_options_table.php +32 -0
- package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_variants_table.php +32 -0
- package/server/migrations/2023_05_03_025310_add_foreign_keys_to_products_table.php +44 -0
- package/server/migrations/2023_05_03_025310_add_foreign_keys_to_reviews_table.php +38 -0
- package/server/migrations/2023_05_03_025310_add_foreign_keys_to_store_hours_table.php +32 -0
- package/server/migrations/2023_05_03_025310_add_foreign_keys_to_store_locations_table.php +40 -0
- package/server/migrations/2023_05_03_025310_add_foreign_keys_to_stores_table.php +42 -0
- package/server/migrations/2023_05_03_025310_add_foreign_keys_to_votes_table.php +38 -0
- package/server/src/Auth/Schemas/Storefront.php +95 -0
- package/server/src/Console/Commands/NotifyStorefrontOrderNearby.php +86 -0
- package/server/src/Expansions/EntityExpansion.php +44 -0
- package/server/src/Http/Controllers/ActionController.php +119 -0
- package/server/src/Http/Controllers/AddonCategoryController.php +13 -0
- package/server/src/Http/Controllers/CustomerController.php +13 -0
- package/server/src/Http/Controllers/GatewayController.php +13 -0
- package/server/src/Http/Controllers/MetricsController.php +71 -0
- package/server/src/Http/Controllers/NetworkController.php +170 -0
- package/server/src/Http/Controllers/NotificationChannelController.php +13 -0
- package/server/src/Http/Controllers/OrderController.php +154 -0
- package/server/src/Http/Controllers/ProductAddonCategoryController.php +14 -0
- package/server/src/Http/Controllers/ProductAddonController.php +14 -0
- package/server/src/Http/Controllers/ProductController.php +123 -0
- package/server/src/Http/Controllers/ProductHourController.php +13 -0
- package/server/src/Http/Controllers/ProductVariantController.php +13 -0
- package/server/src/Http/Controllers/ProductVariantOptionController.php +13 -0
- package/server/src/Http/Controllers/ReviewController.php +13 -0
- package/server/src/Http/Controllers/StoreController.php +26 -0
- package/server/src/Http/Controllers/StoreHourController.php +13 -0
- package/server/src/Http/Controllers/StoreLocationController.php +13 -0
- package/server/src/Http/Controllers/StorefrontController.php +15 -0
- package/server/src/Http/Controllers/VoteController.php +13 -0
- package/server/src/Http/Controllers/v1/CartController.php +161 -0
- package/server/src/Http/Controllers/v1/CategoryController.php +138 -0
- package/server/src/Http/Controllers/v1/CheckoutController.php +957 -0
- package/server/src/Http/Controllers/v1/CustomerController.php +482 -0
- package/server/src/Http/Controllers/v1/GatewayControllerController.php +11 -0
- package/server/src/Http/Controllers/v1/NetworkController.php +281 -0
- package/server/src/Http/Controllers/v1/PaymentMethodController.php +11 -0
- package/server/src/Http/Controllers/v1/ProductController.php +94 -0
- package/server/src/Http/Controllers/v1/ReviewController.php +270 -0
- package/server/src/Http/Controllers/v1/ServiceQuoteController.php +402 -0
- package/server/src/Http/Controllers/v1/StoreController.php +176 -0
- package/server/src/Http/Filter/AddonCategoryFilter.php +19 -0
- package/server/src/Http/Filter/CustomerFilter.php +18 -0
- package/server/src/Http/Filter/GatewayFilter.php +13 -0
- package/server/src/Http/Filter/NetworkFilter.php +18 -0
- package/server/src/Http/Filter/NotificationChannelFilter.php +13 -0
- package/server/src/Http/Filter/OrderFilter.php +46 -0
- package/server/src/Http/Filter/ProductFilter.php +28 -0
- package/server/src/Http/Filter/StoreFilter.php +42 -0
- package/server/src/Http/Filter/StoreLocationFilter.php +23 -0
- package/server/src/Http/Middleware/SetStorefrontSession.php +130 -0
- package/server/src/Http/Requests/AddStoreToNetworkCategory.php +45 -0
- package/server/src/Http/Requests/CaptureOrderRequest.php +30 -0
- package/server/src/Http/Requests/CreateCustomerRequest.php +44 -0
- package/server/src/Http/Requests/CreateReviewRequest.php +34 -0
- package/server/src/Http/Requests/GetServiceQuoteFromCart.php +40 -0
- package/server/src/Http/Requests/InitializeCheckoutRequest.php +38 -0
- package/server/src/Http/Requests/NetworkActionRequest.php +43 -0
- package/server/src/Http/Requests/VerifyCreateCustomerRequest.php +31 -0
- package/server/src/Http/Resources/Cart.php +31 -0
- package/server/src/Http/Resources/Category.php +48 -0
- package/server/src/Http/Resources/Customer.php +36 -0
- package/server/src/Http/Resources/Gateway.php +32 -0
- package/server/src/Http/Resources/Media.php +29 -0
- package/server/src/Http/Resources/Network.php +48 -0
- package/server/src/Http/Resources/Product.php +209 -0
- package/server/src/Http/Resources/Review.php +45 -0
- package/server/src/Http/Resources/ReviewCustomer.php +60 -0
- package/server/src/Http/Resources/Store.php +76 -0
- package/server/src/Http/Resources/StoreHour.php +29 -0
- package/server/src/Http/Resources/StoreLocation.php +33 -0
- package/server/src/Imports/ProductsImport.php +20 -0
- package/server/src/Jobs/DownloadProductImageUrl.php +60 -0
- package/server/src/Listeners/HandleOrderCompleted.php +31 -0
- package/server/src/Listeners/HandleOrderDispatched.php +34 -0
- package/server/src/Listeners/HandleOrderDriverAssigned.php +37 -0
- package/server/src/Listeners/HandleOrderStarted.php +27 -0
- package/server/src/Mail/StorefrontNetworkInvite.php +48 -0
- package/server/src/Models/AddonCategory.php +30 -0
- package/server/src/Models/Cart.php +691 -0
- package/server/src/Models/Checkout.php +166 -0
- package/server/src/Models/Customer.php +88 -0
- package/server/src/Models/Gateway.php +165 -0
- package/server/src/Models/Network.php +300 -0
- package/server/src/Models/NetworkStore.php +86 -0
- package/server/src/Models/NotificationChannel.php +147 -0
- package/server/src/Models/PaymentMethod.php +99 -0
- package/server/src/Models/Product.php +315 -0
- package/server/src/Models/ProductAddon.php +128 -0
- package/server/src/Models/ProductAddonCategory.php +90 -0
- package/server/src/Models/ProductHour.php +59 -0
- package/server/src/Models/ProductStoreLocation.php +77 -0
- package/server/src/Models/ProductVariant.php +125 -0
- package/server/src/Models/ProductVariantOption.php +86 -0
- package/server/src/Models/Review.php +127 -0
- package/server/src/Models/Store.php +478 -0
- package/server/src/Models/StoreHour.php +59 -0
- package/server/src/Models/StoreLocation.php +126 -0
- package/server/src/Models/StorefrontModel.php +22 -0
- package/server/src/Models/Vote.php +84 -0
- package/server/src/Notifications/StorefrontOrderCanceled.php +196 -0
- package/server/src/Notifications/StorefrontOrderCompleted.php +201 -0
- package/server/src/Notifications/StorefrontOrderCreated.php +157 -0
- package/server/src/Notifications/StorefrontOrderDriverAssigned.php +200 -0
- package/server/src/Notifications/StorefrontOrderEnroute.php +199 -0
- package/server/src/Notifications/StorefrontOrderNearby.php +201 -0
- package/server/src/Notifications/StorefrontOrderPreparing.php +202 -0
- package/server/src/Notifications/StorefrontOrderReadyForPickup.php +202 -0
- package/server/src/Observers/NetworkObserver.php +40 -0
- package/server/src/Observers/ProductObserver.php +118 -0
- package/server/src/Providers/EventServiceProvider.php +23 -0
- package/server/src/Providers/StorefrontServiceProvider.php +103 -0
- package/server/src/Support/Metrics.php +193 -0
- package/server/src/Support/OrderConfig.php +13 -0
- package/server/src/Support/QPay.php +208 -0
- package/server/src/Support/Storefront.php +201 -0
- package/server/src/routes.php +180 -0
- 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,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
|
+
}
|