Hackでデータベース操作の巻

# HackとMySQL

HackにはI/Oが発生する処理に関してasyncで処理することができますが、
いくつかHack専用で用意されているものがあります。

MySQL操作もその一つでHack専用のクラス、メソッドなどが用意されています。
これはHHVMで[facebook/squangle](https://github.com/facebook/squangle)を利用しているでの、
そのままメソッドが使えるようになっています。

この辺りは日本語情報がほとんどありませんが、
ソースが読める方、Hackのリファレンスを読んでいる方であれば特に不自由はないと思います。

## 接続

マニュアルにある例そのままですが、基本的にはPHPとそこまで大きく変わりません。

```hack
namespace Acme\Example\Database;

use type AsyncMysqlConnection;
use type AsyncMysqlConnectionOptions;
use type AsyncMysqlConnectionPool;

class MySqlConnection {
  
  public async function factory(): Awaitable<AsyncMysqlConnection> {
    $pool = new AsyncMysqlConnectionPool(darray[]);
    $option = new AsyncMysqlConnectionOptions();
    $p = await $pool->connectWithOpts(
      '127.0.0.1',
      3306,
      'tests',
      'root',
      'root',
      $option
    );
    return $p;
  }
}

```

AsyncMysqlConnectionPoolを使ってMySQLへ接続します。
connectか、connectWithOptsを使うといいでしょう。
ノンブロッキングになっていますので、当然asyncにする必要があります。
戻りは`Awaitable<AsyncMysqlConnection>`となります。

MySQL接続時にattuributeの設定を行いたい場合は、
`AsyncMysqlConnectionOptions`を使うことができます。

```hack
namespace Acme\Example\Database;

use type AsyncMysqlConnection;

class Contents {
  
  public function __construct(
    private Awaitable<AsyncMysqlConnection> $connection
  ) {}

  public async function getAll(): Awaitable<void> {
    $c = await $this->connection;
    $result = await $c->queryf('SELECT * FROM tests');
    if ($result->numRows() != 0) {
      \var_dump($result->dictRowsTyped());
    }
    return;
  }
}

```

接続後は、そのまま他のクラスで利用できます。
クエリの発行は基本的には`queryf`を利用します。
第一引数はSQLで、fの通り文字列のフォーマットで記述します。

```hack
  $result = await $conn->queryf(
    'SELECT name from test_table WHERE userID = %d',
    $user_id,
  );
```

~~エスケープが必要な場合は、~~  
よくみたらマニュアルにqueryf推奨とありました。
下記のものは特別な理由がない限り使うことはありません。

```hack
  $result = await $conn->queryf(
    'SELECT * from test_table WHERE name = %s',
    $conn->escapeString($user),
  );
```

~~となります。簡単ですね。~~　

なおこのクラスを利用する場合は、文字コード指定なしで接続されるため、
my.cnfで`skip-character-set-client-handshake`を設定しておくといいでしょう。

```
[mysqld]
character-set-server=utf8
skip-character-set-client-handshake

```

## 値取得

queryfなどで返却されると、AsyncMysqlQueryResultが返却されます。

```hack
class AsyncMysqlQueryResult extends AsyncMysqlResult {
  public function __construct() { parent::__construct();}
  public function numRowsAffected(): int { }
  public function lastInsertId(): int { }
  public function numRows(): int { }
  public function mapRows(): Vector<Map<string, ?string>> { }
  public function vectorRows(): Vector<KeyedContainer<int, ?string>> { }
  public function mapRowsTyped(): Vector<Map<string, mixed>> { }
  public function dictRowsTyped(): vec<dict<string, mixed>> { }
  public function vectorRowsTyped(): Vector<KeyedContainer<int, mixed>> { }
  public function rowBlocks() { }
  public function noIndexUsed() : bool { }
  public function recvGtid(): string { }
  public function responseAttributes(): Map<string, string> { }
}
```

numRowsで件数を確認し、好みの型で値を取得できます。
PHPのarrayに近い処理をするのであれば、`dictRowsTyped`
Collectionを利用したければ`mapRows`、`vectorRows`、`mapRowsTyped`、
`vectorRowsTyped`が利用できます。

asyncでの処理となりますので、
`AsyncGenerator`を利用するなどして効率的に処理することができます。
DataMapperなどもこの辺のものを組み合わせるといいでしょう！
(HackのDataMapperライブラリはないのでチャンスです！)

通常の描画が必要なWebアプリケーションでは、データベースの処理などで低速になるケースがありますが、
この辺りのものと、XHPを組み合わせることでレンダリングまで効率よく処理できます。

```hack
use namespace Facebook\XHP\Core as x;
use type Facebook\XHP\HTML\strong;

final xhp class introduction extends x\element {
  protected async function renderAsync(): Awaitable<x\node> {
    return <strong>Hello!</strong>;
  }
}

final xhp class intro_plain_str extends x\primitive {
  protected async function stringifyAsync(): Awaitable<string> {
    return 'Hello!';
  }
}

```

既に紹介したようにXHPは最新バージョンからレンダリング処理はrenderAsyncへと変更されていますので
MySQLから取得したものをそのまま利用してレンダリングまでasyncでできるようになっています。
(Hackのasyncは協調的マルチタスクです。)

asyncは非常に強力なものですので、是非覚えて活用してください！