1+ <?php
2+
3+ namespace App \Console \Commands ;
4+
5+ use App \Models \Product ;
6+ use Illuminate \Console \Command ;
7+ use Illuminate \Support \Facades \Log ;
8+
9+ class IndexSingleProductCommand extends Command
10+ {
11+ /**
12+ * The name and signature of the console command.
13+ *
14+ * @var string
15+ */
16+ protected $ signature = 'scout:index-single-product {product_id?} {--all} {--force} {--published-only} {--chunk=100} {--remove-unpublished} ' ;
17+
18+ /**
19+ * The console command description.
20+ *
21+ * @var string
22+ */
23+ protected $ description = 'Indexe un seul produit ou tous les produits dans Elasticsearch ' ;
24+
25+ /**
26+ * Execute the console command.
27+ *
28+ * @return int
29+ */
30+ public function handle ()
31+ {
32+ $ all = $ this ->option ('all ' );
33+ $ force = $ this ->option ('force ' );
34+ $ publishedOnly = $ this ->option ('published-only ' );
35+ $ removeUnpublished = $ this ->option ('remove-unpublished ' );
36+ $ chunkSize = (int ) $ this ->option ('chunk ' );
37+
38+ if ($ all ) {
39+ return $ this ->indexAllProducts ($ force , $ publishedOnly , $ removeUnpublished , $ chunkSize );
40+ } else {
41+ $ productId = $ this ->argument ('product_id ' );
42+ if (!$ productId ) {
43+ $ this ->error ("❌ ID du produit requis ou utilisez --all pour indexer tous les produits " );
44+ return 1 ;
45+ }
46+ return $ this ->indexSingleProduct ((int ) $ productId , $ force , $ removeUnpublished );
47+ }
48+ }
49+
50+ /**
51+ * Indexe un seul produit
52+ */
53+ private function indexSingleProduct (int $ productId , bool $ force , bool $ removeUnpublished ): int
54+ {
55+ $ this ->info ("=== Indexation du produit ID: $ productId === " );
56+
57+ // 1. Trouver le produit
58+ $ product = Product::with ([
59+ 'authors ' , 'productType ' , 'images ' , 'style ' ,
60+ 'materials ' , 'productionOrigin ' , 'entryMode ' , 'period '
61+ ])->find ($ productId );
62+
63+ if (!$ product ) {
64+ $ this ->error ("❌ Produit non trouvé avec l'ID: $ productId " );
65+ return 1 ;
66+ }
67+
68+ $ this ->info ("✅ Produit trouvé: " );
69+ $ this ->info (" - Inventory ID: {$ product ->inventory_id }" );
70+ $ this ->info (" - Titre: {$ product ->title_or_designation }" );
71+ $ this ->info (" - Publié: " . ($ product ->is_published ? 'Oui ' : 'Non ' ));
72+
73+ // 2. Gérer les produits non publiés
74+ if (!$ product ->is_published ) {
75+
76+ if ($ removeUnpublished ) {
77+ // Supprimer le produit de l'index Elasticsearch
78+ $ this ->info ("🗑️ Suppression du produit non publié de l'index... " );
79+
80+ try {
81+ $ product ->unsearchable ();
82+ $ this ->info ("✅ Produit supprimé de l'index Elasticsearch avec succès! " );
83+
84+ // Log de la suppression
85+ Log::info ("Produit {$ product ->inventory_id } (ID: $ productId) supprimé de l'index Elasticsearch car non publié " );
86+
87+ } catch (\Exception $ e ) {
88+ $ this ->error ("❌ Erreur lors de la suppression: " . $ e ->getMessage ());
89+ Log::error ("Erreur de suppression du produit $ productId " , [
90+ 'product_id ' => $ productId ,
91+ 'inventory_id ' => $ product ->inventory_id ,
92+ 'error ' => $ e ->getMessage (),
93+ 'trace ' => $ e ->getTraceAsString ()
94+ ]);
95+ return 1 ;
96+ }
97+
98+ $ this ->info ("=== Suppression terminée === " );
99+ return 0 ;
100+ } elseif (!$ force ) {
101+ $ this ->warn ("⚠️ Le produit n'est pas publié (is_published = false) " );
102+ $ this ->warn (" Utilisez --force pour forcer l'indexation " );
103+ $ this ->warn (" Utilisez --remove-unpublished pour supprimer de l'index " );
104+ return 1 ;
105+ } else {
106+ $ this ->warn ("⚠️ Indexation forcée d'un produit non publié " );
107+ }
108+ }
109+
110+ // 3. Indexer le produit
111+ $ this ->info ("🔄 Indexation en cours... " );
112+
113+ try {
114+ $ product ->searchable ();
115+ $ this ->info ("✅ Indexation réussie! " );
116+
117+ // Log du succès
118+ Log::info ("Produit {$ product ->inventory_id } (ID: $ productId) indexé avec succès dans Elasticsearch " );
119+
120+ } catch (\Exception $ e ) {
121+ $ this ->error ("❌ Erreur lors de l'indexation: " . $ e ->getMessage ());
122+ Log::error ("Erreur d'indexation du produit $ productId " , [
123+ 'product_id ' => $ productId ,
124+ 'inventory_id ' => $ product ->inventory_id ,
125+ 'error ' => $ e ->getMessage (),
126+ 'trace ' => $ e ->getTraceAsString ()
127+ ]);
128+ return 1 ;
129+ }
130+
131+ $ this ->info ("=== Indexation terminée === " );
132+ return 0 ;
133+ }
134+
135+ /**
136+ * Indexe tous les produits un par un
137+ */
138+ private function indexAllProducts (bool $ force , bool $ publishedOnly , bool $ removeUnpublished , int $ chunkSize ): int
139+ {
140+ $ this ->info ("=== Indexation de tous les produits === " );
141+ $ this ->info ("Force: " . ($ force ? 'Oui ' : 'Non ' ));
142+ $ this ->info ("Publiés seulement: " . ($ publishedOnly ? 'Oui ' : 'Non ' ));
143+ $ this ->info ("Supprimer non publiés: " . ($ removeUnpublished ? 'Oui ' : 'Non ' ));
144+ $ this ->info ("Taille des lots: $ chunkSize " );
145+
146+ // Construire la requête
147+ $ query = Product::with ([
148+ 'authors ' , 'productType ' , 'images ' , 'style ' ,
149+ 'materials ' , 'productionOrigin ' , 'entryMode ' , 'period '
150+ ]);
151+ //$query = Product::query();
152+
153+ if ($ publishedOnly && !$ force ) {
154+ $ query ->where ('is_published ' , true );
155+ $ this ->info ("Filtrage: produits publiés seulement " );
156+ }
157+
158+ $ totalProducts = $ query ->count ();
159+ $ this ->info ("Total des produits à traiter: $ totalProducts " );
160+
161+ if ($ totalProducts === 0 ) {
162+ $ this ->warn ("Aucun produit à indexer. " );
163+ return 0 ;
164+ }
165+
166+ $ progressBar = $ this ->output ->createProgressBar ($ totalProducts );
167+ $ progressBar ->start ();
168+
169+ $ successCount = 0 ;
170+ $ errorCount = 0 ;
171+ $ errors = [];
172+
173+ // Traitement par lots
174+ $ query ->orderBy ('id ' )->chunk ($ chunkSize , function ($ products ) use (
175+ $ progressBar ,
176+ $ force ,
177+ $ removeUnpublished ,
178+ &$ successCount ,
179+ &$ errorCount ,
180+ &$ errors
181+ ) {
182+ foreach ($ products as $ product ) {
183+ try {
184+ // Gérer les produits non publiés
185+ if (!$ product ->is_published ) {
186+ if ($ removeUnpublished ) {
187+ // Supprimer le produit de l'index
188+ $ product ->unsearchable ();
189+ $ this ->line ("\n🗑️ Produit {$ product ->inventory_id } (ID: {$ product ->id }) supprimé de l'index (non publié) " );
190+ $ successCount ++;
191+ } elseif (!$ force ) {
192+ $ this ->line ("\n⚠️ Produit {$ product ->inventory_id } (ID: {$ product ->id }) non publié - ignoré " );
193+ } else {
194+ // Indexer le produit même s'il n'est pas publié (force)
195+ $ product ->searchable ();
196+ $ successCount ++;
197+ $ this ->line ("\n✅ Produit {$ product ->inventory_id } (ID: {$ product ->id }) indexé (forcé) " );
198+ }
199+ $ progressBar ->advance ();
200+ continue ;
201+ }
202+
203+ // Indexer le produit publié
204+ $ product ->searchable ();
205+ $ successCount ++;
206+
207+ $ this ->line ("\n✅ Produit {$ product ->inventory_id } (ID: {$ product ->id }) indexé " );
208+
209+ } catch (\Exception $ e ) {
210+ $ errorCount ++;
211+ $ errorMessage = "Erreur lors de l'indexation du produit {$ product ->inventory_id } (ID: {$ product ->id }): " . $ e ->getMessage ();
212+
213+ $ this ->error ("\n❌ " . $ errorMessage );
214+ Log::error ($ errorMessage , [
215+ 'product_id ' => $ product ->id ,
216+ 'inventory_id ' => $ product ->inventory_id ,
217+ 'error ' => $ e ->getMessage ()
218+ ]);
219+
220+ $ errors [] = [
221+ 'id ' => $ product ->id ,
222+ 'inventory_id ' => $ product ->inventory_id ,
223+ 'error ' => $ e ->getMessage ()
224+ ];
225+ }
226+
227+ $ progressBar ->advance ();
228+ }
229+ });
230+
231+ $ progressBar ->finish ();
232+ $ this ->newLine (2 );
233+
234+ // Résumé
235+ $ this ->info ("=== RÉSUMÉ DE L'INDEXATION === " );
236+ $ this ->info ("Produits indexés avec succès: $ successCount " );
237+ $ this ->info ("Produits en erreur: $ errorCount " );
238+ $ this ->info ("Taux de succès: " . round (($ successCount / $ totalProducts ) * 100 , 2 ) . "% " );
239+
240+ if (!empty ($ errors )) {
241+ $ this ->error ("=== ERREURS DÉTAILLÉES === " );
242+ foreach (array_slice ($ errors , 0 , 10 ) as $ error ) { // Limiter à 10 erreurs
243+ $ this ->error ("ID: {$ error ['id ' ]} - {$ error ['inventory_id ' ]}: {$ error ['error ' ]}" );
244+ }
245+ if (count ($ errors ) > 10 ) {
246+ $ this ->error ("... et " . (count ($ errors ) - 10 ) . " autres erreurs " );
247+ }
248+ }
249+
250+ return $ errorCount === 0 ? 0 : 1 ;
251+ }
252+ }
0 commit comments