@@ -645,6 +645,107 @@ public function update(array|object $data = [])
645645 });
646646 }
647647
648+ /**
649+ * Met à jour plusieurs enregistrements en une seule requête
650+ *
651+ * @param list<array|object> $data Tableau de données à mettre à jour, où chaque élément est un tableau associatif
652+ * @param array|Expression|string $constraints Colonne utilisée pour identifier les enregistrements à mettre à jour (par défaut 'id')
653+ * @param int $chunkSize Taille des lots pour le traitement
654+ *
655+ * @return int|string Nombre de lignes affectées ou la requête SQL en mode test
656+ *
657+ * @throws BadMethodCallException
658+ * @throws InvalidArgumentException
659+ *
660+ * @example
661+ * // Mise à jour simple
662+ * $builder->bulkUpdate([
663+ * ['id' => 1, 'name' => 'John', 'email' => 'john@example.com'],
664+ * ['id' => 2, 'name' => 'Jane', 'email' => 'jane@example.com'],
665+ * ]);
666+ *
667+ * // Mise à jour avec colonne personnalisée
668+ * $builder->bulkUpdate([
669+ * ['code' => 'ABC', 'price' => 100],
670+ * ['code' => 'DEF', 'price' => 150],
671+ * ], 'code');
672+ */
673+ public function bulkUpdate (array $ data , string $ column = 'id ' , int $ chunkSize = 100 ): int |string
674+ {
675+ if ($ data === []) {
676+ return 0 ;
677+ }
678+
679+ // Vérifier la structure des données
680+ $ firstRow = reset ($ data );
681+ if (!is_array ($ firstRow )) {
682+ throw new InvalidArgumentException ('Each row must be an associative array. ' );
683+ }
684+
685+ // Vérifier que la colonne d'identification existe dans chaque ligne
686+ foreach ($ data as $ index => $ row ) {
687+ if (!array_key_exists ($ column , $ row )) {
688+ throw new InvalidArgumentException (
689+ "Column ' {$ column }' not found in row at index {$ index }. Each row must contain the identifier column. "
690+ );
691+ }
692+ }
693+
694+ // Extraire les colonnes à mettre à jour (toutes sauf la colonne d'identification)
695+ $ updateColumns = array_diff (array_keys ($ firstRow ), [$ column ]);
696+
697+ if (empty ($ updateColumns )) {
698+ return 0 ; // Rien à mettre à jour
699+ }
700+
701+ // Traitement par lots
702+ $ totalAffected = 0 ;
703+ $ chunks = array_chunk ($ data , $ chunkSize );
704+ $ allSql = [];
705+
706+ $ callback = function () use ($ chunks , $ column , $ updateColumns , &$ totalAffected , &$ allSql ) {
707+ foreach ($ chunks as $ chunk ) {
708+ $ sql = $ this ->compiler ->compileBulkUpdate ($ this , $ chunk , $ column , $ updateColumns );
709+
710+ if ($ this ->testMode ) {
711+ $ allSql [] = $ sql ;
712+ } else {
713+ $ bindings = $ this ->buildBulkUpdateBindings ($ chunk , $ column , $ updateColumns );
714+ $ totalAffected += $ this ->db ->affectingStatement ($ sql , $ bindings );
715+ }
716+ }
717+
718+ return [$ allSql , $ totalAffected ];
719+ };
720+
721+ [$ allSql , $ totalAffected ] = $ this ->db ->transaction ($ callback );
722+
723+ return $ this ->testMode ? implode ('; ' , $ allSql ) : $ totalAffected ;
724+ }
725+
726+ /**
727+ * Construit les bindings pour une requête bulk update
728+ */
729+ protected function buildBulkUpdateBindings (array $ chunk , string $ column , array $ updateColumns ): array
730+ {
731+ $ bindings = [];
732+
733+ foreach ($ updateColumns as $ updateColumn ) {
734+ foreach ($ chunk as $ row ) {
735+ $ value = $ row [$ updateColumn ];
736+ if (!($ value instanceof Expression) && $ value !== null ) {
737+ $ bindings [] = $ value ;
738+ }
739+ $ bindings [] = $ row [$ column ];
740+ }
741+ }
742+
743+ $ ids = array_column ($ chunk , $ column );
744+ $ bindings = array_merge ($ bindings , $ ids );
745+
746+ return $ bindings ;
747+ }
748+
648749 /**
649750 * Exécute une requête de remplacement.
650751 *
0 commit comments