@@ -153,6 +153,16 @@ class BaseBuilder
153
153
*/
154
154
protected $ QBIgnore = false ;
155
155
156
+ /**
157
+ * QB Options data
158
+ * Holds additional data used to render SQL
159
+ *
160
+ * @phpstan-var array{updateFields?: array, constraints?: array, tableIdentity?: string}
161
+ *
162
+ * @var array
163
+ */
164
+ protected $ QBOptions ;
165
+
156
166
/**
157
167
* A reference to the database connection.
158
168
*
@@ -1720,13 +1730,13 @@ public function getWhere($where = null, ?int $limit = null, ?int $offset = 0, bo
1720
1730
}
1721
1731
1722
1732
/**
1723
- * Compiles batch insert strings and runs the queries
1733
+ * Compiles batch insert/upsert strings and runs the queries
1724
1734
*
1725
1735
* @throws DatabaseException
1726
1736
*
1727
1737
* @return false|int|string[] Number of rows inserted or FALSE on failure, SQL array when testMode
1728
1738
*/
1729
- public function insertBatch ( ?array $ set = null , ?bool $ escape = null , int $ batchSize = 100 )
1739
+ protected function batchExecute ( string $ renderMethod , ?array $ set = null , ?bool $ escape = null , int $ batchSize = 100 )
1730
1740
{
1731
1741
if ($ set === null ) {
1732
1742
if (empty ($ this ->QBSet )) {
@@ -1738,15 +1748,15 @@ public function insertBatch(?array $set = null, ?bool $escape = null, int $batch
1738
1748
}
1739
1749
} elseif (empty ($ set )) {
1740
1750
if ($ this ->db ->DBDebug ) {
1741
- throw new DatabaseException ('insertBatch() called with no data ' );
1751
+ throw new DatabaseException ('insertBatch()/upsertBatch called with no data ' );
1742
1752
}
1743
1753
1744
1754
return false ; // @codeCoverageIgnore
1745
1755
}
1746
1756
1747
1757
$ hasQBSet = $ set === null ;
1748
1758
1749
- $ table = $ this ->QBFrom [0 ];
1759
+ $ table = $ this ->db -> protectIdentifiers ( $ this -> QBFrom [0 ], true , null , false ) ;
1750
1760
1751
1761
$ affectedRows = 0 ;
1752
1762
$ savedSQL = [];
@@ -1759,10 +1769,14 @@ public function insertBatch(?array $set = null, ?bool $escape = null, int $batch
1759
1769
if ($ hasQBSet ) {
1760
1770
$ QBSet = array_slice ($ this ->QBSet , $ i , $ batchSize );
1761
1771
} else {
1762
- $ this ->setInsertBatch (array_slice ($ set , $ i , $ batchSize ), '' , $ escape );
1772
+ $ this ->setBatch (array_slice ($ set , $ i , $ batchSize ), $ escape );
1763
1773
$ QBSet = $ this ->QBSet ;
1764
1774
}
1765
- $ sql = $ this ->_insertBatch ($ this ->db ->protectIdentifiers ($ table , true , null , false ), $ this ->QBKeys , $ QBSet );
1775
+ $ sql = $ this ->{$ renderMethod }($ table , $ this ->QBKeys , $ QBSet );
1776
+
1777
+ if ($ sql === '' ) {
1778
+ return false ; // @codeCoverageIgnore
1779
+ }
1766
1780
1767
1781
if ($ this ->testMode ) {
1768
1782
$ savedSQL [] = $ sql ;
@@ -1785,37 +1799,25 @@ public function insertBatch(?array $set = null, ?bool $escape = null, int $batch
1785
1799
}
1786
1800
1787
1801
/**
1788
- * Generates a platform-specific insert string from the supplied data.
1789
- */
1790
- protected function _insertBatch (string $ table , array $ keys , array $ values ): string
1791
- {
1792
- return 'INSERT ' . $ this ->compileIgnore ('insert ' ) . 'INTO ' . $ table . ' ( ' . implode (', ' , $ keys ) . ') VALUES ' . implode (', ' , $ values );
1793
- }
1794
-
1795
- /**
1796
- * Allows key/value pairs to be set for batch inserts
1802
+ * Allows key/value pairs to be set for batch inserts/upserts
1797
1803
*
1798
- * @param mixed $key
1804
+ * @param array|object $set
1799
1805
*
1800
1806
* @return $this|null
1801
1807
*/
1802
- public function setInsertBatch ( $ key , string $ value = '' , ?bool $ escape = null )
1808
+ public function setBatch ( $ set , ?bool $ escape = null )
1803
1809
{
1804
- $ key = $ this ->batchObjectToArray ($ key );
1805
-
1806
- if (! is_array ($ key )) {
1807
- $ key = [$ key => $ value ];
1808
- }
1810
+ $ set = $ this ->batchObjectToArray ($ set );
1809
1811
1810
1812
$ escape = is_bool ($ escape ) ? $ escape : $ this ->db ->protectIdentifiers ;
1811
1813
1812
- $ keys = array_keys ($ this ->objectToArray (current ($ key )));
1814
+ $ keys = array_keys ($ this ->objectToArray (current ($ set )));
1813
1815
sort ($ keys );
1814
1816
1815
- foreach ($ key as $ row ) {
1817
+ foreach ($ set as $ row ) {
1816
1818
$ row = $ this ->objectToArray ($ row );
1817
1819
if (array_diff ($ keys , array_keys ($ row )) !== [] || array_diff (array_keys ($ row ), $ keys ) !== []) {
1818
- // batch function above returns an error on an empty array
1820
+ // batchExecute() function returns an error on an empty array
1819
1821
$ this ->QBSet [] = [];
1820
1822
1821
1823
return null ;
@@ -1831,7 +1833,7 @@ public function setInsertBatch($key, string $value = '', ?bool $escape = null)
1831
1833
1832
1834
$ row = $ clean ;
1833
1835
1834
- $ this ->QBSet [] = ' ( ' . implode ( ' , ' , $ row) . ' ) ' ;
1836
+ $ this ->QBSet [] = $ row ;
1835
1837
}
1836
1838
1837
1839
foreach ($ keys as $ k ) {
@@ -1841,6 +1843,170 @@ public function setInsertBatch($key, string $value = '', ?bool $escape = null)
1841
1843
return $ this ;
1842
1844
}
1843
1845
1846
+ /**
1847
+ * Compiles an upsert query and returns the sql
1848
+ *
1849
+ * @throws DatabaseException
1850
+ *
1851
+ * @return string
1852
+ */
1853
+ public function getCompiledUpsert ()
1854
+ {
1855
+ $ currentTestMode = $ this ->testMode ;
1856
+
1857
+ $ this ->testMode = true ;
1858
+
1859
+ $ sql = implode ("; \n" , $ this ->upsert ());
1860
+
1861
+ $ this ->testMode = $ currentTestMode ;
1862
+
1863
+ return $ this ->compileFinalQuery ($ sql );
1864
+ }
1865
+
1866
+ /**
1867
+ * Converts call to batchUpsert
1868
+ *
1869
+ * @param array|object|null $set
1870
+ *
1871
+ * @throws DatabaseException
1872
+ *
1873
+ * @return false|int|string[] Number of affected rows or FALSE on failure, SQL array when testMode
1874
+ */
1875
+ public function upsert ($ set = null , ?bool $ escape = null )
1876
+ {
1877
+ if ($ set === null ) {
1878
+ $ set = empty ($ this ->binds ) ? null : [array_map (static fn ($ columnName ) => $ columnName [0 ], $ this ->binds )];
1879
+
1880
+ $ this ->binds = [];
1881
+
1882
+ $ this ->resetRun ([
1883
+ 'QBSet ' => [],
1884
+ 'QBKeys ' => [],
1885
+ ]);
1886
+ } else {
1887
+ $ set = [$ set ];
1888
+ }
1889
+
1890
+ return $ this ->batchExecute ('_upsertBatch ' , $ set , $ escape , 1 );
1891
+ }
1892
+
1893
+ /**
1894
+ * Compiles batch upsert strings and runs the queries
1895
+ *
1896
+ * @throws DatabaseException
1897
+ *
1898
+ * @return false|int|string[] Number of affected rows or FALSE on failure, SQL array when testMode
1899
+ */
1900
+ public function upsertBatch (?array $ set = null , ?bool $ escape = null , int $ batchSize = 100 )
1901
+ {
1902
+ return $ this ->batchExecute ('_upsertBatch ' , $ set , $ escape , $ batchSize );
1903
+ }
1904
+
1905
+ /**
1906
+ * Generates a platform-specific upsertBatch string from the supplied data
1907
+ */
1908
+ protected function _upsertBatch (string $ table , array $ keys , array $ values ): string
1909
+ {
1910
+ $ fieldNames = array_map (static fn ($ columnName ) => trim ($ columnName , '` ' ), $ keys );
1911
+
1912
+ $ updateFields = $ this ->QBOptions ['updateFields ' ] ?? $ fieldNames ;
1913
+
1914
+ $ sql = 'INSERT INTO ' . $ table . ' ( ' . implode (', ' , $ keys ) . ') ' . "\n" ;
1915
+
1916
+ $ sql .= 'VALUES ' . implode (', ' , $ this ->getValues ($ values )) . "\n" ;
1917
+
1918
+ $ sql .= 'ON DUPLICATE KEY UPDATE ' . "\n" ;
1919
+
1920
+ return $ sql . implode (
1921
+ ", \n" ,
1922
+ array_map (
1923
+ static fn ($ columnName ) => '` ' . $ columnName . '` = VALUES(` ' . $ columnName . '`) ' ,
1924
+ $ updateFields
1925
+ )
1926
+ );
1927
+ }
1928
+
1929
+ /**
1930
+ * Sets constraints for upsert
1931
+ *
1932
+ * @param string|string[] $keys
1933
+ *
1934
+ * @return $this
1935
+ */
1936
+ public function onConstraint ($ keys )
1937
+ {
1938
+ if (! is_array ($ keys )) {
1939
+ $ keys = explode (', ' , $ keys );
1940
+ }
1941
+
1942
+ $ this ->QBOptions ['constraints ' ] = array_map (static fn ($ key ) => trim ($ key ), $ keys );
1943
+
1944
+ return $ this ;
1945
+ }
1946
+
1947
+ /**
1948
+ * Sets update fields for upsert
1949
+ *
1950
+ * @param string|string[] $keys
1951
+ *
1952
+ * @return $this
1953
+ */
1954
+ public function updateFields ($ keys )
1955
+ {
1956
+ if (! is_array ($ keys )) {
1957
+ $ keys = explode (', ' , $ keys );
1958
+ }
1959
+
1960
+ $ this ->QBOptions ['updateFields ' ] = array_map (static fn ($ key ) => trim ($ key ), $ keys );
1961
+
1962
+ return $ this ;
1963
+ }
1964
+
1965
+ /**
1966
+ * Converts value array of array to array of strings
1967
+ */
1968
+ protected function getValues (array $ values ): array
1969
+ {
1970
+ return array_map (static fn ($ index ) => '( ' . implode (', ' , $ index ) . ') ' , $ values );
1971
+ }
1972
+
1973
+ /**
1974
+ * Compiles batch insert strings and runs the queries
1975
+ *
1976
+ * @throws DatabaseException
1977
+ *
1978
+ * @return false|int|string[] Number of rows inserted or FALSE on failure, SQL array when testMode
1979
+ */
1980
+ public function insertBatch (?array $ set = null , ?bool $ escape = null , int $ batchSize = 100 )
1981
+ {
1982
+ return $ this ->batchExecute ('_insertBatch ' , $ set , $ escape , $ batchSize );
1983
+ }
1984
+
1985
+ /**
1986
+ * Generates a platform-specific insert string from the supplied data.
1987
+ */
1988
+ protected function _insertBatch (string $ table , array $ keys , array $ values ): string
1989
+ {
1990
+ return 'INSERT ' . $ this ->compileIgnore ('insert ' ) . 'INTO ' . $ table
1991
+ . ' ( ' . implode (', ' , $ keys ) . ') VALUES ' . implode (', ' , $ this ->getValues ($ values ));
1992
+ }
1993
+
1994
+ /**
1995
+ * Alias for setBatch()
1996
+ *
1997
+ * @param mixed $key
1998
+ *
1999
+ * @return $this|null
2000
+ */
2001
+ public function setInsertBatch ($ key , string $ value = '' , ?bool $ escape = null )
2002
+ {
2003
+ if (! is_array ($ key )) {
2004
+ $ key = [[$ key => $ value ]];
2005
+ }
2006
+
2007
+ return $ this ->setBatch ($ key , $ escape );
2008
+ }
2009
+
1844
2010
/**
1845
2011
* Compiles an insert query and returns the sql
1846
2012
*
@@ -2828,6 +2994,7 @@ protected function resetWrite()
2828
2994
'QBKeys ' => [],
2829
2995
'QBLimit ' => false ,
2830
2996
'QBIgnore ' => false ,
2997
+ 'QBOptions ' => [],
2831
2998
]);
2832
2999
}
2833
3000
0 commit comments