Veritabanı Gezgini

Nette Database ile iki şekilde çalışabilirsiniz: SQL sorgularının otomatik olarak oluşturulmasına izin verebilir (Explorer yaklaşımı) veya bunları kendiniz yazabilirsiniz(Doğrudan erişim). Explorer veri erişimini önemli ölçüde basitleştirir. Tablolar arasındaki ilişkileri ele alır, böylece uygulamanızın mantığına odaklanabilirsiniz.

  • Verilerle çalışmak doğaldır ve anlaşılması kolaydır
  • Yalnızca gerekli verileri getiren optimize edilmiş SQL sorguları oluşturur
  • JOIN sorguları yazmaya gerek kalmadan ilgili verilere kolay erişim sağlar
  • Herhangi bir yapılandırma veya varlık oluşturma olmadan hemen çalışır

Explorer ile çalışmak, Nette\Database\Explorer nesnesinde table() yöntemini çağırarak başlar (bağlantı oluşturma ve yapılandırma hakkında bilgi için ilgili sayfaya bakın):

$books = $explorer->table('book'); // 'book' tablo adıdır

Yöntem, bir SQL sorgusunu temsil eden bir Selection nesnesi döndürür. Sonuçları filtrelemek ve sıralamak için bu nesneye ek yöntemler zincirlenebilir. Sorgu yalnızca veriler istendiğinde, örneğin foreach ile yinelenerek bir araya getirilir ve yürütülür. Her satır bir ActiveRow nesnesi ile temsil edilir:

foreach ($books as $book) {
	echo $book->title;        // 'başlık' sütunu çıktıları
	echo $book->author_id;    // 'author_id' sütununu çıktılar
}

Explorer, tablo ilişkileriyle çalışmayı büyük ölçüde kolaylaştırır. Aşağıdaki örnek, ilgili tablolardan (kitaplar ve yazarları) ne kadar kolay veri çıktısı alabileceğimizi göstermektedir. JOIN sorgularının yazılmasına gerek olmadığına dikkat edin; Nette bunları bizim için oluşturuyor:

$books = $explorer->table('book');

foreach ($books as $book) {
	echo 'Book: ' . $book->title;
	echo 'Author: ' . $book->author->name; // 'author' tablosuna bir JOIN oluşturur
}

Nette Database Explorer sorguları maksimum verimlilik için optimize eder. Yukarıdaki örnek, 10 veya 10.000 kitap işlememizden bağımsız olarak yalnızca iki SELECT sorgusu gerçekleştirir.

Ayrıca Explorer, kodda hangi sütunların kullanıldığını izler ve veritabanından yalnızca bunları getirerek daha fazla performans tasarrufu sağlar. Bu davranış tamamen otomatik ve uyarlanabilirdir. Daha sonra kodu ek sütunlar kullanacak şekilde değiştirirseniz, Explorer sorguları otomatik olarak ayarlar. Hiçbir şeyi yapılandırmanıza veya hangi sütunlara ihtiyaç duyulacağını düşünmenize gerek yok – bunu Nette'e bırakın.

Filtreleme ve Sıralama

Selection sınıfı, verileri filtrelemek ve sıralamak için yöntemler sağlar.

where($condition, ...$params) Bir WHERE koşulu ekler. Birden fazla koşul AND kullanılarak birleştirilir
whereOr(array $conditions) OR kullanılarak birleştirilmiş bir grup WHERE koşulu ekler
wherePrimary($value) Birincil anahtarı temel alan bir WHERE koşulu ekler
order($columns, ...$params) ORDER BY ile sıralamayı ayarlar
select($columns, ...$params) Hangi sütunların getirileceğini belirtir
limit($limit, $offset = null) Satır sayısını sınırlar (LIMIT) ve isteğe bağlı olarak OFFSET ayarlar
page($page, $itemsPerPage, &$total = null) Sayfalandırmayı ayarlar
group($columns, ...$params) Satırları gruplar (GROUP BY)
having($condition, ...$params) Gruplanmış satırları filtrelemek için bir HAVING koşulu ekler

Yöntemler zincirlenebilir ( akıcı arayüz olarak adlandırılır): $table->where(...)->order(...)->limit(...).

Bu yöntemler ayrıca ilgili tablolardan verilere erişmek için özel gösterimlerin kullanılmasına izin verir.

Kaçış ve Tanımlayıcılar

Yöntemler parametreleri ve alıntı tanımlayıcılarını (tablo ve sütun adları) otomatik olarak kaçarak SQL enjeksiyonunu önler. Düzgün çalışmayı sağlamak için birkaç kurala uyulmalıdır:

  • Anahtar sözcükleri, işlev adlarını, yordamları vb. büyük harfle yazın.
  • Sütun ve tablo adlarını küçük harfle yazın.
  • Dizeleri her zaman parameters kullanarak geçirin.
where('name = ' . $name);         // **DISASTER**: SQL enjeksiyonuna karşı savunmasız
where('name LIKE "%search%"');    // **YANLIŞ**: otomatik alıntılamayı zorlaştırır
where('name LIKE ?', '%search%'); // **CORRECT**: parametre olarak geçirilen değer

where('name like ?', $name);     // **YANLIŞ**: üretir: `name` `like` ?
where('name LIKE ?', $name);     // **DOĞRU**: üretir: isim` GİBİ ?
where('LOWER(name) = ?', $value);// **DOĞRU**: LOWER(`name`) = ?

where (string|array $condition, …$parameters)static

WHERE koşullarını kullanarak sonuçları filtreler. Gücü, çeşitli değer türlerini akıllıca ele almasında ve SQL operatörlerini otomatik olarak seçmesinde yatmaktadır.

Temel kullanım:

$table->where('id', $value);     // WHERE `id` = 123
$table->where('id > ?', $value); // WHERE `id` > 123
$table->where('id = ? OR name = ?', $id, $name); // WHERE `id` = 1 OR `name` = 'Jon Snow'

Uygun operatörlerin otomatik olarak algılanması sayesinde, özel durumlarla uğraşmanıza gerek kalmaz – Nette bunları sizin için halleder:

$table->where('id', 1);          // WHERE `id` = 1
$table->where('id', null);       // WHERE `id` IS NULL
$table->where('id', [1, 2, 3]);  // WHERE `id` IN (1, 2, 3)
// Yer tutucu ? operatör olmadan kullanılabilir:
$table->where('id ?', 1);        // WHERE `id` = 1

Yöntem ayrıca negatif koşulları ve boş dizileri de doğru şekilde işler:

$table->where('id', []);         // WHERE `id` IS NULL AND FALSE -- hiçbir şey bulamaz
$table->where('id NOT', []);     // WHERE `id` IS NULL OR TRUE -- her şeyi bulur
$table->where('NOT (id ?)', []); // WHERE NOT (`id` IS NULL AND FALSE) -- her şeyi bulur
// $table->where('NOT id ?', $ids); // UYARI: Bu sözdizimi desteklenmemektedir

Ayrıca, bir alt sorgu oluşturarak başka bir tablo sorgusunun sonucunu parametre olarak aktarabilirsiniz:

// WHERE `id` IN (SELECT `id` FROM `tableName`)
$table->where('id', $explorer->table($tableName));

// WHERE `id` IN (SELECT `col` FROM `tableName`)
$table->where('id', $explorer->table($tableName)->select('col'));

Koşullar, AND kullanılarak birleştirilen öğelerle birlikte bir dizi olarak da geçirilebilir:

// WHERE (`price_final` < `price_original`) AND (`stock_count` > `min_stock`)
$table->where([
	'price_final &lt; price_original',
	'stock_count > min_stock',
]);

Dizide, anahtar-değer çiftleri kullanılabilir ve Nette yine otomatik olarak doğru operatörleri seçecektir:

// WHERE (`status` = 'active') AND (`id` IN (1, 2, 3))
$table->where([
	'status' => 'active',
	'id' => [1, 2, 3],
]);

SQL ifadelerini yer tutucular ve çoklu parametrelerle de karıştırabiliriz. Bu, kesin olarak tanımlanmış operatörlere sahip karmaşık koşullar için kullanışlıdır:

// WHERE (`yaş` > 18) AND (ROUND(`score`, 2) > 75.5)
$table->where([
	'age > ?' => 18,
	'ROUND(score, ?) > ?' => [2, 75.5], // iki parametre bir dizi olarak geçirilir
]);

where() adresine yapılan birden fazla çağrı, AND kullanarak koşulları otomatik olarak birleştirir.

whereOr (array $parameters)static

where() adresine benzer, ancak OR kullanarak koşulları birleştirir:

// WHERE (`status` = 'active') OR (`deleted` = 1)
$table->whereOr([
	'status' => 'active',
	'deleted' => true,
]);

Daha karmaşık ifadeler de kullanılabilir:

// WHERE (`price` > 1000) OR (`price_with_tax` > 1500)
$table->whereOr([
	'price > ?' => 1000,
	'price_with_tax > ?' => 1500,
]);

wherePrimary (mixed $key)static

Tablonun birincil anahtarı için bir koşul ekler:

// WHERE `id` = 123
$table->wherePrimary(123);

// WHERE `id` IN (1, 2, 3)
$table->wherePrimary([1, 2, 3]);

Tablonun bileşik bir birincil anahtarı varsa (örneğin, foo_id, bar_id), bunu bir dizi olarak iletiriz:

// WHERE `foo_id` = 1 AND `bar_id` = 5
$table->wherePrimary(['foo_id' => 1, 'bar_id' => 5])->fetch();

// WHERE (`foo_id`, `bar_id`) IN ((1, 5), (2, 3))
$table->wherePrimary([
	['foo_id' => 1, 'bar_id' => 5],
	['foo_id' => 2, 'bar_id' => 3],
])->fetchAll();

order (string $columns, …$parameters)static

Satırların döndürüleceği sırayı belirtir. Bir veya daha fazla sütuna göre, artan veya azalan sırada veya özel bir ifadeye göre sıralayabilirsiniz:

$table->order('created');                   // ORDER BY `oluşturulan`
$table->order('created DESC');              // ORDER BY `created` DESC
$table->order('priority DESC, created');    // ORDER BY `öncelik` DESC, `oluşturuldu`
$table->order('status = ? DESC', 'active'); // ORDER BY `status` = 'active' DESC

select (string $columns, …$parameters)static

Veritabanından döndürülecek sütunları belirtir. Varsayılan olarak, Nette Database Explorer yalnızca kodda gerçekten kullanılan sütunları döndürür. Belirli ifadeleri almanız gerektiğinde select() yöntemini kullanın:

// SELECT *, DATE_FORMAT(`created_at`, "%d.%m.%Y") AS `formatted_date`
$table->select('*, DATE_FORMAT(created_at, ?) AS formatted_date', '%d.%m.%Y');

AS kullanılarak tanımlanan takma adlara daha sonra ActiveRow nesnesinin özellikleri olarak erişilebilir:

foreach ($table as $row) {
	echo $row->formatted_date;   // takma ada erişim
}

limit (?int $limit, ?int $offset = null)static

Döndürülen satır sayısını sınırlar (LIMIT) ve isteğe bağlı olarak bir ofset ayarlar:

$table->limit(10);        // LIMIT 10 (ilk 10 satırı döndürür)
$table->limit(10, 20);    // LIMIT 10 OFSET 20

Sayfalandırma için page() yöntemini kullanmak daha uygundur.

page (int $page, int $itemsPerPage, &$numOfPages = null)static

Sonuçların sayfalandırılmasını basitleştirir. Sayfa numarasını (1'den başlayarak) ve sayfa başına öğe sayısını kabul eder. İsteğe bağlı olarak, toplam sayfa sayısının saklanacağı bir değişkene referans geçebilirsiniz:

$numOfPages = null;
$table->page(page: 3, itemsPerPage: 10, $numOfPages);
echo "Total pages: $numOfPages";

group (string $columns, …$parameters)static

Satırları belirtilen sütunlara göre gruplar (GROUP BY). Tipik olarak toplama fonksiyonları ile birlikte kullanılır:

// Her kategorideki ürün sayısını sayar
$table->select('category_id, COUNT(*) AS count')
	->group('category_id');

having (string $having, …$parameters)static

Gruplandırılmış satırları filtrelemek için bir koşul belirler (HAVING). group() yöntemi ve toplama işlevleri ile birlikte kullanılabilir:

// 100'den fazla ürün içeren kategorileri bulur
$table->select('category_id, COUNT(*) AS count')
	->group('category_id')
	->having('count > ?', 100);

Veri Okuma

Veritabanından veri okumak için birkaç kullanışlı yöntem mevcuttur:

foreach ($table as $key => $row) Tüm satırlar arasında yineleme yapar, $key birincil anahtar değeridir, $row bir ActiveRow nesnesidir
$row = $table->get($key) Birincil anahtara göre tek bir satır döndürür
$row = $table->fetch() Geçerli satırı döndürür ve işaretçiyi bir sonrakine ilerletir
$array = $table->fetchPairs() Sonuçlardan ilişkisel bir dizi oluşturur
$array = $table->fetchAll() Tüm satırları bir dizi olarak döndürür
count($table) Seçim nesnesindeki satır sayısını döndürür

ActiveRow nesnesi salt okunurdur. Bu, özelliklerinin değerlerini değiştiremeyeceğiniz anlamına gelir. Bu kısıtlama, veri tutarlılığını sağlar ve beklenmedik yan etkileri önler. Veriler veritabanından alınır ve herhangi bir değişiklik açıkça ve kontrollü bir şekilde yapılmalıdır.

foreach – Tüm Satırlar Arasında Yineleme

Bir sorguyu çalıştırmanın ve satırları almanın en kolay yolu foreach döngüsü ile yinelemektir. SQL sorgusunu otomatik olarak yürütür.

$books = $explorer->table('book');
foreach ($books as $key => $book) {
	// $key = birincil anahtar, $book = ActiveRow
	echo "$book->title ({$book->author->name})";
}

get ($key): ?ActiveRow

Bir SQL sorgusu çalıştırır ve bir satırı birincil anahtarına göre veya yoksa null adresine döndürür.

$book = $explorer->table('book')->get(123); // ID 123 veya null ile ActiveRow döndürür
if ($book) {
	echo $book->title;
}

fetch(): ?ActiveRow

Bir satır döndürür ve dahili işaretçiyi bir sonrakine ilerletir. Başka satır yoksa null döndürür.

$books = $explorer->table('book');
while ($book = $books->fetch()) {
	$this->processBook($book);
}

fetchPairs (string|int|null $key = null, string|int|null $value = null)array

Sonuçları ilişkisel bir dizi olarak döndürür. İlk bağımsız değişken, dizi anahtarı olarak kullanılacak sütunun adını ve ikinci bağımsız değişken, değer olarak kullanılacak sütunun adını belirtir:

$authors = $explorer->table('author')->fetchPairs('id', 'name');
// [1 => 'John Doe', 2 => 'Jane Doe', ...]

Yalnızca ilk parametre sağlanırsa, tüm satır değer olarak kullanılır ve ActiveRow nesnesi olarak temsil edilir:

$authors = $explorer->table('author')->fetchPairs('id');
// [1 => ActiveRow(id: 1, ...), 2 => ActiveRow(id: 2, ...), ...]

Anahtar olarak null sağlanırsa, dizi sıfırdan başlayarak sayısal olarak indekslenecektir:

$authors = $explorer->table('author')->fetchPairs(null, 'name');
// [0 => 'John Doe', 1 => 'Jane Doe', ...]

fetchPairs (Closure $callback)array

Alternatif olarak, parametre olarak bir geri arama iletebilirsiniz. Geri arama, tek bir değer veya bir anahtar-değer çifti döndürmek için her satıra uygulanacaktır.

$titles = $explorer->table('book')
	->fetchPairs(fn($row) => "$row->title ({$row->author->name})");
// [1 => 'İlk Kitap (Jan Novak)', ...]

// Geri arama, anahtar-değer çifti içeren bir dizi de döndürebilir:
$titles = $explorer->table('book')
	->fetchPairs(fn($row) => [$row->title, $row->author->name]);
// ['İlk Kitap' => 'Jan Novak', ...]

fetchAll(): array

Tüm satırları, anahtarların birincil anahtar değerleri olduğu ActiveRow nesnelerinin ilişkisel bir dizisi olarak döndürür.

$allBooks = $explorer->table('book')->fetchAll();
// [1 => ActiveRow(id: 1, ...), 2 => ActiveRow(id: 2, ...), ...]

count(): int

Parametreleri olmayan count() yöntemi, Selection nesnesindeki satır sayısını döndürür:

$table->where('category', 1);
$count = $table->count();
$count = count($table); // alternatif

Not: count() bir parametre ile birlikte, aşağıda açıklandığı gibi veritabanında COUNT toplama işlevini gerçekleştirir.

ActiveRow::toArray(): array

ActiveRow nesnesini, anahtarların sütun adları ve değerlerin karşılık gelen veriler olduğu bir ilişkisel diziye dönüştürür.

$book = $explorer->table('book')->get(1);
$bookArray = $book->toArray();
// $bookArray ['id' => 1, 'title' => '...', 'author_id' => ..., ...] olacaktır.

Birleştirme

Selection sınıfı, toplama işlevlerini (COUNT, SUM, MIN, MAX, AVG, vb.) kolayca gerçekleştirmek için yöntemler sağlar.

count($expr) Satır sayısını sayar
min($expr) Bir sütundaki minimum değeri döndürür
max($expr) Bir sütundaki maksimum değeri döndürür
sum($expr) Bir sütundaki değerlerin toplamını verir
aggregation($function) AVG() veya GROUP_CONCAT() gibi herhangi bir toplama işlevine izin verir

count (string $expr): int

COUNT işleviyle bir SQL sorgusu çalıştırır ve sonucu döndürür. Bu yöntem, kaç satırın belirli bir koşulla eşleştiğini belirlemek için kullanılır:

$count = $table->count('*');                 // SELECT COUNT(*) FROM `table`
$count = $table->count('DISTINCT column');   // SELECT COUNT(DISTINCT `column`) FROM `table`

Not: count() parametresiz olarak sadece Selection nesnesindeki satır sayısını döndürür.

min (string $expr) and max(string $expr)

min() ve max() yöntemleri, belirtilen sütun veya ifadedeki minimum ve maksimum değerleri döndürür:

// SELECT MAX(`price`) FROM `products` WHERE `active` = 1
$maxPrice = $products->where('active', true)
	->max('price');

sum (string $expr): int

Belirtilen sütundaki veya ifadedeki değerlerin toplamını döndürür:

// SELECT SUM(`price` * `items_in_stock`) FROM `products` WHERE `active` = 1
$totalPrice = $products->where('active', true)
	->sum('price * items_in_stock');

aggregation (string $function, ?string $groupFunction = null)mixed

Herhangi bir toplama fonksiyonunun yürütülmesine izin verir.

// Bir kategorideki ürünlerin ortalama fiyatını hesaplar
$avgPrice = $products->where('category_id', 1)
	->aggregation('AVG(price)');

// Ürün etiketlerini tek bir dizede birleştirir
$tags = $products->where('id', 1)
	->aggregation('GROUP_CONCAT(tag.name) AS tags')
	->fetch()
	->tags;

Kendileri bir toplama ve gruplamadan kaynaklanan sonuçları toplamamız gerekiyorsa (örneğin, gruplanmış satırlar üzerinden SUM(value) ), bu ara sonuçlara uygulanacak toplama işlevini ikinci bağımsız değişken olarak belirtiriz:

// Her kategori için stoktaki ürünlerin toplam fiyatını hesaplar, ardından bu fiyatları toplar
$totalPrice = $products->select('category_id, SUM(price * stock) AS category_total')
	->group('category_id')
	->aggregation('SUM(category_total)', 'SUM');

Bu örnekte, önce her kategorideki ürünlerin toplam fiyatını hesaplıyoruz (SUM(price * stock) AS category_total) ve sonuçları category_id'a göre gruplandırıyoruz. Daha sonra bu alt toplamları toplamak için aggregation('SUM(category_total)', 'SUM') adresini kullanıyoruz. İkinci bağımsız değişken 'SUM' ara sonuçlara uygulanacak toplama işlevini belirtir.

Ekle, Güncelle ve Sil

Nette Database Explorer veri eklemeyi, güncellemeyi ve silmeyi basitleştirir. Bahsedilen tüm yöntemler hata durumunda bir Nette\Database\DriverException fırlatır.

Selection::insert (iterable $data)static

Bir tabloya yeni kayıtlar ekler.

Tek bir kayıt ekleniyor:

Yeni kayıt, anahtarların tablodaki sütun adlarıyla eşleştiği bir ilişkisel dizi veya yinelenebilir nesne ( formlarda kullanılan ArrayHash gibi) olarak aktarılır.

Tablonun tanımlanmış bir birincil anahtarı varsa, yöntem, veritabanı düzeyinde yapılan değişiklikleri (örn. tetikleyiciler, varsayılan sütun değerleri veya otomatik artırma hesaplamaları) yansıtmak için veritabanından yeniden yüklenen bir ActiveRow nesnesi döndürür. Bu, veri tutarlılığını sağlar ve nesne her zaman geçerli veritabanı verilerini içerir. Birincil anahtar açıkça tanımlanmamışsa, yöntem girdi verilerini bir dizi olarak döndürür.

$row = $explorer->table('users')->insert([
	'name' => 'John Doe',
	'email' => 'john.doe@example.com',
]);
// row, eklenen satırın tam verilerini içeren bir ActiveRow örneğidir,
// otomatik olarak oluşturulan kimlik ve tetikleyiciler tarafından yapılan değişiklikler dahil
echo $row->id;          // Yeni eklenen kullanıcının kimliğini verir
echo $row->created_at;  // Bir tetikleyici tarafından ayarlanmışsa oluşturma zamanını verir

Tek seferde birden fazla kayıt ekleme:

insert() yöntemi, tek bir SQL sorgusu ile birden fazla kayıt eklemenize olanak tanır. Bu durumda, eklenen satırların sayısını döndürür.

$insertedRows = $explorer->table('users')->insert([
	[
		'name' => 'John',
		'year' => 1994,
	],
	[
		'name' => 'Jack',
		'year' => 1995,
	],
]);
// INSERT INTO `users` (`name`, `year`) VALUES ('John', 1994), ('Jack', 1995)
// $insertedRows 2 olacaktır

Parametre olarak veri seçimi içeren bir Selection nesnesi de aktarabilirsiniz.

$newUsers = $explorer->table('potential_users')
	->where('approved', 1)
	->select('name, email');

$insertedRows = $explorer->table('users')->insert($newUsers);

Özel değerlerin eklenmesi:

Değerler dosyaları, DateTime nesnelerini veya SQL değişmezlerini içerebilir:

$explorer->table('users')->insert([
	'name' => 'John',
	'created_at' => new DateTime,           // veritabanı formatına dönüştürür
	'avatar' => fopen('image.jpg', 'rb'),   // ikili dosya içeriğini ekler
	'uuid' => $explorer::literal('UUID()'), // UUID() işlevini çağırır
]);

Selection::update (iterable $data)int

Bir tablodaki satırları belirtilen filtreye göre günceller. Gerçekte değiştirilen satır sayısını döndürür.

Güncellenecek sütunlar, anahtarların tablodaki sütun adlarıyla eşleştiği bir ilişkisel dizi veya yinelenebilir nesne ( formlarda kullanılan ArrayHash gibi) olarak geçirilir:

$affected = $explorer->table('users')
	->where('id', 10)
	->update([
		'name' => 'John Smith',
		'year' => 1994,
	]);
// UPDATE `users` SET `name` = 'John Smith', `year` = 1994 WHERE `id` = 10

Sayısal değerleri değiştirmek için += ve -= operatörlerini kullanabilirsiniz:

$explorer->table('users')
	->where('id', 10)
	->update([
		'points+=' => 1,  // 'puanlar' sütununun değerini 1 artırır
		'coins-=' => 1,   // 'madeni paralar' sütununun değerini 1 azaltır
	]);
// UPDATE `users` SET `points` = `points` + 1, `coins` = `coins` - 1 WHERE `id` = 10

Selection::delete(): int

Belirtilen filtreye göre bir tablodan satırları siler. Silinen satırların sayısını döndürür.

$count = $explorer->table('users')
	->where('id', 10)
	->delete();
// DELETE FROM `users` WHERE `id` = 10

update() veya delete() adreslerini çağırırken, güncellenecek veya silinecek satırları belirtmek için where() adresini kullandığınızdan emin olun. where() kullanılmazsa, işlem tüm tablo üzerinde gerçekleştirilecektir!

ActiveRow::update (iterable $data)bool

ActiveRow nesnesi tarafından temsil edilen bir veritabanı satırındaki verileri günceller. Anahtarların sütun adları olduğu yinelenebilir verileri parametre olarak kabul eder. Sayısal değerleri değiştirmek için += ve -= operatörlerini kullanabilirsiniz:

Güncelleme gerçekleştirildikten sonra, ActiveRow veritabanı düzeyinde yapılan değişiklikleri (örn. tetikleyiciler) yansıtmak için veritabanından otomatik olarak yeniden yüklenir. Yöntem, yalnızca gerçek bir veri değişikliği meydana gelmişse true döndürür.

$article = $explorer->table('article')->get(1);
$article->update([
	'views += 1',  // görüntüleme sayısını artırır
]);
echo $article->views; // Geçerli görüntüleme sayısını verir

Bu yöntem, veritabanında yalnızca belirli bir satırı günceller. Birden fazla satırın toplu güncellemeleri için Selection::update() yöntemini kullanın.

ActiveRow::delete()

Veritabanından ActiveRow nesnesi tarafından temsil edilen bir satırı siler.

$book = $explorer->table('book')->get(1);
$book->delete(); // ID 1 olan kitabı siler

Bu yöntem, veritabanındaki yalnızca belirli bir satırı siler. Birden fazla satırı toplu olarak silmek için Selection::delete() yöntemini kullanın.

Tablolar Arasındaki İlişkiler

İlişkisel veritabanlarında veriler birden fazla tabloya bölünür ve yabancı anahtarlar aracılığıyla birbirine bağlanır. Nette Database Explorer, JOIN sorguları yazmadan veya herhangi bir yapılandırma veya varlık oluşturma gerektirmeden bu ilişkilerle çalışmak için devrim niteliğinde bir yol sunar.

Gösterim için example veritabanını kullanacağız(GitHub'da mevcuttur). Veritabanı aşağıdaki tabloları içerir:

  • author – yazarlar ve çevirmenler ( id, name, web, born sütunları)
  • book – kitaplar ( id, author_id, translator_id, title, sequel_id sütunları)
  • tag – etiketler ( id, name sütunları)
  • book_tag – kitaplar ve etiketler arasındaki bağlantı tablosu ( book_id, tag_id sütunları)

Veritabanı yapısı

Bu kitap veritabanı örneğinde, çeşitli ilişki türleri buluyoruz (gerçeğe kıyasla basitleştirilmiş):

  • Birden çoğa (1:N)** – Her kitabın bir yazarı vardır; bir yazar birden fazla kitap yazabilir.
  • Sıfırdan çoğa (0:N)** – Bir kitabın bir çevirmeni olabilir; bir çevirmen birden fazla kitabı çevirebilir.
  • Sıfırdan bire (0:1)** – Bir kitabın bir devam kitabı olabilir.
  • Çoktan çoğa (M:N)** – Bir kitap birden fazla etikete sahip olabilir ve bir etiket birden fazla kitaba atanabilir.

Bu ilişkilerde her zaman bir ana tablo ve bir çocuk tablo vardır. Örneğin, yazarlar ve kitaplar arasındaki ilişkide, author tablosu üst tablodur ve book tablosu alt tablodur – bunu her zaman bir yazara “ait” bir kitap olarak düşünebilirsiniz. Bu durum veritabanı yapısına da yansır: book alt tablosu, author üst tablosuna referans veren author_id yabancı anahtarını içerir.

Kitapları yazarlarının adlarıyla birlikte görüntülemek istiyorsak, iki seçeneğimiz vardır. Ya bir JOIN ile tek bir SQL sorgusu kullanarak verileri alırız:

SELECT book.*, author.name FROM book LEFT JOIN author ON book.author_id = author.id;

Ya da verileri iki adımda alırız – önce kitapları, sonra yazarlarını – ve bunları PHP'de bir araya getiririz:

SELECT * FROM book;
SELECT * FROM author WHERE id IN (1, 2, 3); -- IDs of authors retrieved from books

İkinci yaklaşım şaşırtıcı bir şekilde daha verimlidir. Veriler yalnızca bir kez getirilir ve önbellekte daha iyi kullanılabilir. Nette Database Explorer tam olarak bu şekilde çalışır – her şeyi kaputun altında halleder ve size temiz bir API sağlar:

$books = $explorer->table('book');
foreach ($books as $book) {
	echo 'title: ' . $book->title;
	echo 'written by: ' . $book->author->name; // $book->author, 'author' tablosundan bir kayıttır
	echo 'translated by: ' . $book->translator?->name;
}

Ana Tabloya Erişim

Ana tabloya erişim basittir. Bunlar bir kitabın bir yazarı vardır veya bir kitabın bir çevirmeni olabilir gibi ilişkilerdir. İlgili kayda ActiveRow nesne özelliği aracılığıyla erişilebilir – özellik adı, id son eki olmadan yabancı anahtarın sütun adıyla eşleşir:

$book = $explorer->table('book')->get(1);
echo $book->author->name;      // 'author_id' sütunu aracılığıyla yazarı bulur
echo $book->translator?->name; // 'translator_id' sütunu aracılığıyla çevirmeni bulur

Explorer, $book->author özelliğine erişirken, book tablosunda author dizesini (yani, author_id) içeren bir sütun arar. Bu sütundaki değere bağlı olarak, author tablosundan ilgili kaydı alır ve bunu bir ActiveRow nesnesi olarak döndürür. Benzer şekilde $book->translator, translator_id sütununu kullanır. translator_id sütunu null içerebileceğinden ?-> operatörü kullanılır.

Alternatif bir yaklaşım, iki bağımsız değişken (hedef tablonun adı ve bağlantı sütunu) kabul eden ve bir ActiveRow örneği veya null döndüren ref() yöntemi tarafından sağlanır:

echo $book->ref('author', 'author_id')->name;      // yazara bağlantı
echo $book->ref('author', 'translator_id')->name;  // çevirmene bağlantı

ref() yöntemi, özellik tabanlı erişimin kullanılamadığı durumlarda, örneğin tabloda özellik ile aynı ada sahip bir sütun varsa kullanışlıdır (author). Diğer durumlarda, daha iyi okunabilirlik için özellik tabanlı erişim kullanılması önerilir.

Explorer veritabanı sorgularını otomatik olarak optimize eder. Kitaplar arasında yineleme yaparken ve ilgili kayıtlarına (yazarlar, çevirmenler) erişirken, Explorer her kitap için ayrı ayrı bir sorgu oluşturmaz. Bunun yerine, her bir ilişki türü için yalnızca bir SELECT sorgusu çalıştırarak veritabanı yükünü önemli ölçüde azaltır. Örneğin:

$books = $explorer->table('book');
foreach ($books as $book) {
	echo $book->title . ': ';
	echo $book->author->name;
	echo $book->translator?->name;
}

Bu kod yalnızca üç optimize edilmiş veritabanı sorgusu yürütecektir:

SELECT * FROM `book`;
SELECT * FROM `author` WHERE (`id` IN (1, 2, 3)); -- IDs from 'author_id' column in selected books
SELECT * FROM `author` WHERE (`id` IN (2, 3));    -- IDs from 'translator_id' column in selected books

Bağlantı sütununu tanımlama mantığı Conventions uygulaması tarafından tanımlanır. Yabancı anahtarları analiz eden ve mevcut tablo ilişkileriyle sorunsuz bir şekilde çalışmanıza olanak tanıyan DiscoveredConventions'ı kullanmanızı öneririz.

Çocuk Tablosuna Erişim

Alt tabloya erişim ters yönde çalışır. Şimdi bu yazar hangi kitapları yazdı veya bu çevirmen hangi kitapları çevirdi diye soruyoruz. Bu tür bir sorgu için, ilgili kayıtları içeren bir Selection nesnesi döndüren related() yöntemini kullanırız. İşte bir örnek:

$author = $explorer->table('author')->get(1);

// Yazar tarafından yazılan tüm kitapları çıkarır
foreach ($author->related('book.author_id') as $book) {
	echo "Wrote: $book->title";
}

// Yazar tarafından çevrilen tüm kitapları çıkarır
foreach ($author->related('book.translator_id') as $book) {
	echo "Translated: $book->title";
}

related() yöntemi, ilişki açıklamasını nokta gösterimini kullanarak tek bir bağımsız değişken olarak veya iki ayrı bağımsız değişken olarak kabul eder:

$author->related('book.translator_id');    // tek argüman
$author->related('book', 'translator_id'); // iki argüman

Explorer, üst tablonun adına göre doğru bağlantı sütununu otomatik olarak algılayabilir. Bu durumda, kaynak tablonun adı author olduğu için book.author_id sütunu üzerinden bağlantı verir:

$author->related('book'); // book.author_id kullanır

Birden fazla olası bağlantı varsa, Explorer bir AmbiguousReferenceKeyException istisnası atacaktır.

Elbette, bir döngü içinde birden fazla kayıt arasında yineleme yaparken related() yöntemini de kullanabiliriz ve Explorer bu durumda da sorguları otomatik olarak optimize edecektir:

$authors = $explorer->table('author');
foreach ($authors as $author) {
	echo $author->name . ' wrote:';
	foreach ($author->related('book') as $book) {
		echo $book->title;
	}
}

Bu kod yalnızca iki verimli SQL sorgusu oluşturur:

SELECT * FROM `author`;
SELECT * FROM `book` WHERE (`author_id` IN (1, 2, 3)); -- IDs of the selected authors

Çoktan Çoka İlişki

Çoktan çoka (M:N) ilişki için bir birleşim tablosu (bizim durumumuzda book_tag) gereklidir. Bu tablo iki yabancı anahtar sütunu içerir (book_id, tag_id). Her sütun, bağlı tablolardan birinin birincil anahtarına referans verir. İlgili verileri almak için, önce related('book_tag') adresini kullanarak bağlantı tablosundan kayıtları getiriyoruz ve ardından hedef verilere devam ediyoruz:

$book = $explorer->table('book')->get(1);
// Kitaba atanan etiketlerin adlarını çıktı olarak verir
foreach ($book->related('book_tag') as $bookTag) {
	echo $bookTag->tag->name; // bağlantı tablosu aracılığıyla etiket adını getirir
}

$tag = $explorer->table('tag')->get(1);
// Ters yön: bu etikete sahip kitapların başlıklarını çıkarır
foreach ($tag->related('book_tag') as $bookTag) {
	echo $bookTag->book->title; // kitap başlığını getirir
}

Explorer SQL sorgularını tekrar verimli bir forma optimize eder:

SELECT * FROM `book`;
SELECT * FROM `book_tag` WHERE (`book_tag`.`book_id` IN (1, 2, ...));  -- IDs of the selected books
SELECT * FROM `tag` WHERE (`tag`.`id` IN (1, 2, ...));                 -- IDs of the tags found in book_tag

where(), select(), order() ve group() yöntemlerinde, diğer tablolardan sütunlara erişmek için özel gösterimler kullanabilirsiniz. Explorer gerekli JOIN'leri otomatik olarak oluşturur.

Ana tablonun perspektifinden bakıldığında 1:N ilişkiler için Nokta gösterimi (parent_table.column) kullanılır:

$books = $explorer->table('book');

// Yazarlarının adı 'Jon' ile başlayan kitapları bulur
$books->where('author.name LIKE ?', 'Jon%');

// Kitapları yazar adına göre azalan şekilde sıralar
$books->order('author.name DESC');

// Kitap başlığı ve yazar adı çıktıları
$books->select('book.title, author.name');

Üst tablo açısından 1:N ilişkiler için Kolon gösterimi kullanılır:

$authors = $explorer->table('author');

// Başlığında 'PHP' geçen bir kitap yazan yazarları bulur
$authors->where(':book.title LIKE ?', '%PHP%');

// Her yazar için kitap sayısını sayar
$authors->select('*, COUNT(:book.id) AS book_count')
	->group('author.id');

Yukarıdaki iki nokta üst üste gösterimli örnekte (:book.title), yabancı anahtar sütunu açıkça belirtilmemiştir. Explorer, üst tablo adına göre doğru sütunu otomatik olarak algılar. Bu durumda, kaynak tablonun adı author olduğu için book.author_id sütunu üzerinden birleşir. Birden fazla olası bağlantı varsa, Explorer AmbiguousReferenceKeyException istisnasını atar.

Bağlantı sütunu parantez içinde açıkça belirtilebilir:

// Başlığında 'PHP' geçen bir kitabı çeviren yazarları bulur
$authors->where(':book(translator).title LIKE ?', '%PHP%');

Birden fazla tablodaki verilere erişmek için gösterimler zincirlenebilir:

// 'PHP' ile etiketlenmiş kitapların yazarlarını bulur
$authors->where(':book:book_tag.tag.name', 'PHP')
	->group('author.id');

JOIN için Genişletme Koşulları

joinWhere() yöntemi, SQL'deki tablo birleştirmelerine ON anahtar sözcüğünden sonra ek koşullar ekler.

Örneğin, belirli bir çevirmen tarafından çevrilmiş kitapları bulmak istediğimizi varsayalım:

// 'David' adında bir çevirmen tarafından çevrilmiş kitaplar bulur
$books = $explorer->table('book')
	->joinWhere('translator', 'translator.name', 'David');
// LEFT JOIN author translator ON book.translator_id = translator.id AND (translator.name = 'David')

joinWhere() koşulunda, where() yönteminde olduğu gibi aynı yapıları kullanabilirsiniz – operatörler, yer tutucular, değer dizileri veya SQL ifadeleri.

Birden fazla JOIN içeren daha karmaşık sorgular için tablo takma adları tanımlanabilir:

$tags = $explorer->table('tag')
	->joinWhere(':book_tag.book.author', 'book_author.born &lt; ?', 1950)
	->alias(':book_tag.book.author', 'book_author');
// LEFT JOIN `book_tag` ON `tag`.`id` = `book_tag`.`tag_id`
// LEFT JOIN `book` ON `book_tag`.`book_id` = `book`.`id`
// LEFT JOIN `author` `book_author` ON `book`.`author_id` = `book_author`.`id`
//   AND (`kitap_yazarı`.`doğum` < 1950)

where() yöntemi WHERE cümlesine koşullar eklerken, joinWhere() yönteminin tablo birleştirmeleri sırasında ON cümlesindeki koşulları genişlettiğini unutmayın.

Elle Gezgin Oluşturma

Nette DI konteynerini kullanmıyorsanız, Nette\Database\Explorer örneğini manuel olarak oluşturabilirsiniz:

use Nette\Database;

// $storage, Nette\Caching\Storage'ı uygular, örn:
$storage = new Nette\Caching\Storages\FileStorage('/path/to/temp/dir');
// veritabanı bağlantısı
$connection = new Database\Connection('mysql:host=127.0.0.1;dbname=mydatabase', 'user', 'password');
// veritabanı yapısının yansımasını yönetir
$structure = new Database\Structure($connection, $storage);
// tablo adlarını, sütunları ve yabancı anahtarları eşlemek için kuralları tanımlar
$conventions = new Database\Conventions\DiscoveredConventions($structure);
$explorer = new Database\Explorer($connection, $structure, $conventions, $storage);
versiyon: 4.0