SQL Yaklaşımı
Nette Database iki yol sunar: SQL sorgularını kendiniz yazabilir (SQL yaklaşımı) veya otomatik olarak oluşturulmalarını sağlayabilirsiniz (bkz. Explorer). SQL yaklaşımı size sorgular üzerinde tam kontrol sağlarken, güvenli bir şekilde oluşturulmalarını da garanti eder.
Veritabanı bağlantısı ve yapılandırmasıyla ilgili ayrıntıları Bağlantı ve Yapılandırma bölümünde bulabilirsiniz.
Temel Sorgulama
Veritabanına sorgu yapmak için query()
metodu kullanılır. Bu metot, sorgu sonucunu temsil eden bir ResultSet nesnesi döndürür. Başarısızlık
durumunda metot istisna fırlatır. Sorgu sonucunu foreach
döngüsüyle gezebilir veya yardımcı fonksiyonlardan birini kullanabiliriz.
$result = $database->query('SELECT * FROM users');
foreach ($result as $row) {
echo $row->id;
echo $row->name;
}
SQL sorgularına güvenli bir şekilde değer eklemek için parametreli sorgular kullanırız. Nette Database bunları son derece basit hale getirir – SQL sorgusundan sonra virgül ve değeri eklemeniz yeterlidir:
$database->query('SELECT * FROM users WHERE name = ?', $name);
Birden fazla parametre olduğunda iki yazım seçeneğiniz vardır. SQL sorgusunu parametrelerle “serpiştirebilirsiniz”:
$database->query('SELECT * FROM users WHERE name = ?', $name, 'AND age > ?', $age);
Veya önce tüm SQL sorgusunu yazıp sonra tüm parametreleri ekleyebilirsiniz:
$database->query('SELECT * FROM users WHERE name = ? AND age > ?', $name, $age);
SQL Injection'dan Korunma
Parametreli sorguları kullanmak neden önemlidir? Çünkü sizi, saldırganın kendi SQL deyimlerini ekleyerek veritabanındaki verilere erişebileceği veya zarar verebileceği SQL injection adlı saldırıdan korurlar.
Asla değişkenleri doğrudan SQL sorgusuna eklemeyin! Sizi SQL injection'dan koruyan parametreli sorguları her zaman kullanın.
// ❌ TEHLİKELİ KOD - SQL injection'a karşı savunmasız
$database->query("SELECT * FROM users WHERE name = '$name'");
// ✅ Güvenli parametreli sorgu
$database->query('SELECT * FROM users WHERE name = ?', $name);
Olası güvenlik riskleri hakkında bilgi edinin.
Sorgulama Teknikleri
WHERE Koşulları
WHERE koşullarını, anahtarların sütun adları ve değerlerin karşılaştırma için veriler olduğu ilişkisel bir dizi olarak yazabilirsiniz. Nette Database, değerin tipine göre en uygun SQL operatörünü otomatik olarak seçer.
$database->query('SELECT * FROM users WHERE', [
'name' => 'John',
'active' => true,
]);
// WHERE `name` = 'John' AND `active` = 1
Anahtarda karşılaştırma için operatörü açıkça belirtebilirsiniz:
$database->query('SELECT * FROM users WHERE', [
'age >' => 25, // > operatörünü kullanır
'name LIKE' => '%John%', // LIKE operatörünü kullanır
'email NOT LIKE' => '%example.com%', // NOT LIKE operatörünü kullanır
]);
// WHERE `age` > 25 AND `name` LIKE '%John%' AND `email` NOT LIKE '%example.com%'
Nette, null
değerleri veya diziler gibi özel durumları otomatik olarak işler.
$database->query('SELECT * FROM products WHERE', [
'name' => 'Laptop', // = operatörünü kullanır
'category_id' => [1, 2, 3], // IN kullanır
'description' => null, // IS NULL kullanır
]);
// WHERE `name` = 'Laptop' AND `category_id` IN (1, 2, 3) AND `description` IS NULL
Negatif koşullar için NOT
operatörünü kullanın:
$database->query('SELECT * FROM products WHERE', [
'name NOT' => 'Laptop', // <> operatörünü kullanır
'category_id NOT' => [1, 2, 3], // NOT IN kullanır
'description NOT' => null, // IS NOT NULL kullanır
'id' => [], // atlanır
]);
// WHERE `name` <> 'Laptop' AND `category_id` NOT IN (1, 2, 3) AND `description` IS NOT NULL
Koşulları birleştirmek için AND
operatörü kullanılır. Bu, ?or yer
tutucu sembolü kullanılarak değiştirilebilir.
ORDER BY Kuralları
ORDER BY
sıralaması bir dizi kullanılarak yazılabilir. Anahtarlarda sütunları belirtiriz ve değer, artan
sırada sıralanıp sıralanmayacağını belirleyen bir boolean olur:
$database->query('SELECT id FROM author ORDER BY', [
'id' => true, // artan sırada
'name' => false, // azalan sırada
]);
// SELECT id FROM author ORDER BY `id`, `name` DESC
Veri Ekleme (INSERT)
Kayıt eklemek için INSERT
SQL deyimi kullanılır.
$values = [
'name' => 'John Doe',
'email' => 'john@example.com',
];
$database->query('INSERT INTO users ?', $values);
$userId = $database->getInsertId();
getInsertId()
metodu, son eklenen satırın ID'sini döndürür. Bazı veritabanlarında (örn. PostgreSQL),
ID'nin oluşturulacağı sıra adını $database->getInsertId($sequenceId)
kullanarak parametre olarak belirtmek
gerekir.
Parametre olarak dosyalar, DateTime nesneleri veya enum tipleri gibi speciální hodnoty de iletebiliriz.
Aynı anda birden fazla kayıt ekleme:
$database->query('INSERT INTO users ?', [
['name' => 'User 1', 'email' => 'user1@mail.com'],
['name' => 'User 2', 'email' => 'user2@mail.com'],
]);
Çoklu INSERT çok daha hızlıdır, çünkü birçok tekil sorgu yerine tek bir veritabanı sorgusu yürütülür.
Güvenlik uyarısı: Asla $values
olarak doğrulanmamış verileri kullanmayın. Olası riskler hakkında bilgi edinin.
Veri Güncelleme (UPDATE)
Kayıtları güncellemek için UPDATE
SQL deyimi kullanılır.
// Tek bir kaydı güncelleme
$values = [
'name' => 'John Smith',
];
$result = $database->query('UPDATE users SET ? WHERE id = ?', $values, 1);
Etkilenen satır sayısı $result->getRowCount()
tarafından döndürülür.
UPDATE için +=
ve -=
operatörlerini kullanabiliriz:
$database->query('UPDATE users SET ? WHERE id = ?', [
'login_count+=' => 1, // login_count'u artırma
], 1);
Zaten varsa bir kaydı ekleme veya düzenleme örneği. ON DUPLICATE KEY UPDATE
tekniğini kullanıyoruz:
$values = [
'name' => $name,
'year' => $year,
];
$database->query('INSERT INTO users ? ON DUPLICATE KEY UPDATE ?',
$values + ['id' => $id],
$values,
);
// INSERT INTO users (`id`, `name`, `year`) VALUES (123, 'Jim', 1978)
// ON DUPLICATE KEY UPDATE `name` = 'Jim', `year` = 1978
Nette Database'in, parametreyi dizi ile SQL deyiminin hangi bağlamına eklediğimizi tanıdığına ve buna göre SQL kodunu
oluşturduğuna dikkat edin. Yani ilk diziden (id, name, year) VALUES (123, 'Jim', 1978)
oluştururken, ikincisini
name = 'Jim', year = 1978
şekline dönüştürdü. Buna SQL Oluşturma
İpuçları bölümünde daha ayrıntılı olarak değiniyoruz.
Veri Silme (DELETE)
Kayıtları silmek için DELETE
SQL deyimi kullanılır. Silinen satır sayısını alma örneği:
$count = $database->query('DELETE FROM users WHERE id = ?', 1)
->getRowCount();
SQL Oluşturma İpuçları
İpucu, parametre değerinin SQL ifadesine nasıl çevrileceğini belirten SQL sorgusundaki özel bir yer tutucu semboldür:
İpucu | Açıklama | Otomatik olarak kullanılır |
---|---|---|
?name |
tablo veya sütun adı eklemek için kullanılır | – |
?values |
(key, ...) VALUES (value, ...) üretir |
INSERT ... ? , REPLACE ... ? |
?set |
key = value, ... atamasını üretir |
SET ? , KEY UPDATE ? |
?and |
dizideki koşulları AND operatörüyle birleştirir |
WHERE ? , HAVING ? |
?or |
dizideki koşulları OR operatörüyle birleştirir |
– |
?order |
ORDER BY yan tümcesini üretir |
ORDER BY ? , GROUP BY ? |
Tablo ve sütun adlarını sorguya dinamik olarak eklemek için ?name
yer tutucu sembolü kullanılır. Nette
Database, tanımlayıcıların ilgili veritabanının kurallarına göre (örn. MySQL'de geri tırnak içine alma) doğru bir
şekilde işlenmesini sağlar.
$table = 'users';
$column = 'name';
$database->query('SELECT ?name FROM ?name WHERE id = 1', $column, $table);
// SELECT `name` FROM `users` WHERE id = 1 (MySQL'de)
Uyarı: ?name
sembolünü yalnızca doğrulanmış girdilerden gelen tablo ve sütun adları için
kullanın, aksi takdirde güvenlik riskine
maruz kalırsınız.
Diğer ipuçlarını genellikle belirtmeye gerek yoktur, çünkü Nette SQL sorgusunu oluştururken akıllı otomatik algılama
kullanır (tablonun üçüncü sütununa bakın). Ancak, örneğin koşulları AND
yerine OR
ile
birleştirmek istediğiniz bir durumda kullanabilirsiniz:
$database->query('SELECT * FROM users WHERE ?or', [
'name' => 'John',
'email' => 'john@example.com',
]);
// SELECT * FROM users WHERE `name` = 'John' OR `email` = 'john@example.com'
Özel Değerler
Normal skaler tiplerin (string, int, bool) yanı sıra, parametre olarak özel değerler de iletebilirsiniz:
- dosyalar:
fopen('image.gif', 'r')
dosyanın ikili içeriğini ekler - tarih ve saat:
DateTime
nesneleri veritabanı formatına dönüştürülür - enum tipleri:
enum
örnekleri değerlerine dönüştürülür - SQL literalleri:
Connection::literal('NOW()')
ile oluşturulanlar doğrudan sorguya eklenir
$database->query('INSERT INTO articles ?', [
'title' => 'My Article',
'published_at' => new DateTime,
'content' => fopen('image.png', 'r'),
'state' => Status::Draft,
]);
datetime
veri tipi için yerel desteği olmayan veritabanlarında (SQLite ve Oracle gibi), DateTime
veritabanı yapılandırmasındaki formatDateTime
öğesiyle belirtilen değere dönüştürülür (varsayılan değer U
– unix zaman damgasıdır).
SQL Literalleri
Bazı durumlarda, değer olarak doğrudan SQL kodu belirtmeniz gerekir, ancak bu kodun bir karakter dizisi olarak
anlaşılmaması ve kaçış yapılmaması gerekir. Bunun için Nette\Database\SqlLiteral
sınıfının nesneleri
kullanılır. Bunları Connection::literal()
metodu oluşturur.
$result = $database->query('SELECT * FROM users WHERE', [
'name' => $name,
'year >' => $database::literal('YEAR()'),
]);
// SELECT * FROM users WHERE (`name` = 'Jim') AND (`year` > YEAR())
Veya alternatif olarak:
$result = $database->query('SELECT * FROM users WHERE', [
'name' => $name,
$database::literal('year > YEAR()'),
]);
// SELECT * FROM users WHERE (`name` = 'Jim') AND (year > YEAR())
SQL literalleri parametreler içerebilir:
$result = $database->query('SELECT * FROM users WHERE', [
'name' => $name,
$database::literal('year > ? AND year < ?', $min, $max),
]);
// SELECT * FROM users WHERE `name` = 'Jim' AND (year > 1978 AND year < 2017)
Bu sayede ilginç kombinasyonlar oluşturabiliriz:
$result = $database->query('SELECT * FROM users WHERE', [
'name' => $name,
$database::literal('?or', [
'active' => true,
'role' => $role,
]),
]);
// SELECT * FROM users WHERE `name` = 'Jim' AND (`active` = 1 OR `role` = 'admin')
Veri Alma
SELECT Sorguları için Kısayollar
Veri alımını basitleştirmek için Connection
, query()
çağrısını ardından
fetch*()
ile birleştiren birkaç kısayol sunar. Bu metotlar, query()
ile aynı parametreleri kabul
eder, yani SQL sorgusu ve isteğe bağlı parametreler. fetch*()
metotlarının tam açıklaması aşağıda bulunabilir.
fetch($sql, ...$params): ?Row |
Sorguyu yürütür ve ilk satırı Row nesnesi olarak döndürür |
fetchAll($sql, ...$params): array |
Sorguyu yürütür ve tüm satırları Row nesneleri dizisi olarak döndürür |
fetchPairs($sql, ...$params): array |
Sorguyu yürütür ve ilk sütunun anahtar, ikinci sütunun değer olduğu ilişkisel bir dizi döndürür |
fetchField($sql, ...$params): mixed |
Sorguyu yürütür ve ilk satırdaki ilk hücrenin değerini döndürür |
fetchList($sql, ...$params): ?array |
Sorguyu yürütür ve ilk satırı indeksli bir dizi olarak döndürür |
Örnek:
// fetchField() - ilk hücrenin değerini döndürür
$count = $database->query('SELECT COUNT(*) FROM articles')
->fetchField();
foreach
– Satırlar Üzerinde Yineleme
Sorgu yürütüldükten sonra, sonuçları birkaç şekilde gezmenizi sağlayan bir ResultSet nesnesi döndürülür. Bir sorguyu
yürütmenin ve satırları almanın en kolay yolu foreach
döngüsünde yinelemektir. Bu yöntem bellek açısından
en verimli olanıdır, çünkü verileri kademeli olarak döndürür ve hepsini aynı anda bellekte saklamaz.
$result = $database->query('SELECT * FROM users');
foreach ($result as $row) {
echo $row->id;
echo $row->name;
// ...
}
ResultSet
yalnızca bir kez yinelenebilir. Tekrar tekrar yinelemeniz gerekiyorsa, önce verileri bir
diziye yüklemeniz gerekir, örneğin fetchAll()
metodunu kullanarak.
fetch(): ?Row
Satırı Row
nesnesi olarak döndürür. Başka satır yoksa null
döndürür. Dahili göstericiyi
bir sonraki satıra taşır.
$result = $database->query('SELECT * FROM users');
$row = $result->fetch(); // ilk satırı yükler
if ($row) {
echo $row->name;
}
fetchAll(): array
ResultSet
'ten kalan tüm satırları Row
nesneleri dizisi olarak döndürür.
$result = $database->query('SELECT * FROM users');
$rows = $result->fetchAll(); // tüm satırları yükler
foreach ($rows as $row) {
echo $row->name;
}
fetchPairs (string|int|null $key = null, string|int|null $value = null): array
Sonuçları ilişkisel bir dizi olarak döndürür. İlk argüman, dizide anahtar olarak kullanılacak sütun adını belirtir, ikinci argüman değer olarak kullanılacak sütun adını belirtir:
$result = $database->query('SELECT id, name FROM users');
$names = $result->fetchPairs('id', 'name');
// [1 => 'John Doe', 2 => 'Jane Doe', ...]
Yalnızca ilk parametreyi belirtirsek, değer tüm satır, yani Row
nesnesi olacaktır:
$rows = $result->fetchPairs('id');
// [1 => Row(id: 1, name: 'John'), 2 => Row(id: 2, name: 'Jane'), ...]
Yinelenen anahtarlar durumunda, son satırdaki değer kullanılır. Anahtar olarak null
kullanıldığında, dizi
sıfırdan başlayarak sayısal olarak indekslenir (o zaman çakışma olmaz):
$names = $result->fetchPairs(null, 'name');
// [0 => 'John Doe', 1 => 'Jane Doe', ...]
fetchPairs (Closure $callback): array
Alternatif olarak, parametre olarak her satır için ya değeri ya da anahtar-değer çiftini döndürecek bir geri arama (callback) belirtebilirsiniz.
$result = $database->query('SELECT * FROM users');
$items = $result->fetchPairs(fn($row) => "$row->id - $row->name");
// ['1 - John', '2 - Jane', ...]
// Geri arama ayrıca anahtar & değer çifti içeren bir dizi döndürebilir:
$names = $result->fetchPairs(fn($row) => [$row->name, $row->age]);
// ['John' => 46, 'Jane' => 21, ...]
fetchField(): mixed
Geçerli satırdaki ilk hücrenin değerini döndürür. Başka satır yoksa null
döndürür. Dahili göstericiyi
bir sonraki satıra taşır.
$result = $database->query('SELECT name FROM users');
$name = $result->fetchField(); // ilk satırdan adı yükler
fetchList(): ?array
Satırı indeksli bir dizi olarak döndürür. Başka satır yoksa null
döndürür. Dahili göstericiyi bir
sonraki satıra taşır.
$result = $database->query('SELECT name, email FROM users');
$row = $result->fetchList(); // ['John', 'john@example.com']
getRowCount(): ?int
Son UPDATE
veya DELETE
sorgusundan etkilenen satır sayısını döndürür. SELECT
için bu, döndürülen satır sayısıdır, ancak bu bilinmeyebilir – bu durumda metot null
döndürür.
getColumnCount(): ?int
ResultSet
'teki sütun sayısını döndürür.
Sorgu Bilgileri
Hata ayıklama amacıyla, son yürütülen sorgu hakkında bilgi alabiliriz:
echo $database->getLastQueryString(); // SQL sorgusunu yazdırır
$result = $database->query('SELECT * FROM articles');
echo $result->getQueryString(); // SQL sorgusunu yazdırır
echo $result->getTime(); // yürütme süresini saniye cinsinden yazdırır
Sonucu HTML tablosu olarak görüntülemek için şunu kullanabilirsiniz:
$result = $database->query('SELECT * FROM articles');
$result->dump();
ResultSet, sütun tipleri hakkında bilgi sunar:
$result = $database->query('SELECT * FROM articles');
$types = $result->getColumnTypes();
foreach ($types as $column => $type) {
echo "$column tipi $type->type"; // örn. 'id tipi int'
}
Sorgu Günlüklemesi
Kendi sorgu günlüklememizi uygulayabiliriz. onQuery
olayı, her yürütülen sorgudan sonra çağrılacak geri
arama (callback) dizisidir:
$database->onQuery[] = function ($database, $result) use ($logger) {
$logger->info('Sorgu: ' . $result->getQueryString());
$logger->info('Süre: ' . $result->getTime());
if ($result->getRowCount() > 1000) {
$logger->warning('Büyük sonuç kümesi: ' . $result->getRowCount() . ' satır');
}
};