diff --git a/extensions/sphinx/Command.php b/extensions/sphinx/Command.php index cab9487..2341d98 100644 --- a/extensions/sphinx/Command.php +++ b/extensions/sphinx/Command.php @@ -437,6 +437,58 @@ class Command extends Component } /** + * Creates an REPLACE command. + * For example, + * + * ~~~ + * $connection->createCommand()->insert('idx_user', [ + * 'name' => 'Sam', + * 'age' => 30, + * ])->execute(); + * ~~~ + * + * The method will properly escape the column names, and bind the values to be replaced. + * + * Note that the created command is not executed until [[execute()]] is called. + * + * @param string $index the index that new rows will be replaced into. + * @param array $columns the column data (name => value) to be replaced into the index. + * @return static the command object itself + */ + public function replace($index, $columns) + { + $params = []; + $sql = $this->db->getQueryBuilder()->replace($index, $columns, $params); + return $this->setSql($sql)->bindValues($params); + } + + /** + * Creates a batch REPLACE command. + * For example, + * + * ~~~ + * $connection->createCommand()->batchInsert('idx_user', ['name', 'age'], [ + * ['Tom', 30], + * ['Jane', 20], + * ['Linda', 25], + * ])->execute(); + * ~~~ + * + * Note that the values in each row must match the corresponding column names. + * + * @param string $index the index that new rows will be replaced. + * @param array $columns the column names + * @param array $rows the rows to be batch replaced in the index + * @return static the command object itself + */ + public function batchReplace($index, $columns, $rows) + { + $params = []; + $sql = $this->db->getQueryBuilder()->batchReplace($index, $columns, $rows, $params); + return $this->setSql($sql)->bindValues($params); + } + + /** * Creates an UPDATE command. * For example, * diff --git a/extensions/sphinx/QueryBuilder.php b/extensions/sphinx/QueryBuilder.php index 997e997..860e629 100644 --- a/extensions/sphinx/QueryBuilder.php +++ b/extensions/sphinx/QueryBuilder.php @@ -83,11 +83,48 @@ class QueryBuilder extends Object * @param string $index the index that new rows will be inserted into. * @param array $columns the column data (name => value) to be inserted into the index. * @param array $params the binding parameters that will be generated by this method. - * They should be bound to the DB command later. + * They should be bound to the Sphinx command later. * @return string the INSERT SQL */ public function insert($index, $columns, &$params) { + return $this->generateInsertReplace('INSERT', $index, $columns, $params); + } + + /** + * Creates an REPLACE SQL statement. + * For example, + * + * ~~~ + * $sql = $queryBuilder->replace('idx_user', [ + * 'name' => 'Sam', + * 'age' => 30, + * ], $params); + * ~~~ + * + * The method will properly escape the index and column names. + * + * @param string $index the index that new rows will be replaced. + * @param array $columns the column data (name => value) to be replaced in the index. + * @param array $params the binding parameters that will be generated by this method. + * They should be bound to the Sphinx command later. + * @return string the INSERT SQL + */ + public function replace($index, $columns, &$params) + { + return $this->generateInsertReplace('REPLACE', $index, $columns, $params); + } + + /** + * Generates INSERT/REPLACE SQL statement. + * @param string $statement statement ot be generated. + * @param string $index the affected index name. + * @param array $columns the column data (name => value). + * @param array $params the binding parameters that will be generated by this method. + * @return string generated SQL + */ + protected function generateInsertReplace($statement, $index, $columns, &$params) + { if (($indexSchema = $this->db->getIndexSchema($index)) !== null) { $columnSchemas = $indexSchema->columns; } else { @@ -120,7 +157,7 @@ class QueryBuilder extends Object } } - return 'INSERT INTO ' . $this->db->quoteIndexName($index) + return $statement . ' INTO ' . $this->db->quoteIndexName($index) . ' (' . implode(', ', $names) . ') VALUES (' . implode(', ', $placeholders) . ')'; } @@ -148,6 +185,46 @@ class QueryBuilder extends Object */ public function batchInsert($index, $columns, $rows, &$params) { + return $this->generateBatchInsertReplace('INSERT', $index, $columns, $rows, $params); + } + + /** + * Generates a batch REPLACE SQL statement. + * For example, + * + * ~~~ + * $connection->createCommand()->batchReplace('idx_user', ['name', 'age'], [ + * ['Tom', 30], + * ['Jane', 20], + * ['Linda', 25], + * ])->execute(); + * ~~~ + * + * Note that the values in each row must match the corresponding column names. + * + * @param string $index the index that new rows will be replaced. + * @param array $columns the column names + * @param array $rows the rows to be batch replaced in the index + * @param array $params the binding parameters that will be generated by this method. + * They should be bound to the DB command later. + * @return string the batch INSERT SQL statement + */ + public function batchReplace($index, $columns, $rows, &$params) + { + return $this->generateBatchInsertReplace('REPLACE', $index, $columns, $rows, $params); + } + + /** + * Generates a batch INSERT/REPLACE SQL statement. + * @param string $statement statement ot be generated. + * @param string $index the affected index name. + * @param array $columns the column data (name => value). + * @param array $rows the rows to be batch inserted into the index + * @param array $params the binding parameters that will be generated by this method. + * @return string generated SQL + */ + protected function generateBatchInsertReplace($statement, $index, $columns, $rows, &$params) + { if (($indexSchema = $this->db->getIndexSchema($index)) !== null) { $columnSchemas = $indexSchema->columns; } else { @@ -183,7 +260,7 @@ class QueryBuilder extends Object $values[] = '(' . implode(', ', $vs) . ')'; } - return 'INSERT INTO ' . $this->db->quoteIndexName($index) + return $statement . ' INTO ' . $this->db->quoteIndexName($index) . ' (' . implode(', ', $columns) . ') VALUES ' . implode(', ', $values); } diff --git a/tests/unit/extensions/sphinx/CommandTest.php b/tests/unit/extensions/sphinx/CommandTest.php index 5986182..e83fad2 100644 --- a/tests/unit/extensions/sphinx/CommandTest.php +++ b/tests/unit/extensions/sphinx/CommandTest.php @@ -206,6 +206,85 @@ class CommandTest extends SphinxTestCase /** * @depends testInsert */ + public function testReplace() + { + $db = $this->getConnection(); + + $command = $db->createCommand()->replace('yii2_test_rt_index', [ + 'title' => 'Test title', + 'content' => 'Test content', + 'type_id' => 2, + 'category' => [1, 2], + 'id' => 1, + ]); + $this->assertEquals(1, $command->execute(), 'Unable to execute replace!'); + + $rows = $db->createCommand('SELECT * FROM yii2_test_rt_index')->queryAll(); + $this->assertEquals(1, count($rows), 'No row inserted!'); + + $newTypeId = 5; + $command = $db->createCommand()->replace('yii2_test_rt_index',[ + 'type_id' => $newTypeId, + 'category' => [3, 4], + 'id' => 1, + ]); + $this->assertEquals(1, $command->execute(), 'Unable to update via replace!'); + + list($row) = $db->createCommand('SELECT * FROM yii2_test_rt_index')->queryAll(); + $this->assertEquals($newTypeId, $row['type_id'], 'Unable to update attribute value!'); + } + + /** + * @depends testReplace + */ + public function testBatchReplace() + { + $db = $this->getConnection(); + + $command = $db->createCommand()->batchReplace( + 'yii2_test_rt_index', + [ + 'title', + 'content', + 'type_id', + 'category', + 'id', + ], + [ + [ + 'Test title 1', + 'Test content 1', + 1, + [1, 2], + 1, + ], + [ + 'Test title 2', + 'Test content 2', + 2, + [3, 4], + 2, + ], + ] + ); + $this->assertEquals(2, $command->execute(), 'Unable to execute batch replace!'); + + $rows = $db->createCommand('SELECT * FROM yii2_test_rt_index')->queryAll(); + $this->assertEquals(2, count($rows), 'No rows inserted!'); + + $newTypeId = 5; + $command = $db->createCommand()->replace('yii2_test_rt_index',[ + 'type_id' => $newTypeId, + 'id' => 1, + ]); + $this->assertEquals(1, $command->execute(), 'Unable to update via replace!'); + list($row) = $db->createCommand('SELECT * FROM yii2_test_rt_index')->queryAll(); + $this->assertEquals($newTypeId, $row['type_id'], 'Unable to update attribute value!'); + } + + /** + * @depends testInsert + */ public function testUpdate() { $db = $this->getConnection();