Veritabanı Gezgini
Nette Database ile iki şekilde çalışabilirsiniz: SQL sorgularının otomatik olarak oluşturulmasına izin verebilir (Explorer yaklaşımı) veya bunları kendiniz yazabilirsiniz(Doğrudan erişim). Explorer veri erişimini önemli ölçüde basitleştirir. Tablolar arasındaki ilişkileri ele alır, böylece uygulamanızın mantığına odaklanabilirsiniz.
- Verilerle çalışmak doğaldır ve anlaşılması kolaydır
- Yalnızca gerekli verileri getiren optimize edilmiş SQL sorguları oluşturur
- JOIN sorguları yazmaya gerek kalmadan ilgili verilere kolay erişim sağlar
- Herhangi bir yapılandırma veya varlık oluşturma olmadan hemen çalışır
Explorer ile çalışmak, Nette\Database\Explorer nesnesinde table() yöntemini çağırarak başlar (bağlantı oluşturma ve yapılandırma hakkında bilgi için ilgili sayfaya bakın):
$books = $explorer->table('book'); // 'book' tablo adıdır
Yöntem, bir SQL sorgusunu temsil eden bir Selection nesnesi döndürür. Sonuçları
filtrelemek ve sıralamak için bu nesneye ek yöntemler zincirlenebilir. Sorgu yalnızca veriler istendiğinde, örneğin
foreach
ile yinelenerek bir araya getirilir ve yürütülür. Her satır bir ActiveRow nesnesi ile temsil edilir:
foreach ($books as $book) {
echo $book->title; // 'başlık' sütunu çıktıları
echo $book->author_id; // 'author_id' sütununu çıktılar
}
Explorer, tablo ilişkileriyle çalışmayı büyük ölçüde kolaylaştırır. Aşağıdaki örnek, ilgili tablolardan (kitaplar ve yazarları) ne kadar kolay veri çıktısı alabileceğimizi göstermektedir. JOIN sorgularının yazılmasına gerek olmadığına dikkat edin; Nette bunları bizim için oluşturuyor:
$books = $explorer->table('book');
foreach ($books as $book) {
echo 'Book: ' . $book->title;
echo 'Author: ' . $book->author->name; // 'author' tablosuna bir JOIN oluşturur
}
Nette Database Explorer sorguları maksimum verimlilik için optimize eder. Yukarıdaki örnek, 10 veya 10.000 kitap işlememizden bağımsız olarak yalnızca iki SELECT sorgusu gerçekleştirir.
Ayrıca Explorer, kodda hangi sütunların kullanıldığını izler ve veritabanından yalnızca bunları getirerek daha fazla performans tasarrufu sağlar. Bu davranış tamamen otomatik ve uyarlanabilirdir. Daha sonra kodu ek sütunlar kullanacak şekilde değiştirirseniz, Explorer sorguları otomatik olarak ayarlar. Hiçbir şeyi yapılandırmanıza veya hangi sütunlara ihtiyaç duyulacağını düşünmenize gerek yok – bunu Nette'e bırakın.
Filtreleme ve Sıralama
Selection
sınıfı, verileri filtrelemek ve sıralamak için yöntemler sağlar.
where($condition, ...$params) |
Bir WHERE koşulu ekler. Birden fazla koşul AND kullanılarak birleştirilir |
whereOr(array $conditions) |
OR kullanılarak birleştirilmiş bir grup WHERE koşulu ekler |
wherePrimary($value) |
Birincil anahtarı temel alan bir WHERE koşulu ekler |
order($columns, ...$params) |
ORDER BY ile sıralamayı ayarlar |
select($columns, ...$params) |
Hangi sütunların getirileceğini belirtir |
limit($limit, $offset = null) |
Satır sayısını sınırlar (LIMIT) ve isteğe bağlı olarak OFFSET ayarlar |
page($page, $itemsPerPage, &$total = null) |
Sayfalandırmayı ayarlar |
group($columns, ...$params) |
Satırları gruplar (GROUP BY) |
having($condition, ...$params) |
Gruplanmış satırları filtrelemek için bir HAVING koşulu ekler |
Yöntemler zincirlenebilir ( akıcı arayüz olarak
adlandırılır): $table->where(...)->order(...)->limit(...)
.
Bu yöntemler ayrıca ilgili tablolardan verilere erişmek için özel gösterimlerin kullanılmasına izin verir.
Kaçış ve Tanımlayıcılar
Yöntemler parametreleri ve alıntı tanımlayıcılarını (tablo ve sütun adları) otomatik olarak kaçarak SQL enjeksiyonunu önler. Düzgün çalışmayı sağlamak için birkaç kurala uyulmalıdır:
- Anahtar sözcükleri, işlev adlarını, yordamları vb. büyük harfle yazın.
- Sütun ve tablo adlarını küçük harfle yazın.
- Dizeleri her zaman parameters kullanarak geçirin.
where('name = ' . $name); // **DISASTER**: SQL enjeksiyonuna karşı savunmasız
where('name LIKE "%search%"'); // **YANLIŞ**: otomatik alıntılamayı zorlaştırır
where('name LIKE ?', '%search%'); // **CORRECT**: parametre olarak geçirilen değer
where('name like ?', $name); // **YANLIŞ**: üretir: `name` `like` ?
where('name LIKE ?', $name); // **DOĞRU**: üretir: isim` GİBİ ?
where('LOWER(name) = ?', $value);// **DOĞRU**: LOWER(`name`) = ?
where (string|array $condition, …$parameters): static
WHERE koşullarını kullanarak sonuçları filtreler. Gücü, çeşitli değer türlerini akıllıca ele almasında ve SQL operatörlerini otomatik olarak seçmesinde yatmaktadır.
Temel kullanım:
$table->where('id', $value); // WHERE `id` = 123
$table->where('id > ?', $value); // WHERE `id` > 123
$table->where('id = ? OR name = ?', $id, $name); // WHERE `id` = 1 OR `name` = 'Jon Snow'
Uygun operatörlerin otomatik olarak algılanması sayesinde, özel durumlarla uğraşmanıza gerek kalmaz – Nette bunları sizin için halleder:
$table->where('id', 1); // WHERE `id` = 1
$table->where('id', null); // WHERE `id` IS NULL
$table->where('id', [1, 2, 3]); // WHERE `id` IN (1, 2, 3)
// Yer tutucu ? operatör olmadan kullanılabilir:
$table->where('id ?', 1); // WHERE `id` = 1
Yöntem ayrıca negatif koşulları ve boş dizileri de doğru şekilde işler:
$table->where('id', []); // WHERE `id` IS NULL AND FALSE -- hiçbir şey bulamaz
$table->where('id NOT', []); // WHERE `id` IS NULL OR TRUE -- her şeyi bulur
$table->where('NOT (id ?)', []); // WHERE NOT (`id` IS NULL AND FALSE) -- her şeyi bulur
// $table->where('NOT id ?', $ids); // UYARI: Bu sözdizimi desteklenmemektedir
Ayrıca, bir alt sorgu oluşturarak başka bir tablo sorgusunun sonucunu parametre olarak aktarabilirsiniz:
// WHERE `id` IN (SELECT `id` FROM `tableName`)
$table->where('id', $explorer->table($tableName));
// WHERE `id` IN (SELECT `col` FROM `tableName`)
$table->where('id', $explorer->table($tableName)->select('col'));
Koşullar, AND kullanılarak birleştirilen öğelerle birlikte bir dizi olarak da geçirilebilir:
// WHERE (`price_final` < `price_original`) AND (`stock_count` > `min_stock`)
$table->where([
'price_final < price_original',
'stock_count > min_stock',
]);
Dizide, anahtar-değer çiftleri kullanılabilir ve Nette yine otomatik olarak doğru operatörleri seçecektir:
// WHERE (`status` = 'active') AND (`id` IN (1, 2, 3))
$table->where([
'status' => 'active',
'id' => [1, 2, 3],
]);
SQL ifadelerini yer tutucular ve çoklu parametrelerle de karıştırabiliriz. Bu, kesin olarak tanımlanmış operatörlere sahip karmaşık koşullar için kullanışlıdır:
// WHERE (`yaş` > 18) AND (ROUND(`score`, 2) > 75.5)
$table->where([
'age > ?' => 18,
'ROUND(score, ?) > ?' => [2, 75.5], // iki parametre bir dizi olarak geçirilir
]);
where()
adresine yapılan birden fazla çağrı, AND kullanarak koşulları otomatik olarak birleştirir.
whereOr (array $parameters): static
where()
adresine benzer, ancak OR kullanarak koşulları birleştirir:
// WHERE (`status` = 'active') OR (`deleted` = 1)
$table->whereOr([
'status' => 'active',
'deleted' => true,
]);
Daha karmaşık ifadeler de kullanılabilir:
// WHERE (`price` > 1000) OR (`price_with_tax` > 1500)
$table->whereOr([
'price > ?' => 1000,
'price_with_tax > ?' => 1500,
]);
wherePrimary (mixed $key): static
Tablonun birincil anahtarı için bir koşul ekler:
// WHERE `id` = 123
$table->wherePrimary(123);
// WHERE `id` IN (1, 2, 3)
$table->wherePrimary([1, 2, 3]);
Tablonun bileşik bir birincil anahtarı varsa (örneğin, foo_id
, bar_id
), bunu bir dizi olarak
iletiriz:
// WHERE `foo_id` = 1 AND `bar_id` = 5
$table->wherePrimary(['foo_id' => 1, 'bar_id' => 5])->fetch();
// WHERE (`foo_id`, `bar_id`) IN ((1, 5), (2, 3))
$table->wherePrimary([
['foo_id' => 1, 'bar_id' => 5],
['foo_id' => 2, 'bar_id' => 3],
])->fetchAll();
order (string $columns, …$parameters): static
Satırların döndürüleceği sırayı belirtir. Bir veya daha fazla sütuna göre, artan veya azalan sırada veya özel bir ifadeye göre sıralayabilirsiniz:
$table->order('created'); // ORDER BY `oluşturulan`
$table->order('created DESC'); // ORDER BY `created` DESC
$table->order('priority DESC, created'); // ORDER BY `öncelik` DESC, `oluşturuldu`
$table->order('status = ? DESC', 'active'); // ORDER BY `status` = 'active' DESC
select (string $columns, …$parameters): static
Veritabanından döndürülecek sütunları belirtir. Varsayılan olarak, Nette Database Explorer yalnızca kodda gerçekten
kullanılan sütunları döndürür. Belirli ifadeleri almanız gerektiğinde select()
yöntemini kullanın:
// SELECT *, DATE_FORMAT(`created_at`, "%d.%m.%Y") AS `formatted_date`
$table->select('*, DATE_FORMAT(created_at, ?) AS formatted_date', '%d.%m.%Y');
AS
kullanılarak tanımlanan takma adlara daha sonra ActiveRow
nesnesinin özellikleri olarak
erişilebilir:
foreach ($table as $row) {
echo $row->formatted_date; // takma ada erişim
}
limit (?int $limit, ?int $offset = null): static
Döndürülen satır sayısını sınırlar (LIMIT) ve isteğe bağlı olarak bir ofset ayarlar:
$table->limit(10); // LIMIT 10 (ilk 10 satırı döndürür)
$table->limit(10, 20); // LIMIT 10 OFSET 20
Sayfalandırma için page()
yöntemini kullanmak daha uygundur.
page (int $page, int $itemsPerPage, &$numOfPages = null): static
Sonuçların sayfalandırılmasını basitleştirir. Sayfa numarasını (1'den başlayarak) ve sayfa başına öğe sayısını kabul eder. İsteğe bağlı olarak, toplam sayfa sayısının saklanacağı bir değişkene referans geçebilirsiniz:
$numOfPages = null;
$table->page(page: 3, itemsPerPage: 10, $numOfPages);
echo "Total pages: $numOfPages";
group (string $columns, …$parameters): static
Satırları belirtilen sütunlara göre gruplar (GROUP BY). Tipik olarak toplama fonksiyonları ile birlikte kullanılır:
// Her kategorideki ürün sayısını sayar
$table->select('category_id, COUNT(*) AS count')
->group('category_id');
having (string $having, …$parameters): static
Gruplandırılmış satırları filtrelemek için bir koşul belirler (HAVING). group()
yöntemi ve toplama
işlevleri ile birlikte kullanılabilir:
// 100'den fazla ürün içeren kategorileri bulur
$table->select('category_id, COUNT(*) AS count')
->group('category_id')
->having('count > ?', 100);
Veri Okuma
Veritabanından veri okumak için birkaç kullanışlı yöntem mevcuttur:
foreach ($table as $key => $row) |
Tüm satırlar arasında yineleme yapar, $key birincil anahtar değeridir, $row bir ActiveRow
nesnesidir |
$row = $table->get($key) |
Birincil anahtara göre tek bir satır döndürür |
$row = $table->fetch() |
Geçerli satırı döndürür ve işaretçiyi bir sonrakine ilerletir |
$array = $table->fetchPairs() |
Sonuçlardan ilişkisel bir dizi oluşturur |
$array = $table->fetchAll() |
Tüm satırları bir dizi olarak döndürür |
count($table) |
Seçim nesnesindeki satır sayısını döndürür |
ActiveRow nesnesi salt okunurdur. Bu, özelliklerinin değerlerini değiştiremeyeceğiniz anlamına gelir. Bu kısıtlama, veri tutarlılığını sağlar ve beklenmedik yan etkileri önler. Veriler veritabanından alınır ve herhangi bir değişiklik açıkça ve kontrollü bir şekilde yapılmalıdır.
foreach
– Tüm Satırlar Arasında Yineleme
Bir sorguyu çalıştırmanın ve satırları almanın en kolay yolu foreach
döngüsü ile yinelemektir. SQL
sorgusunu otomatik olarak yürütür.
$books = $explorer->table('book');
foreach ($books as $key => $book) {
// $key = birincil anahtar, $book = ActiveRow
echo "$book->title ({$book->author->name})";
}
get ($key): ?ActiveRow
Bir SQL sorgusu çalıştırır ve bir satırı birincil anahtarına göre veya yoksa null
adresine
döndürür.
$book = $explorer->table('book')->get(123); // ID 123 veya null ile ActiveRow döndürür
if ($book) {
echo $book->title;
}
fetch(): ?ActiveRow
Bir satır döndürür ve dahili işaretçiyi bir sonrakine ilerletir. Başka satır yoksa null
döndürür.
$books = $explorer->table('book');
while ($book = $books->fetch()) {
$this->processBook($book);
}
fetchPairs (string|int|null $key = null, string|int|null $value = null): array
Sonuçları ilişkisel bir dizi olarak döndürür. İlk bağımsız değişken, dizi anahtarı olarak kullanılacak sütunun adını ve ikinci bağımsız değişken, değer olarak kullanılacak sütunun adını belirtir:
$authors = $explorer->table('author')->fetchPairs('id', 'name');
// [1 => 'John Doe', 2 => 'Jane Doe', ...]
Yalnızca ilk parametre sağlanırsa, tüm satır değer olarak kullanılır ve ActiveRow
nesnesi olarak temsil
edilir:
$authors = $explorer->table('author')->fetchPairs('id');
// [1 => ActiveRow(id: 1, ...), 2 => ActiveRow(id: 2, ...), ...]
Anahtar olarak null
sağlanırsa, dizi sıfırdan başlayarak sayısal olarak indekslenecektir:
$authors = $explorer->table('author')->fetchPairs(null, 'name');
// [0 => 'John Doe', 1 => 'Jane Doe', ...]
fetchPairs (Closure $callback): array
Alternatif olarak, parametre olarak bir geri arama iletebilirsiniz. Geri arama, tek bir değer veya bir anahtar-değer çifti döndürmek için her satıra uygulanacaktır.
$titles = $explorer->table('book')
->fetchPairs(fn($row) => "$row->title ({$row->author->name})");
// [1 => 'İlk Kitap (Jan Novak)', ...]
// Geri arama, anahtar-değer çifti içeren bir dizi de döndürebilir:
$titles = $explorer->table('book')
->fetchPairs(fn($row) => [$row->title, $row->author->name]);
// ['İlk Kitap' => 'Jan Novak', ...]
fetchAll(): array
Tüm satırları, anahtarların birincil anahtar değerleri olduğu ActiveRow
nesnelerinin ilişkisel bir dizisi
olarak döndürür.
$allBooks = $explorer->table('book')->fetchAll();
// [1 => ActiveRow(id: 1, ...), 2 => ActiveRow(id: 2, ...), ...]
count(): int
Parametreleri olmayan count()
yöntemi, Selection
nesnesindeki satır sayısını döndürür:
$table->where('category', 1);
$count = $table->count();
$count = count($table); // alternatif
Not: count()
bir parametre ile birlikte, aşağıda açıklandığı gibi veritabanında COUNT toplama işlevini
gerçekleştirir.
ActiveRow::toArray(): array
ActiveRow
nesnesini, anahtarların sütun adları ve değerlerin karşılık gelen veriler olduğu bir ilişkisel
diziye dönüştürür.
$book = $explorer->table('book')->get(1);
$bookArray = $book->toArray();
// $bookArray ['id' => 1, 'title' => '...', 'author_id' => ..., ...] olacaktır.
Birleştirme
Selection
sınıfı, toplama işlevlerini (COUNT, SUM, MIN, MAX, AVG, vb.) kolayca gerçekleştirmek için
yöntemler sağlar.
count($expr) |
Satır sayısını sayar |
min($expr) |
Bir sütundaki minimum değeri döndürür |
max($expr) |
Bir sütundaki maksimum değeri döndürür |
sum($expr) |
Bir sütundaki değerlerin toplamını verir |
aggregation($function) |
AVG() veya GROUP_CONCAT() gibi herhangi bir toplama işlevine izin verir |
count (string $expr): int
COUNT işleviyle bir SQL sorgusu çalıştırır ve sonucu döndürür. Bu yöntem, kaç satırın belirli bir koşulla eşleştiğini belirlemek için kullanılır:
$count = $table->count('*'); // SELECT COUNT(*) FROM `table`
$count = $table->count('DISTINCT column'); // SELECT COUNT(DISTINCT `column`) FROM `table`
Not: count() parametresiz olarak sadece Selection
nesnesindeki satır sayısını
döndürür.
min (string $expr) and max(string $expr)
min()
ve max()
yöntemleri, belirtilen sütun veya ifadedeki minimum ve maksimum değerleri
döndürür:
// SELECT MAX(`price`) FROM `products` WHERE `active` = 1
$maxPrice = $products->where('active', true)
->max('price');
sum (string $expr): int
Belirtilen sütundaki veya ifadedeki değerlerin toplamını döndürür:
// SELECT SUM(`price` * `items_in_stock`) FROM `products` WHERE `active` = 1
$totalPrice = $products->where('active', true)
->sum('price * items_in_stock');
aggregation (string $function, ?string $groupFunction = null): mixed
Herhangi bir toplama fonksiyonunun yürütülmesine izin verir.
// Bir kategorideki ürünlerin ortalama fiyatını hesaplar
$avgPrice = $products->where('category_id', 1)
->aggregation('AVG(price)');
// Ürün etiketlerini tek bir dizede birleştirir
$tags = $products->where('id', 1)
->aggregation('GROUP_CONCAT(tag.name) AS tags')
->fetch()
->tags;
Kendileri bir toplama ve gruplamadan kaynaklanan sonuçları toplamamız gerekiyorsa (örneğin, gruplanmış satırlar
üzerinden SUM(value)
), bu ara sonuçlara uygulanacak toplama işlevini ikinci bağımsız değişken olarak
belirtiriz:
// Her kategori için stoktaki ürünlerin toplam fiyatını hesaplar, ardından bu fiyatları toplar
$totalPrice = $products->select('category_id, SUM(price * stock) AS category_total')
->group('category_id')
->aggregation('SUM(category_total)', 'SUM');
Bu örnekte, önce her kategorideki ürünlerin toplam fiyatını hesaplıyoruz
(SUM(price * stock) AS category_total
) ve sonuçları category_id
'a göre gruplandırıyoruz. Daha sonra
bu alt toplamları toplamak için aggregation('SUM(category_total)', 'SUM')
adresini kullanıyoruz. İkinci
bağımsız değişken 'SUM'
ara sonuçlara uygulanacak toplama işlevini belirtir.
Ekle, Güncelle ve Sil
Nette Database Explorer veri eklemeyi, güncellemeyi ve silmeyi basitleştirir. Bahsedilen tüm yöntemler hata durumunda bir
Nette\Database\DriverException
fırlatır.
Selection::insert (iterable $data): static
Bir tabloya yeni kayıtlar ekler.
Tek bir kayıt ekleniyor:
Yeni kayıt, anahtarların tablodaki sütun adlarıyla eşleştiği bir ilişkisel dizi veya yinelenebilir nesne ( formlarda kullanılan ArrayHash
gibi) olarak aktarılır.
Tablonun tanımlanmış bir birincil anahtarı varsa, yöntem, veritabanı düzeyinde yapılan değişiklikleri (örn.
tetikleyiciler, varsayılan sütun değerleri veya otomatik artırma hesaplamaları) yansıtmak için veritabanından yeniden
yüklenen bir ActiveRow
nesnesi döndürür. Bu, veri tutarlılığını sağlar ve nesne her zaman geçerli
veritabanı verilerini içerir. Birincil anahtar açıkça tanımlanmamışsa, yöntem girdi verilerini bir dizi olarak
döndürür.
$row = $explorer->table('users')->insert([
'name' => 'John Doe',
'email' => 'john.doe@example.com',
]);
// row, eklenen satırın tam verilerini içeren bir ActiveRow örneğidir,
// otomatik olarak oluşturulan kimlik ve tetikleyiciler tarafından yapılan değişiklikler dahil
echo $row->id; // Yeni eklenen kullanıcının kimliğini verir
echo $row->created_at; // Bir tetikleyici tarafından ayarlanmışsa oluşturma zamanını verir
Tek seferde birden fazla kayıt ekleme:
insert()
yöntemi, tek bir SQL sorgusu ile birden fazla kayıt eklemenize olanak tanır. Bu durumda, eklenen
satırların sayısını döndürür.
$insertedRows = $explorer->table('users')->insert([
[
'name' => 'John',
'year' => 1994,
],
[
'name' => 'Jack',
'year' => 1995,
],
]);
// INSERT INTO `users` (`name`, `year`) VALUES ('John', 1994), ('Jack', 1995)
// $insertedRows 2 olacaktır
Parametre olarak veri seçimi içeren bir Selection
nesnesi de aktarabilirsiniz.
$newUsers = $explorer->table('potential_users')
->where('approved', 1)
->select('name, email');
$insertedRows = $explorer->table('users')->insert($newUsers);
Özel değerlerin eklenmesi:
Değerler dosyaları, DateTime
nesnelerini veya SQL değişmezlerini içerebilir:
$explorer->table('users')->insert([
'name' => 'John',
'created_at' => new DateTime, // veritabanı formatına dönüştürür
'avatar' => fopen('image.jpg', 'rb'), // ikili dosya içeriğini ekler
'uuid' => $explorer::literal('UUID()'), // UUID() işlevini çağırır
]);
Selection::update (iterable $data): int
Bir tablodaki satırları belirtilen filtreye göre günceller. Gerçekte değiştirilen satır sayısını döndürür.
Güncellenecek sütunlar, anahtarların tablodaki sütun adlarıyla eşleştiği bir ilişkisel dizi veya yinelenebilir nesne (
formlarda kullanılan ArrayHash
gibi) olarak geçirilir:
$affected = $explorer->table('users')
->where('id', 10)
->update([
'name' => 'John Smith',
'year' => 1994,
]);
// UPDATE `users` SET `name` = 'John Smith', `year` = 1994 WHERE `id` = 10
Sayısal değerleri değiştirmek için +=
ve -=
operatörlerini kullanabilirsiniz:
$explorer->table('users')
->where('id', 10)
->update([
'points+=' => 1, // 'puanlar' sütununun değerini 1 artırır
'coins-=' => 1, // 'madeni paralar' sütununun değerini 1 azaltır
]);
// UPDATE `users` SET `points` = `points` + 1, `coins` = `coins` - 1 WHERE `id` = 10
Selection::delete(): int
Belirtilen filtreye göre bir tablodan satırları siler. Silinen satırların sayısını döndürür.
$count = $explorer->table('users')
->where('id', 10)
->delete();
// DELETE FROM `users` WHERE `id` = 10
update()
veya delete()
adreslerini çağırırken, güncellenecek veya silinecek
satırları belirtmek için where()
adresini kullandığınızdan emin olun. where()
kullanılmazsa,
işlem tüm tablo üzerinde gerçekleştirilecektir!
ActiveRow::update (iterable $data): bool
ActiveRow
nesnesi tarafından temsil edilen bir veritabanı satırındaki verileri günceller. Anahtarların
sütun adları olduğu yinelenebilir verileri parametre olarak kabul eder. Sayısal değerleri değiştirmek için +=
ve -=
operatörlerini kullanabilirsiniz:
Güncelleme gerçekleştirildikten sonra, ActiveRow
veritabanı düzeyinde yapılan değişiklikleri (örn.
tetikleyiciler) yansıtmak için veritabanından otomatik olarak yeniden yüklenir. Yöntem, yalnızca gerçek bir veri
değişikliği meydana gelmişse true
döndürür.
$article = $explorer->table('article')->get(1);
$article->update([
'views += 1', // görüntüleme sayısını artırır
]);
echo $article->views; // Geçerli görüntüleme sayısını verir
Bu yöntem, veritabanında yalnızca belirli bir satırı günceller. Birden fazla satırın toplu güncellemeleri için Selection::update() yöntemini kullanın.
ActiveRow::delete()
Veritabanından ActiveRow
nesnesi tarafından temsil edilen bir satırı siler.
$book = $explorer->table('book')->get(1);
$book->delete(); // ID 1 olan kitabı siler
Bu yöntem, veritabanındaki yalnızca belirli bir satırı siler. Birden fazla satırı toplu olarak silmek için Selection::delete() yöntemini kullanın.
Tablolar Arasındaki İlişkiler
İlişkisel veritabanlarında veriler birden fazla tabloya bölünür ve yabancı anahtarlar aracılığıyla birbirine bağlanır. Nette Database Explorer, JOIN sorguları yazmadan veya herhangi bir yapılandırma veya varlık oluşturma gerektirmeden bu ilişkilerle çalışmak için devrim niteliğinde bir yol sunar.
Gösterim için example veritabanını kullanacağız(GitHub'da mevcuttur). Veritabanı aşağıdaki tabloları içerir:
author
– yazarlar ve çevirmenler (id
,name
,web
,born
sütunları)book
– kitaplar (id
,author_id
,translator_id
,title
,sequel_id
sütunları)tag
– etiketler (id
,name
sütunları)book_tag
– kitaplar ve etiketler arasındaki bağlantı tablosu (book_id
,tag_id
sütunları)
Veritabanı yapısı
Bu kitap veritabanı örneğinde, çeşitli ilişki türleri buluyoruz (gerçeğe kıyasla basitleştirilmiş):
- Birden çoğa (1:N)** – Her kitabın bir yazarı vardır; bir yazar birden fazla kitap yazabilir.
- Sıfırdan çoğa (0:N)** – Bir kitabın bir çevirmeni olabilir; bir çevirmen birden fazla kitabı çevirebilir.
- Sıfırdan bire (0:1)** – Bir kitabın bir devam kitabı olabilir.
- Çoktan çoğa (M:N)** – Bir kitap birden fazla etikete sahip olabilir ve bir etiket birden fazla kitaba atanabilir.
Bu ilişkilerde her zaman bir ana tablo ve bir çocuk tablo vardır. Örneğin, yazarlar ve kitaplar arasındaki
ilişkide, author
tablosu üst tablodur ve book
tablosu alt tablodur – bunu her zaman bir yazara
“ait” bir kitap olarak düşünebilirsiniz. Bu durum veritabanı yapısına da yansır: book
alt tablosu,
author
üst tablosuna referans veren author_id
yabancı anahtarını içerir.
Kitapları yazarlarının adlarıyla birlikte görüntülemek istiyorsak, iki seçeneğimiz vardır. Ya bir JOIN ile tek bir SQL sorgusu kullanarak verileri alırız:
SELECT book.*, author.name FROM book LEFT JOIN author ON book.author_id = author.id;
Ya da verileri iki adımda alırız – önce kitapları, sonra yazarlarını – ve bunları PHP'de bir araya getiririz:
SELECT * FROM book;
SELECT * FROM author WHERE id IN (1, 2, 3); -- IDs of authors retrieved from books
İkinci yaklaşım şaşırtıcı bir şekilde daha verimlidir. Veriler yalnızca bir kez getirilir ve önbellekte daha iyi kullanılabilir. Nette Database Explorer tam olarak bu şekilde çalışır – her şeyi kaputun altında halleder ve size temiz bir API sağlar:
$books = $explorer->table('book');
foreach ($books as $book) {
echo 'title: ' . $book->title;
echo 'written by: ' . $book->author->name; // $book->author, 'author' tablosundan bir kayıttır
echo 'translated by: ' . $book->translator?->name;
}
Ana Tabloya Erişim
Ana tabloya erişim basittir. Bunlar bir kitabın bir yazarı vardır veya bir kitabın bir çevirmeni
olabilir gibi ilişkilerdir. İlgili kayda ActiveRow
nesne özelliği aracılığıyla erişilebilir –
özellik adı, id
son eki olmadan yabancı anahtarın sütun adıyla eşleşir:
$book = $explorer->table('book')->get(1);
echo $book->author->name; // 'author_id' sütunu aracılığıyla yazarı bulur
echo $book->translator?->name; // 'translator_id' sütunu aracılığıyla çevirmeni bulur
Explorer, $book->author
özelliğine erişirken, book
tablosunda author
dizesini
(yani, author_id
) içeren bir sütun arar. Bu sütundaki değere bağlı olarak, author
tablosundan
ilgili kaydı alır ve bunu bir ActiveRow
nesnesi olarak döndürür. Benzer şekilde
$book->translator
, translator_id
sütununu kullanır. translator_id
sütunu
null
içerebileceğinden ?->
operatörü kullanılır.
Alternatif bir yaklaşım, iki bağımsız değişken (hedef tablonun adı ve bağlantı sütunu) kabul eden ve bir
ActiveRow
örneği veya null
döndüren ref()
yöntemi tarafından sağlanır:
echo $book->ref('author', 'author_id')->name; // yazara bağlantı
echo $book->ref('author', 'translator_id')->name; // çevirmene bağlantı
ref()
yöntemi, özellik tabanlı erişimin kullanılamadığı durumlarda, örneğin tabloda özellik ile aynı
ada sahip bir sütun varsa kullanışlıdır (author
). Diğer durumlarda, daha iyi okunabilirlik için özellik
tabanlı erişim kullanılması önerilir.
Explorer veritabanı sorgularını otomatik olarak optimize eder. Kitaplar arasında yineleme yaparken ve ilgili kayıtlarına (yazarlar, çevirmenler) erişirken, Explorer her kitap için ayrı ayrı bir sorgu oluşturmaz. Bunun yerine, her bir ilişki türü için yalnızca bir SELECT sorgusu çalıştırarak veritabanı yükünü önemli ölçüde azaltır. Örneğin:
$books = $explorer->table('book');
foreach ($books as $book) {
echo $book->title . ': ';
echo $book->author->name;
echo $book->translator?->name;
}
Bu kod yalnızca üç optimize edilmiş veritabanı sorgusu yürütecektir:
SELECT * FROM `book`;
SELECT * FROM `author` WHERE (`id` IN (1, 2, 3)); -- IDs from 'author_id' column in selected books
SELECT * FROM `author` WHERE (`id` IN (2, 3)); -- IDs from 'translator_id' column in selected books
Bağlantı sütununu tanımlama mantığı Conventions uygulaması tarafından tanımlanır. Yabancı anahtarları analiz eden ve mevcut tablo ilişkileriyle sorunsuz bir şekilde çalışmanıza olanak tanıyan DiscoveredConventions'ı kullanmanızı öneririz.
Çocuk Tablosuna Erişim
Alt tabloya erişim ters yönde çalışır. Şimdi bu yazar hangi kitapları yazdı veya bu çevirmen hangi
kitapları çevirdi diye soruyoruz. Bu tür bir sorgu için, ilgili kayıtları içeren bir Selection
nesnesi
döndüren related()
yöntemini kullanırız. İşte bir örnek:
$author = $explorer->table('author')->get(1);
// Yazar tarafından yazılan tüm kitapları çıkarır
foreach ($author->related('book.author_id') as $book) {
echo "Wrote: $book->title";
}
// Yazar tarafından çevrilen tüm kitapları çıkarır
foreach ($author->related('book.translator_id') as $book) {
echo "Translated: $book->title";
}
related()
yöntemi, ilişki açıklamasını nokta gösterimini kullanarak tek bir bağımsız değişken olarak
veya iki ayrı bağımsız değişken olarak kabul eder:
$author->related('book.translator_id'); // tek argüman
$author->related('book', 'translator_id'); // iki argüman
Explorer, üst tablonun adına göre doğru bağlantı sütununu otomatik olarak algılayabilir. Bu durumda, kaynak tablonun
adı author
olduğu için book.author_id
sütunu üzerinden bağlantı verir:
$author->related('book'); // book.author_id kullanır
Birden fazla olası bağlantı varsa, Explorer bir AmbiguousReferenceKeyException istisnası atacaktır.
Elbette, bir döngü içinde birden fazla kayıt arasında yineleme yaparken related()
yöntemini de
kullanabiliriz ve Explorer bu durumda da sorguları otomatik olarak optimize edecektir:
$authors = $explorer->table('author');
foreach ($authors as $author) {
echo $author->name . ' wrote:';
foreach ($author->related('book') as $book) {
echo $book->title;
}
}
Bu kod yalnızca iki verimli SQL sorgusu oluşturur:
SELECT * FROM `author`;
SELECT * FROM `book` WHERE (`author_id` IN (1, 2, 3)); -- IDs of the selected authors
Çoktan Çoka İlişki
Çoktan çoka (M:N) ilişki için bir birleşim tablosu (bizim durumumuzda book_tag
) gereklidir. Bu tablo
iki yabancı anahtar sütunu içerir (book_id
, tag_id
). Her sütun, bağlı tablolardan birinin birincil
anahtarına referans verir. İlgili verileri almak için, önce related('book_tag')
adresini kullanarak bağlantı
tablosundan kayıtları getiriyoruz ve ardından hedef verilere devam ediyoruz:
$book = $explorer->table('book')->get(1);
// Kitaba atanan etiketlerin adlarını çıktı olarak verir
foreach ($book->related('book_tag') as $bookTag) {
echo $bookTag->tag->name; // bağlantı tablosu aracılığıyla etiket adını getirir
}
$tag = $explorer->table('tag')->get(1);
// Ters yön: bu etikete sahip kitapların başlıklarını çıkarır
foreach ($tag->related('book_tag') as $bookTag) {
echo $bookTag->book->title; // kitap başlığını getirir
}
Explorer SQL sorgularını tekrar verimli bir forma optimize eder:
SELECT * FROM `book`;
SELECT * FROM `book_tag` WHERE (`book_tag`.`book_id` IN (1, 2, ...)); -- IDs of the selected books
SELECT * FROM `tag` WHERE (`tag`.`id` IN (1, 2, ...)); -- IDs of the tags found in book_tag
İlgili Tablolar Üzerinden Sorgulama
where()
, select()
, order()
ve group()
yöntemlerinde, diğer tablolardan
sütunlara erişmek için özel gösterimler kullanabilirsiniz. Explorer gerekli JOIN'leri otomatik olarak oluşturur.
Ana tablonun perspektifinden bakıldığında 1:N ilişkiler için Nokta gösterimi (parent_table.column
)
kullanılır:
$books = $explorer->table('book');
// Yazarlarının adı 'Jon' ile başlayan kitapları bulur
$books->where('author.name LIKE ?', 'Jon%');
// Kitapları yazar adına göre azalan şekilde sıralar
$books->order('author.name DESC');
// Kitap başlığı ve yazar adı çıktıları
$books->select('book.title, author.name');
Üst tablo açısından 1:N ilişkiler için Kolon gösterimi kullanılır:
$authors = $explorer->table('author');
// Başlığında 'PHP' geçen bir kitap yazan yazarları bulur
$authors->where(':book.title LIKE ?', '%PHP%');
// Her yazar için kitap sayısını sayar
$authors->select('*, COUNT(:book.id) AS book_count')
->group('author.id');
Yukarıdaki iki nokta üst üste gösterimli örnekte (:book.title
), yabancı anahtar sütunu açıkça
belirtilmemiştir. Explorer, üst tablo adına göre doğru sütunu otomatik olarak algılar. Bu durumda, kaynak tablonun adı
author
olduğu için book.author_id
sütunu üzerinden birleşir. Birden fazla olası bağlantı varsa,
Explorer AmbiguousReferenceKeyException
istisnasını atar.
Bağlantı sütunu parantez içinde açıkça belirtilebilir:
// Başlığında 'PHP' geçen bir kitabı çeviren yazarları bulur
$authors->where(':book(translator).title LIKE ?', '%PHP%');
Birden fazla tablodaki verilere erişmek için gösterimler zincirlenebilir:
// 'PHP' ile etiketlenmiş kitapların yazarlarını bulur
$authors->where(':book:book_tag.tag.name', 'PHP')
->group('author.id');
JOIN için Genişletme Koşulları
joinWhere()
yöntemi, SQL'deki tablo birleştirmelerine ON
anahtar sözcüğünden sonra ek
koşullar ekler.
Örneğin, belirli bir çevirmen tarafından çevrilmiş kitapları bulmak istediğimizi varsayalım:
// 'David' adında bir çevirmen tarafından çevrilmiş kitaplar bulur
$books = $explorer->table('book')
->joinWhere('translator', 'translator.name', 'David');
// LEFT JOIN author translator ON book.translator_id = translator.id AND (translator.name = 'David')
joinWhere()
koşulunda, where()
yönteminde olduğu gibi aynı yapıları kullanabilirsiniz –
operatörler, yer tutucular, değer dizileri veya SQL ifadeleri.
Birden fazla JOIN içeren daha karmaşık sorgular için tablo takma adları tanımlanabilir:
$tags = $explorer->table('tag')
->joinWhere(':book_tag.book.author', 'book_author.born < ?', 1950)
->alias(':book_tag.book.author', 'book_author');
// LEFT JOIN `book_tag` ON `tag`.`id` = `book_tag`.`tag_id`
// LEFT JOIN `book` ON `book_tag`.`book_id` = `book`.`id`
// LEFT JOIN `author` `book_author` ON `book`.`author_id` = `book_author`.`id`
// AND (`kitap_yazarı`.`doğum` < 1950)
where()
yöntemi WHERE
cümlesine koşullar eklerken, joinWhere()
yönteminin tablo
birleştirmeleri sırasında ON
cümlesindeki koşulları genişlettiğini unutmayın.
Elle Gezgin Oluşturma
Nette DI konteynerini kullanmıyorsanız, Nette\Database\Explorer
örneğini manuel olarak
oluşturabilirsiniz:
use Nette\Database;
// $storage, Nette\Caching\Storage'ı uygular, örn:
$storage = new Nette\Caching\Storages\FileStorage('/path/to/temp/dir');
// veritabanı bağlantısı
$connection = new Database\Connection('mysql:host=127.0.0.1;dbname=mydatabase', 'user', 'password');
// veritabanı yapısının yansımasını yönetir
$structure = new Database\Structure($connection, $storage);
// tablo adlarını, sütunları ve yabancı anahtarları eşlemek için kuralları tanımlar
$conventions = new Database\Conventions\DiscoveredConventions($structure);
$explorer = new Database\Explorer($connection, $structure, $conventions, $storage);