Veritabanı sorguları sayfanın kendisinden soyutlanmalı mı?


10

PHP'de sayfa oluştururken, genellikle kendimi veritabanı sorgularıyla dolu bir dosya kümesi yazarken bulurum. Örneğin, aşağıdaki gibi bir sayfada görüntülemek için doğrudan veritabanından bir yazı hakkında bazı verileri almak için bir sorgu olabilir:

$statement = $db->prepare('SELECT * FROM posts WHERE id=:id');
$statement->bindValue(':id', $id, PDO::PARAM_INT);
$statement->execute();
$post = $statement->fetch(PDO::FETCH_ASSOC);
$content = $post['content']
// do something with the content

Bu hızlı, tek seferlik sorgular genellikle küçüktür, ancak bazen oldukça dağınık görünmeye başlayan veritabanı etkileşim kodunun büyük bölümleriyle sonuçlanır.

Bazı durumlarda, post-ilgili db sorgularımı işlemek için basit bir fonksiyon kütüphanesi oluşturarak bu kod bloğunu basit bir kısaltmaya yaratarak bu sorunu çözdüm:

$content = post_get_content($id);

Ve bu harika. Ya da en azından başka bir şey yapmam gerekene kadar. Belki bir listede görüntülenmesi için en son beş gönderiyi almam gerekiyor. Her zaman başka bir işlev ekleyebilirim:

$recent_posts = post_get_recent(5);
foreach ($recent_posts as $post) { ... }

Ancak bu SELECT *, genellikle gerçekten ihtiyacım olmayan bir sorgu kullanarak sonuçlanır , ancak genellikle makul soyut için çok karmaşıktır. Sonunda, her bir kullanım durumu için büyük bir veritabanı etkileşimi işlevleri kütüphanesi veya her sayfanın kodunun içinde bir dizi dağınık sorgu ile sonuçlanır. Ve bu kütüphaneleri inşa ettikten sonra bile, kendimi daha önce kullanmadığım küçük bir birleşim yapmaya ihtiyaç duyacağım ve aniden işi yapmak için başka bir yüksek uzmanlık işlevi yazmam gerekiyor.

Elbette, işlevleri özel etkileşimler için genel kullanım durumları ve sorguları için kullanabilirim, ancak ham sorgular yazmaya başlar başlamaz her şey için doğrudan erişime geri dönmeye başlarım. Ya bu, ya da tembelleşeceğim ve gerçekten de doğrudan MySQL sorgularında yapılması gereken PHP döngülerinde bir şeyler yapmaya başlayacağım.

İnternet uygulamaları yazarken daha deneyimli olanlara sormak istiyorum: sürdürülebilirlik artışı, ekstra kod satırlarına ve soyutlamaların getirebileceği olası verimsizliklere değer mi? Yoksa doğrudan sorgu dizelerini kullanmak, veritabanı etkileşimlerini ele almak için kabul edilebilir bir yöntem midir?


Belki dağınık selects - si "sarmak" için saklı yordamlar kullanabilirsiniz sadece ihtiyacınız olan bazı parametreler ile bu prosedürleri çağırmak gerekir
k102

Yanıtlar:


7

Çok fazla özel sorgu işleviniz olduğunda bunları birleştirilebilir bitlere bölmeyi deneyebilirsiniz. Örneğin

$posts = posts()->joinWithComments()->orderBy("post.post_date")->first(5);

Akılda tutulması yararlı bulabileceğiniz bir soyutlama seviyesi hiyerarşisi de vardır. Var

  1. mysql API'sı
  2. senin örneğin seçin MySQL fonksiyonları, ( "mesajların seçin * nerede foo = çubuğu"); ya da belkiselect("posts")->where("foo = bar")->first(5)
  3. uygulama alanınıza özgü işlevler, örneğin posts()->joinWithComments()
  4. belirli bir sayfaya özgü işlevler (ör. commentsToBeReviewed($currentUser)

Bu soyutlama sırasına uymak bakım kolaylığı açısından çok para öder. Sayfa komut dosyalarında yalnızca düzey 4 işlevleri kullanılmalı, düzey 4 işlevleri düzey 3 işlevleri açısından yazılmalıdır, vb. Bunun biraz daha zaman alacağı doğrudur, ancak bakım maliyetlerinizi zaman içinde sabit tutmaya yardımcı olacaktır ("Aman tanrım başka bir değişiklik istiyorlar!" Yerine)


2
+1 Bu sözdizimi temel olarak kendi ORM'nizi oluşturur. Veritabanı erişim şeyleri karmaşık hale gelmesinden gerçekten endişeleniyorsanız ve ayrıntılarla uğraşmak için çok fazla zaman harcamak istemiyorsanız, zaten bu şeyleri anlayan olgun bir web çerçevesi (örn. CodeIgniter ) kullanmanızı öneririm . Ya da en azından xpmatteo'nun gösterdiği gibi, size ne tür bir sözdizimsel şeker verdiğini görmek için kullanmayı deneyin.
Hartley Brody

5

Endişelerin Ayrılması hakkında okumaya değer bir prensiptir, üzerindeki Wikipedia makalesine bakın.

http://en.wikipedia.org/wiki/Separation_of_concerns

Hakkında okumaya değer bir diğer ilke ise:

http://en.wikipedia.org/wiki/Coupling_(computer_science )

İki farklı kaygınız var, biri veritabanından verilerin birleştirilmesidir ve ikincisi bu verilerin oluşturulmasıdır. Gerçekten basit bir uygulamada endişelenmeniz gereken çok az şey var, veritabanı erişiminizi ve yönetim katmanınızı oluşturma katmanınızla sıkıca bağladınız, ancak küçük uygulamalar için bu önemli değil. Sorun, web uygulamalarının gelişme eğilimindedir ve herhangi bir şekilde bir web uygulamasını, performans veya işlevsellik gibi ölçeklendirmek istiyorsanız, o zaman bazı sorunlarla karşılaşırsınız.

Kullanıcı tarafından oluşturulan yorumlardan oluşan bir web sayfası oluşturduğunuzu varsayalım. Boyunca sivri saçlı patron geliyor ve iPhone / Android vb Yerli Uygulamaları desteklemeye başlamanızı istiyor. Bazı JSON çıktılarına ihtiyacımız var, şimdi HTML üreten oluşturma kodunu çıkarmanız gerekiyor. Bunu yaptığınızda, artık iki render motoru ve her şeyin yolunda olduğu bir veri erişim kitaplığınız var, işlevsel olarak ölçeklendirdiniz. Hatta her şeyi ayrı tutmayı, yani iş mantığını oluşturmayı başardınız bile.

Patron geliyor ve size web sitelerinde yayınları görüntülemek isteyen bir müşteri olduğunu, XML'e ihtiyaç duyduklarını ve saniyede yaklaşık 5000 performansa ihtiyaç duyduklarını söylüyor. Şimdi XML / JSON / HTML oluşturmanız gerekiyor. Oluşturma işleminizi daha önce olduğu gibi tekrar ayırabilirsiniz. Ancak artık ihtiyacınız olan performansı rahat bir şekilde elde etmek için 100 sunucu eklemeniz gerekiyor. Şimdi veritabanınız her biri farklı gereksinimleri ve farklı sorguları olan üç farklı uygulamaya doğrudan maruz kalan sunucu başına düzinelerce bağlantı ile 100 sunucudan vuruluyor. Her bir ön uç makinesinde veritabanı erişimine sahip olmak bir güvenlik riski ve büyüyen bir ama ben oraya gitmeyeceğim. Şimdi performans için ölçeklendirmeniz gerekiyor, her uygulama farklı önbellekleme gereksinimlerine sahip, yani farklı endişeler. Bunu sıkıca bağlanmış tek bir katmanda (ör. Veritabanı erişiminiz / iş mantığınız / oluşturma katmanınız) deneyebilir ve yönetebilirsiniz. Artık her bir katmanın endişeleri birbirinin önüne geçmeye başlıyor, yani veritabanındaki verilerin önbellekleme gereksinimleri oluşturma katmanından çok farklı olabilir, iş katmanında sahip olduğunuz mantık, SQL yani geriye doğru hareket edin veya oluşturma katmanına doğru ilerleyebilir, bu her şeyin tek bir katmanda olmasıyla karşılaştığım en büyük sorunlardan biri, iyi bir şekilde değil, uygulamanıza betonarme dökmek gibi.

Bu tür sorunlara yaklaşmanın standart yolları vardır, örneğin web hizmetlerinin HTTP önbelleğe alınması (kalamar / yts vb.). Memcached / redis gibi bir şey ile web hizmetleri içinde uygulama düzeyinde önbellekleme. Ayrıca, veritabanınızı genişletmeye başladığınızda, yani birden çok okunan ana bilgisayar ve bir ana sunucu veya ana bilgisayarlardaki kırılmış veriler gibi sorunlarla karşılaşırsınız. Bir kullanıcı "usera" tüm yazma istekleri için "[table / database] foo" yu içine alırsa, veritabanınıza yazma veya okuma isteklerine veya farklı bir veritabanına göre değişen çeşitli bağlantıları yöneten 100 ana bilgisayarın olmasını istemezsiniz.

Endişelerin ayrılması arkadaşınızdır, ne zaman ve nerede yapılacağını seçmek mimari bir karar ve bir sanattır. Çok farklı gereksinimlere sahip olacak şekilde gelişecek her şeyin sıkıca birleştirilmesinden kaçının. İşleri ayrı tutmak için bir dizi başka neden vardır, yani test, değişikliklerin konuşlandırılması, güvenlik, yeniden kullanım, esneklik vb.


Nereden geldiğini anlıyorum ve söylediğin hiçbir şeye katılmıyorum, ama şu anda bu küçük bir endişe. Söz konusu proje kişisel bir projedir ve mevcut modelimle ilgili sorunumun çoğu, programlamacımın sıkı bağlantıdan kaçınma içgüdüsünden geliyor, ancak gerçekten karmaşık sunucu tarafı gelişimine yeni başlayan biriyim, bu yüzden bunun sonu biraz gitti kafamın üstünde. Yine de, bu proje için tamamen takip etmese bile, bana iyi bir tavsiye gibi görünen şey için +1.
Alexis King

Yaptığınız şey küçük kalacaksa, onu olabildiğince basit tutacağım. Burada takip edilmesi gereken iyi bir ilke YAGNI'dır .
Harry

1

"Sayfanın kendisi" dediğinizde dinamik olarak HTML üreten PHP kaynak dosyasını kastediyorsunuz.

Veritabanını sorgulamayın ve aynı kaynak dosyada HTML oluşturmayın.

Veritabanını sorguladığınız kaynak dosya bir PHP kaynak dosyası olsa da bir "sayfa" değildir.

HTML'yi dinamik olarak oluşturduğunuz PHP kaynak dosyasında, veritabanına erişildiği PHP kaynak dosyasında tanımlanan işlevlere çağrı yaparsınız.


0

Çoğu orta ölçekli proje için kullandığım kalıp şudur:

  • Tüm SQL sorguları ayrı bir konumda sunucu tarafı kodundan ayrı tutulur.

    C # ile çalışmak, kısmi sınıfların kullanılması, yani sorgulara tek bir sınıftan erişilebileceği göz önüne alındığında, sorguların ayrı bir dosyaya konulması anlamına gelir (aşağıdaki koda bakın).

  • Bu SQL sorguları sabittir . Bu önemlidir, çünkü SQL sorgularının anında oluşturulmasını önler (böylece SQL enjeksiyonu riskini arttırır ve aynı zamanda sorguları daha sonra gözden geçirmeyi zorlaştırır).

C # 'da güncel yaklaşım

Misal:

// Demo.cs
public partial class Demo : DataRepository
{
    public IEnumerable<Stuff> LoadStuff(int categoryId)
    {
        return this
            .Query(Queries.LoadStuff)
            .With(new { CategoryId = categoryId })
            .ReadRows<Stuff>();
    }

    // Other methods go here.
}

public partial class Demo
{
    private static class Queries
    {
        public const string LoadStuff = @"
select top 100 [StuffId], [SomeText]
    from [Schema].[Table]
    where [CategoryId] = @CategoryId
    order by [CreationUtcTime]";

        // Other queries go here.
    }
}

Bu yaklaşım, sorguların ayrı bir dosyada yer almasını sağlar. Bu, bir DBA'nın sorguları gözden geçirmesini ve değiştirmesini / optimize etmesini ve değiştirilen dosyayı geliştiriciler tarafından yapılan taahhütlerle çelişmeden kaynak kontrolüne almasını sağlar.

İlgili bir avantaj, kaynak denetiminin DBA erişimini yalnızca sorguları içeren dosyalarla sınırlayacak ve kodun geri kalanına erişimi reddedecek şekilde yapılandırılabilmesidir.

PHP ile yapmak mümkün mü?

PHP hem kısmi sınıflardan hem de iç sınıflardan yoksundur, bu yüzden PHP'de uygulanamaz.

DemoQueriesSınıfa her yerden erişilebileceği göz önüne alındığında, sabitleri içeren ayrı bir statik sınıf ( ) içeren ayrı bir dosya oluşturabilirsiniz . Ayrıca, küresel kapsamı kirletmekten kaçınmak için, tüm sorgu sınıflarını özel bir ad alanına koyabilirsiniz. Bu oldukça ayrıntılı bir sözdizimi yaratacaktır, ancak şüpheli ayrıntılardan kaçınabileceğinizden şüpheliyim:

namespace Data {
    public class Demo inherit DataRepository {
        public function LoadStuff($categoryId) {
            $query = \Queries\Demo::$LoadStuff;
            // Do the stuff with the query.
        }

        // Other methods go here.
    }
}

namespace Queries {
    public static class Demo {
        public const $LoadStuff = '...';

        // Other queries go here.
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.