ChadoRecord.inc 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708
  1. <?php
  2. /**
  3. * Provide a class for basic querying of Chado.
  4. *
  5. * Specifically tihs class provides select, insert, update and delete.
  6. *
  7. * Eventually this class is meants to replace the existing
  8. * chado_select_record(), chado_insert_record(), chado_update_record() and
  9. * chado_delete_record() API functions to create a cleaner, more maintainable
  10. * and more easily tested interface to querying Chado.
  11. *
  12. * @todo Add documentation for save() and delete().
  13. *
  14. * Basic Usage:
  15. * - Select/Find
  16. * The following example selects an organism with the scientific name
  17. * "Tripalus databasica" from the organism table of Chado.
  18. * @code
  19. * // First we create an instance of the ChadoRecord class
  20. * // specifying the table we want to query.
  21. * $record = new \ChadoRecord('organism');
  22. *
  23. * // Next we indicate the values we know.
  24. * $record->setValues([
  25. * 'genus' => 'Tripalus',
  26. * 'species' => 'databasica',
  27. * ]);
  28. *
  29. * // And finally we simply ask the class to find the chado record
  30. * // we indicated when we set the values above.
  31. * $success = $record->find();
  32. * if ($success) {
  33. * // Retrieve the values if we were successful in finding the record.
  34. * $result = $record->getValues();
  35. * }
  36. * @endcode
  37. * - Insert:
  38. * The following example inserts a sample record into the stock table.
  39. * @code
  40. * // First we create an instance of the ChadoRecord class
  41. * // specifying the table we want to query.
  42. * $record = new \ChadoRecord('stock');
  43. *
  44. * // Next we indicate the values we know.
  45. * $record->setValues([
  46. * 'name' => 'My Favourite Plant',
  47. * 'uniquename' => 'Plectranthus scutellarioides Trailing Plum Brocade',
  48. * 'organism_id' => [ 'genus' => 'Tripalus', 'species' => 'databasica' ],
  49. * 'type_id' => [ 'name' => 'sample', 'cv_id' => [ 'name' => 'Sample processing
  50. * and separation techniques' ] ],
  51. * ]);
  52. *
  53. * // And finally, we ask the class to insert the chado record
  54. * // we described when we set the values above.
  55. * $result = $record->insert();
  56. * @endcode
  57. * - Update:
  58. * The following example updates the "Tripalus databasica" record to
  59. * specify the common name.
  60. * @code
  61. * // For brevity we're going to hardcode the original record
  62. * // including the id although you would Never do this in practice.
  63. * // Rather you would first find the record as shown in a previous example.
  64. * $original_record = [
  65. * 'organism_id' => 1,
  66. * 'genus' => 'Tripalus',
  67. * 'species' => 'databasica',
  68. * ];
  69. *
  70. * // First we create an instance of the ChadoRecord class
  71. * // specifying the table we want to query.
  72. * // NOTICE: this time we set the record_id when creating the instance.
  73. * $record = new \ChadoRecord('organism', $original_record['organism_id']);
  74. *
  75. * // Now we set the values we want to change.
  76. * $record->setValues([
  77. * 'common_name' => 'Tripal',
  78. * ]);
  79. *
  80. * // And then tell the class to update the record.
  81. * $record->update();
  82. * @endcode
  83. */
  84. class ChadoRecord {
  85. /**
  86. * @var string
  87. * Holds the name of the table that this record belogns to.
  88. */
  89. protected $table_name = '';
  90. /**
  91. * @var array
  92. * Holds the Drupal schema definition for this table.
  93. */
  94. protected $schema = [];
  95. /**
  96. * @var array
  97. * Holds the values for the columns of the record
  98. */
  99. protected $values = [];
  100. /**
  101. * @var array
  102. * An array of required columns.
  103. */
  104. protected $required_cols = [];
  105. /**
  106. * @var array
  107. * An array of default values.
  108. */
  109. protected $default_values = [];
  110. /**
  111. * @var boolean
  112. * An array of required columns which have yet to be set.
  113. */
  114. protected $missing_required_col = [];
  115. /**
  116. * @var integer
  117. * The numeric Id for this record.
  118. */
  119. protected $record_id = NULL;
  120. /**
  121. * @var string
  122. * The column name for the primary key.
  123. */
  124. protected $pkey = '';
  125. /**
  126. * The list of column names in the table.
  127. *
  128. * @var array
  129. */
  130. protected $column_names = [];
  131. /**
  132. * The ChadoRecord constructor
  133. *
  134. * @param string $table_name
  135. * The name of the table that the record belongs to.
  136. *
  137. * @param string $record_id
  138. * An optional record ID if this record is already present in Chado.
  139. */
  140. public function __construct($table_name, $record_id = NULL) {
  141. if (!$table_name) {
  142. $message = t('ChadoRecord::_construct(). The $table_name argument is required for a ChadoRecord instance.');
  143. throw new Exception($message);
  144. }
  145. // Set the table name and schema.
  146. $this->table_name = $table_name;
  147. $this->schema = chado_get_schema($this->table_name);
  148. if (!$this->schema) {
  149. $message = t('ChadoRecord::_construct(). Could not find a matching table schema in Chado for the table: !table.',
  150. ['!table' => $this->table_name]);
  151. throw new Exception($message);
  152. }
  153. // Chado tables never have more than one column as a primary key so
  154. // we are good just getting the first element.
  155. $this->pkey = $this->schema['primary key'][0];
  156. // Save the column names.
  157. foreach ($this->schema['fields'] as $column_name => $col_details) {
  158. $this->column_names[] = $column_name;
  159. }
  160. // Get the required columns and default values.
  161. foreach ($this->schema['fields'] as $column => $col_schema) {
  162. foreach ($col_schema as $param => $val) {
  163. if (preg_match('/not null/i', $param) and $col_schema[$param]) {
  164. $this->required_cols[] = $column;
  165. // Currently all required columns are missing.
  166. $this->missing_required_col[$column] = TRUE;
  167. }
  168. if (preg_match('/default/i', $param) and $col_schema[$param] !== '') {
  169. $this->default_values[$column] = $col_schema[$param];
  170. }
  171. }
  172. }
  173. // If a record_id was provided then lookup the record and set the values.
  174. if ($record_id) {
  175. try {
  176. $sql = 'SELECT * FROM {' . $this->table_name . '} WHERE ' . $this->pkey . ' = :record_id';
  177. $result = chado_query($sql, [':record_id' => $record_id]);
  178. $values = $result->fetchAssoc();
  179. if (empty($values)) {
  180. $message = t('ChadoRecord::_construct(). Could not find a record in table, !table, with the given !pkey: !record_id.',
  181. [
  182. '!pkey' => $this->pkey,
  183. '!record_id' => $record_id,
  184. '!table' => $this->table_name,
  185. ]);
  186. throw new Exception($message);
  187. }
  188. $this->record_id = $record_id;
  189. $this->values = $values;
  190. $this->missing_required_col = [];
  191. } catch (Exception $e) {
  192. $message = t('ChadoRecord::_construct(). Could not find a record in table, !table, with the given !pkey: !record_id. ERROR: !error',
  193. [
  194. '!pkey' => $this->pkey,
  195. '!record_id' => $record_id,
  196. '!table' => $this->table_name,
  197. '!error' => $e->getMessage(),
  198. ]);
  199. throw new Exception($message);
  200. }
  201. }
  202. }
  203. /**
  204. * Retrieves the record ID.
  205. *
  206. * @return number
  207. */
  208. public function getID() {
  209. return $this->record_id;
  210. }
  211. /**
  212. * Retrieves the table name.
  213. *
  214. * @return string
  215. * The name of the table that the record belongs to.
  216. */
  217. public function getTable() {
  218. return $this->table_name;
  219. }
  220. /**
  221. * Retrieves the table schema.
  222. *
  223. * @return array
  224. * The Drupal schema array for the table.
  225. */
  226. public function getSchema() {
  227. return $this->schema;
  228. }
  229. /**
  230. * Performs either an update or insert into the table using the values.
  231. *
  232. * If the record already exists it will be updated. If the record does not
  233. * exist it will be inserted. This function adds a bit more overhead by
  234. * checking for the existence of the record and performing the appropriate
  235. * action. You can save time by using the insert or update functions directly
  236. * if you only need to do one of those actions specifically.
  237. *
  238. * @throws Exception
  239. */
  240. public function save() {
  241. // Determine if we need to perform an update or an insert.
  242. $num_matches = $this->find();
  243. if ($num_matches == 1) {
  244. $this->update();
  245. }
  246. if ($num_matches == 0) {
  247. $this->insert();
  248. }
  249. if ($num_matches > 1) {
  250. $message = t('ChadoRecord::save(). Could not save the record into the table, !table. ' .
  251. 'Multiple records already exist that match the values: !values. ' .
  252. 'Please provide a set of values that can uniquely identify a record.',
  253. [
  254. '!table' => $this->table_name,
  255. '!values' => print_r($this->values, TRUE),
  256. '!error' => $e->getMessage(),
  257. ]);
  258. throw new Exception($message);
  259. }
  260. }
  261. /**
  262. * Inserts the values of this object as a new record.
  263. *
  264. * @todo Support options from chado_insert_record: return_record.
  265. * @todo check for violation of unique constraint.
  266. *
  267. * @throws Exception
  268. */
  269. public function insert() {
  270. // Make sure we have values for this record before inserting.
  271. if (empty($this->values)) {
  272. $message = t('ChadoRecord::insert(). Could not insert a record into the table, !table, without any values.',
  273. ['!table' => $this->table_name]);
  274. throw new Exception($message);
  275. }
  276. // Additionally, make sure we have all the required values!
  277. if (!empty($this->missing_required_col)) {
  278. $message = t('ChadoRecord::insert(). The columns named, "!columns", ' .
  279. 'require a value for the table: "!table". You can set these values ' .
  280. 'using ChadoRecord::setValues(). Current values: !values.',
  281. [
  282. '!columns' => implode('", "', array_keys($this->missing_required_col)),
  283. '!table' => $this->table_name,
  284. '!values' => print_r($this->values, TRUE),
  285. ]);
  286. throw new Exception($message);
  287. }
  288. // Build the SQL statement for insertion.
  289. $insert_cols = [];
  290. $insert_vals = [];
  291. $insert_args = [];
  292. foreach ($this->values as $column => $value) {
  293. $insert_cols[] = $column;
  294. $insert_vals[] = ':' . $column;
  295. $insert_args[':' . $column] = $value;
  296. }
  297. $sql = 'INSERT INTO {' . $this->table_name . '} (' .
  298. implode(", ", $insert_cols) . ') VALUES (' .
  299. implode(", ", $insert_vals) . ')';
  300. if ($this->pkey) {
  301. $sql .= ' RETURNING ' . $this->pkey;
  302. }
  303. try {
  304. $result = chado_query($sql, $insert_args);
  305. if ($this->pkey) {
  306. $record_id = $result->fetchField();
  307. $this->values[$this->pkey] = $record_id;
  308. $this->record_id = $record_id;
  309. }
  310. }
  311. catch (Exception $e) {
  312. $message = t('ChadoRecord::insert(). Could not insert a record into the ' .
  313. 'table, !table, with the following values: !values. ERROR: !error',
  314. [
  315. '!table' => $this->table_name,
  316. '!values' => print_r($this->values, TRUE),
  317. '!error' => $e->getMessage(),
  318. ]);
  319. throw new Exception($message);
  320. }
  321. }
  322. /**
  323. * Updates the values of this object as a new record.
  324. *
  325. * @todo set defaults for columns not already set in values.
  326. * @todo Support options from chado_update_record: return_record.
  327. * @todo check for violation of unique constraint.
  328. * @todo if record_id not set then try finding it.
  329. *
  330. * @throws Exception
  331. */
  332. public function update() {
  333. // Make sure we have values for this record before updating.
  334. if (empty($this->values)) {
  335. $message = t('ChadoRecord::update(). Could not update a record into the ' .
  336. 'table, !table, without any values.',
  337. ['!table' => $this->table_name]);
  338. throw new Exception($message);
  339. }
  340. // Additionally, make sure we have all the required values!
  341. if (!empty($this->missing_required_col)) {
  342. $message = t('ChadoRecord::update(). The columns named, "!columns", ' .
  343. 'require a value for the table: "!table". You can set these values ' .
  344. 'using ChadoRecord::setValues(). Current values: !values.',
  345. [
  346. '!columns' => implode('", "', $this->missing_required_col),
  347. '!table' => $this->table_name,
  348. '!values' => print_r($this->values, TRUE),
  349. ]);
  350. throw new Exception($message);
  351. }
  352. // We have to have a record ID for the record to update.
  353. if (!$this->record_id) {
  354. $message = t('ChadoRecord::update(). Could not update a record in the ' .
  355. 'table, !table, without a record ID. Current values: !values.',
  356. [
  357. '!table' => $this->table_name,
  358. '!values' => print_r($this->values, TRUE),
  359. ]);
  360. throw new Exception($message);
  361. }
  362. // Build the SQL statement for updating.
  363. $update_args = [];
  364. $sql = 'UPDATE {' . $this->table_name . '} SET ';
  365. foreach ($this->values as $column => $value) {
  366. // We're not updating the primary key so skip that if it's in the values.
  367. if ($column == $this->pkey) {
  368. continue;
  369. }
  370. if ($value === '__NULL__') {
  371. $sql .= $column . ' = NULL, ';
  372. }
  373. else {
  374. $sql .= $column . ' = :' . $column . ', ';
  375. $update_args[':' . $column] = $value;
  376. }
  377. }
  378. // Remove the trailing comma and space.
  379. $sql = substr($sql, 0, -2);
  380. $sql .= ' WHERE ' . $this->pkey . ' = :record_id';
  381. $update_args[':record_id'] = $this->record_id;
  382. // Now try the update.
  383. try {
  384. chado_query($sql, $update_args);
  385. }
  386. catch (Exception $e) {
  387. $message = t('ChadoRecord::update(). Could not update a record in the ' .
  388. 'table, !table, with !record_id as the record ID and the following ' .
  389. 'values: !values. ERROR: !error',
  390. [
  391. '!table' => $this->table_name,
  392. '!record_id' => $this->record_id,
  393. '!values' => print_r($this->values, TRUE),
  394. '!error' => $e->getMessage(),
  395. ]);
  396. throw new Exception($message);
  397. }
  398. }
  399. /**
  400. * Deletes the record that matches the given values.
  401. *
  402. * A record ID must be part of the current values.
  403. *
  404. * @throws Exception
  405. */
  406. public function delete() {
  407. // We have to have a record ID for the record to be deleted.
  408. if (!$this->record_id) {
  409. $message = t('ChadoRecord::delete(). Could not delete a record in the table, !table, without a record ID.',
  410. ['!table' => $this->table_name]);
  411. throw new Exception($message);
  412. }
  413. try {
  414. $sql = 'DELETE FROM {' . $this->table_name . '} WHERE ' . $this->pkey . ' = :record_id';
  415. chado_query($sql, [':record_id' => $this->record_id]);
  416. } catch (Exception $e) {
  417. $message = t('ChadoRecord::delete(). Could not delete a record in the table, !table, with !record_id as the record ID. ERROR: !error',
  418. [
  419. '!table' => $this->table_name,
  420. '!record_id' => $this->record_id,
  421. '!error' => $e->getMessage(),
  422. ]);
  423. throw new Exception($message);
  424. }
  425. }
  426. /**
  427. * A general-purpose setter function to set the column values for the record.
  428. *
  429. * This function should be used prior to insert or update of a record. For
  430. * an update, be sure to include the record ID in the list of values passed
  431. * to the function.
  432. *
  433. * @todo Support options from chado_insert_record: skip_validation.
  434. * @todo Validate the types match what is expected based on the schema.
  435. * @todo Set default values for columns not in this array?
  436. * @todo Support foreign key relationships: lookup the key.
  437. * @todo Support value = [a, b, c] for IN select statements?
  438. *
  439. * @param array $values
  440. * An associative array where the keys are the table column names and
  441. * the values are the record values for each column.
  442. *
  443. * @throws Exception
  444. */
  445. public function setValues($values) {
  446. // Intiailze the values array.
  447. $this->values = [];
  448. // Add the values provided into the values property.
  449. foreach ($values as $column => $value) {
  450. if (in_array($column, $this->column_names)) {
  451. $this->values[$column] = $value;
  452. }
  453. else {
  454. $message = t('ChadoRecord::setValues(). The column named, "!column", ' .
  455. 'does not exist in table: "!table". Values: !values".',
  456. [
  457. '!column' => $column,
  458. '!table' => $this->table_name,
  459. '!values' => print_r($values, TRUE),
  460. ]);
  461. throw new Exception($message);
  462. }
  463. }
  464. // Check whether all required columns are set and indicate using the
  465. // $required_values_set flag for faster checking in insert/update.
  466. $this->missing_required_col = [];
  467. foreach ($this->required_cols as $rcol) {
  468. // It's okay if the primary key is missing, esepecially if the user
  469. // wants to use the find() or insert() functions.
  470. if ($rcol == $this->pkey) {
  471. continue;
  472. }
  473. if (in_array($rcol, array_keys($this->values)) and $this->values[$rcol] === '__NULL__') {
  474. $this->missing_required_col[$rcol] = TRUE;
  475. }
  476. }
  477. // Check to see if the user provided the primary key (record_id).
  478. if (in_array($this->pkey, array_keys($values))) {
  479. $this->record_id = $values[$this->pkey];
  480. }
  481. // Ensure that no values are arrays.
  482. foreach ($values as $column => $value) {
  483. if (is_array($value)) {
  484. $message = t('ChadoRecord::setValues(). The column named, "!column", ' .
  485. 'must be a single value but is currently: "!values". NOTE: this function ' .
  486. 'currently does not support expanding foreign key relationships or ' .
  487. 'multiple values for a given column.',
  488. [
  489. '!column' => $column,
  490. '!table' => $this->table_name,
  491. '!values' => implode('", "', $value),
  492. ]);
  493. throw new Exception($message);
  494. }
  495. }
  496. }
  497. /**
  498. * Returns all values for the record.
  499. *
  500. * @todo We need to follow foreign key constraints.
  501. *
  502. * @return array
  503. */
  504. public function getValues() {
  505. return $this->values;
  506. }
  507. /**
  508. * Sets the value for a specific column.
  509. *
  510. * @todo Support options from chado_insert_record: skip_validation.
  511. * @todo Validate the types match what is expected based on the schema.
  512. * @todo Set default values for columns not in this array?
  513. * @todo Support foreign key relationships: lookup the key.
  514. * @todo Support value = [a, b, c] for IN select statements?
  515. *
  516. * @param string $column_name
  517. * The name of the column to which the value should be set.
  518. * @param $value
  519. * The value to set.
  520. */
  521. public function setValue($column_name, $value) {
  522. // Make sure the column is valid.
  523. if (!in_array($column_name, $this->column_names)) {
  524. $message = t('ChadoRecord::setValue(). The column named, "!column", does ' .
  525. 'not exist in table: "!table".',
  526. [
  527. '!column' => $column_name,
  528. '!table' => $this->table_name,
  529. ]);
  530. throw new Exception($message);
  531. }
  532. // Make sure that the value is not NULL if this is a required field and if
  533. // it doesn't have a default value.
  534. if (in_array($column_name, $this->required_cols) and $value === '__NULL__' and
  535. !array_key_exists($column_name, $this->default_values)) {
  536. $message = t('ChadoRecord::setValue(). The column named, "!column", ' .
  537. 'requires a value for the table: "!table".',
  538. [
  539. '!column' => $column_name,
  540. '!table' => $this->table_name,
  541. ]);
  542. throw new Exception($message);
  543. }
  544. // Remove from the missing list if it was there.
  545. if (isset($this->missing_required_col[$column_name])) {
  546. unset($this->missing_required_col[$column_name]);
  547. }
  548. // Ensure that no values are arrays.
  549. if (is_array($value)) {
  550. $message = t('ChadoRecord::setValue(). The column named, "!column", ' .
  551. 'must be a single value but is currently: "!values". NOTE: this function ' .
  552. 'currently does not support expanding foreign key relationships or ' .
  553. 'multiple values for a given column.',
  554. [
  555. '!column' => $column,
  556. '!table' => $this->table_name,
  557. '!values' => implode('", "', $value),
  558. ]);
  559. throw new Exception($message);
  560. }
  561. $this->values[$column_name] = $value;
  562. }
  563. /**
  564. * Returns the value of a specific column.
  565. *
  566. * @param string $column_name
  567. * The name of a column from the table from which to retrieve the value.
  568. */
  569. public function getValue($column_name) {
  570. // Make sure the column is valid.
  571. if (!in_array($column_name, $this->column_names)) {
  572. $message = t('ChadoRecord::getValue(). The column named, "!column", ' .
  573. 'does not exist in table: "!table".',
  574. [
  575. '!column' => $column_name,
  576. '!table' => $this->table_name,
  577. ]);
  578. throw new Exception($message);
  579. }
  580. return $this->values[$column_name];
  581. }
  582. /**
  583. * Uses the current values given to this object to find a record.
  584. *
  585. * Use the setValues function first to set values for searching, then call
  586. * this function to find matching record. The values provided to the
  587. * setValues function must uniquely identify a record.
  588. *
  589. * @todo Support options from chado_select_record: skip_validation,
  590. * has_record, return_sql, case_insensitive_columns, regex_columns,
  591. * order_by, is_duplicate, pager, limit, offset.
  592. * @todo Support following the foreign key
  593. * @todo Support complex filtering (e.g. fmin > 50)
  594. * @todo Support multiple records being returned?
  595. *
  596. * @return
  597. * The number of matches found. If 1 is returned then the query
  598. * successfully found a match. If 0 then no matching records were found.
  599. *
  600. * @throws Exception
  601. */
  602. public function find() {
  603. // Make sure we have values for this record before searching.
  604. if (empty($this->values)) {
  605. $message = t('ChadoRecord::find(). Could not find a record from ' .
  606. 'the table, !table, without any values.',
  607. ['!table' => $this->table_name]);
  608. throw new Exception($message);
  609. }
  610. // Build the SQL statement for searching.
  611. $select_args = [];
  612. $sql = 'SELECT * FROM {' . $this->table_name . '} WHERE 1=1 ';
  613. foreach ($this->values as $column => $value) {
  614. $sql .= ' AND ' . $column . ' = :' . $column;
  615. $select_args[':' . $column] = $value;
  616. }
  617. try {
  618. $results = chado_query($sql, $select_args);
  619. }
  620. catch (Exception $e) {
  621. $message = t('ChadoRecord::find(). Could not find a record in the ' .
  622. 'table, !table, with the following values: !values. ERROR: !error',
  623. [
  624. '!table' => $this->table_name,
  625. '!values' => print_r($this->values, TRUE),
  626. '!error' => $e->getMessage(),
  627. ]);
  628. throw new Exception($message);
  629. }
  630. // If we only have a single match then we're good and we can update the
  631. // values for this object.
  632. $num_matches = $results->rowCount();
  633. if ($num_matches == 1) {
  634. $record = $results->fetchAssoc();
  635. $this->values = [];
  636. foreach ($record as $column => $value) {
  637. $this->values[$column] = $value;
  638. }
  639. $this->record_id = $record[$this->pkey];
  640. // We are no longer missing any required columns because we loaded
  641. // from the database record.
  642. $this->missing_required_col = [];
  643. }
  644. // Return the number of matches.
  645. return $num_matches;
  646. }
  647. }