Nette Documentation Preview

syntax
Database Explorer
*****************

<div class=perex>

Explorer, veritabanınızla çalışmanın sezgisel ve verimli bir yolunu sunar. Tablo ilişkilerini kendiliğinden ele alır ve sorguları iyileştirir; böylece siz uygulama mantığınıza odaklanabilirsiniz. Yapılandırma olmadan hemen çalışır. SQL sorguları üzerinde tam denetim isterseniz [SQL yolunu |SQL way] kullanabilirsiniz.

- Verilerle çalışmak doğal ve anlaşılması kolaydır
- Yalnızca gereken veriyi getiren iyileştirilmiş SQL sorguları üretir
- JOIN sorguları yazmadan ilişkili verilere kolay erişim sağlar
- Hiçbir yapılandırma ya da varlık üretimi olmadan hemen çalışır

</div>


Explorer ile çalışmak, [api:Nette\Database\Explorer] nesnesinde `table()` metodunu çağırmakla başlar (veritabanı bağlantısını kurmanın ayrıntıları için bkz. [Bağlantı ve yapılandırma |guide#Bağlantı ve Yapılandırma]):

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

Metot, bir SQL sorgusunu temsil eden bir [Selection |api:Nette\Database\Table\Selection] nesnesi döndürür. Sonuçları filtrelemek ve sıralamak için bu nesneye başka metotlar zincirlenebilir. Sorgu, veri istendiğinde (örneğin `foreach` ile dolaşıldığında) kurulur ve çalıştırılır. Her satır bir [ActiveRow |api:Nette\Database\Table\ActiveRow] nesnesiyle temsil edilir:

```php
foreach ($books as $book) {
	echo $book->title;        // 'title' sütununu çıktılar
	echo $book->author_id;    // 'author_id' sütununu çıktılar
}
```

Explorer, [tablo ilişkileriyle |#Tablolar Arası İlişkiler] çalışmayı büyük ölçüde kolaylaştırır. Aşağıdaki örnek, ilişkili tablolardan (kitaplar ve yazarları) veriyi ne kadar kolay çıktılayabildiğimizi gösteriyor. Hiçbir JOIN sorgusu yazmaya gerek olmadığına dikkat edin; onları Nette bizim için üretir:

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

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

Nette Database Explorer, sorguları en yüksek verim için iyileştirir. Yukarıdaki örnek, 10 ya da 10.000 kitap işlememizden bağımsız olarak yalnızca iki SELECT sorgusu çalıştırır.

Ayrıca Explorer, kodda hangi sütunların kullanıldığını izler ve veritabanından yalnızca onları getirir; bu da başarımdan daha fazla kazandırır. Bu davranış tümüyle otomatik ve uyarlanabilirdir. Kodu sonradan başka sütunları kullanacak şekilde değiştirirseniz, Explorer sorguları kendiliğinden ayarlar. Hiçbir şey yapılandırmanıza ya da hangi sütunların gerekeceğini düşünmenize gerek yok; bunu Nette'ye bırakın.


Filtreleme ve Sıralama
======================

`Selection` sınıfı, veri seçimlerini filtrelemek ve sıralamak için metotlar sunar.

.[language-php]
| `where($condition, ...$params)` | Bir WHERE koşulu ekler. Birden çok koşul AND ile birleştirilir |
| `whereOr(array $conditions)`    | OR ile birleştirilen bir WHERE koşulları grubu ekler               |
| `wherePrimary($value)`          | Birincil anahtara dayalı 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ı OFFSET ayarlar    |
| `page($page, $itemsPerPage, &$numOfPages = null)` | Sayfalamayı ayarlar                             |
| `group($columns, ...$params)`   | Satırları gruplar (GROUP BY)                                      |
| `having($condition, ...$params)`| Gruplanmış satırları filtrelemek için HAVING koşulu ekler          |

Metotlar zincirlenebilir (buna [akıcı arayüz |nette:introduction-to-object-oriented-programming#Akıcı Arayüzler] denir): `$table->where(...)->order(...)->limit(...)`.

Bu metotlarda, [ilişkili tablolardan gelen verilere |#İlişkili Tablolar Üzerinden Sorgulama] erişmek için özel yazımlar da kullanabilirsiniz.


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

Metotlar, parametreleri otomatik kaçışlar ve tanımlayıcıları (tablo ve sütun adlarını) tırnaklar; böylece SQL injection'ı önler. Düzgün çalışmasını sağlamak için birkaç kurala uyulmalıdır:

- Anahtar sözcükleri, fonksiyon ve prosedür adlarını vb. **büyük harfle** yazın.
- Sütun ve tablo adlarını **küçük harfle** yazın.
- Dizeleri her zaman **parametrelerle** aktarın.

```php
where('name = ' . $name);         // KRİTİK AÇIK: SQL injection
where('name LIKE "%search%"');    // YANLIŞ: otomatik tırnaklamayı zorlaştırır
where('name LIKE ?', '%search%'); // DOĞRU: değer parametre olarak aktarılır

where('name like ?', $name);     // YANLIŞ: şunu üretir: `name` `like` ?
where('name LIKE ?', $name);     // DOĞRU: şunu üretir: `name` LIKE ?
where('LOWER(name) = ?', $value);// DOĞRU: LOWER(`name`) = ?
```


where(string|array $condition, ...$parameters): static .[method]
----------------------------------------------------------------

Sonuçları WHERE koşullarıyla filtreler. Gücü, çeşitli değer türlerini akıllıca ele almasında ve uygun SQL operatörlerini otomatik seçmesindedir.

Temel kullanım:

```php
$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 saptanması sayesinde çeşitli özel durumları ele almanız gerekmez; onları Nette çözer:

```php
$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)
// Operatörsüz ? yer tutucusunu da kullanabilirsiniz:
$table->where('id ?', 1);        // WHERE `id` = 1
```

Metot, olumsuz koşulları ve boş dizileri doğru şekilde ele alır:

```php
$table->where('id', []);         // WHERE `id` IS NULL AND FALSE -- hiçbir şey bulmaz
$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öz dizimi desteklenmiyor
```

Parametre olarak başka bir tablo sorgusunun sonucunu da aktarabilir, böylece bir alt sorgu oluşturabilirsiniz:

```php
// 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, öğeleri AND ile birleştirilen bir dizi olarak da aktarılabilir:

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

Dizide anahtar => değer çiftleri kullanabilirsiniz; Nette yine doğru operatörleri otomatik seçer:

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

Dizide SQL ifadelerini yer tutucular ve birden çok parametreyle birleştirebilirsiniz. Bu, operatörleri kesin biçimde belirlenmiş karmaşık koşullar için uygundur:

```php
// WHERE (`age` > 18) AND (ROUND(`score`, 2) > 75.5)
$table->where([
	'age > ?' => 18,
	'ROUND(score, ?) > ?' => [2, 75.5], // iki parametre dizi olarak aktarılır
]);
```

`where()` metodunun birden çok çağrısı, koşulları otomatik olarak AND ile birleştirir.


whereOr(array $parameters): static .[method]
--------------------------------------------

`where()` metoduna benzer şekilde koşul ekler, ama onları OR ile birleştirir:

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

Burada daha karmaşık ifadeler de kullanılabilir:

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


wherePrimary(mixed $key): static .[method]
------------------------------------------

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

```php
// 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`), onu dizi olarak verin:

```php
// 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 .[method]
--------------------------------------------------------

Satırların hangi sırayla döndürüleceğini belirtir. Bir ya da daha çok sütuna göre, artan ya da azalan sırada veya özel bir ifadeye göre sıralayabilirsiniz:

```php
$table->order('created');                   // ORDER BY `created`
$table->order('created DESC');              // ORDER BY `created` DESC
$table->order('priority DESC, created');    // ORDER BY `priority` DESC, `created`
$table->order('status = ? DESC', 'active'); // ORDER BY `status` = 'active' DESC
```


select(string $columns, ...$parameters): static .[method]
---------------------------------------------------------

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

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

`AS` ile tanımlanan takma adlara sonra `ActiveRow` nesnesinin özellikleri olarak erişilir:

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


limit(?int $limit, ?int $offset = null): static .[method]
---------------------------------------------------------

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

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

Sayfalama için `page()` metodunu kullanmak daha uygundur.


page(int $page, int $itemsPerPage, &$numOfPages = null): static .[method]
-------------------------------------------------------------------------

Sonuçların sayfalanmasını kolaylaştırır. Sayfa numarasını (1'den başlar) ve sayfa başına öğe sayısını alır. İsteğe bağlı olarak, toplam sayfa sayısının saklanacağı bir değişkene referans verebilirsiniz:

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


group(string $columns, ...$parameters): static .[method]
--------------------------------------------------------

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

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


having(string $having, ...$parameters): static .[method]
--------------------------------------------------------

Gruplanmış satırları filtrelemek için bir koşul ayarlar (HAVING). `group()` metodu ve toplama fonksiyonlarıyla birlikte kullanılabilir:

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


Veri Okuma
==========

Veritabanından veri okumak için çeşitli kullanışlı metotlar vardır:

.[language-php]
| `foreach ($table as $key => $row)` | Tüm satırları dolaşır; `$key` birincil anahtar değeri, `$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 sonrakine ilerletir |
| `$array = $table->fetchPairs()`    | Sonuçlardan ilişkisel bir dizi oluşturur |
| `$array = $table->fetchAll()`      | Tüm satırları dizi olarak döndürür |
| `count($table)`                    | Selection nesnesindeki satır sayısını döndürür |

[ActiveRow |api:Nette\Database\Table\ActiveRow] nesnesi salt okunurdur. Yani özelliklerinin değerlerini değiştiremezsiniz. Bu kısıtlama veri tutarlılığını güvence altına alır ve beklenmedik yan etkileri önler. Veri veritabanından yüklenir ve her türlü değişiklik açıkça ve denetimli biçimde yapılmalıdır.


`foreach` - Tüm Satırları Dolaşma
---------------------------------

Bir sorguyu çalıştırıp satırları almanın en kolay yolu, `foreach` döngüsüyle dolaşmaktır. SQL sorgusunu otomatik olarak çalıştırır.

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


get($key): ?ActiveRow .[method]
-------------------------------

SQL sorgusunu çalıştırır ve satırı birincil anahtara göre döndürür; yoksa `null` döndürür.

```php
$book = $explorer->table('book')->get(123);  // ID'si 123 olan ActiveRow'u ya da null döndürür
if ($book) {
	echo $book->title;
}
```


fetch(): ?ActiveRow .[method]
-----------------------------

Geçerli satırı döndürür ve iç işaretçiyi sonrakine ilerletir. Başka satır kalmadıysa `null` döndürür.

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


fetchPairs(string|int|null $key = null, string|int|null $value = null): array .[method]
---------------------------------------------------------------------------------------

Sonuçları ilişkisel bir dizi olarak döndürür. İlk argüman dizide anahtar olarak kullanılacak sütunun adını, ikinci argüman değer olarak kullanılacak sütunun adını belirtir:

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

Yalnızca ilk parametre verilirse değer, satırın tamamı, yani `ActiveRow` nesnesi olur:

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

Anahtarlar yinelenirse son satırdaki değer kullanılır. Anahtar olarak `null` kullanıldığında dizi sıfırdan başlayarak sayısal indekslenir (o zaman çakışma olmaz):

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


fetchPairs(Closure $callback): array .[method]
----------------------------------------------

Alternatif olarak parametre olarak bir callback verebilirsiniz; bu callback her satır için ya tek bir değer ya da bir anahtar-değer çifti döndürür.

```php
$titles = $explorer->table('book')
	->fetchPairs(fn($row) => "$row->title ({$row->author->name})");
// ['First Book (John Novak)', ...]

// Callback, anahtar ve değer çiftinden oluşan bir dizi de döndürebilir:
$titles = $explorer->table('book')
	->fetchPairs(fn($row) => [$row->title, $row->author->name]);
// ['First Book' => 'John Novak', ...]
```


fetchAll(): array .[method]
---------------------------

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

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


count(): int .[method]
----------------------

Parametresiz `count()` metodu, `Selection` nesnesindeki satır sayısını döndürür:

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

Not: Parametreli `count()`, veritabanında COUNT toplama fonksiyonunu çalıştırır, aşağıya bakın.


ActiveRow::toArray(): array .[method]
-------------------------------------

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

```php
$book = $explorer->table('book')->get(1);
$bookArray = $book->toArray();
// $bookArray şu olur: ['id' => 1, 'title' => '...', 'author_id' => ..., ...]
```


Toplama
=======

`Selection` sınıfı, toplama fonksiyonlarını (COUNT, SUM, MIN, MAX, AVG vb.) kolayca çalıştırmak için metotlar sunar.

.[language-php]
| `count($expr)`           | Satır sayısını sayar |
| `min($expr)`             | Bir sütundaki en küçük değeri döndürür |
| `max($expr)`             | Bir sütundaki en büyük değeri döndürür |
| `sum($expr)`             | Bir sütundaki değerlerin toplamını döndürür |
| `aggregation($function)` | `AVG()` ya da `GROUP_CONCAT()` gibi herhangi bir toplama fonksiyonuna izin verir |


count(string $expr): int .[method]
----------------------------------

COUNT fonksiyonuyla bir SQL sorgusu çalıştırır ve sonucu döndürür. Metot, belirli bir koşula kaç satırın uyduğunu belirlemek için kullanılır:

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

Not: Parametresiz [#count()], yalnızca `Selection` nesnesindeki satır sayısını döndürür.


min(string $expr) ve max(string $expr) .[method]
------------------------------------------------

`min()` ve `max()` metotları, belirtilen sütundaki ya da ifadedeki en küçük ve en büyük değerleri döndürür:

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


sum(string $expr): mixed .[method]
----------------------------------

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

```php
// 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 .[method]
-----------------------------------------------------------------------------

Herhangi bir toplama fonksiyonunu çalıştırmaya olanak tanır.

```php
// bir kategorideki ürünlerin ortalama fiyatı
$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;
```

Zaten bir toplama fonksiyonundan ve gruplamadan doğan sonuçları toplamamız gerekiyorsa (örneğin gruplanmış satırlarda `SUM(value)`), bu ara sonuçlara uygulanacak toplama fonksiyonunu ikinci argüman olarak belirtiriz:

```php
// Tek tek kategorilerdeki stoktaki ürünlerin toplam fiyatını hesaplar ve sonra 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` değerine göre gruplandırıyoruz. Sonra bu ara toplamları `category_total` toplamak için `aggregation('SUM(category_total)', 'SUM')` kullanıyoruz. İkinci argüman `'SUM'`, ara sonuçlara SUM fonksiyonunun uygulanacağını belirtir.


Insert, Update ve Delete
========================

Nette Database Explorer, veri eklemeyi, güncellemeyi ve silmeyi kolaylaştırır. Sözü edilen tüm metotlar hata durumunda `Nette\Database\DriverException` fırlatır.


Selection::insert(iterable $data) .[method]
-------------------------------------------

Tabloya yeni kayıtlar ekler.

**Tek bir kayıt ekleme:**

Yeni kaydı, anahtarları tablodaki sütun adlarına karşılık gelen ilişkisel bir dizi ya da iterable nesne ([formlarda |forms:] kullanılan `ArrayHash` gibi) olarak verin.

Tablonun tanımlı bir birincil anahtarı varsa, metot bir `ActiveRow` nesnesi döndürür; bu nesne, veritabanı düzeyinde yapılan değişiklikleri (tetikleyiciler, varsayılan sütun değerleri, auto-increment sütun hesapları) yansıtmak için veritabanından yeniden yüklenir. Bu, veri tutarlılığını güvence altına alır ve nesne her zaman veritabanındaki güncel veriyi içerir. Tablonun birincil anahtarı yoksa tanımlanabilir bir satır olmadığından metot `null` döndürür.

```php
$row = $explorer->table('users')->insert([
	'name' => 'John Doe',
	'email' => 'john.doe@example.com',
]);
// $row bir ActiveRow örneğidir ve eklenen satırın tüm verilerini içerir;
// otomatik üretilen ID ve tetikleyicilerin yaptığı değişiklikler dahil
echo $row->id; // Yeni eklenen kullanıcının ID'sini çıktılar
echo $row->created_at; // Bir tetikleyici ayarladıysa oluşturulma zamanını çıktılar
```

**Birden çok kaydı tek seferde ekleme:**

`insert()` metodu, tek bir SQL sorgusuyla birden çok kayıt eklemeye olanak tanır. Bu durumda eklenen satır sayısını döndürür.

```php
$insertedRows = $explorer->table('users')->insert([
	[
		'name' => 'John',
		'year' => 1994,
	],
	[
		'name' => 'Jack',
		'year' => 1995,
	],
]);
// INSERT INTO `users` (`name`, `year`) VALUES ('John', 1994), ('Jack', 1995)
// $insertedRows değeri 2 olur
```

Parametre olarak, veri seçimi içeren bir `Selection` nesnesi de aktarılabilir.

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

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

**Özel değerler ekleme:**

Değer olarak dosyaları, `DateTime` nesnelerini ya da SQL sabit değerlerini de aktarabiliriz:

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


Selection::update(iterable $data): int .[method]
------------------------------------------------

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

Değiştirilecek sütunları, anahtarları tablodaki sütun adlarına karşılık gelen ilişkisel bir dizi ya da iterable nesne ([formlarda |forms:] kullanılan `ArrayHash` gibi) olarak verin:

```php
$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:

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


Selection::delete(): int .[method]
----------------------------------

Tablodaki satırları belirtilen filtreye göre siler. Silinen satır sayısını döndürür.

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

.[caution]
`update()` ya da `delete()` çağırırken, değiştirilecek/silinecek satırları belirtmek için `where()` kullanmayı unutmayın. `where()` kullanılmazsa işlem tablonun tamamında gerçekleştirilir!


ActiveRow::update(iterable $data): bool .[method]
-------------------------------------------------

`ActiveRow` nesnesinin temsil ettiği veritabanı satırındaki veriyi günceller. Güncellenecek veriyi içeren bir iterable alır (anahtarlar sütun adlarıdır). Sayısal değerleri değiştirmek için `+=` ve `-=` operatörlerini kullanabilirsiniz:

Güncelleme yapıldıktan sonra `ActiveRow`, veritabanı düzeyinde yapılan değişiklikleri (örneğin tetikleyicileri) yansıtmak için veritabanından otomatik olarak yeniden yüklenir. Metot yalnızca gerçek bir veri değişikliği olduysa `true` döndürür.

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

Bu metot veritabanında yalnızca belirli bir satırı günceller. Birden çok satırı toplu güncellemek için [#Selection::update()] metodunu kullanın.


ActiveRow::delete(): int .[method]
----------------------------------

`ActiveRow` nesnesinin temsil ettiği satırı veritabanından siler. Silinen satır sayısını döndürür; bu 1 olmalıdır.

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

Bu metot veritabanında yalnızca belirli bir satırı siler. Birden çok satırı toplu silmek için [#Selection::delete()] metodunu kullanın.


Tablolar Arası İlişkiler
========================

İlişkisel veritabanlarında veriler birden çok tabloya bölünür ve yabancı anahtarlarla birbirine bağlanır. Nette Database Explorer, bu ilişkilerle çalışmanın devrim niteliğinde bir yolunu sunar: JOIN sorguları yazmadan ve hiçbir şeyi yapılandırmaya ya da üretmeye gerek kalmadan.

İlişkilerle çalışmayı göstermek için örnek bir kitap veritabanı kullanacağız ([GitHub'da bulabilirsiniz |https://github.com/nette-examples/books]). Veritabanında şu tablolar var:

- `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` - kitaplarla etiketler arasındaki bağlantı tablosu (`book_id`, `tag_id` sütunları)

[* db-schema-1-.webp *] *** Örneklerde kullanılan veritabanı yapısı .<>

Örnek kitap veritabanımızda birkaç ilişki türü buluyoruz (model gerçeğe göre basitleştirilmiş olsa da):

- **Bire çok (1:N)** - Her kitabın **bir** yazarı vardır; bir yazar **birden çok** kitap yazabilir.
- **Sıfıra çok (0:N)** - Bir kitabın çevirmeni **olabilir**; bir çevirmen **birden çok** kitap çevirebilir.
- **Sıfıra bir (0:1)** - Bir kitabın devamı **olabilir**.
- **Çoka çok (M:N)** - Bir kitabın **birkaç** etiketi olabilir ve bir etiket **birkaç** kitaba atanabilir.

Bu ilişkilerde her zaman bir **üst tablo** ve bir **alt tablo** vardır. Örneğin yazarlarla kitaplar arasındaki ilişkide `author` tablosu üst, `book` tablosu alttır; bir kitabın her zaman bir yazara "ait olduğunu" düşünebilirsiniz. Bu, veritabanı yapısına da yansır: alt tablo `book`, üst tablo `author` tablosuna başvuran `author_id` yabancı anahtarını içerir.

Kitapları yazarlarının adlarıyla birlikte listelememiz gerekiyorsa iki seçeneğimiz var. Ya veriyi JOIN kullanan tek bir SQL sorgusuyla alırız:

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

Ya da veriyi iki adımda alırız (önce kitaplar, sonra yazarları) ve sonra PHP'de birleştiririz:

```sql
SELECT * FROM book;
SELECT * FROM author WHERE id IN (1, 2, 3);  -- seçilen kitapların yazar ID'leri
```

İkinci yaklaşım aslında **daha verimlidir**, şaşırtıcı gelse de. Veri yalnızca bir kez getirilir ve önbellekte daha iyi kullanılabilir. Nette Database Explorer tam olarak böyle çalışır; her şeyi perde arkasında halleder ve size şık bir API sunar:

```php
$books = $explorer->table('book');
foreach ($books as $book) {
	echo 'başlık: ' . $book->title;
	echo 'yazan: ' . $book->author->name; // $book->author, 'author' tablosundan bir kayıttır
	echo 'çeviren: ' . $book->translator?->name;
}
```


Üst Tabloya Erişim
------------------

Üst tabloya erişim dolaysızdır. Bunlar *bir kitabın bir yazarı vardır* ya da *bir kitabın çevirmeni olabilir* türünden ilişkilerdir. İlişkili kayıt, ActiveRow nesnesinin bir özelliğiyle alınır; özelliğin adı, yabancı anahtar sütununun `_id` son eki olmadan yazılmış hâline karşılık gelir:

```php
$book = $explorer->table('book')->get(1);
echo $book->author->name;      // author_id sütununa göre yazarı bulur
echo $book->translator?->name; // translator_id sütununa göre çevirmeni bulur
```

`$book->author` özelliğine erişilirken Explorer, `book` tablosunda adında `author` dizesi geçen bir sütun arar (yani `author_id`). Bu sütundaki değere göre `author` tablosundan ilgili kaydı yükler ve onu bir `ActiveRow` olarak döndürür. `$book->translator` da benzer şekilde `translator_id` sütununu kullanır. `translator_id` sütunu `null` içerebildiğinden kodda nullsafe operatörü `?->` kullanıyoruz.

Bir başka yaklaşımı, hedef tablonun adını ve birleştirme sütununun adını alan ve bir `ActiveRow` örneği ya da `null` döndüren `ref()` metodu sunar:

```php
echo $book->ref('author', 'author_id')->name;      // yazarla ilişki
echo $book->ref('author', 'translator_id')->name;  // çevirmenle ilişki
```

`ref()` metodu, örneğin tablo aynı adda bir sütun içerdiği için (yani `author`) özellik erişimi kullanılamadığında işe yarar. Diğer durumlarda, daha iyi okunurluk için özellik erişimini kullanmanız önerilir.

Explorer, veritabanı sorgularını otomatik olarak iyileştirir. Bir döngüde kitapları dolaşıp ilişkili kayıtlarına (yazarlara, çevirmenlere) erişirken Explorer her kitap için ayrı bir sorgu üretmez. Bunun yerine **her ilişki türü için yalnızca bir SELECT sorgusu** çalıştırır ve veritabanı yükünü belirgin biçimde azaltır. Örneğin:

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

Bu kod, veritabanına yalnızca şu üç şimşek hızındaki sorguyu çalıştırır:

```sql
SELECT * FROM `book`;
SELECT * FROM `author` WHERE (`id` IN (1, 2, 3)); -- seçilen kitapların author_id sütunundaki ID'ler
SELECT * FROM `author` WHERE (`id` IN (2, 3));    -- seçilen kitapların translator_id sütunundaki ID'ler
```

.[note]
Birleştirme sütununu bulma mantığını [Conventions |api:Nette\Database\Conventions] gerçekleştirimi belirler. Yabancı anahtarları çözümleyen ve tablolar arasındaki var olan ilişkilerle kolayca çalışmanızı sağlayan [DiscoveredConventions |api:Nette\Database\Conventions\DiscoveredConventions] kullanmanızı öneririz.


Alt Tabloya Erişim
------------------

Alt tabloya erişim ters yönde çalışır. Şimdi *bu yazar hangi kitapları yazdı* ya da *bu çevirmen hangi kitapları çevirdi* diye soruyoruz. Bu tür sorgu için, ilişkili kayıtları içeren bir `Selection` döndüren `related()` metodunu kullanırız. Bir örneğe bakalım:

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

// Yazarın tüm kitaplarını çıktılar
foreach ($author->related('book.author_id') as $book) {
	echo "Yazdı: $book->title";
}

// Yazarın çevirdiği tüm kitapları çıktılar
foreach ($author->related('book.translator_id') as $book) {
	echo "Çevirdi: $book->title";
}
```

`related()` metodu, birleştirme açıklamasını nokta yazımıyla tek argüman olarak ya da iki ayrı argüman olarak alır:

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

Explorer, doğru birleştirme sütununu üst tablonun adına göre otomatik saptayabilir. Bu durumda kaynak tablonun adı `author` olduğu için `book.author_id` sütunu üzerinden birleştirir:

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

Birden çok olası bağlantı varsa Explorer bir [AmbiguousReferenceKeyException |api:Nette\Database\Conventions\AmbiguousReferenceKeyException] fırlatır.

`related()` metodunu elbette bir döngüde birden çok kaydı dolaşırken de kullanabiliriz; Explorer bu durumda da sorguları otomatik iyileştirir:

```php
$authors = $explorer->table('author');
foreach ($authors as $author) {
	echo $author->name . ' şunları yazdı:';
	foreach ($author->related('book') as $book) {
		echo $book->title;
	}
}
```

Bu kod yalnızca iki şimşek hızında SQL sorgusu üretir:

```sql
SELECT * FROM `author`;
SELECT * FROM `book` WHERE (`author_id` IN (1, 2, 3)); -- seçilen yazarların ID'leri
```


Çoka Çok İlişki
---------------

Çoka çok (M:N) ilişkisi için, iki yabancı anahtar sütunu (`book_id`, `tag_id`) içeren bir **bağlantı tablosu** (bizim durumumuzda `book_tag`) gerekir. Bu sütunların her biri, bağlanan tablolardan birinin birincil anahtarına başvurur. İlişkili veriyi almak için önce `related('book_tag')` ile bağlantı tablosundaki kayıtları alır, sonra hedef veriye geçeriz:

```php
$book = $explorer->table('book')->get(1);
// kitaba atanan etiketlerin adlarını çıktılar
foreach ($book->related('book_tag') as $bookTag) {
	echo $bookTag->tag->name;  // etiket adını bağlantı tablosu üzerinden çıktılar
}

$tag = $explorer->table('tag')->get(1);
// ya da tersi: bu etiketle işaretlenmiş kitapların adlarını çıktılar
foreach ($tag->related('book_tag') as $bookTag) {
	echo $bookTag->book->title; // kitabın başlığını çıktılar
}
```

Explorer, SQL sorgularını yine verimli bir biçime dönüştürür:

```sql
SELECT * FROM `book`;
SELECT * FROM `book_tag` WHERE (`book_tag`.`book_id` IN (1, 2, ...));  -- seçilen kitapların ID'leri
SELECT * FROM `tag` WHERE (`tag`.`id` IN (1, 2, ...));                 -- book_tag içinde bulunan etiketlerin ID'leri
```


İlişkili Tablolar Üzerinden Sorgulama
-------------------------------------

`where()`, `select()`, `order()` ve `group()` metotlarında, başka tablolardaki sütunlara erişmek için özel yazımlar kullanabilirsiniz. Explorer gereken JOIN'leri otomatik oluşturur.

**Nokta yazımı** (`ust_tablo.sutun`), alt tablonun bakış açısından 1:N ilişkilerinde kullanılır:

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

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

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

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

**İki nokta yazımı** (`:alt_tablo.sutun`), üst tablonun bakış açısından 1:N ilişkilerinde kullanılır:

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

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

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

Yukarıdaki iki nokta yazımı örneğinde (`:book.title`) yabancı anahtar sütunu belirtilmemiştir. Explorer, doğru sütunu üst tablonun adına göre otomatik saptar. Bu durumda kaynak tablonun adı `author` olduğu için `book.author_id` sütunu üzerinden birleştirir. Birden çok olası bağlantı varsa Explorer bir [AmbiguousReferenceKeyException |api:Nette\Database\Conventions\AmbiguousReferenceKeyException] fırlatır.

Birleştirme sütunu parantez içinde açıkça belirtilebilir:

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

Yazımlar, birden çok tabloya yayılan veriye erişmek için zincirlenebilir:

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


JOIN Koşullarını Genişletme
---------------------------

`joinWhere()` metodu, SQL'de tabloları birleştirirken `ON` anahtar sözcüğünden sonra belirtilen koşulları genişletir.

Diyelim ki belirli bir çevirmenin çevirdiği kitapları bulmak istiyoruz:

```php
// 'David' adlı bir çevirmenin çevirdiği 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()` metodundakiyle aynı yapıları kullanabilirsiniz: operatörler, yer tutucular, değer dizileri ya da SQL ifadeleri.

Birden çok JOIN içeren daha karmaşık sorgularda tablo takma adları tanımlayabilirsiniz:

```php
$tags = $explorer->table('tag')
	->joinWhere(':book_tag.book.author', 'book_author.born < ?', 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 (`book_author`.`born` < 1950)
```

`where()` metodunun koşulları `WHERE` yan tümcesine eklediğine, `joinWhere()` metodunun ise tabloları birleştirirken `ON` yan tümcesindeki koşulları genişlettiğine dikkat edin.

Database Explorer

Explorer, veritabanınızla çalışmanın sezgisel ve verimli bir yolunu sunar. Tablo ilişkilerini kendiliğinden ele alır ve sorguları iyileştirir; böylece siz uygulama mantığınıza odaklanabilirsiniz. Yapılandırma olmadan hemen çalışır. SQL sorguları üzerinde tam denetim isterseniz SQL yolunu kullanabilirsiniz.

  • Verilerle çalışmak doğal ve anlaşılması kolaydır
  • Yalnızca gereken veriyi getiren iyileştirilmiş SQL sorguları üretir
  • JOIN sorguları yazmadan ilişkili verilere kolay erişim sağlar
  • Hiçbir yapılandırma ya da varlık üretimi olmadan hemen çalışır

Explorer ile çalışmak, Nette\Database\Explorer nesnesinde table() metodunu çağırmakla başlar (veritabanı bağlantısını kurmanın ayrıntıları için bkz. Bağlantı ve yapılandırma):

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

Metot, bir SQL sorgusunu temsil eden bir Selection nesnesi döndürür. Sonuçları filtrelemek ve sıralamak için bu nesneye başka metotlar zincirlenebilir. Sorgu, veri istendiğinde (örneğin foreach ile dolaşıldığında) kurulur ve çalıştırılır. Her satır bir ActiveRow nesnesiyle temsil edilir:

foreach ($books as $book) {
	echo $book->title;        // 'title' sütununu çı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, ilişkili tablolardan (kitaplar ve yazarları) veriyi ne kadar kolay çıktılayabildiğimizi gösteriyor. Hiçbir JOIN sorgusu yazmaya gerek olmadığına dikkat edin; onları Nette bizim için üretir:

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

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

Nette Database Explorer, sorguları en yüksek verim için iyileştirir. Yukarıdaki örnek, 10 ya da 10.000 kitap işlememizden bağımsız olarak yalnızca iki SELECT sorgusu çalıştırır.

Ayrıca Explorer, kodda hangi sütunların kullanıldığını izler ve veritabanından yalnızca onları getirir; bu da başarımdan daha fazla kazandırır. Bu davranış tümüyle otomatik ve uyarlanabilirdir. Kodu sonradan başka sütunları kullanacak şekilde değiştirirseniz, Explorer sorguları kendiliğinden ayarlar. Hiçbir şey yapılandırmanıza ya da hangi sütunların gerekeceğini düşünmenize gerek yok; bunu Nette'ye bırakın.

Filtreleme ve Sıralama

Selection sınıfı, veri seçimlerini filtrelemek ve sıralamak için metotlar sunar.

where($condition, ...$params) Bir WHERE koşulu ekler. Birden çok koşul AND ile birleştirilir
whereOr(array $conditions) OR ile birleştirilen bir WHERE koşulları grubu ekler
wherePrimary($value) Birincil anahtara dayalı 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ı OFFSET ayarlar
page($page, $itemsPerPage, &$numOfPages = null) Sayfalamayı ayarlar
group($columns, ...$params) Satırları gruplar (GROUP BY)
having($condition, ...$params) Gruplanmış satırları filtrelemek için HAVING koşulu ekler

Metotlar zincirlenebilir (buna akıcı arayüz denir): $table->where(...)->order(...)->limit(...).

Bu metotlarda, ilişkili tablolardan gelen verilere erişmek için özel yazımlar da kullanabilirsiniz.

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

Metotlar, parametreleri otomatik kaçışlar ve tanımlayıcıları (tablo ve sütun adlarını) tırnaklar; böylece SQL injection'ı önler. Düzgün çalışmasını sağlamak için birkaç kurala uyulmalıdır:

  • Anahtar sözcükleri, fonksiyon ve prosedür adlarını vb. büyük harfle yazın.
  • Sütun ve tablo adlarını küçük harfle yazın.
  • Dizeleri her zaman parametrelerle aktarın.
where('name = ' . $name);         // KRİTİK AÇIK: SQL injection
where('name LIKE "%search%"');    // YANLIŞ: otomatik tırnaklamayı zorlaştırır
where('name LIKE ?', '%search%'); // DOĞRU: değer parametre olarak aktarılır

where('name like ?', $name);     // YANLIŞ: şunu üretir: `name` `like` ?
where('name LIKE ?', $name);     // DOĞRU: şunu üretir: `name` LIKE ?
where('LOWER(name) = ?', $value);// DOĞRU: LOWER(`name`) = ?

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

Sonuçları WHERE koşullarıyla filtreler. Gücü, çeşitli değer türlerini akıllıca ele almasında ve uygun SQL operatörlerini otomatik seçmesindedir.

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 saptanması sayesinde çeşitli özel durumları ele almanız gerekmez; onları Nette çözer:

$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)
// Operatörsüz ? yer tutucusunu da kullanabilirsiniz:
$table->where('id ?', 1);        // WHERE `id` = 1

Metot, olumsuz koşulları ve boş dizileri doğru şekilde ele alır:

$table->where('id', []);         // WHERE `id` IS NULL AND FALSE -- hiçbir şey bulmaz
$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öz dizimi desteklenmiyor

Parametre olarak başka bir tablo sorgusunun sonucunu da aktarabilir, böylece bir alt sorgu oluşturabilirsiniz:

// 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, öğeleri AND ile birleştirilen bir dizi olarak da aktarılabilir:

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

Dizide anahtar ⇒ değer çiftleri kullanabilirsiniz; Nette yine doğru operatörleri otomatik seçer:

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

Dizide SQL ifadelerini yer tutucular ve birden çok parametreyle birleştirebilirsiniz. Bu, operatörleri kesin biçimde belirlenmiş karmaşık koşullar için uygundur:

// WHERE (`age` > 18) AND (ROUND(`score`, 2) > 75.5)
$table->where([
	'age > ?' => 18,
	'ROUND(score, ?) > ?' => [2, 75.5], // iki parametre dizi olarak aktarılır
]);

where() metodunun birden çok çağrısı, koşulları otomatik olarak AND ile birleştirir.

whereOr(array $parameters)static

where() metoduna benzer şekilde koşul ekler, ama onları OR ile birleştirir:

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

Burada 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), onu dizi olarak verin:

// 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 hangi sırayla döndürüleceğini belirtir. Bir ya da daha çok sütuna göre, artan ya da azalan sırada veya özel bir ifadeye göre sıralayabilirsiniz:

$table->order('created');                   // ORDER BY `created`
$table->order('created DESC');              // ORDER BY `created` DESC
$table->order('priority DESC, created');    // ORDER BY `priority` DESC, `created`
$table->order('status = ? DESC', 'active'); // ORDER BY `status` = 'active' DESC

select(string $columns, …$parameters)static

Veritabanından döndürülecek sütunları belirtir. Nette Database Explorer varsayılan olarak yalnızca kodda gerçekten kullanılan sütunları döndürür. Belirli ifadeleri almanız gerektiğinde select() metodunu 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 ile tanımlanan takma adlara sonra ActiveRow nesnesinin özellikleri olarak erişilir:

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 kayma ayarlamaya izin verir:

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

Sayfalama için page() metodunu kullanmak daha uygundur.

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

Sonuçların sayfalanmasını kolaylaştırır. Sayfa numarasını (1'den başlar) ve sayfa başına öğe sayısını alır. İsteğe bağlı olarak, toplam sayfa sayısının saklanacağı bir değişkene referans verebilirsiniz:

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

group(string $columns, …$parameters)static

Satırları belirtilen sütunlara göre gruplar (GROUP BY). Genellikle toplama fonksiyonlarıyla 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

Gruplanmış satırları filtrelemek için bir koşul ayarlar (HAVING). group() metodu ve toplama fonksiyonlarıyla birlikte kullanılabilir:

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

Veri Okuma

Veritabanından veri okumak için çeşitli kullanışlı metotlar vardır:

foreach ($table as $key => $row) Tüm satırları dolaşır; $key birincil anahtar değeri, $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 sonrakine ilerletir
$array = $table->fetchPairs() Sonuçlardan ilişkisel bir dizi oluşturur
$array = $table->fetchAll() Tüm satırları dizi olarak döndürür
count($table) Selection nesnesindeki satır sayısını döndürür

ActiveRow nesnesi salt okunurdur. Yani özelliklerinin değerlerini değiştiremezsiniz. Bu kısıtlama veri tutarlılığını güvence altına alır ve beklenmedik yan etkileri önler. Veri veritabanından yüklenir ve her türlü değişiklik açıkça ve denetimli biçimde yapılmalıdır.

foreach – Tüm Satırları Dolaşma

Bir sorguyu çalıştırıp satırları almanın en kolay yolu, foreach döngüsüyle dolaşmaktır. SQL sorgusunu otomatik olarak çalıştırır.

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

get($key): ?ActiveRow

SQL sorgusunu çalıştırır ve satırı birincil anahtara göre döndürür; yoksa null döndürür.

$book = $explorer->table('book')->get(123);  // ID'si 123 olan ActiveRow'u ya da null döndürür
if ($book) {
	echo $book->title;
}

fetch(): ?ActiveRow

Geçerli satırı döndürür ve iç işaretçiyi sonrakine ilerletir. Başka satır kalmadıysa 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 argüman dizide anahtar olarak kullanılacak sütunun adını, ikinci argüman 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 verilirse değer, satırın tamamı, yani ActiveRow nesnesi olur:

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

Anahtarlar yinelenirse son satırdaki değer kullanılır. Anahtar olarak null kullanıldığında dizi sıfırdan başlayarak sayısal indekslenir (o zaman çakışma olmaz):

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

fetchPairs(Closure $callback)array

Alternatif olarak parametre olarak bir callback verebilirsiniz; bu callback her satır için ya tek bir değer ya da bir anahtar-değer çifti döndürür.

$titles = $explorer->table('book')
	->fetchPairs(fn($row) => "$row->title ({$row->author->name})");
// ['First Book (John Novak)', ...]

// Callback, anahtar ve değer çiftinden oluşan bir dizi de döndürebilir:
$titles = $explorer->table('book')
	->fetchPairs(fn($row) => [$row->title, $row->author->name]);
// ['First Book' => 'John Novak', ...]

fetchAll(): array

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

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

count(): int

Parametresiz count() metodu, Selection nesnesindeki satır sayısını döndürür:

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

Not: Parametreli count(), veritabanında COUNT toplama fonksiyonunu çalıştırır, aşağıya bakın.

ActiveRow::toArray(): array

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

$book = $explorer->table('book')->get(1);
$bookArray = $book->toArray();
// $bookArray şu olur: ['id' => 1, 'title' => '...', 'author_id' => ..., ...]

Toplama

Selection sınıfı, toplama fonksiyonlarını (COUNT, SUM, MIN, MAX, AVG vb.) kolayca çalıştırmak için metotlar sunar.

count($expr) Satır sayısını sayar
min($expr) Bir sütundaki en küçük değeri döndürür
max($expr) Bir sütundaki en büyük değeri döndürür
sum($expr) Bir sütundaki değerlerin toplamını döndürür
aggregation($function) AVG() ya da GROUP_CONCAT() gibi herhangi bir toplama fonksiyonuna izin verir

count(string $expr): int

COUNT fonksiyonuyla bir SQL sorgusu çalıştırır ve sonucu döndürür. Metot, belirli bir koşula kaç satırın uyduğunu 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: Parametresiz count(), yalnızca Selection nesnesindeki satır sayısını döndürür.

min(string $expr) ve max(string $expr)

min() ve max() metotları, belirtilen sütundaki ya da ifadedeki en küçük ve en büyük değerleri döndürür:

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

sum(string $expr): mixed

Belirtilen sütundaki ya da 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 fonksiyonunu çalıştırmaya olanak tanır.

// bir kategorideki ürünlerin ortalama fiyatı
$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;

Zaten bir toplama fonksiyonundan ve gruplamadan doğan sonuçları toplamamız gerekiyorsa (örneğin gruplanmış satırlarda SUM(value)), bu ara sonuçlara uygulanacak toplama fonksiyonunu ikinci argüman olarak belirtiriz:

// Tek tek kategorilerdeki stoktaki ürünlerin toplam fiyatını hesaplar ve sonra 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 değerine göre gruplandırıyoruz. Sonra bu ara toplamları category_total toplamak için aggregation('SUM(category_total)', 'SUM') kullanıyoruz. İkinci argüman 'SUM', ara sonuçlara SUM fonksiyonunun uygulanacağını belirtir.

Insert, Update ve Delete

Nette Database Explorer, veri eklemeyi, güncellemeyi ve silmeyi kolaylaştırır. Sözü edilen tüm metotlar hata durumunda Nette\Database\DriverException fırlatır.

Selection::insert(iterable $data)

Tabloya yeni kayıtlar ekler.

Tek bir kayıt ekleme:

Yeni kaydı, anahtarları tablodaki sütun adlarına karşılık gelen ilişkisel bir dizi ya da iterable nesne (formlarda kullanılan ArrayHash gibi) olarak verin.

Tablonun tanımlı bir birincil anahtarı varsa, metot bir ActiveRow nesnesi döndürür; bu nesne, veritabanı düzeyinde yapılan değişiklikleri (tetikleyiciler, varsayılan sütun değerleri, auto-increment sütun hesapları) yansıtmak için veritabanından yeniden yüklenir. Bu, veri tutarlılığını güvence altına alır ve nesne her zaman veritabanındaki güncel veriyi içerir. Tablonun birincil anahtarı yoksa tanımlanabilir bir satır olmadığından metot null döndürür.

$row = $explorer->table('users')->insert([
	'name' => 'John Doe',
	'email' => 'john.doe@example.com',
]);
// $row bir ActiveRow örneğidir ve eklenen satırın tüm verilerini içerir;
// otomatik üretilen ID ve tetikleyicilerin yaptığı değişiklikler dahil
echo $row->id; // Yeni eklenen kullanıcının ID'sini çıktılar
echo $row->created_at; // Bir tetikleyici ayarladıysa oluşturulma zamanını çıktılar

Birden çok kaydı tek seferde ekleme:

insert() metodu, tek bir SQL sorgusuyla birden çok kayıt eklemeye olanak tanır. Bu durumda eklenen satır 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 değeri 2 olur

Parametre olarak, veri seçimi içeren bir Selection nesnesi de aktarılabilir.

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

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

Özel değerler ekleme:

Değer olarak dosyaları, DateTime nesnelerini ya da SQL sabit değerlerini de aktarabiliriz:

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

Selection::update(iterable $data)int

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

Değiştirilecek sütunları, anahtarları tablodaki sütun adlarına karşılık gelen ilişkisel bir dizi ya da iterable nesne (formlarda kullanılan ArrayHash gibi) olarak verin:

$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,  // 'points' sütununun değerini 1 artırır
		'coins-=' => 1,   // 'coins' sütununun değerini 1 azaltır
	]);
// UPDATE `users` SET `points` = `points` + 1, `coins` = `coins` - 1 WHERE `id` = 10

Selection::delete(): int

Tablodaki satırları belirtilen filtreye göre siler. Silinen satır sayısını döndürür.

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

update() ya da delete() çağırırken, değiştirilecek/silinecek satırları belirtmek için where() kullanmayı unutmayın. where() kullanılmazsa işlem tablonun tamamında gerçekleştirilir!

ActiveRow::update(iterable $data)bool

ActiveRow nesnesinin temsil ettiği veritabanı satırındaki veriyi günceller. Güncellenecek veriyi içeren bir iterable alır (anahtarlar sütun adlarıdır). Sayısal değerleri değiştirmek için += ve -= operatörlerini kullanabilirsiniz:

Güncelleme yapıldıktan sonra ActiveRow, veritabanı düzeyinde yapılan değişiklikleri (örneğin tetikleyicileri) yansıtmak için veritabanından otomatik olarak yeniden yüklenir. Metot yalnızca gerçek bir veri değişikliği olduysa true döndürür.

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

Bu metot veritabanında yalnızca belirli bir satırı günceller. Birden çok satırı toplu güncellemek için Selection::update() metodunu kullanın.

ActiveRow::delete(): int

ActiveRow nesnesinin temsil ettiği satırı veritabanından siler. Silinen satır sayısını döndürür; bu 1 olmalıdır.

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

Bu metot veritabanında yalnızca belirli bir satırı siler. Birden çok satırı toplu silmek için Selection::delete() metodunu kullanın.

Tablolar Arası İlişkiler

İlişkisel veritabanlarında veriler birden çok tabloya bölünür ve yabancı anahtarlarla birbirine bağlanır. Nette Database Explorer, bu ilişkilerle çalışmanın devrim niteliğinde bir yolunu sunar: JOIN sorguları yazmadan ve hiçbir şeyi yapılandırmaya ya da üretmeye gerek kalmadan.

İlişkilerle çalışmayı göstermek için örnek bir kitap veritabanı kullanacağız (GitHub'da bulabilirsiniz). Veritabanında şu tablolar var:

  • 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 – kitaplarla etiketler arasındaki bağlantı tablosu (book_id, tag_id sütunları)

Örneklerde kullanılan veritabanı yapısı

Örnek kitap veritabanımızda birkaç ilişki türü buluyoruz (model gerçeğe göre basitleştirilmiş olsa da):

  • Bire çok (1:N) – Her kitabın bir yazarı vardır; bir yazar birden çok kitap yazabilir.
  • Sıfıra çok (0:N) – Bir kitabın çevirmeni olabilir; bir çevirmen birden çok kitap çevirebilir.
  • Sıfıra bir (0:1) – Bir kitabın devamı olabilir.
  • Çoka çok (M:N) – Bir kitabın birkaç etiketi olabilir ve bir etiket birkaç kitaba atanabilir.

Bu ilişkilerde her zaman bir üst tablo ve bir alt tablo vardır. Örneğin yazarlarla kitaplar arasındaki ilişkide author tablosu üst, book tablosu alttır; bir kitabın her zaman bir yazara „ait olduğunu“ düşünebilirsiniz. Bu, veritabanı yapısına da yansır: alt tablo book, üst tablo author tablosuna başvuran author_id yabancı anahtarını içerir.

Kitapları yazarlarının adlarıyla birlikte listelememiz gerekiyorsa iki seçeneğimiz var. Ya veriyi JOIN kullanan tek bir SQL sorgusuyla alırız:

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

Ya da veriyi iki adımda alırız (önce kitaplar, sonra yazarları) ve sonra PHP'de birleştiririz:

SELECT * FROM book;
SELECT * FROM author WHERE id IN (1, 2, 3);  -- seçilen kitapların yazar ID'leri

İkinci yaklaşım aslında daha verimlidir, şaşırtıcı gelse de. Veri yalnızca bir kez getirilir ve önbellekte daha iyi kullanılabilir. Nette Database Explorer tam olarak böyle çalışır; her şeyi perde arkasında halleder ve size şık bir API sunar:

$books = $explorer->table('book');
foreach ($books as $book) {
	echo 'başlık: ' . $book->title;
	echo 'yazan: ' . $book->author->name; // $book->author, 'author' tablosundan bir kayıttır
	echo 'çeviren: ' . $book->translator?->name;
}

Üst Tabloya Erişim

Üst tabloya erişim dolaysızdır. Bunlar bir kitabın bir yazarı vardır ya da bir kitabın çevirmeni olabilir türünden ilişkilerdir. İlişkili kayıt, ActiveRow nesnesinin bir özelliğiyle alınır; özelliğin adı, yabancı anahtar sütununun _id son eki olmadan yazılmış hâline karşılık gelir:

$book = $explorer->table('book')->get(1);
echo $book->author->name;      // author_id sütununa göre yazarı bulur
echo $book->translator?->name; // translator_id sütununa göre çevirmeni bulur

$book->author özelliğine erişilirken Explorer, book tablosunda adında author dizesi geçen bir sütun arar (yani author_id). Bu sütundaki değere göre author tablosundan ilgili kaydı yükler ve onu bir ActiveRow olarak döndürür. $book->translator da benzer şekilde translator_id sütununu kullanır. translator_id sütunu null içerebildiğinden kodda nullsafe operatörü ?-> kullanıyoruz.

Bir başka yaklaşımı, hedef tablonun adını ve birleştirme sütununun adını alan ve bir ActiveRow örneği ya da null döndüren ref() metodu sunar:

echo $book->ref('author', 'author_id')->name;      // yazarla ilişki
echo $book->ref('author', 'translator_id')->name;  // çevirmenle ilişki

ref() metodu, örneğin tablo aynı adda bir sütun içerdiği için (yani author) özellik erişimi kullanılamadığında işe yarar. Diğer durumlarda, daha iyi okunurluk için özellik erişimini kullanmanız önerilir.

Explorer, veritabanı sorgularını otomatik olarak iyileştirir. Bir döngüde kitapları dolaşıp ilişkili kayıtlarına (yazarlara, çevirmenlere) erişirken Explorer her kitap için ayrı bir sorgu üretmez. Bunun yerine her ilişki türü için yalnızca bir SELECT sorgusu çalıştırır ve veritabanı yükünü belirgin biçimde 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, veritabanına yalnızca şu üç şimşek hızındaki sorguyu çalıştırır:

SELECT * FROM `book`;
SELECT * FROM `author` WHERE (`id` IN (1, 2, 3)); -- seçilen kitapların author_id sütunundaki ID'ler
SELECT * FROM `author` WHERE (`id` IN (2, 3));    -- seçilen kitapların translator_id sütunundaki ID'ler

Birleştirme sütununu bulma mantığını Conventions gerçekleştirimi belirler. Yabancı anahtarları çözümleyen ve tablolar arasındaki var olan ilişkilerle kolayca çalışmanızı sağlayan DiscoveredConventions kullanmanızı öneririz.

Alt Tabloya Erişim

Alt tabloya erişim ters yönde çalışır. Şimdi bu yazar hangi kitapları yazdı ya da bu çevirmen hangi kitapları çevirdi diye soruyoruz. Bu tür sorgu için, ilişkili kayıtları içeren bir Selection döndüren related() metodunu kullanırız. Bir örneğe bakalım:

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

// Yazarın tüm kitaplarını çıktılar
foreach ($author->related('book.author_id') as $book) {
	echo "Yazdı: $book->title";
}

// Yazarın çevirdiği tüm kitapları çıktılar
foreach ($author->related('book.translator_id') as $book) {
	echo "Çevirdi: $book->title";
}

related() metodu, birleştirme açıklamasını nokta yazımıyla tek argüman olarak ya da iki ayrı argüman olarak alır:

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

Explorer, doğru birleştirme sütununu üst tablonun adına göre otomatik saptayabilir. Bu durumda kaynak tablonun adı author olduğu için book.author_id sütunu üzerinden birleştirir:

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

Birden çok olası bağlantı varsa Explorer bir AmbiguousReferenceKeyException fırlatır.

related() metodunu elbette bir döngüde birden çok kaydı dolaşırken de kullanabiliriz; Explorer bu durumda da sorguları otomatik iyileştirir:

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

Bu kod yalnızca iki şimşek hızında SQL sorgusu üretir:

SELECT * FROM `author`;
SELECT * FROM `book` WHERE (`author_id` IN (1, 2, 3)); -- seçilen yazarların ID'leri

Çoka Çok İlişki

Çoka çok (M:N) ilişkisi için, iki yabancı anahtar sütunu (book_id, tag_id) içeren bir bağlantı tablosu (bizim durumumuzda book_tag) gerekir. Bu sütunların her biri, bağlanan tablolardan birinin birincil anahtarına başvurur. İlişkili veriyi almak için önce related('book_tag') ile bağlantı tablosundaki kayıtları alır, sonra hedef veriye geçeriz:

$book = $explorer->table('book')->get(1);
// kitaba atanan etiketlerin adlarını çıktılar
foreach ($book->related('book_tag') as $bookTag) {
	echo $bookTag->tag->name;  // etiket adını bağlantı tablosu üzerinden çıktılar
}

$tag = $explorer->table('tag')->get(1);
// ya da tersi: bu etiketle işaretlenmiş kitapların adlarını çıktılar
foreach ($tag->related('book_tag') as $bookTag) {
	echo $bookTag->book->title; // kitabın başlığını çıktılar
}

Explorer, SQL sorgularını yine verimli bir biçime dönüştürür:

SELECT * FROM `book`;
SELECT * FROM `book_tag` WHERE (`book_tag`.`book_id` IN (1, 2, ...));  -- seçilen kitapların ID'leri
SELECT * FROM `tag` WHERE (`tag`.`id` IN (1, 2, ...));                 -- book_tag içinde bulunan etiketlerin ID'leri

İlişkili Tablolar Üzerinden Sorgulama

where(), select(), order() ve group() metotlarında, başka tablolardaki sütunlara erişmek için özel yazımlar kullanabilirsiniz. Explorer gereken JOIN'leri otomatik oluşturur.

Nokta yazımı (ust_tablo.sutun), alt tablonun bakış açısından 1:N ilişkilerinde kullanılır:

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

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

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

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

İki nokta yazımı (:alt_tablo.sutun), üst tablonun bakış açısından 1:N ilişkilerinde 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ın kitap sayısını sayar
$authors->select('*, COUNT(:book.id) AS book_count')
	->group('author.id');

Yukarıdaki iki nokta yazımı örneğinde (:book.title) yabancı anahtar sütunu belirtilmemiştir. Explorer, doğru sütunu üst tablonun adına göre otomatik saptar. Bu durumda kaynak tablonun adı author olduğu için book.author_id sütunu üzerinden birleştirir. Birden çok olası bağlantı varsa Explorer bir AmbiguousReferenceKeyException fırlatır.

Birleştirme sütunu parantez içinde açıkça belirtilebilir:

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

Yazımlar, birden çok tabloya yayılan veriye erişmek için zincirlenebilir:

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

JOIN Koşullarını Genişletme

joinWhere() metodu, SQL'de tabloları birleştirirken ON anahtar sözcüğünden sonra belirtilen koşulları genişletir.

Diyelim ki belirli bir çevirmenin çevirdiği kitapları bulmak istiyoruz:

// 'David' adlı bir çevirmenin çevirdiği 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() metodundakiyle aynı yapıları kullanabilirsiniz: operatörler, yer tutucular, değer dizileri ya da SQL ifadeleri.

Birden çok JOIN içeren daha karmaşık sorgularda tablo takma adları tanımlayabilirsiniz:

$tags = $explorer->table('tag')
	->joinWhere(':book_tag.book.author', 'book_author.born < ?', 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 (`book_author`.`born` < 1950)

where() metodunun koşulları WHERE yan tümcesine eklediğine, joinWhere() metodunun ise tabloları birleştirirken ON yan tümcesindeki koşulları genişlettiğine dikkat edin.