SQLアクセス
Nette Databaseは2つの方法を提供します:SQLクエリを自分で記述する(SQLアクセス)、または自動的に生成させる(Explorerを参照)。SQLアクセスはクエリを完全に制御でき、同時に安全な構築を保証します。
データベース接続と設定の詳細については、接続と設定の章を参照してください。
基本的なクエリ
データベースにクエリを実行するには、query()
メソッドを使用します。これは、クエリの結果を表すResultSetオブジェクトを返します。失敗した場合、メソッドは例外をスローします。
クエリの結果はforeach
ループを使用して反復処理するか、ヘルパー関数のいずれかを使用できます。
SQLクエリに値を安全に挿入するには、パラメータ化されたクエリを使用します。Nette Databaseはこれを最大限に簡単にします – SQLクエリの後にカンマと値を追加するだけです:
複数のパラメータがある場合、2つの記述方法があります。SQLクエリにパラメータを「散りばめる」ことができます:
または、まず完全なSQLクエリを記述し、次にすべてのパラメータを追加します:
SQLインジェクションからの保護
パラメータ化されたクエリを使用することが重要なのはなぜでしょうか? なぜなら、SQLインジェクションと呼ばれる攻撃から保護してくれるからです。この攻撃では、攻撃者が独自のSQLコマンドを挿入し、それによってデータベース内のデータを取得または破損させる可能性があります。
変数をSQLクエリに直接挿入しないでください! SQLインジェクションから保護するために、常にパラメータ化されたクエリを使用してください。
クエリ技術
WHERE条件
WHERE条件は連想配列として記述でき、キーはカラム名、値は比較データです。Nette Databaseは、値の型に基づいて最適なSQL演算子を自動的に選択します。
キーで比較演算子を明示的に指定することもできます:
Netteは、null
値や配列などの特殊なケースを自動的に処理します。
否定条件には演算子 NOT
を使用します:
条件を結合するには演算子 AND
が使用されます。これはプレースホルダ ?orを使用して変更できます。
ORDER BYルール
ORDER BY
ソートは配列を使用して記述できます。キーにカラムを指定し、値は昇順でソートするかどうかを示すブール値になります:
データの挿入 (INSERT)
レコードを挿入するには、SQLコマンド INSERT
を使用します。
getInsertId()
メソッドは、最後に挿入された行のIDを返します。一部のデータベース(例:PostgreSQL)では、$database->getInsertId($sequenceId)
を使用してIDを生成するシーケンス名をパラメータとして指定する必要があります。
パラメータとして、ファイル、DateTimeオブジェクト、または列挙型などの特別な値を渡すこともできます。
複数のレコードを一度に挿入する:
複数INSERTは、多くの個別のクエリではなく単一のデータベースクエリが実行されるため、はるかに高速です。
セキュリティ警告:
$values
として検証されていないデータを使用しないでください。潜在的なリスクについて理解してください。
データの更新 (UPDATE)
レコードを更新するには、SQLコマンド UPDATE
を使用します。
影響を受けた行数は $result->getRowCount()
で返されます。
UPDATEには演算子 +=
および -=
を使用できます:
レコードが存在する場合は挿入、存在しない場合は更新する例。ON DUPLICATE KEY UPDATE
テクニックを使用します:
Nette
Databaseが、SQLコマンドのどのコンテキストに配列パラメータを挿入するかを認識し、それに応じてSQLコードを構築することに注意してください。したがって、最初の配列から(id, name, year) VALUES (123, 'Jim', 1978)
を構築し、2番目の配列をname = 'Jim', year = 1978
の形式に変換しました。これについては、SQL構築のヒントセクションで詳しく説明します。
データの削除 (DELETE)
レコードを削除するには、SQLコマンド DELETE
を使用します。削除された行数を取得する例:
SQL構築のヒント
ヒントは、SQLクエリ内の特別なプレースホルダーであり、パラメータ値をSQL式にどのように書き換えるかを示します:
ヒント | 説明 | 自動的に使用される |
---|---|---|
?name |
テーブル名またはカラム名の挿入に使用します | – |
?values |
(key, ...) VALUES (value, ...) を生成します |
INSERT ... ? , REPLACE ... ? |
?set |
割り当て key = value, ... を生成します |
SET ? , KEY UPDATE ? |
?and |
配列内の条件を AND 演算子で結合します |
WHERE ? , HAVING ? |
?or |
配列内の条件を OR 演算子で結合します |
– |
?order |
ORDER BY 句を生成します |
ORDER BY ? , GROUP BY ? |
テーブル名とカラム名をクエリに動的に挿入するには、プレースホルダ ?name
を使用します。Nette
Databaseは、特定のデータベースの規則に従って識別子を正しく処理します(たとえば、MySQLではバッククォートで囲む)。
警告: シンボル ?name
は、検証された入力からのテーブル名とカラム名にのみ使用してください。そうしないと、セキュリティリスクにさらされます。
他のヒントは通常、NetteがSQLクエリを構築する際に賢い自動検出を使用するため(表の3番目の列を参照)、指定する必要はありません。ただし、たとえば
AND
の代わりに OR
を使用して条件を結合したい場合などに使用できます:
特別な値
通常のスカラ型(string、int、bool)に加えて、パラメータとして特別な値を渡すこともできます:
- ファイル:
fopen('image.gif', 'r')
はファイルのバイナリコンテンツを挿入します - 日付と時刻:
DateTime
オブジェクトはデータベース形式に変換されます - 列挙型:
enum
インスタンスはその値に変換されます - SQLリテラル:
Connection::literal('NOW()')
を使用して作成されたものは、クエリに直接挿入されます
datetime
データ型をネイティブにサポートしていないデータベース(SQLiteやOracleなど)の場合、DateTime
はデータベース設定のformatDateTime
項目で指定された値(デフォルト値はU
–
Unixタイムスタンプ)に変換されます。
SQLリテラル
場合によっては、値として直接SQLコードを指定する必要がありますが、これは文字列として解釈されず、エスケープされるべきではありません。この目的のために、Nette\Database\SqlLiteral
クラスのオブジェクトが使用されます。これらはConnection::literal()
メソッドによって作成されます。
または代替案:
SQLリテラルにはパラメータを含めることができます:
これにより、興味深い組み合わせを作成できます:
データの取得
SELECTクエリのショートカット
データ取得を簡略化するために、Connection
はquery()
の呼び出しとそれに続くfetch*()
を組み合わせたいくつかのショートカットを提供します。これらのメソッドはquery()
と同じパラメータ、つまりSQLクエリとオプションのパラメータを受け入れます。fetch*()
メソッドの完全な説明は以下にあります。
fetch($sql, ...$params): ?Row |
クエリを実行し、最初の行をRow オブジェクトとして返します |
fetchAll($sql, ...$params): array |
クエリを実行し、すべての行をRow オブジェクトの配列として返します |
fetchPairs($sql, ...$params): array |
クエリを実行し、最初のカラムがキー、2番目のカラムが値である連想配列を返します |
fetchField($sql, ...$params): mixed |
クエリを実行し、最初の行の最初のフィールドの値を返します |
fetchList($sql, ...$params): ?array |
クエリを実行し、最初の行をインデックス付き配列として返します |
例:
foreach
– 行の反復処理
クエリを実行した後、ResultSetオブジェクトが返され、これにより結果をいくつかの方法で反復処理できます。クエリを実行して行を取得する最も簡単な方法は、foreach
ループで反復処理することです。この方法は、データを段階的に返し、一度にメモリに保存しないため、メモリ効率が最も高くなります。
ResultSet
は一度しか反復処理できません。繰り返し反復処理する必要がある場合は、まずfetchAll()
メソッドなどを使用してデータを配列に読み込む必要があります。
fetch(): ?Row
行をRow
オブジェクトとして返します。これ以上行がない場合はnull
を返します。内部ポインタを次の行に進めます。
fetchAll(): array
ResultSet
から残りのすべての行をRow
オブジェクトの配列として返します。
fetchPairs(string|int|null $key = null, string|int|null $value = null): array
結果を連想配列として返します。最初の引数は配列のキーとして使用されるカラム名を指定し、2番目の引数は値として使用されるカラム名を指定します:
最初のパラメータのみを指定した場合、値は行全体、つまりRow
オブジェクトになります:
キーが重複する場合、最後の行の値が使用されます。キーとしてnull
を使用すると、配列はゼロから数値でインデックス付けされます(衝突は発生しません):
fetchPairs(Closure $callback): array
あるいは、パラメータとしてコールバックを指定できます。これは、各行に対して値自体、またはキーと値のペアのいずれかを返します。
fetchField(): mixed
現在の行の最初のフィールドの値を返します。これ以上行がない場合はnull
を返します。内部ポインタを次の行に進めます。
fetchList(): ?array
行をインデックス付き配列として返します。これ以上行がない場合はnull
を返します。内部ポインタを次の行に進めます。
getRowCount(): ?int
最後のUPDATE
またはDELETE
クエリによって影響を受けた行数を返します。SELECT
の場合、これは返された行数ですが、これは不明な場合があり、その場合メソッドはnull
を返します。
getColumnCount(): ?int
ResultSet
内のカラム数を返します。
クエリ情報
デバッグ目的で、最後に実行されたクエリに関する情報を取得できます:
結果をHTMLテーブルとして表示するには、次を使用できます:
ResultSetはカラムの型に関する情報を提供します:
クエリのロギング
独自のクエリロギングを実装できます。イベントonQuery
は、実行された各クエリの後に呼び出されるコールバックの配列です: