查询语言.having

Testing Is Documentation

tests/Database/Query/HavingTest.php查询语言.having - 图1

having 和 where 用法几乎一致。

Uses

  1. <?php
  2. use Leevel\Database\Condition;
  3. use Tests\Database\DatabaseTestCase as TestCase;

having 查询条件

最基本的用法为字段 (表达式) 值。

  1. public function testBaseUse(): void
  2. {
  3. $connect = $this->createDatabaseConnectMock();
  4. // 字段 (表达式) 值
  5. $sql = <<<'eot'
  6. [
  7. "SELECT `test_query`.`tid` AS `id`,`test_query`.`tname` AS `value` FROM `test_query` GROUP BY `test_query`.`tid` HAVING `test_query`.`tid` > :test_query_tid",
  8. {
  9. "test_query_tid": [
  10. 5
  11. ]
  12. },
  13. false
  14. ]
  15. eot;
  16. $this->assertSame(
  17. $sql,
  18. $this->varJson(
  19. $connect
  20. ->table('test_query', 'tid as id,tname as value')
  21. ->groupBy('tid')
  22. ->having('tid', '>', 5)
  23. ->findAll(true)
  24. )
  25. );
  26. }

having 查询条件支持数组方式

  1. public function testArray(): void
  2. {
  3. $connect = $this->createDatabaseConnectMock();
  4. $sql = <<<'eot'
  5. [
  6. "SELECT `test_query`.`name` AS `id`,`test_query`.`tname` AS `value` FROM `test_query` GROUP BY `test_query`.`name` HAVING `test_query`.`name` LIKE :test_query_name",
  7. {
  8. "test_query_name": [
  9. "技术"
  10. ]
  11. },
  12. false
  13. ]
  14. eot;
  15. $this->assertSame(
  16. $sql,
  17. $this->varJson(
  18. $connect
  19. ->table('test_query', 'name as id,tname as value')
  20. ->groupBy('name')
  21. ->having(['name', 'like', '技术'])
  22. ->findAll(true)
  23. )
  24. );
  25. }

orHaving 查询条件

  1. public function testOrHaving(): void
  2. {
  3. $connect = $this->createDatabaseConnectMock();
  4. $sql = <<<'eot'
  5. [
  6. "SELECT `test_query`.`name` AS `id`,`test_query`.`tname` AS `value` FROM `test_query` GROUP BY `test_query`.`name` HAVING `test_query`.`name` LIKE :test_query_name OR `test_query`.`tname` LIKE :test_query_tname",
  7. {
  8. "test_query_name": [
  9. "技术"
  10. ],
  11. "test_query_tname": [
  12. "技术"
  13. ]
  14. },
  15. false
  16. ]
  17. eot;
  18. $this->assertSame(
  19. $sql,
  20. $this->varJson(
  21. $connect
  22. ->table('test_query', 'name as id,tname as value')
  23. ->groupBy('name')
  24. ->having(['name', 'like', '技术'])
  25. ->orHaving(['tname', 'like', '技术'])
  26. ->findAll(true)
  27. )
  28. );
  29. }

havingBetween 查询条件

  1. public function testHavingBetween(): void
  2. {
  3. $connect = $this->createDatabaseConnectMock();
  4. $sql = <<<'eot'
  5. [
  6. "SELECT `test_query`.`name` AS `id`,`test_query`.`tname` AS `value` FROM `test_query` GROUP BY `test_query`.`name` HAVING `test_query`.`name` BETWEEN :test_query_name_between0 AND :test_query_name_between1 AND `test_query`.`name` BETWEEN :test_query_name_1_between0 AND :test_query_name_1_between1",
  7. {
  8. "test_query_name_between0": [
  9. 1
  10. ],
  11. "test_query_name_between1": [
  12. 10
  13. ],
  14. "test_query_name_1_between0": [
  15. 1
  16. ],
  17. "test_query_name_1_between1": [
  18. 100
  19. ]
  20. },
  21. false
  22. ]
  23. eot;
  24. $this->assertSame(
  25. $sql,
  26. $this->varJson(
  27. $connect
  28. ->table('test_query', 'name as id,tname as value')
  29. ->groupBy('name')
  30. ->having('name', 'between', [1, 10])
  31. ->havingBetween('name', [1, 100])
  32. ->findAll(true)
  33. )
  34. );
  35. $sql = <<<'eot'
  36. [
  37. "SELECT `test_query`.`name` AS `id`,`test_query`.`tname` AS `value` FROM `test_query` GROUP BY `test_query`.`name` HAVING `test_query`.`name` BETWEEN :test_query_name_between0 AND :test_query_name_between1 AND `test_query`.`tname` BETWEEN :test_query_tname_between0 AND :test_query_tname_between1",
  38. {
  39. "test_query_name_between0": [
  40. 1
  41. ],
  42. "test_query_name_between1": [
  43. 100
  44. ],
  45. "test_query_tname_between0": [
  46. 5
  47. ],
  48. "test_query_tname_between1": [
  49. 22
  50. ]
  51. },
  52. false
  53. ]
  54. eot;
  55. $this->assertSame(
  56. $sql,
  57. $this->varJson(
  58. $connect
  59. ->table('test_query', 'name as id,tname as value')
  60. ->groupBy('name')
  61. ->havingBetween([
  62. ['name', [1, 100]],
  63. ['tname', [5, 22]],
  64. ])
  65. ->findAll(true),
  66. 1
  67. )
  68. );
  69. }

havingNotBetween 查询条件

  1. public function testHavingNotBetween(): void
  2. {
  3. $connect = $this->createDatabaseConnectMock();
  4. $sql = <<<'eot'
  5. [
  6. "SELECT `test_query`.* FROM `test_query` GROUP BY `test_query`.`name` HAVING `test_query`.`id` NOT BETWEEN :test_query_id_notbetween0 AND :test_query_id_notbetween1 AND `test_query`.`id` NOT BETWEEN :test_query_id_1_notbetween0 AND :test_query_id_1_notbetween1",
  7. {
  8. "test_query_id_notbetween0": [
  9. 1
  10. ],
  11. "test_query_id_notbetween1": [
  12. 10
  13. ],
  14. "test_query_id_1_notbetween0": [
  15. 1
  16. ],
  17. "test_query_id_1_notbetween1": [
  18. 100
  19. ]
  20. },
  21. false
  22. ]
  23. eot;
  24. $this->assertSame(
  25. $sql,
  26. $this->varJson(
  27. $connect
  28. ->table('test_query')
  29. ->groupBy('name')
  30. ->having('id', 'not between', [1, 10])
  31. ->havingNotBetween('id', [1, 100])
  32. ->findAll(true)
  33. )
  34. );
  35. }

havingIn 查询条件

  1. public function testHavingIn(): void
  2. {
  3. $connect = $this->createDatabaseConnectMock();
  4. $sql = <<<'eot'
  5. [
  6. "SELECT `test_query`.* FROM `test_query` GROUP BY `test_query`.`name` HAVING `test_query`.`id` IN (:test_query_id_in0,:test_query_id_in1) AND `test_query`.`num` IN (:test_query_num_in0,:test_query_num_in1)",
  7. {
  8. "test_query_id_in0": [
  9. 2
  10. ],
  11. "test_query_id_in1": [
  12. 50
  13. ],
  14. "test_query_num_in0": [
  15. 2
  16. ],
  17. "test_query_num_in1": [
  18. 50
  19. ]
  20. },
  21. false
  22. ]
  23. eot;
  24. $this->assertSame(
  25. $sql,
  26. $this->varJson(
  27. $connect
  28. ->table('test_query')
  29. ->groupBy('name')
  30. ->having('id', 'in', [2, 50])
  31. ->havingIn('num', [2, 50])
  32. ->findAll(true)
  33. )
  34. );
  35. }

havingNotIn 查询条件

  1. public function testHavingNotIn(): void
  2. {
  3. $connect = $this->createDatabaseConnectMock();
  4. $sql = <<<'eot'
  5. [
  6. "SELECT `test_query`.* FROM `test_query` GROUP BY `test_query`.`name` HAVING `test_query`.`id` NOT IN (:test_query_id_in0,:test_query_id_in1) AND `test_query`.`num` NOT IN (:test_query_num_in0,:test_query_num_in1)",
  7. {
  8. "test_query_id_in0": [
  9. 2
  10. ],
  11. "test_query_id_in1": [
  12. 50
  13. ],
  14. "test_query_num_in0": [
  15. 2
  16. ],
  17. "test_query_num_in1": [
  18. 50
  19. ]
  20. },
  21. false
  22. ]
  23. eot;
  24. $this->assertSame(
  25. $sql,
  26. $this->varJson(
  27. $connect
  28. ->table('test_query')
  29. ->groupBy('name')
  30. ->having('id', 'not in', [2, 50])
  31. ->havingNotIn('num', [2, 50])
  32. ->findAll(true)
  33. )
  34. );
  35. }

havingNull 查询条件

  1. public function testHavingNull(): void
  2. {
  3. $connect = $this->createDatabaseConnectMock();
  4. $sql = <<<'eot'
  5. [
  6. "SELECT `test_query`.* FROM `test_query` GROUP BY `test_query`.`name` HAVING `test_query`.`id` IS NULL AND `test_query`.`num` IS NULL",
  7. [],
  8. false
  9. ]
  10. eot;
  11. $this->assertSame(
  12. $sql,
  13. $this->varJson(
  14. $connect
  15. ->table('test_query')
  16. ->groupBy('name')
  17. ->having('id', 'null')
  18. ->havingNull('num')
  19. ->findAll(true)
  20. )
  21. );
  22. }

havingNotNull 查询条件

  1. public function testHavingNotNull(): void
  2. {
  3. $connect = $this->createDatabaseConnectMock();
  4. $sql = <<<'eot'
  5. [
  6. "SELECT `test_query`.* FROM `test_query` GROUP BY `test_query`.`name` HAVING `test_query`.`id` IS NOT NULL AND `test_query`.`num` IS NOT NULL",
  7. [],
  8. false
  9. ]
  10. eot;
  11. $this->assertSame(
  12. $sql,
  13. $this->varJson(
  14. $connect
  15. ->table('test_query')
  16. ->groupBy('name')
  17. ->having('id', 'not null')
  18. ->havingNotNull('num')
  19. ->findAll(true)
  20. )
  21. );
  22. }

having 查询条件未指定值默认为 null

  1. public function testHavingDefaultNull(): void
  2. {
  3. $connect = $this->createDatabaseConnectMock();
  4. $sql = <<<'eot'
  5. [
  6. "SELECT `test_query`.* FROM `test_query` GROUP BY `test_query`.`name` HAVING `test_query`.`id` IS NULL",
  7. [],
  8. false
  9. ]
  10. eot;
  11. $this->assertSame(
  12. $sql,
  13. $this->varJson(
  14. $connect
  15. ->table('test_query')
  16. ->groupBy('name')
  17. ->having('id')
  18. ->findAll(true)
  19. )
  20. );
  21. }

having 查询条件指定值为 null

  1. public function testHavingEqualNull(): void
  2. {
  3. $connect = $this->createDatabaseConnectMock();
  4. $sql = <<<'eot'
  5. [
  6. "SELECT `test_query`.* FROM `test_query` GROUP BY `test_query`.`name` HAVING `test_query`.`id` IS NULL",
  7. [],
  8. false
  9. ]
  10. eot;
  11. $this->assertSame(
  12. $sql,
  13. $this->varJson(
  14. $connect
  15. ->table('test_query')
  16. ->groupBy('name')
  17. ->having('id', '=', null)
  18. ->findAll(true)
  19. )
  20. );
  21. }

havingLike 查询条件

  1. public function testHavingLike(): void
  2. {
  3. $connect = $this->createDatabaseConnectMock();
  4. $sql = <<<'eot'
  5. [
  6. "SELECT `test_query`.* FROM `test_query` GROUP BY `test_query`.`name` HAVING `test_query`.`id` LIKE :test_query_id AND `test_query`.`num` LIKE :test_query_num",
  7. {
  8. "test_query_id": [
  9. "123"
  10. ],
  11. "test_query_num": [
  12. "55"
  13. ]
  14. },
  15. false
  16. ]
  17. eot;
  18. $this->assertSame(
  19. $sql,
  20. $this->varJson(
  21. $connect
  22. ->table('test_query')
  23. ->groupBy('name')
  24. ->having('id', 'like', '123')
  25. ->havingLike('num', '55')
  26. ->findAll(true)
  27. )
  28. );
  29. }

havingNotLike 查询条件

  1. public function testHavingNotLike(): void
  2. {
  3. $connect = $this->createDatabaseConnectMock();
  4. $sql = <<<'eot'
  5. [
  6. "SELECT `test_query`.* FROM `test_query` GROUP BY `test_query`.`name` HAVING `test_query`.`id` NOT LIKE :test_query_id AND `test_query`.`num` NOT LIKE :test_query_num",
  7. {
  8. "test_query_id": [
  9. "123"
  10. ],
  11. "test_query_num": [
  12. "55"
  13. ]
  14. },
  15. false
  16. ]
  17. eot;
  18. $this->assertSame(
  19. $sql,
  20. $this->varJson(
  21. $connect
  22. ->table('test_query')
  23. ->groupBy('name')
  24. ->having('id', 'not like', '123')
  25. ->havingNotLike('num', '55')
  26. ->findAll(true)
  27. )
  28. );
  29. }

having 查询条件支持分组

  1. public function testHavingGroup(): void
  2. {
  3. $connect = $this->createDatabaseConnectMock();
  4. $sql = <<<'eot'
  5. [
  6. "SELECT `test_query`.* FROM `test_query` GROUP BY `test_query`.`id` HAVING `test_query`.`id` = :test_query_id OR (`test_query`.`votes` > :test_query_votes AND `test_query`.`title` <> :test_query_title)",
  7. {
  8. "test_query_votes": [
  9. 100
  10. ],
  11. "test_query_title": [
  12. "Admin"
  13. ],
  14. "test_query_id": [
  15. 5
  16. ]
  17. },
  18. false
  19. ]
  20. eot;
  21. $this->assertSame(
  22. $sql,
  23. $this->varJson(
  24. $connect
  25. ->table('test_query')
  26. ->groupBy('id')
  27. ->having('id', 5)
  28. ->orHaving(function ($select) {
  29. $select
  30. ->having('votes', '>', 100)
  31. ->having('title', '<>', 'Admin');
  32. })
  33. ->findAll(true)
  34. )
  35. );
  36. }

having 查询条件支持表达式

  1. public function testConditionalExpression(): void
  2. {
  3. $connect = $this->createDatabaseConnectMock();
  4. $sql = <<<'eot'
  5. [
  6. "SELECT `test_query`.`posts`,`test_query`.`value`,concat(\"tt_\",`test_query`.`id`) FROM `test_query` GROUP BY `test_query`.`id` HAVING concat(\"hello_\",`test_query`.`posts`) = `test_query`.`id`",
  7. [],
  8. false
  9. ]
  10. eot;
  11. $this->assertSame(
  12. $sql,
  13. $this->varJson(
  14. $connect
  15. ->table('test_query', 'posts,value,'.Condition::raw('concat("tt_",[id])'))
  16. ->groupBy('id')
  17. ->having(Condition::raw('concat("hello_",[posts])'), '=', Condition::raw('[id]'))
  18. ->findAll(true)
  19. )
  20. );
  21. }

having 查询条件支持二维数组的键值为字段

  1. public function testArrayKeyAsField(): void
  2. {
  3. $connect = $this->createDatabaseConnectMock();
  4. $sql = <<<'eot'
  5. [
  6. "SELECT `test_query`.* FROM `test_query` GROUP BY `test_query`.`id` HAVING `test_query`.`id` = :test_query_id AND `test_query`.`name` IN (:test_query_name_in0,:test_query_name_in1,:test_query_name_in2) AND `test_query`.`weidao` BETWEEN :test_query_weidao_between0 AND :test_query_weidao_between1 AND `test_query`.`value` IS NULL AND `test_query`.`remark` IS NOT NULL AND `test_query`.`goods` = :test_query_goods AND `test_query`.`hello` = :test_query_hello",
  7. {
  8. "test_query_id": [
  9. "故事"
  10. ],
  11. "test_query_name_in0": [
  12. 1
  13. ],
  14. "test_query_name_in1": [
  15. 2
  16. ],
  17. "test_query_name_in2": [
  18. 3
  19. ],
  20. "test_query_weidao_between0": [
  21. "40"
  22. ],
  23. "test_query_weidao_between1": [
  24. "100"
  25. ],
  26. "test_query_goods": [
  27. "东亚商品"
  28. ],
  29. "test_query_hello": [
  30. "world"
  31. ]
  32. },
  33. false
  34. ]
  35. eot;
  36. $this->assertSame(
  37. $sql,
  38. $this->varJson(
  39. $connect
  40. ->table('test_query')
  41. ->groupBy('id')
  42. ->having([
  43. 'id' => ['=', '故事'],
  44. 'name' => ['in', [1, 2, 3]],
  45. 'weidao' => ['between', '40,100'],
  46. 'value' => 'null',
  47. 'remark' => ['not null'],
  48. 'goods' => '东亚商品',
  49. 'hello' => ['world'],
  50. ])
  51. ->findAll(true)
  52. )
  53. );
  54. }

having 查询条件支持字符串语法 :string

  1. public function testSupportString(): void
  2. {
  3. $connect = $this->createDatabaseConnectMock();
  4. $sql = <<<'eot'
  5. [
  6. "SELECT `test_query`.* FROM `test_query` GROUP BY `test_query`.`id` HAVING `test_query`.`name` = 11 and `test_query`.`value` = 22 and concat(\"tt_\",`test_query`.`id`)",
  7. [],
  8. false
  9. ]
  10. eot;
  11. $this->assertSame(
  12. $sql,
  13. $this->varJson(
  14. $connect
  15. ->table('test_query')
  16. ->groupBy('id')
  17. ->having([':string' => Condition::raw('[name] = 11 and [test_query.value] = 22 and concat("tt_",[id])')])
  18. ->findAll(true)
  19. )
  20. );
  21. }

having 查询条件支持分组语法 :subor 和 suband

  1. public function testSupportSubandSubor(): void
  2. {
  3. $connect = $this->createDatabaseConnectMock();
  4. $sql = <<<'eot'
  5. [
  6. "SELECT `test_query`.* FROM `test_query` GROUP BY `test_query`.`id` HAVING `test_query`.`hello` = :test_query_hello OR (`test_query`.`id` LIKE :test_query_subor_test_query_id)",
  7. {
  8. "test_query_subor_test_query_id": [
  9. "你好"
  10. ],
  11. "test_query_hello": [
  12. "world"
  13. ]
  14. },
  15. false
  16. ]
  17. eot;
  18. $this->assertSame(
  19. $sql,
  20. $this->varJson(
  21. $connect
  22. ->table('test_query')
  23. ->groupBy('id')
  24. ->having(
  25. [
  26. 'hello' => 'world',
  27. ':subor' => ['id', 'like', '你好'],
  28. ]
  29. )
  30. ->findAll(true)
  31. )
  32. );
  33. }

having 查询条件支持分组语法 :subor 和 suband 任意嵌套

  1. public function testSupportSubandSuborMore(): void
  2. {
  3. $connect = $this->createDatabaseConnectMock();
  4. $sql = <<<'eot'
  5. [
  6. "SELECT `test_query`.* FROM `test_query` GROUP BY `test_query`.`id` HAVING `test_query`.`hello` = :test_query_hello OR (`test_query`.`id` LIKE :test_query_subor_test_query_id AND `test_query`.`value` = :test_query_subor_test_query_value) AND (`test_query`.`id` LIKE :test_query_suband_test_query_id OR `test_query`.`value` = :test_query_suband_test_query_value OR (`test_query`.`child_one` > :test_query_subor_test_query_child_one AND `test_query`.`child_two` LIKE :test_query_subor_test_query_child_two))",
  7. {
  8. "test_query_subor_test_query_child_one": [
  9. "123"
  10. ],
  11. "test_query_subor_test_query_child_two": [
  12. "123"
  13. ],
  14. "test_query_suband_test_query_id": [
  15. "你好"
  16. ],
  17. "test_query_suband_test_query_value": [
  18. "helloworld"
  19. ],
  20. "test_query_subor_test_query_id": [
  21. "你好"
  22. ],
  23. "test_query_subor_test_query_value": [
  24. "helloworld"
  25. ],
  26. "test_query_hello": [
  27. "111"
  28. ]
  29. },
  30. false
  31. ]
  32. eot;
  33. $this->assertSame(
  34. $sql,
  35. $this->varJson(
  36. $connect
  37. ->table('test_query')
  38. ->groupBy('id')
  39. ->having(
  40. [
  41. 'hello' => '111',
  42. ':subor' => [
  43. ['id', 'like', '你好'],
  44. ['value', '=', 'helloworld'],
  45. ],
  46. ':suband' => [
  47. ':logic' => 'or',
  48. ['id', 'like', '你好'],
  49. ['value', '=', 'helloworld'],
  50. ':subor' => [
  51. ['child_one', '>', '123'],
  52. ['child_two', 'like', '123'],
  53. ],
  54. ],
  55. ]
  56. )
  57. ->findAll(true),
  58. 1
  59. )
  60. );
  61. }

having 查询条件字段可以指定表

字段条件用法和 table 中的字段用法一致,详情可以查看《查询语言.table》。

  1. public function testHavingFieldWithTable(): void
  2. {
  3. $connect = $this->createDatabaseConnectMock();
  4. $sql = <<<'eot'
  5. [
  6. "SELECT `test_query`.* FROM `test_query` GROUP BY `test_query`.`name` HAVING `test_query`.`name` = :test_query_name",
  7. {
  8. "test_query_name": [
  9. 1
  10. ]
  11. },
  12. false
  13. ]
  14. eot;
  15. $this->assertSame(
  16. $sql,
  17. $this->varJson(
  18. $connect
  19. ->table('test_query')
  20. ->groupBy('name')
  21. ->having('test_query.name', '=', 1)
  22. ->findAll(true)
  23. )
  24. );
  25. }

having 查询条件支持复杂的子查询

  1. public function testHavingInArrayItemIsClosure(): void
  2. {
  3. $connect = $this->createDatabaseConnectMock();
  4. $sql = <<<'eot'
  5. [
  6. "SELECT `test_query`.* FROM `test_query` GROUP BY `test_query`.`name` HAVING `test_query`.`id` IN ((SELECT `test_query_subsql`.`id` FROM `test_query_subsql` WHERE `test_query_subsql`.`id` = :test_query_id_test_query_subsql_id),:test_query_id_in1)",
  7. {
  8. "test_query_id_test_query_subsql_id": [
  9. 1
  10. ],
  11. "test_query_id_in1": [
  12. 100
  13. ]
  14. },
  15. false
  16. ]
  17. eot;
  18. $this->assertSame(
  19. $sql,
  20. $this->varJson(
  21. $connect
  22. ->table('test_query')
  23. ->groupBy('name')
  24. ->havingIn('id', [function ($select) {
  25. $select
  26. ->table('test_query_subsql', 'id')
  27. ->where('id', 1);
  28. }, 100])
  29. ->findAll(true)
  30. )
  31. );
  32. }

havingRaw 查询条件

  1. public function testHavingRaw(): void
  2. {
  3. $connect = $this->createDatabaseConnectMock();
  4. $sql = <<<'eot'
  5. [
  6. "SELECT `test_query`.`name` AS `id`,`test_query`.`tname` AS `value`,`test_query`.`id` FROM `test_query` GROUP BY `test_query`.`name` HAVING FIND_IN_SET(1, `test_query`.`id`)",
  7. [],
  8. false
  9. ]
  10. eot;
  11. $this->assertSame(
  12. $sql,
  13. $this->varJson(
  14. $connect
  15. ->table('test_query', 'name as id,tname as value,id')
  16. ->groupBy('name')
  17. ->havingRaw('FIND_IN_SET(1, `test_query`.`id`)')
  18. ->findAll(true)
  19. )
  20. );
  21. }

orHavingRaw 查询条件

  1. public function testOrHavingRaw(): void
  2. {
  3. $connect = $this->createDatabaseConnectMock();
  4. $sql = <<<'eot'
  5. [
  6. "SELECT `test_query`.`name` AS `id`,`test_query`.`tname` AS `value`,`test_query`.`id`,`test_query`.`value` FROM `test_query` GROUP BY `test_query`.`name` HAVING FIND_IN_SET(1, `test_query`.`id`) OR FIND_IN_SET(1, `test_query`.`value`)",
  7. [],
  8. false
  9. ]
  10. eot;
  11. $this->assertSame(
  12. $sql,
  13. $this->varJson(
  14. $connect
  15. ->table('test_query', 'name as id,tname as value,id,value')
  16. ->groupBy('name')
  17. ->havingRaw('FIND_IN_SET(1, `test_query`.`id`)')
  18. ->orHavingRaw('FIND_IN_SET(1, `test_query`.`value`)')
  19. ->findAll(true)
  20. )
  21. );
  22. }