SQL Yolu

Nette Veritabanı ile iki şekilde çalışabilirsiniz: SQL sorguları yazarak (SQL yolu) veya SQL'in otomatik olarak oluşturulmasına izin vererek(Explorer yolu). SQL yolu, yapıları üzerinde tam kontrol sahibi olurken güvenli bir şekilde sorgular oluşturmanıza olanak tanır.

Veritabanı bağlantısı kurulumu hakkında ayrıntılar için Bağlantı ve Yapılandırma bölümüne bakın.

Temel Sorgulama

query() yöntemi veritabanı sorgularını yürütür ve sonucu temsil eden bir ResultSet nesnesi döndürür. Sorgu başarısız olursa, yöntem bir istisna atar. Bir foreach döngüsü kullanarak sorgu sonucu boyunca döngü yapabilir veya yardımcı fonksiyonlardan birini kullanabilirsiniz.

$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 parametrelendirilmiş sorgular kullanın. Nette Database bunu çok basit hale getirir: SQL sorgusuna virgül ve değer eklemeniz yeterlidir.

$database->query('SELECT * FROM users WHERE name = ?', $name);

Birden fazla parametre için SQL sorgusunu parametrelerle iç içe geçirebilirsiniz:

$database->query('SELECT * FROM users WHERE name = ?', $name, 'AND age > ?', $age);

Ya da önce tüm SQL sorgusunu yazın ve ardından tüm parametreleri ekleyin:

$database->query('SELECT * FROM users WHERE name = ? AND age > ?', $name, $age);

SQL Enjeksiyonuna Karşı Koruma

Parametrelendirilmiş sorgular kullanmak neden önemlidir? Çünkü sizi, saldırganların veritabanı verilerini manipüle etmek veya bunlara erişmek için kötü niyetli SQL komutları enjekte edebildiği SQL enjeksiyon saldırılarından korurlar.

**Asla değişkenleri doğrudan bir SQL sorgusuna eklemeyin! SQL enjeksiyonuna karşı kendinizi korumak için her zaman parametrelendirilmiş sorgular kullanın.

// ❌ GÜVENLİ OLMAYAN KOD - SQL enjeksiyonuna karşı savunmasız
$database->query("SELECT * FROM users WHERE name = '$name'");

// ✅ Güvenli parametrelendirilmiş sorgu
$database->query('SELECT * FROM users WHERE name = ?', $name);

Potansiyel güvenlik riskleri hakkında bilgi sahibi olduğunuzdan emin olun.

Sorgu Teknikleri

NEREDE Koşullar

WHERE koşullarını, anahtarların sütun adları ve değerlerin karşılaştırılacak veriler olduğu bir ilişkisel dizi olarak yazabilirsiniz. Nette Database, değer türüne 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

Operatörü anahtarda açıkça da 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 işlecini kullanır
]);
// WHERE `age` > 25 AND `name` LIKE '%John%' AND `email` NOT LIKE '%example.com%'

null değerleri veya diziler gibi özel durumlar otomatik olarak ele alınır:

$database->query('SELECT * FROM products WHERE', [
	'name' => 'Laptop',         // = operatörünü kullanır
	'category_id' => [1, 2, 3], // IN kullanır
	'description' => null,      // kullanır IS NULL
]);
// 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,      // uses IS NOT NULL
	'id' => [],                     // atlandı
]);
// WHERE `name` <> 'Laptop' AND `category_id` NOT IN (1, 2, 3) AND `description` IS NOT NULL

Varsayılan olarak, koşullar AND operatörü kullanılarak birleştirilir. Bu davranışı ?or yer tutucusunu kullanarak değiştirebilirsiniz.

ORDER BY Kurallar

ORDER BY cümlesi, anahtarların sütunları temsil ettiği ve değerlerin artan sırayı gösteren boolean'lar olduğu bir dizi olarak tanımlanabilir:

$database->query('SELECT id FROM author ORDER BY', [
	'id' => true,  // artan
	'name' => false, // azalan
]);
// SELECT id FROM author ORDER BY `id`, `name` DESC

Veri Ekleme (INSERT)

Kayıt eklemek için SQL INSERT deyimini kullanın.

$values = [
	'name' => 'John Doe',
	'email' => 'john@example.com',
];
$database->query('INSERT INTO users ?', $values);
$userId = $database->getInsertId();

getInsertId() yöntemi, son eklenen satırın kimliğini döndürür. Bazı veritabanları için (örneğin PostgreSQL), $database->getInsertId($sequenceId) adresini kullanarak sıra adını belirtmeniz gerekir.

Dosyalar, DateTime nesneleri veya enum türleri gibi özel değerleri de parametre olarak aktarabilirsiniz.

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'],
]);

Toplu INSERT gerçekleştirmek çok daha hızlıdır çünkü birden fazla ayrı sorgu yerine yalnızca tek bir veritabanı sorgusu yürütülür.

Güvenlik Notu: Doğrulanmamış verileri asla $values olarak kullanmayın. Olası riskler hakkında bilgi edinin.

Veri Güncelleme (UPDATE)

Kayıtları güncellemek için SQL UPDATE deyimini kullanın.

// Tek bir kaydı güncelleme
$values = [
	'name' => 'John Smith',
];
$result = $database->query('UPDATE users SET ? WHERE id = ?', $values, 1);

Etkilenen satır sayısını $result->getRowCount() adresini kullanarak kontrol edebilirsiniz.

+= ve -= operatörlerini UPDATE adresinde kullanabilirsiniz:

$database->query('UPDATE users SET ? WHERE id = ?', [
	'login_count+=' => 1, // increment login_count
], 1);

Zaten mevcut olan bir kaydı eklemek veya güncellemek için ON DUPLICATE KEY UPDATE tekniğini kullanın:

$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 dizi içeren bir parametrenin kullanıldığı SQL komutunun bağlamını tanıdığını ve SQL kodunu buna göre oluşturduğunu unutmayın. Örneğin, ilk diziden (id, name, year) VALUES (123, 'Jim', 1978) oluştururken, ikincisini name = 'Jim', year = 1978'a dönüştürür. Bu konu SQL oluşturmak için ipuçları bölümünde daha ayrıntılı olarak ele alınmaktadır.

Veri Silme (DELETE)

Kayıtları silmek için SQL DELETE deyimini kullanın. Silinen satır sayısı ile örnek:

$count = $database->query('DELETE FROM users WHERE id = ?', 1)
	->getRowCount();

SQL Oluşturma İpuçları

SQL yer tutucuları, parametre değerlerinin SQL ifadelerine nasıl dahil edileceğini kontrol etmenize olanak tanır:

İpucu Açıklama Otomatik Olarak Şunun İçin Kullanılır
?name Tablo veya sütun adları için kullanılır
?values (key, ...) VALUES (value, ...) INSERT ... ? oluşturur, REPLACE ... ?
?set Atamaları oluşturur key = value, ... SET ?, KEY UPDATE ?
?and Bir dizideki koşulları AND WHERE ? ile birleştirir, HAVING ?
?or Bir dizideki koşulları OR ile birleştirir
?order ORDER BY cümlesini oluşturur ORDER BY ?, GROUP BY ?

Tablo veya sütun adlarını dinamik olarak eklemek için ?name yer tutucusunu kullanın. Nette Veritabanı, veritabanının kurallarına göre uygun kaçış sağlar (örneğin, MySQL için backtick içine alma).

$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 yer tutucusunu yalnızca doğrulanmış tablo ve sütun adları için kullanın. Aksi takdirde, güvenlik açıkları riskiyle karşı karşıya kalırsınız.

Nette SQL sorgularını oluştururken akıllı otomatik algılama kullandığından (tablonun üçüncü sütununa bakın) diğer ipuçlarını belirtmek genellikle gerekli değildir. Ancak, AND yerine OR kullanarak koşulları birleştirmek istediğiniz durumlarda bunları 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

Standart skaler tiplere (örneğin, string, int, bool) ek olarak, özel değerleri de parametre olarak aktarabilirsiniz:

  • Dosyalar: Bir dosyanın ikili içeriğini eklemek için fopen('file.png', 'r') adresini kullanın.
  • Tarih ve Saat: DateTime nesneleri otomatik olarak veritabanının tarih biçimine dönüştürülür.
  • Enum Değerleri: enum örnekleri karşılık gelen değerlerine dönüştürülür.
  • SQL Literals: Connection::literal('NOW()') kullanılarak oluşturulan bunlar 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 türü için yerel desteği olmayan veritabanları için (örneğin, SQLite ve Oracle), DateTime değerleri formatDateTime yapılandırma seçeneğine göre dönüştürülür (varsayılan: Unix zaman damgası için U ).

SQL Literatürleri

Bazı durumlarda, ham SQL kodunu bir dize olarak ele almadan veya kaçış yapmadan bir değer olarak eklemeniz gerekebilir. Bunun için, Connection::literal() yöntemi kullanılarak oluşturulabilen Nette\Database\SqlLiteral sınıfının nesnelerini kullanın.

$result = $database->query('SELECT * FROM users WHERE', [
	'name' => $name,
	'year >' => $database::literal('YEAR()'),
]);
// SELECT * FROM users WHERE (`name` = 'Jim') AND (`year` > YEAR())

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 değişmezleri parametre de içerebilir:

$result = $database->query('SELECT * FROM users WHERE', [
	'name' => $name,
	$database::literal('year > ? AND year &lt; ?', $min, $max),
]);
// SELECT * FROM users WHERE `name` = 'Jim' AND (year > 1978 AND year < 2017)

Bu, esnek kombinasyonlara olanak sağlar:

$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 Getirme

SELECT Sorguları için Kısayollar

Veri alımını basitleştirmek için Connection sınıfı, query() çağrısını sonraki bir fetch*() çağrısıyla birleştiren birkaç kısayol sağlar. Bu yöntemler query() ile aynı parametreleri, yani bir SQL sorgusu ve isteğe bağlı parametreleri kabul eder.
fetch*() yöntemlerinin ayrıntılı bir açıklaması aşağıda bulunabilir.

fetch($sql, ...$params): ?Row Sorguyu çalıştırır ve ilk satırı bir Row nesnesi olarak getirir.
fetchAll($sql, ...$params): array Sorguyu çalıştırır ve tüm satırları Row nesnelerinden oluşan bir dizi olarak getirir.
fetchPairs($sql, ...$params): array Sorguyu çalıştırır ve ilk sütunun anahtar, ikincisinin değer olduğu bir ilişkisel dizi getirir.
fetchField($sql, ...$params): mixed Sorguyu çalıştırır ve ilk satırdaki ilk hücrenin değerini getirir.
fetchList($sql, ...$params): ?array Sorguyu yürütür ve ilk satırı dizinlenmiş bir dizi olarak getirir.

Örnek:

// fetchField() - ilk hücrenin değerini döndürür
$count = $database->query('SELECT COUNT(*) FROM articles')
	->fetchField();

foreach – Satırlar Üzerinde Yineleme

Bir sorgu yürütüldükten sonra, sonuçlar üzerinde çeşitli şekillerde yineleme yapmanıza olanak tanıyan bir ResultSet nesnesi döndürülür. Satırları getirmenin en basit ve bellek açısından en verimli yolu, bir foreach döngüsünde yinelemektir. Bu yöntem satırları teker teker işler ve tüm verilerin bir kerede bellekte depolanmasını önler.

$result = $database->query('SELECT * FROM users');

foreach ($result as $row) {
	echo $row->id;
	echo $row->name;
	//...
}

ResultSet yalnızca bir kez yinelenebilir. Üzerinde birden çok kez yineleme yapmanız gerekiyorsa, önce verileri bir diziye yüklemeniz, örneğin fetchAll() yöntemini kullanmanız gerekir.

fetch(): ?Row

Sorguyu çalıştırır ve Row nesnesi olarak tek bir satır getirir. Başka satır yoksa null döndürür. Bu yöntem dahili işaretçiyi bir sonraki satıra ilerletir.

$result = $database->query('SELECT * FROM users');
$row = $result->fetch(); // ilk satırı getirir
if ($row) {
	echo $row->name;
}

fetchAll(): array

Kalan tüm satırları ResultSet adresinden Row nesnelerinden oluşan bir dizi olarak getirir.

$result = $database->query('SELECT * FROM users');
$rows = $result->fetchAll(); // tüm satırları getirir
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 getirir. İlk bağımsız değişken anahtar olarak kullanılacak sütunu, ikincisi ise değer olarak kullanılacak sütunu belirtir:

$result = $database->query('SELECT id, name FROM users');
$names = $result->fetchPairs('id', 'name');
// [1 => 'John Doe', 2 => 'Jane Doe', ...]

Yalnızca ilk parametre sağlanırsa, değer tüm satır olacaktır (bir Row nesnesi olarak):

$rows = $result->fetchPairs('id');
// [1 => Satır(id: 1, ad: 'John'), 2 => Satır(id: 2, ad: 'Jane'), ...]

Anahtar olarak null geçilirse, dizi sıfırdan başlayarak sayısal olarak indekslenecektir:

$names = $result->fetchPairs(null, 'name');
// [0 => 'John Doe', 1 => 'Jane Doe', ...]

fetchPairs (Closure $callback)array

Alternatif olarak, her satır için anahtar-değer çiftlerini veya değerleri belirleyen bir geri arama sağlayabilirsiniz.

$result = $database->query('SELECT * FROM users');
$items = $result->fetchPairs(fn($row) => "$row->id - $row->name");
// ['1 - John', '2 - Jane', ...]

// Geri arama ayrıca anahtar ve değer çifti içeren bir dizi de 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 getirir. Daha fazla satır yoksa, null döndürür. Bu yöntem dahili işaretçiyi bir sonraki satıra ilerletir.

$result = $database->query('SELECT name FROM users');
$name = $result->fetchField(); // ilk satırdaki adı getirir

fetchList(): ?array

Satırı indeksli bir dizi olarak getirir. Daha fazla satır yoksa, null döndürür. Bu yöntem dahili işaretçiyi bir sonraki satıra ilerletir.

$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 sorguları için getirilen satır sayısını döndürür, ancak bu her zaman bilinmeyebilir; bu gibi durumlarda null döndürür.

getColumnCount(): ?int

ResultSet adresindeki sütun sayısını döndürür.

Sorgu Bilgileri

En son yürütülen sorgu hakkındaki ayrıntıları almak için şunu kullanın:

echo $database->getLastQueryString(); // SQL sorgusunu çıktılar

$result = $database->query('SELECT * FROM articles');
echo $result->getQueryString();    // SQL sorgusunu çıktılar
echo $result->getTime();           // saniye cinsinden yürütme süresini verir

Sonucu bir HTML tablosu olarak görüntülemek için şunu kullanın:

$result = $database->query('SELECT * FROM articles');
$result->dump();

Sütun türleri hakkındaki bilgileri ResultSet adresinden de alabilirsiniz:

$result = $database->query('SELECT * FROM articles');
$types = $result->getColumnTypes();

foreach ($types as $column => $type) {
	echo "$column is of type $type->type"; // örneğin, 'id int türündedir'
}

Sorgu Günlüğü

Özel sorgu günlüğü uygulayabilirsiniz. onQuery olayı, her sorgu yürütüldükten sonra çağrılan bir geri çağırma dizisidir:

$database->onQuery[] = function ($database, $result) use ($logger) {
	$logger->info('Query: ' . $result->getQueryString());
	$logger->info('Time: ' . $result->getTime());

	if ($result->getRowCount() > 1000) {
		$logger->warning('Large result set: ' . $result->getRowCount() . ' rows');
	}
};
versiyon: 4.0