Skip to content

Commit a89e4ad

Browse files
committed
Implement seller update functionality in CurrentSellerController
This commit introduces the updateSeller method, allowing sellers to update their personal and shop information. It includes validation for provided fields, file uploads for identity documents and shop profile, and handles the synchronization of shop categories and images. Additionally, it enhances error handling and logging for better traceability during the update process.
1 parent 27d8203 commit a89e4ad

File tree

3 files changed

+203
-1
lines changed

3 files changed

+203
-1
lines changed

app/Http/Controllers/Seller/CurrentSellerController.php

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,19 @@
22

33
namespace App\Http\Controllers\Seller;
44

5+
use App\Models\Shop;
6+
use App\Models\Town;
57
use App\Models\User;
8+
use App\Models\Image;
9+
use App\Models\Quarter;
610
use Illuminate\Http\Request;
11+
use Illuminate\Support\Facades\DB;
12+
use Illuminate\Support\Facades\Log;
713
use App\Http\Controllers\Controller;
814
use Illuminate\Support\Facades\Auth;
915
use App\Http\Resources\SellerResource;
16+
use Illuminate\Validation\Rule;
17+
use Illuminate\Support\Facades\Storage;
1018

1119
class CurrentSellerController extends Controller
1220
{
@@ -15,4 +23,197 @@ public function currentSeller(){
1523
$user=Auth::guard('api')->user();
1624
return SellerResource::make(User::find($user->id));
1725
}
26+
27+
public function updateSeller(Request $request){
28+
$user=Auth::guard('api')->user();
29+
$seller = User::query()->with(['shops', 'shops.images', 'shops.categories'])->findOrFail($user->id);
30+
$shop = $seller->shops->first();
31+
32+
33+
$notEmpty = fn($v) => !is_null($v) && (!is_string($v) || trim($v) !== ''); // garde 0/'0'
34+
$onlyProvided = function(\Illuminate\Http\Request $r, array $keys) use ($notEmpty) {
35+
return array_filter($r->only($keys), $notEmpty);
36+
};
37+
38+
39+
40+
41+
try {
42+
// 1) Mise à jour vendeur
43+
$seller->fill([
44+
'firstName' => $request->input('firstName', $seller->firstName),
45+
'lastName' => $request->input('lastName', $seller->lastName),
46+
'email' => $request->input('email', $seller->email),
47+
'phone_number' => $request->input('phone_number', $seller->phone_number),
48+
'birthDate' => $request->input('birthDate', $seller->birthDate),
49+
'nationality' => $request->input('nationality', $seller->nationality),
50+
]);
51+
52+
DB::transaction(function() use ($request, $seller, $onlyProvided, $notEmpty) {
53+
// USER: update partiel sans null
54+
$userData = $onlyProvided($request, [
55+
'firstName','lastName','email','phone_number','birthDate','nationality','isWholesaler'
56+
]);
57+
if (!empty($userData)) {
58+
// caster isWholesaler si présent
59+
if (array_key_exists('isWholesaler', $userData)) {
60+
$userData['isWholesaler'] = (string)$userData['isWholesaler'];
61+
}
62+
$seller->update($userData);
63+
}
64+
65+
// FICHIERS USER
66+
if ($request->hasFile('identity_card_in_front')) {
67+
$seller->identity_card_in_front = $request->file('identity_card_in_front')->store('cni/front','public');
68+
}
69+
if ($request->hasFile('identity_card_in_back')) {
70+
$seller->identity_card_in_back = $request->file('identity_card_in_back')->store('cni/back','public');
71+
}
72+
if ($request->hasFile('identity_card_with_the_person')) {
73+
$seller->identity_card_with_the_person = $request->file('identity_card_with_the_person')->store('cni/person','public');
74+
}
75+
$seller->save();
76+
77+
// SHOP
78+
$shop = $seller->shop ?: new Shop(['user_id' => $seller->id]);
79+
80+
$shopData = $onlyProvided($request, [
81+
'shop_name','shop_description','product_type','town_id','quarter_id'
82+
]);
83+
84+
if (array_key_exists('product_type', $shopData)) {
85+
$shopData['product_type'] = (string)$shopData['product_type'];
86+
}
87+
88+
89+
if (!empty($shopData)) {
90+
$shop->fill($shopData); // fillable requis dans Shop
91+
}
92+
93+
// FICHIER SHOP PROFILE
94+
if ($request->hasFile('shop_profile')) {
95+
$shop->shop_profile = $request->file('shop_profile')->store('shop/profile','public');
96+
}
97+
98+
// Localisation par nom (optionnel, uniquement si fourni et non vide)
99+
if ($request->filled('town')) {
100+
$town = \App\Models\Town::where('town_name', $request->input('town'))->first();
101+
if ($town) $shop->town_id = $town->id;
102+
}
103+
if ($request->filled('quarter')) {
104+
$quarter = \App\Models\Quarter::where('quarter_name', $request->input('quarter'))->first();
105+
if ($quarter) $shop->quarter_id = $quarter->id;
106+
}
107+
108+
$shop->save();
109+
110+
// Catégories: sync seulement si fourni
111+
if ($request->has('categories') && is_array($request->input('categories'))) {
112+
$ids = collect($request->input('categories'))
113+
->map(fn($it) => is_array($it) ? ($it['id'] ?? $it['value'] ?? null) : $it)
114+
->filter()->unique()->values()->all();
115+
$shop->categories()->sync($ids);
116+
}
117+
118+
// Images: remplacer seulement si fichiers fournis
119+
if ($request->hasFile('images')) {
120+
$files = $request->file('images');
121+
if (is_array($files) && count($files) > 0) {
122+
$shop->images()->detach();
123+
$attach = [];
124+
foreach ($files as $f) {
125+
$img = new \App\Models\Image();
126+
$img->image_path = $f->store('shop/images','public');
127+
$img->save();
128+
$attach[] = $img->id;
129+
}
130+
if ($attach) $shop->images()->attach($attach);
131+
}
132+
}
133+
});
134+
135+
return response()->json([
136+
'success' => true,
137+
'message' => 'Shop updated successfully',
138+
], 200);
139+
140+
} catch (\Throwable $e) {
141+
DB::rollBack();
142+
Log::error('Shop update error', ['error' => $e->getMessage(), 'trace' => $e->getTraceAsString()]);
143+
return response()->json([
144+
'success' => false,
145+
'message' => 'Something went wrong',
146+
'errors' => $e->getMessage(),
147+
], 500);
148+
}
149+
}
150+
151+
152+
protected function storeMaybeBase64OrFile(Request $request, string $field, ?string $existingPath, string $diskPath): ?string
153+
{
154+
// 1) fichier multipart
155+
if ($request->hasFile($field)) {
156+
$file = $request->file($field);
157+
return $file->store($diskPath, 'public');
158+
}
159+
160+
// 2) base64 data URL string
161+
if ($request->filled($field) && is_string($request->input($field))) {
162+
$data = $request->input($field);
163+
if ($this->isDataUrl($data)) {
164+
return $this->storeBase64DataUrl($data, $diskPath);
165+
}
166+
// Chaîne vide => suppression
167+
if ($data === '') {
168+
return null;
169+
}
170+
}
171+
172+
// sinon, on garde l’existant
173+
return $existingPath;
174+
}
175+
176+
/**
177+
* Variante générique pour un item qui peut être un UploadedFile (dans $request->images[])
178+
* ou une base64 string passée directement dans le tableau (cas JSON).
179+
*/
180+
protected function storeMaybeBase64OrFileGeneric($value, string $diskPath): ?string
181+
{
182+
// UploadedFile
183+
if (is_object($value) && method_exists($value, 'store')) {
184+
return $value->store($diskPath, 'public');
185+
}
186+
// base64 string
187+
if (is_string($value)) {
188+
if ($this->isDataUrl($value)) {
189+
return $this->storeBase64DataUrl($value, $diskPath);
190+
}
191+
// sinon on ignore (non base64)
192+
}
193+
// objet {path: 'dataurl'} venant du front
194+
if (is_array($value) && isset($value['path']) && is_string($value['path']) && $this->isDataUrl($value['path'])) {
195+
return $this->storeBase64DataUrl($value['path'], $diskPath);
196+
}
197+
return null;
198+
}
199+
200+
protected function isDataUrl(string $value): bool
201+
{
202+
return str_starts_with($value, 'data:image/');
203+
}
204+
205+
protected function storeBase64DataUrl(string $dataUrl, string $diskPath): string
206+
{
207+
// data:image/png;base64,XXXX
208+
[$meta, $content] = explode(',', $dataUrl, 2);
209+
$extension = 'png';
210+
if (preg_match('/data:image\\/(\\w+);base64/i', $meta, $m)) {
211+
$extension = strtolower($m[1]);
212+
}
213+
$binary = base64_decode($content);
214+
$filename = $diskPath.'/'.uniqid('img_').'.'.$extension;
215+
Storage::disk('public')->put($filename, $binary);
216+
return $filename;
217+
}
218+
18219
}

app/Http/Resources/ShopResource.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public function toArray(Request $request): array
3535
"products"=>ProductResource::collection($this->products),
3636
"expire"=>$this->expire,
3737
"subscribe_id"=>$this->subscribe_id,
38+
"gender"=>$this->shop_gender,
3839
"town"=>$this->town->town_name,
3940
"quarter"=>$this->quarter->quarter_name,
4041
"isPublished"=>$this->isPublished,

routes/api.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@
201201

202202
Route::middleware(['auth:api', 'scopes:seller'])->prefix('v1')->group(function () {
203203
Route::get('/current/seller', [CurrentSellerController::class, 'currentSeller']);
204-
204+
Route::post('update/seller',[CurrentSellerController::class,'updateSeller']);
205205
Route::get('/seller/notifications',[ListNotificationController::class,'list']);
206206
Route::get("/seller/recents/notifications",[ListNotificationController::class,'recentNotification']);
207207
Route::get("/seller/get/notification/{id}",[ListNotificationController::class,'getNotification']);

0 commit comments

Comments
 (0)