2 namespace Codeception\Module;
4 use Codeception\Module as CodeceptionModule;
5 use Codeception\Configuration;
6 use Codeception\Exception\ModuleException;
7 use Codeception\Exception\ModuleConfigException;
8 use Codeception\Lib\Interfaces\Db as DbInterface;
9 use Codeception\Lib\Driver\Db as Driver;
10 use Codeception\Lib\DbPopulator;
11 use Codeception\TestInterface;
12 use Codeception\Lib\Notification;
13 use Codeception\Util\ActionSequence;
18 * The most important function of this module is to clean a database before each test.
19 * This module also provides actions to perform checks in a database, e.g. [seeInDatabase()](http://codeception.com/docs/modules/Db#seeInDatabase)
21 * In order to have your database populated with data you need a raw SQL dump.
22 * Simply put the dump in the `tests/_data` directory (by default) and specify the path in the config.
23 * The next time after the database is cleared, all your data will be restored from the dump.
24 * Don't forget to include `CREATE TABLE` statements in the dump.
26 * Supported and tested databases are:
29 * * SQLite (i.e. just one file)
37 * Connection is done by database Drivers, which are stored in the `Codeception\Lib\Driver` namespace.
38 * [Check out the drivers](https://github.com/Codeception/Codeception/tree/2.4/src/Codeception/Lib/Driver)
39 * if you run into problems loading dumps and cleaning databases.
43 * * dsn *required* - PDO DSN
44 * * user *required* - username to access database
45 * * password *required* - password
46 * * dump - path to database dump
47 * * populate: false - whether the the dump should be loaded before the test suite is started
48 * * cleanup: false - whether the dump should be reloaded before each test
49 * * reconnect: false - whether the module should reconnect to the database before each test
50 * * waitlock: 0 - wait lock (in seconds) that the database session should use for DDL statements
51 * * ssl_key - path to the SSL key (MySQL specific, @see http://php.net/manual/de/ref.pdo-mysql.php#pdo.constants.mysql-attr-key)
52 * * ssl_cert - path to the SSL certificate (MySQL specific, @see http://php.net/manual/de/ref.pdo-mysql.php#pdo.constants.mysql-attr-ssl-cert)
53 * * ssl_ca - path to the SSL certificate authority (MySQL specific, @see http://php.net/manual/de/ref.pdo-mysql.php#pdo.constants.mysql-attr-ssl-ca)
54 * * ssl_verify_server_cert - disables certificate CN verification (MySQL specific, @see http://php.net/manual/de/ref.pdo-mysql.php)
55 * * ssl_cipher - list of one or more permissible ciphers to use for SSL encryption (MySQL specific, @see http://php.net/manual/de/ref.pdo-mysql.php#pdo.constants.mysql-attr-cipher)
56 * * databases - include more database configs and switch between them in tests.
57 * * initial_queries - list of queries to be executed right after connection to the database has been initiated, i.e. creating the database if it does not exist or preparing the database collation
64 * dsn: 'mysql:host=localhost;dbname=testdb'
67 * dump: 'tests/_data/dump.sql'
72 * ssl_key: '/path/to/client-key.pem'
73 * ssl_cert: '/path/to/client-cert.pem'
74 * ssl_ca: '/path/to/ca-cert.pem'
75 * ssl_verify_server_cert: false
76 * ssl_cipher: 'AES256-SHA'
78 * - 'CREATE DATABASE IF NOT EXISTS temp_db;'
82 * ## Example with multi-dumps
86 * dsn: 'mysql:host=localhost;dbname=testdb'
90 * - 'tests/_data/dump.sql'
91 * - 'tests/_data/dump-2.sql'
93 * ## Example with multi-databases
98 * dsn: 'mysql:host=localhost;dbname=testdb'
103 * dsn: 'mysql:host=localhost;dbname=testdb2'
109 * There are two ways of loading the dump into your database:
113 * The recommended approach is to configure a `populator`, an external command to load a dump. Command parameters like host, username, password, database
114 * can be obtained from the config and inserted into placeholders:
122 * dsn: 'mysql:host=localhost;dbname=testdb'
125 * dump: 'tests/_data/dump.sql'
126 * populate: true # run populator before all tests
127 * cleanup: true # run populator before each test
128 * populator: 'mysql -u $user -h $host $dbname < $dump'
131 * For PostgreSQL (using pg_restore)
137 * dsn: 'pgsql:host=localhost;dbname=testdb'
140 * dump: 'tests/_data/db_backup.dump'
141 * populate: true # run populator before all tests
142 * cleanup: true # run populator before each test
143 * populator: 'pg_restore -u $user -h $host -D $dbname < $dump'
146 * Variable names are being taken from config and DSN which has a `keyword=value` format, so you should expect to have a variable named as the
147 * keyword with the full value inside it.
149 * PDO dsn elements for the supported drivers:
150 * * MySQL: [PDO_MYSQL DSN](https://secure.php.net/manual/en/ref.pdo-mysql.connection.php)
151 * * SQLite: [PDO_SQLITE DSN](https://secure.php.net/manual/en/ref.pdo-sqlite.connection.php)
152 * * PostgreSQL: [PDO_PGSQL DSN](https://secure.php.net/manual/en/ref.pdo-pgsql.connection.php)
153 * * MSSQL: [PDO_SQLSRV DSN](https://secure.php.net/manual/en/ref.pdo-sqlsrv.connection.php)
154 * * Oracle: [PDO_OCI DSN](https://secure.php.net/manual/en/ref.pdo-oci.connection.php)
158 * Db module by itself can load SQL dump without external tools by using current database connection.
159 * This approach is system-independent, however, it is slower than using a populator and may have parsing issues (see below).
161 * Provide a path to SQL file in `dump` config option:
167 * dsn: 'mysql:host=localhost;dbname=testdb'
170 * populate: true # load dump before all tests
171 * cleanup: true # load dump for each test
172 * dump: 'tests/_data/dump.sql'
175 * To parse SQL Db file, it should follow this specification:
176 * * Comments are permitted.
177 * * The `dump.sql` may contain multiline statements.
178 * * The delimiter, a semi-colon in this case, must be on the same line as the last statement:
181 * -- Add a few contacts to the table.
182 * REPLACE INTO `Contacts` (`created`, `modified`, `status`, `contact`, `first`, `last`) VALUES
183 * (NOW(), NOW(), 1, 'Bob Ross', 'Bob', 'Ross'),
184 * (NOW(), NOW(), 1, 'Fred Flintstone', 'Fred', 'Flintstone');
186 * -- Remove existing orders for testing.
187 * DELETE FROM `Order`;
189 * ## Query generation
191 * `seeInDatabase`, `dontSeeInDatabase`, `seeNumRecords`, `grabFromDatabase` and `grabNumRecords` methods
192 * accept arrays as criteria. WHERE condition is generated using item key as a field name and
193 * item value as a field value.
198 * $I->seeInDatabase('users', ['name' => 'Davert', 'email' => 'davert@mail.com']);
204 * SELECT COUNT(*) FROM `users` WHERE `name` = 'Davert' AND `email` = 'davert@mail.com'
206 * Since version 2.1.9 it's possible to use LIKE in a condition, as shown here:
210 * $I->seeInDatabase('users', ['name' => 'Davert', 'email like' => 'davert%']);
216 * SELECT COUNT(*) FROM `users` WHERE `name` = 'Davert' AND `email` LIKE 'davert%'
218 * ## Public Properties
219 * * dbh - contains the PDO connection
220 * * driver - contains the Connection Driver
223 class Db extends CodeceptionModule implements DbInterface
228 protected $config = [
231 'reconnect' => false,
240 protected $requiredFields = ['dsn', 'user', 'password'];
241 const DEFAULT_DATABASE = 'default';
246 public $drivers = [];
251 public $databasesPopulated = [];
252 public $databasesSql = [];
253 protected $insertedRows = [];
254 public $currentDatabase = self::DEFAULT_DATABASE;
256 protected function getDatabases()
258 $databases = [$this->currentDatabase => $this->config];
260 if (!empty($this->config['databases'])) {
261 foreach ($this->config['databases'] as $databaseKey => $databaseConfig) {
262 $databases[$databaseKey] = array_merge([
265 'reconnect' => false,
274 protected function connectToDatabases()
276 foreach ($this->getDatabases() as $databaseKey => $databaseConfig) {
277 $this->connect($databaseKey, $databaseConfig);
280 protected function cleanUpDatabases()
282 foreach ($this->getDatabases() as $databaseKey => $databaseConfig) {
283 $this->_cleanup($databaseKey, $databaseConfig);
286 protected function populateDatabases($configKey)
288 foreach ($this->getDatabases() as $databaseKey => $databaseConfig) {
289 if ($databaseConfig[$configKey]) {
290 if (!$databaseConfig['populate']) {
294 if (isset($this->databasesPopulated[$databaseKey]) && $this->databasesPopulated[$databaseKey]) {
297 $this->_loadDump($databaseKey, $databaseConfig);
301 protected function readSqlForDatabases()
303 foreach ($this->getDatabases() as $databaseKey => $databaseConfig) {
304 $this->readSql($databaseKey, $databaseConfig);
307 protected function removeInsertedForDatabases()
309 foreach ($this->getDatabases() as $databaseKey => $databaseConfig) {
310 $this->amConnectedToDatabase($databaseKey);
311 $this->removeInserted($databaseKey);
314 protected function disconnectDatabases()
316 foreach ($this->getDatabases() as $databaseKey => $databaseConfig) {
317 $this->disconnect($databaseKey);
320 protected function reconnectDatabases()
322 foreach ($this->getDatabases() as $databaseKey => $databaseConfig) {
323 if ($databaseConfig['reconnect']) {
324 $this->disconnect($databaseKey);
325 $this->connect($databaseKey, $databaseConfig);
330 public function __get($name)
332 Notification::deprecate("Properties dbh and driver are deprecated in favor of Db::_getDbh and Db::_getDriver", "Db module");
334 if ($name == 'driver') {
335 return $this->_getDriver();
337 if ($name == 'dbh') {
338 return $this->_getDbh();
345 public function _getDriver()
347 return $this->drivers[$this->currentDatabase];
349 public function _getDbh()
351 return $this->dbhs[$this->currentDatabase];
355 * Make sure you are connected to the right database.
359 * $I->seeNumRecords(2, 'users'); //executed on default database
360 * $I->amConnectedToDatabase('db_books');
361 * $I->seeNumRecords(30, 'books'); //executed on db_books database
362 * //All the next queries will be on db_books
364 * @param $databaseKey
365 * @throws ModuleConfigException
367 public function amConnectedToDatabase($databaseKey)
369 if (empty($this->getDatabases()[$databaseKey]) && $databaseKey != self::DEFAULT_DATABASE) {
370 throw new ModuleConfigException(
372 "\nNo database $databaseKey in the key databases.\n"
375 $this->currentDatabase = $databaseKey;
379 * Can be used with a callback if you don't want to change the current database in your test.
383 * $I->seeNumRecords(2, 'users'); //executed on default database
384 * $I->performInDatabase('db_books', function($I) {
385 * $I->seeNumRecords(30, 'books'); //executed on db_books database
387 * $I->seeNumRecords(2, 'users'); //executed on default database
389 * List of actions can be pragmatically built using `Codeception\Util\ActionSequence`:
393 * $I->performInDatabase('db_books', ActionSequence::build()
394 * ->seeNumRecords(30, 'books')
397 * Alternatively an array can be used:
400 * $I->performInDatabase('db_books', ['seeNumRecords' => [30, 'books']]);
403 * Choose the syntax you like the most and use it,
405 * Actions executed from array or ActionSequence will print debug output for actions, and adds an action name to
406 * exception on failure.
408 * @param $databaseKey
409 * @param \Codeception\Util\ActionSequence|array|callable $actions
410 * @throws ModuleConfigException
412 public function performInDatabase($databaseKey, $actions)
414 $backupDatabase = $this->currentDatabase;
415 $this->amConnectedToDatabase($databaseKey);
417 if (is_callable($actions)) {
419 $this->amConnectedToDatabase($backupDatabase);
422 if (is_array($actions)) {
423 $actions = ActionSequence::build()->fromArray($actions);
426 if (!$actions instanceof ActionSequence) {
427 throw new \InvalidArgumentException("2nd parameter, actions should be callback, ActionSequence or array");
430 $actions->run($this);
431 $this->amConnectedToDatabase($backupDatabase);
434 public function _initialize()
436 $this->connectToDatabases();
439 public function __destruct()
441 $this->disconnectDatabases();
444 public function _beforeSuite($settings = [])
446 $this->readSqlForDatabases();
447 $this->connectToDatabases();
448 $this->cleanUpDatabases();
449 $this->populateDatabases('populate');
452 private function readSql($databaseKey = null, $databaseConfig = null)
454 if ($databaseConfig['populator']) {
457 if (!$databaseConfig['cleanup'] && !$databaseConfig['populate']) {
460 if (empty($databaseConfig['dump'])) {
464 if (!is_array($databaseConfig['dump'])) {
465 $databaseConfig['dump'] = [$databaseConfig['dump']];
470 foreach ($databaseConfig['dump'] as $filePath) {
471 $sql .= $this->readSqlFile($filePath);
475 // split SQL dump into lines
476 $this->databasesSql[$databaseKey] = preg_split('/\r\n|\n|\r/', $sql, -1, PREG_SPLIT_NO_EMPTY);
483 * @return bool|null|string|string[]
484 * @throws \Codeception\Exception\ModuleConfigException
486 private function readSqlFile($filePath)
488 if (!file_exists(Configuration::projectDir() . $filePath)) {
489 throw new ModuleConfigException(
491 "\nFile with dump doesn't exist.\n"
492 . "Please, check path for sql file: "
497 $sql = file_get_contents(Configuration::projectDir() . $filePath);
499 // remove C-style comments (except MySQL directives)
500 $sql = preg_replace('%/\*(?!!\d+).*?\*/%s', '', $sql);
505 private function connect($databaseKey, $databaseConfig)
507 if (!empty($this->drivers[$databaseKey]) && !empty($this->dbhs[$databaseKey])) {
513 * @see http://php.net/manual/en/pdo.construct.php
514 * @see http://php.net/manual/de/ref.pdo-mysql.php#pdo-mysql.constants
516 if (array_key_exists('ssl_key', $databaseConfig)
517 && !empty($databaseConfig['ssl_key'])
518 && defined('\PDO::MYSQL_ATTR_SSL_KEY')
520 $options[\PDO::MYSQL_ATTR_SSL_KEY] = (string) $databaseConfig['ssl_key'];
523 if (array_key_exists('ssl_cert', $databaseConfig)
524 && !empty($databaseConfig['ssl_cert'])
525 && defined('\PDO::MYSQL_ATTR_SSL_CERT')
527 $options[\PDO::MYSQL_ATTR_SSL_CERT] = (string) $databaseConfig['ssl_cert'];
530 if (array_key_exists('ssl_ca', $databaseConfig)
531 && !empty($databaseConfig['ssl_ca'])
532 && defined('\PDO::MYSQL_ATTR_SSL_CA')
534 $options[\PDO::MYSQL_ATTR_SSL_CA] = (string) $databaseConfig['ssl_ca'];
537 if (array_key_exists('ssl_cipher', $databaseConfig)
538 && !empty($databaseConfig['ssl_cipher'])
539 && defined('\PDO::MYSQL_ATTR_SSL_CIPHER')
541 $options[\PDO::MYSQL_ATTR_SSL_CIPHER] = (string) $databaseConfig['ssl_cipher'];
544 if (array_key_exists('ssl_verify_server_cert', $databaseConfig)
545 && defined('\PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT')
547 $options[\PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = (boolean) $databaseConfig[ 'ssl_verify_server_cert' ];
551 $this->debugSection('Connecting To Db', ['config' => $databaseConfig, 'options' => $options]);
552 $this->drivers[$databaseKey] = Driver::create($databaseConfig['dsn'], $databaseConfig['user'], $databaseConfig['password'], $options);
553 } catch (\PDOException $e) {
554 $message = $e->getMessage();
555 if ($message === 'could not find driver') {
556 list ($missingDriver, ) = explode(':', $databaseConfig['dsn'], 2);
557 $message = "could not find $missingDriver driver";
560 throw new ModuleException(__CLASS__, $message . ' while creating PDO connection');
563 if ($databaseConfig['waitlock']) {
564 $this->_getDriver()->setWaitLock($databaseConfig['waitlock']);
567 if (isset($databaseConfig['initial_queries'])) {
568 foreach ($databaseConfig['initial_queries'] as $initialQuery) {
569 $this->drivers[$databaseKey]->executeQuery($initialQuery, []);
573 $this->debugSection('Db', 'Connected to ' . $databaseKey . ' ' . $this->drivers[$databaseKey]->getDb());
574 $this->dbhs[$databaseKey] = $this->drivers[$databaseKey]->getDbh();
577 private function disconnect($databaseKey)
579 $this->debugSection('Db', 'Disconnected from ' . $databaseKey);
580 $this->dbhs[$databaseKey] = null;
581 $this->drivers[$databaseKey] = null;
584 public function _before(TestInterface $test)
586 $this->reconnectDatabases();
587 $this->amConnectedToDatabase(self::DEFAULT_DATABASE);
589 $this->cleanUpDatabases();
591 $this->populateDatabases('cleanup');
593 parent::_before($test);
596 public function _after(TestInterface $test)
598 $this->removeInsertedForDatabases();
599 parent::_after($test);
602 protected function removeInserted($databaseKey = null)
604 $databaseKey = empty($databaseKey) ? self::DEFAULT_DATABASE : $databaseKey;
606 if (empty($this->insertedRows[$databaseKey])) {
610 foreach (array_reverse($this->insertedRows[$databaseKey]) as $row) {
612 $this->_getDriver()->deleteQueryByCriteria($row['table'], $row['primary']);
613 } catch (\Exception $e) {
614 $this->debug("Couldn't delete record " . json_encode($row['primary']) ." from {$row['table']}");
617 $this->insertedRows[$databaseKey] = [];
620 public function _cleanup($databaseKey = null, $databaseConfig = null)
622 $databaseKey = empty($databaseKey) ? self::DEFAULT_DATABASE : $databaseKey;
623 $databaseConfig = empty($databaseConfig) ? $this->config : $databaseConfig;
625 if (!$databaseConfig['populate']) {
628 if (!$databaseConfig['cleanup']) {
631 if (isset($this->databasesPopulated[$databaseKey]) && !$this->databasesPopulated[$databaseKey]) {
634 $dbh = $this->dbhs[$databaseKey];
636 throw new ModuleConfigException(
638 'No connection to database. Remove this module from config if you don\'t need database repopulation'
642 if (false === $this->shouldCleanup($databaseConfig, $databaseKey)) {
645 $this->drivers[$databaseKey]->cleanup();
646 $this->databasesPopulated[$databaseKey] = false;
647 } catch (\Exception $e) {
648 throw new ModuleException(__CLASS__, $e->getMessage());
653 * @param array $databaseConfig
654 * @param string $databaseKey
657 protected function shouldCleanup($databaseConfig, $databaseKey)
659 // If using populator and it's not empty, clean up regardless
660 if (!empty($databaseConfig['populator'])) {
664 // If no sql dump for $databaseKey or sql dump is empty, don't clean up
665 return !empty($this->databasesSql[$databaseKey]);
668 public function _isPopulated()
670 return $this->databasesPopulated[$this->currentDatabase];
673 public function _loadDump($databaseKey = null, $databaseConfig = null)
675 $databaseKey = empty($databaseKey) ? self::DEFAULT_DATABASE : $databaseKey;
676 $databaseConfig = empty($databaseConfig) ? $this->config : $databaseConfig;
678 if ($databaseConfig['populator']) {
679 $this->loadDumpUsingPopulator($databaseKey, $databaseConfig);
682 $this->loadDumpUsingDriver($databaseKey);
685 protected function loadDumpUsingPopulator($databaseKey, $databaseConfig)
687 $populator = new DbPopulator($databaseConfig);
688 $this->databasesPopulated[$databaseKey] = $populator->run();
691 protected function loadDumpUsingDriver($databaseKey)
693 if (!isset($this->databasesSql[$databaseKey])) {
696 if (!$this->databasesSql[$databaseKey]) {
697 $this->debugSection('Db', 'No SQL loaded, loading dump skipped');
700 $this->drivers[$databaseKey]->load($this->databasesSql[$databaseKey]);
701 $this->databasesPopulated[$databaseKey] = true;
705 * Inserts an SQL record into a database. This record will be erased after the test.
709 * $I->haveInDatabase('users', array('name' => 'miles', 'email' => 'miles@davis.com'));
713 * @param string $table
716 * @return integer $id
718 public function haveInDatabase($table, array $data)
720 $lastInsertId = $this->_insertInDatabase($table, $data);
722 $this->addInsertedRow($table, $data, $lastInsertId);
724 return $lastInsertId;
727 public function _insertInDatabase($table, array $data)
729 $query = $this->_getDriver()->insert($table, $data);
730 $parameters = array_values($data);
731 $this->debugSection('Query', $query);
732 $this->debugSection('Parameters', $parameters);
733 $this->_getDriver()->executeQuery($query, $parameters);
736 $lastInsertId = (int)$this->_getDriver()->lastInsertId($table);
737 } catch (\PDOException $e) {
738 // ignore errors due to uncommon DB structure,
739 // such as tables without _id_seq in PGSQL
741 $this->debugSection('DB error', $e->getMessage());
743 return $lastInsertId;
746 private function addInsertedRow($table, array $row, $id)
748 $primaryKey = $this->_getDriver()->getPrimaryKey($table);
751 if ($id && count($primaryKey) === 1) {
752 $primary [$primaryKey[0]] = $id;
754 foreach ($primaryKey as $column) {
755 if (isset($row[$column])) {
756 $primary[$column] = $row[$column];
758 throw new \InvalidArgumentException(
759 'Primary key field ' . $column . ' is not set for table ' . $table
768 $this->insertedRows[$this->currentDatabase][] = [
770 'primary' => $primary,
774 public function seeInDatabase($table, $criteria = [])
776 $res = $this->countInDatabase($table, $criteria);
777 $this->assertGreaterThan(
780 'No matching records found for criteria ' . json_encode($criteria) . ' in table ' . $table
785 * Asserts that the given number of records were found in the database.
789 * $I->seeNumRecords(1, 'users', ['name' => 'davert'])
793 * @param int $expectedNumber Expected number
794 * @param string $table Table name
795 * @param array $criteria Search criteria [Optional]
797 public function seeNumRecords($expectedNumber, $table, array $criteria = [])
799 $actualNumber = $this->countInDatabase($table, $criteria);
804 'The number of found rows (%d) does not match expected number %d for criteria %s in table %s',
807 json_encode($criteria),
813 public function dontSeeInDatabase($table, $criteria = [])
815 $count = $this->countInDatabase($table, $criteria);
816 $this->assertLessThan(
819 'Unexpectedly found matching records for criteria ' . json_encode($criteria) . ' in table ' . $table
824 * Count rows in a database
826 * @param string $table Table name
827 * @param array $criteria Search criteria [Optional]
831 protected function countInDatabase($table, array $criteria = [])
833 return (int) $this->proceedSeeInDatabase($table, 'count(*)', $criteria);
837 * Fetches all values from the column in database.
838 * Provide table name, desired column and criteria.
840 * @param string $table
841 * @param string $column
842 * @param array $criteria
846 protected function proceedSeeInDatabase($table, $column, $criteria)
848 $query = $this->_getDriver()->select($column, $table, $criteria);
849 $parameters = array_values($criteria);
850 $this->debugSection('Query', $query);
851 if (!empty($parameters)) {
852 $this->debugSection('Parameters', $parameters);
854 $sth = $this->_getDriver()->executeQuery($query, $parameters);
856 return $sth->fetchColumn();
860 * Fetches all values from the column in database.
861 * Provide table name, desired column and criteria.
865 * $mails = $I->grabColumnFromDatabase('users', 'email', array('name' => 'RebOOter'));
868 * @param string $table
869 * @param string $column
870 * @param array $criteria
874 public function grabColumnFromDatabase($table, $column, array $criteria = [])
876 $query = $this->_getDriver()->select($column, $table, $criteria);
877 $parameters = array_values($criteria);
878 $this->debugSection('Query', $query);
879 $this->debugSection('Parameters', $parameters);
880 $sth = $this->_getDriver()->executeQuery($query, $parameters);
882 return $sth->fetchAll(\PDO::FETCH_COLUMN, 0);
886 * Fetches a single column value from a database.
887 * Provide table name, desired column and criteria.
891 * $mail = $I->grabFromDatabase('users', 'email', array('name' => 'Davert'));
893 * Comparison expressions can be used as well:
897 * $post = $I->grabFromDatabase('posts', ['num_comments >=' => 100]);
898 * $user = $I->grabFromDatabase('users', ['email like' => 'miles%']);
901 * Supported operators: `<`, `>`, `>=`, `<=`, `!=`, `like`.
903 * @param string $table
904 * @param string $column
905 * @param array $criteria
907 * @return mixed Returns a single column value or false
909 public function grabFromDatabase($table, $column, $criteria = [])
911 return $this->proceedSeeInDatabase($table, $column, $criteria);
915 * Returns the number of rows in a database
917 * @param string $table Table name
918 * @param array $criteria Search criteria [Optional]
922 public function grabNumRecords($table, array $criteria = [])
924 return $this->countInDatabase($table, $criteria);
928 * Update an SQL record into a database.
932 * $I->updateInDatabase('users', array('isAdmin' => true), array('email' => 'miles@davis.com'));
936 * @param string $table
938 * @param array $criteria
940 public function updateInDatabase($table, array $data, array $criteria = [])
942 $query = $this->_getDriver()->update($table, $data, $criteria);
943 $parameters = array_merge(array_values($data), array_values($criteria));
944 $this->debugSection('Query', $query);
945 if (!empty($parameters)) {
946 $this->debugSection('Parameters', $parameters);
948 $this->_getDriver()->executeQuery($query, $parameters);