Database Explorer

Explorer, veritabanıyla çalışmak için sezgisel ve etkili bir yol sunar. Tablolar arasındaki ilişkileri ve sorgu optimizasyonunu otomatik olarak halleder, böylece uygulamanıza odaklanabilirsiniz. Ayarlama yapmadan hemen çalışır. SQL sorguları üzerinde tam kontrol sahibi olmanız gerekiyorsa, SQL yaklaşımını kullanabilirsiniz.

  • Verilerle çalışmak doğal ve anlaşılması kolaydır
  • Yalnızca gerekli verileri yükleyen 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 anında çalışır

Explorer ile Nette\Database\Explorer nesnesinin table() metodunu çağırarak başlarsınız (bağlantı ayrıntıları Bağlantı ve yapılandırma bölümünde bulunabilir):

$books = $explorer->table('book'); // 'book' tablo 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 ek metotlar zincirleyebiliriz. Sorgu, veri talep etmeye başladığımızda oluşturulur ve yürütülür. Örneğin, bir foreach döngüsüyle. Her satır bir ActiveRow nesnesiyle temsil edilir:

foreach ($books as $book) {
	echo $book->title;        // 'title' sütununu yazdır
	echo $book->author_id;    // 'author_id' sütununu yazdır
}

Explorer, tablolar arasındaki ilişkilerle çalışmayı önemli ölçüde kolaylaştırır. Aşağıdaki örnek, ilişkili tablolardan (kitaplar ve yazarları) verilerin ne kadar kolay listelenebileceğini gösterir. Herhangi bir JOIN sorgusu yazmamıza gerek olmadığına dikkat edin, Nette bunları bizim için oluşturur:

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

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

Nette Database Explorer, sorguları mümkün olduğunca verimli olacak şekilde optimize eder. Yukarıdaki örnek, 10 veya 10.000 kitap işliyor olsak da yalnızca iki SELECT sorgusu gerçekleştirir.

Ek olarak, Explorer kodda hangi sütunların kullanıldığını izler ve veritabanından yalnızca bunları yükleyerek daha fazla performans tasarrufu sağlar. Bu davranış tamamen otomatik ve uyarlanabilirdir. Daha sonra kodu değiştirir ve ek sütunlar kullanmaya başlarsanız, Explorer sorguları otomatik olarak ayarlar. Hiçbir şey ayarlamanıza veya hangi sütunlara ihtiyacınız olacağını düşünmenize gerek yok – bırakın Nette halletsin.

Filtreleme ve sıralama

Selection sınıfı, veri seçimini filtrelemek ve sıralamak için metotlar sağlar.

where($condition, ...$params) WHERE koşulu ekler. Birden çok koşul AND operatörü ile birleştirilir
whereOr(array $conditions) OR operatörü ile birleştirilmiş bir WHERE koşulları grubu ekler
wherePrimary($value) Birincil anahtara göre WHERE koşulu ekler
order($columns, ...$params) ORDER BY sıralamasını ayarlar
select($columns, ...$params) Yüklenecek sütunları 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) 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 (sözde akıcı arayüz): $table->where(...)->order(...)->limit(...).

Bu metotlarda, ilişkili tablolardaki verilere erişmek için özel bir gösterim de kullanabilirsiniz.

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

Metotlar parametreleri otomatik olarak kaçar ve tanımlayıcıları (tablo ve sütun adları) tırnak içine alır, böylece SQL injection'u önler. Doğru çalışması için birkaç kurala uymak gerekir:

  • Anahtar kelimeleri, fonksiyon adlarını, prosedürleri vb. büyük harflerle yazın.
  • Sütun ve tablo adlarını küçük harflerle yazın.
  • Karakter dizilerini her zaman parametreler aracılığıyla ekleyin.
where('name = ' . $name);         // KRİTİK GÜVENLİK AÇIĞI: SQL injection
where('name LIKE "%search%"');    // YANLIŞ: otomatik tırnak içine almayı zorlaştırır
where('name LIKE ?', '%search%'); // DOĞRU: parametre aracılığıyla eklenen değer

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

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

Sonuçları WHERE koşullarıyla filtreler. Güçlü yanı, farklı değer tipleriyle akıllıca çalışması ve SQL operatörlerini otomatik olarak seçmesidir.

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, farklı özel durumlarla uğraşmamıza gerek kalmaz. Nette bunları bizim 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)
// operatör olmadan yer tutucu soru işareti de kullanılabilir:
$table->where('id ?', 1);        // WHERE `id` = 1

Metot, negatif koşulları ve boş dizileri de doğru şekilde işler:

$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);  Dikkat - bu sözdizimi desteklenmiyor

Parametre olarak başka bir tablodan sonuç da iletebiliriz – bir alt sorgu oluşturulur:

// 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ştirilecek bir dizi olarak da iletebiliriz:

// 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 kullanabiliriz ve Nette yine doğru operatörleri otomatik olarak seçer:

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

Dizide, yer tutucu soru işaretleri ve birden çok parametre içeren SQL ifadelerini birleştirebiliriz. Bu, tam olarak tanımlanmış operatörlere sahip 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 parametreyi dizi olarak iletiriz
]);

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

whereOr (array $parameters)static

where() gibi koşullar ekler, ancak farkı bunları OR kullanarak birleştirmesidir:

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

Burada da daha karmaşık ifadeler kullanabiliriz:

// 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ı belirler. Bir veya daha fazla sütuna göre, azalan veya artan sırada veya özel bir ifadeye göre sıralayabiliriz:

$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. Varsayılan olarak, Nette Database Explorer yalnızca kodda gerçekten kullanılan sütunları döndürür. Bu nedenle select() metodunu, belirli ifadeleri döndürmemiz gereken durumlarda kullanırız:

// 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 adlar daha sonra ActiveRow nesnesinin özellikleri olarak kullanılabilir:

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 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şlayarak sayılır) 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 iletilebilir:

$numOfPages = null;
$table->page(page: 3, itemsPerPage: 10, $numOfPages);
echo "Toplam sayfa sayısı: $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 birkaç kullanışlı metodumuz var:

foreach ($table as $key => $row) Tüm satırlar üzerinde yinelenir, $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 taşır
$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 yalnızca okuma amaçlıdır. 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 yüklenir ve herhangi bir değişiklik açıkça ve kontrollü bir şekilde yapılmalıdır.

foreach – tüm satırlar üzerinde yineleme

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

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

get ($key): ?ActiveRow

SQL sorgusunu yürütür ve birincil anahtara göre satırı veya mevcut değilse null döndürür.

$book = $explorer->table('book')->get(123);  // ID 123 olan ActiveRow'u veya null 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 taşır. 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 argüman, dizide anahtar olarak kullanılacak sütun adını belirtir, ikinci argüman değer olarak kullanılacak sütun adını belirtir:

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

Yalnızca ilk parametreyi belirtirsek, değer tüm satır, yani ActiveRow nesnesi olacaktır:

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

Yinelenen anahtarlar durumunda, son satırdan gelen değer kullanılır. Anahtar olarak null kullanıldığında, dizi sıfırdan başlayarak sayısal olarak dizine eklenir (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 her satır için ya değerin kendisini ya da bir anahtar-değer çiftini döndürecek bir geri arama (callback) belirtebilirsiniz.

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

// Geri arama ayrıca bir 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 Novák', ...]

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

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

Dikkat, parametreli count() veritabanında COUNT toplama fonksiyonunu gerçekleştirir, aşağıya bakın.

ActiveRow::toArray(): array

ActiveRow nesnesini, anahtarların sütun adları ve değerlerin 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 ['id' => 1, 'title' => '...', 'author_id' => ..., ...] olacaktır

Toplama

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

count($expr) Satır sayısını sayar
min($expr) Sütundaki minimum değeri döndürür
max($expr) Sütundaki maksimum değeri döndürür
sum($expr) Sütundaki değerlerin toplamını döndürür
aggregation($function) Herhangi bir toplama fonksiyonunun gerçekleştirilmesini sağlar. Örn. AVG()GROUP_CONCAT()

count (string $expr): int

COUNT fonksiyonuyla bir SQL sorgusu gerçekleştirir ve sonucu döndürür. Metot, belirli bir koşula kaç satırın karşılık geldiğini bulmak için kullanılır:

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

Dikkat, count() parametresiz 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ütun veya ifadedeki minimum ve maksimum değeri döndürür:

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

sum (string $expr)

Belirtilen sütun 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)

Herhangi bir toplama fonksiyonunun gerçekleştirilmesini sağlar.

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

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

Zaten bir toplama fonksiyonu ve gruplamadan (örneğin, gruplanmış satırlar üzerinde SUM(değer)) kaynaklanan sonuçları toplamamız gerekiyorsa, ikinci argüman olarak bu ara sonuçlara uygulanacak toplama fonksiyonunu belirtiriz:

// Her kategori için stoktaki ürünlerin toplam fiyatını hesaplar ve 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ı hesaplarız (SUM(price * stock) AS category_total) ve sonuçları category_id'ye göre gruplarız. Ardından, bu ara toplamları category_total toplamak için aggregation('SUM(category_total)', 'SUM') kullanırız. İkinci argüman 'SUM', ara sonuçlara SUM fonksiyonunun uygulanması gerektiğini söyler.

Ekleme, Güncelleme ve Silme

Nette Database Explorer, veri eklemeyi, güncellemeyi ve silmeyi basitleştirir. Belirtilen tüm metotlar, bir hata durumunda Nette\Database\DriverException istisnası fırlatır.

Selection::insert (iterable $data)

Tabloya yeni kayıtlar ekler.

Tek bir kayıt ekleme:

Yeni kaydı, anahtarların tablodaki sütun adlarına karşılık geldiği ilişkisel bir dizi veya yinelenebilir bir nesne (örneğin, formlarda kullanılan ArrayHash) olarak iletiriz.

Tablonun tanımlanmış bir birincil anahtarı varsa, metot veritabanı düzeyinde yapılan olası değişiklikleri (tetikleyiciler, varsayılan sütun değerleri, otomatik artan sütun 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 veritabanındaki güncel verileri içerir. Benzersiz bir birincil anahtarı yoksa, iletilen verileri dizi biçiminde döndürür.

$row = $explorer->table('users')->insert([
	'name' => 'John Doe',
	'email' => 'john.doe@example.com',
]);
// $row, ActiveRow örneğidir ve eklenen satırın tam verilerini içerir,
// otomatik olarak oluşturulan ID ve tetikleyiciler tarafından yapılan olası değişiklikler dahil
echo $row->id; // Yeni eklenen kullanıcının ID'sini yazdırır
echo $row->created_at; // Tetikleyici tarafından ayarlandıysa oluşturma zamanını yazdırır

Aynı anda birden çok kayıt ekleme:

insert() metodu, tek bir SQL sorgusu kullanarak birden çok kayıt eklemeye izin verir. 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 2 olacaktır

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

$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 veya SQL değişmezlerini de iletebiliriz:

$explorer->table('users')->insert([
	'name' => 'John',
	'created_at' => new DateTime,           // veritabanı formatına 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

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

Değiştirilecek sütunları, anahtarların tablodaki sütun adlarına karşılık geldiği ilişkisel bir dizi veya yinelenebilir bir nesne (örneğin, formlarda kullanılan ArrayHash) olarak iletiriz:

$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 kullanabiliriz:

$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

Belirtilen filtreye göre tablodan satırları 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() ve delete() çağırırken, where() kullanarak değiştirilecek/silinecek satırları belirtmeyi unutmayın. where() kullanmazsanız, işlem tüm tablo üzerinde gerçekleştirilir!

ActiveRow::update (iterable $data)bool

ActiveRow nesnesi tarafından temsil edilen veritabanı satırındaki verileri günceller. Parametre olarak, güncellenecek verileri içeren yinelenebilir bir değer alır (anahtarlar sütun adlarıdır). Sayısal değerleri değiştirmek için += ve -= operatörlerini kullanabiliriz:

Güncelleme yapıldıktan sonra, ActiveRow veritabanı düzeyinde yapılan olası değişiklikleri (örneğin tetikleyiciler) yansıtmak için otomatik olarak veritabanından yeniden yüklenir. Metot, yalnızca verilerde gerçek bir değişiklik yapıldıysa 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ız
]);
echo $article->views; // Geçerli görüntülenme sayısını yazdırır

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

ActiveRow::delete()

ActiveRow nesnesi tarafından temsil edilen satırı veritabanından siler.

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

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

Tablolar arasındaki ilişkiler

İlişkisel veritabanlarında, veriler birden çok tabloya bölünür ve yabancı anahtarlar kullanılarak birbirine bağlanır. Nette Database Explorer, bu ilişkilerle çalışmak için devrim niteliğinde bir yol sunar – JOIN sorguları yazmadan ve herhangi bir şeyi yapılandırmaya veya oluşturmaya gerek kalmadan.

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

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

Kitap veritabanı örneğimizde, birkaç ilişki tipi buluruz (model gerçekliğe göre basitleştirilmiş olsa da):

  • Bire çok 1:N – her kitabın bir yazarı vardır, bir yazar birkaç kitap yazabilir
  • Sıfıra çok 0:N – kitabın bir çevirmeni olabilir, bir çevirmen birkaç kitap çevirebilir
  • Sıfıra bir 0:1 – kitabın bir devamı olabilir
  • Çoka çok M:N – kitabın birkaç etiketi olabilir ve bir etiket birkaç kitaba atanabilir

Bu ilişkilerde her zaman bir üst ve bir alt tablo bulunur. Örneğin, yazar ve kitap arasındaki ilişkide, author tablosu üst, book tablosu alttır – bir kitabın her zaman bir yazara “ait olduğunu” düşünebiliriz. Bu, veritabanı yapısında da kendini gösterir: alt book tablosu, üst author tablosuna başvuran author_id yabancı anahtarını içerir.

Yazarlarının adları da dahil olmak üzere kitapları listelememiz gerekiyorsa, iki seçeneğimiz vardır. Ya verileri JOIN kullanarak tek bir SQL sorgusuyla alırız:

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

Ya da verileri iki adımda yükleriz – önce kitapları, sonra yazarlarını – ve sonra bunları PHP'de birleştiririz:

SELECT * FROM book;
SELECT * FROM author WHERE id IN (1, 2, 3);  -- alınan kitapların yazar kimlikleri

İkinci yaklaşım, şaşırtıcı olsa da aslında daha verimlidir. Veriler yalnızca bir kez yüklenir ve önbellekte daha iyi kullanılabilir. Nette Database Explorer tam olarak bu şekilde çalışır – her şeyi perde arkasında halleder ve size zarif 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 basittir. Bunlar kitabın bir yazarı var veya kitabın bir çevirmeni olabilir gibi ilişkilerdir. İlgili kaydı ActiveRow nesnesinin özelliği aracılığıyla alırız – adı, id olmadan yabancı anahtar sütununun adına 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'ye göre çevirmeni bulur

$book->author özelliğine eriştiğimizde, Explorer book tablosunda adı author karakter dizisini içeren bir sütun arar (yani author_id). Bu sütundaki değere göre, author tablosundan karşılık gelen kaydı yükler ve ActiveRow olarak döndürür. Benzer şekilde, translator_id sütununu kullanan $book->translator da çalışır. translator_id sütunu null içerebileceğinden, kodda ?-> operatörünü kullanırız.

Alternatif bir yol, iki argüman alan ref() metodudur: hedef tablo adı ve birleştirme sütunu adı ve bir ActiveRow örneği veya null döndürür:

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

ref() metodu, tablo aynı ada sahip bir sütun içerdiği için (yani author) özellik üzerinden erişim kullanılamadığında kullanışlıdır. Diğer durumlarda, daha okunabilir olan özellik üzerinden erişim kullanılması önerilir.

Explorer, veritabanı sorgularını otomatik olarak optimize eder. Bir döngüde kitapları dolaşırken ve ilgili kayıtlarına (yazarlar, çevirmenler) erişirken, Explorer her kitap için ayrı bir sorgu oluşturmaz. Bunun yerine, her ilişki tipi için yalnızca bir SELECT gerçekleştirir, bu da 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, veritabanına yalnızca şu üç yıldırım hızında sorguyu çağırır:

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

Birleştirme sütununu bulma mantığı, Conventions uygulaması tarafından belirlenir. Yabancı anahtarları analiz eden ve mevcut tablo ilişkileriyle kolayca çalışmanıza olanak tanıyan DiscoveredConventions kullanmanızı öneririz.

Alt tabloya 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ıtlarla 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ı yazdırır
foreach ($author->related('book.author_id') as $book) {
	echo "Yazdı: $book->title";
}

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

related() metodu, birleştirmeyi nokta gösterimiyle tek bir argüman olarak veya iki ayrı argüman 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 birleştirme sütununu otomatik olarak algılayabilir. Bu durumda, kaynak tablonun adı author olduğu için book.author_id sütunu üzerinden birleştirme yapılır:

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

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

related() metodunu elbette bir döngüde birden çok kaydı dolaşırken de kullanabiliriz ve Explorer bu durumda da sorguları otomatik olarak optimize eder:

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

Bu kod yalnızca iki yıldırım hızında SQL sorgusu oluşturur:

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

Çoka çok ilişki

Çoka çok (M:N) ilişkisi için bir ilişki tablosunun (bizim durumumuzda book_tag) varlığı gereklidir, bu tablo iki yabancı anahtar sütunu (book_id, tag_id) içerir. Bu sütunların her biri, birleştirilen tablolardan birinin birincil anahtarına başvurur. İlgili verileri almak için önce related('book_tag') kullanarak ilişki tablosundan kayıtları alırız ve ardından hedef verilere devam ederiz:

$book = $explorer->table('book')->get(1);
// kitaba atanan etiketlerin adlarını yazdırır
foreach ($book->related('book_tag') as $bookTag) {
	echo $bookTag->tag->name;  // ilişki tablosu üzerinden etiketin adını yazdırır
}

$tag = $explorer->table('tag')->get(1);
// veya tersi: bu etiketle işaretlenmiş kitapların adlarını yazdırır
foreach ($tag->related('book_tag') as $bookTag) {
	echo $bookTag->book->title; // kitabın adını yazdırır
}

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

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

İlişkili tablolar üzerinden sorgulama

where(), select(), order() ve group() metotlarında, diğer tablolardaki sütunlara erişmek için özel gösterimler kullanabiliriz. Explorer gerekli JOIN'leri otomatik olarak oluşturur.

Nokta gösterimi (üst_tablo.sütun), alt tablo açısından 1:N ilişkisi için kullanılır:

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

// Yazarı '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');

// Kitap adını ve yazar adını yazdırır
$books->select('book.title, author.name');

İki nokta üst üste gösterimi (:alt_tablo.sütun), üst tablo açısından 1:N ilişkisi için 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 örnekte iki nokta üst üste gösterimi (:book.title) ile yabancı anahtar sütunu belirtilmemiştir. Explorer, üst tablonun 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ştirme yapılır. Birden fazla olası bağlantı varsa, Explorer bir AmbiguousReferenceKeyException istisnası 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%');

Gösterimler, birden çok tablo üzerinden erişim için zincirlenebilir:

// 'PHP' etiketiyle işaretlenmiş 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 kelimesinden sonra belirtilen koşulları genişletir.

Belirli bir çevirmen tarafından çevrilen kitapları bulmak istediğimizi varsayalım:

// 'David' adlı çevirmen tarafından çevrilen 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() metodunda olduğu gibi aynı yapıları kullanabiliriz – operatörler, yer tutucu soru işaretleri, değer dizileri veya SQL ifadeleri.

Daha karmaşık, birden çok JOIN içeren sorgular için tablo takma adları tanımlayabiliriz:

$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 eklerken, joinWhere() metodunun tabloları birleştirirken ON yan tümcesindeki koşulları genişlettiğine dikkat edin.

versiyon: 4.0