Haruskah permintaan basis data disarikan dari halaman itu sendiri?

10

Saat menulis pembuatan halaman di PHP, saya sering menemukan diri saya menulis satu set file yang dikotori dengan query database. Misalnya, saya mungkin memiliki kueri untuk mengambil beberapa data tentang kiriman langsung dari database untuk ditampilkan pada halaman, seperti ini:

$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

Pertanyaan cepat dan sekali saja ini biasanya kecil, tetapi saya terkadang berakhir dengan sebagian besar kode interaksi basis data yang mulai terlihat sangat berantakan.

Dalam beberapa kasus, saya telah memecahkan masalah ini dengan membuat pustaka fungsi sederhana untuk menangani kueri db terkait postingan saya, memperpendek blok kode menjadi sederhana:

$content = post_get_content($id);

Dan itu bagus. Atau setidaknya sampai saya perlu melakukan sesuatu yang lain. Mungkin saya perlu mendapatkan lima posting terbaru untuk ditampilkan dalam daftar. Yah, saya selalu bisa menambahkan fungsi lain:

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

Tapi itu akhirnya menggunakan SELECT *kueri, yang biasanya sebenarnya tidak saya butuhkan, tetapi seringkali terlalu rumit untuk abstrak. Saya akhirnya berakhir dengan perpustakaan besar fungsi interaksi database untuk setiap kasus penggunaan tunggal, atau serangkaian pertanyaan berantakan di dalam kode setiap halaman. Dan bahkan setelah saya membangun perpustakaan ini, saya akan merasa perlu melakukan satu join kecil yang belum pernah saya gunakan sebelumnya, dan tiba-tiba saya perlu menulis fungsi lain yang sangat terspesialisasi untuk melakukan pekerjaan itu.

Tentu, saya bisa menggunakan fungsi untuk kasus penggunaan umum dan permintaan untuk interaksi tertentu, tetapi segera setelah saya mulai menulis permintaan mentah saya mulai menyelinap kembali ke akses langsung untuk semuanya. Entah itu, atau saya akan malas dan akan mulai melakukan hal-hal dalam loop PHP yang benar-benar harus dilakukan secara langsung dalam permintaan MySQL.

Saya ingin bertanya kepada mereka yang lebih berpengalaman dalam menulis aplikasi internet: apakah peningkatan rawatan sebanding dengan baris kode tambahan dan kemungkinan inefisiensi yang mungkin diperkenalkan oleh abstraksi? Atau hanya menggunakan string kueri langsung metode yang dapat diterima untuk menangani interaksi database?

Alexis King
sumber
Mungkin Anda dapat menggunakan prosedur tersimpan untuk "membungkus" berantakan selects - si Anda hanya perlu memanggil prosedur seperti itu dengan beberapa parameter yang Anda butuhkan
k102

Jawaban:

7

Ketika Anda memiliki terlalu banyak fungsi kueri khusus, Anda dapat mencoba memecahnya menjadi bit yang dapat dikomposisikan. Contohnya

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

Ada juga hierarki tingkat abstraksi yang mungkin berguna untuk Anda ingat. Kamu punya

  1. API mysql
  2. fungsi mysql Anda , seperti pilih ("pilih * dari pos di mana foo = bar"); atau mungkin lebih komposer sebagaiselect("posts")->where("foo = bar")->first(5)
  3. fungsi yang spesifik untuk domain aplikasi Anda, misalnya posts()->joinWithComments()
  4. fungsi yang spesifik untuk halaman tertentu, seperti commentsToBeReviewed($currentUser)

Membayar banyak dalam hal kemudahan pemeliharaan untuk menghormati urutan abstraksi ini. Skrip halaman harus hanya menggunakan fungsi level 4, fungsi level 4 harus ditulis dalam hal fungsi level 3, dan seterusnya. Memang benar bahwa ini membutuhkan sedikit waktu lebih awal tetapi itu akan membantu menjaga biaya pemeliharaan Anda konstan dari waktu ke waktu (sebagai lawan dari "ya ampun, mereka ingin perubahan lain !!!")

xpmatteo
sumber
2
+1 Sintaks ini pada dasarnya membuat ORM Anda sendiri. Jika Anda benar-benar khawatir tentang hal-hal akses basis data yang semakin rumit, dan tidak ingin menghabiskan banyak waktu untuk mengutak-atik perincian, saya sarankan menggunakan kerangka kerja web yang matang (misalnya CodeIgniter ) yang sudah memikirkan hal ini. Atau setidaknya coba gunakan untuk melihat gula sintaksis yang diberikannya kepada Anda, mirip dengan apa yang telah ditunjukkan xpmatteo.
Hartley Brody
5

Separation of Concerns adalah prinsip yang layak dibaca, lihat artikel Wikipedia di atasnya.

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

Prinsip lain yang layak dibaca adalah Coupling:

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

Anda memiliki dua masalah berbeda, satu adalah pengurutan data dari database dan yang kedua adalah rendering data itu. Dalam aplikasi yang sangat sederhana mungkin ada sedikit yang perlu dikhawatirkan, Anda telah dengan erat menggabung akses basis data dan lapisan manajemen dengan lapisan rendering Anda, tetapi untuk aplikasi kecil ini bukan masalah besar. Masalahnya adalah bahwa aplikasi web cenderung berkembang dan jika Anda ingin meningkatkan aplikasi web, dengan cara apa pun yaitu kinerja atau fungsionalitas, maka Anda mengalami beberapa masalah.

Katakanlah Anda membuat halaman web dari komentar yang dibuat pengguna. Seiring datang bos berambut runcing dan meminta Anda untuk mulai mendukung Aplikasi Asli yaitu iPhone / Android dll. Kami membutuhkan beberapa output JSON, Anda sekarang perlu mengekstrak kode rendering yang menghasilkan HTML. Ketika Anda telah melakukan ini, sekarang Anda memiliki pustaka akses data dengan dua mesin render dan semuanya baik-baik saja, Anda diskalakan secara fungsional. Anda bahkan mungkin telah berhasil menjaga semuanya terpisah yaitu logika bisnis dari rendering.

Seiring datangnya bos dan memberi tahu Anda bahwa ia memiliki pelanggan yang ingin menampilkan posting di situs web mereka, mereka membutuhkan XML dan mereka membutuhkan sekitar 5.000 permintaan per kinerja puncak per detik. Sekarang Anda perlu menghasilkan XML / JSON / HTML. Anda dapat memisahkan rendering Anda lagi, sama seperti sebelumnya. Namun Anda sekarang perlu menambahkan 100 server untuk mendapatkan kinerja yang Anda butuhkan dengan nyaman. Sekarang basis data Anda terkena dari 100 server dengan kemungkinan lusinan koneksi per server, yang masing-masing secara langsung terpapar ke tiga aplikasi berbeda dengan persyaratan dan permintaan berbeda, dll. Memiliki akses basis data pada setiap mesin frontend adalah risiko keamanan dan pertumbuhan satu tapi saya tidak akan pergi ke sana. Sekarang Anda perlu meningkatkan kinerja, setiap aplikasi memiliki persyaratan caching yang berbeda, yaitu masalah yang berbeda. Anda dapat mencoba dan mengelola ini dalam satu lapisan yang sangat erat yaitu akses database / logika bisnis / rendering Anda. Kekhawatiran dari masing-masing lapisan sekarang mulai menghalangi satu sama lain yaitu persyaratan caching data dari database bisa sangat berbeda dari lapisan rendering, logika yang Anda miliki di lapisan bisnis cenderung berdarah ke dalam SQL yaitu bergerak mundur atau bisa berdarah ke depan ke lapisan rendering, ini adalah salah satu masalah terbesar yang saya lihat dengan memiliki semuanya dalam satu lapisan, itu seperti menuangkan beton bertulang ke dalam aplikasi Anda dan tidak dengan cara yang baik.

Ada cara standar untuk mendekati jenis masalah ini yaitu HTTP caching dari layanan web (squid / yts dll). Caching level aplikasi dalam layanan web sendiri dengan sesuatu seperti memcached / redis. Anda juga akan mengalami masalah saat Anda mulai memperbesar basis data Anda yaitu beberapa host baca dan satu master, atau data yang dibagi di seluruh host. Anda tidak ingin 100 host mengelola berbagai koneksi ke database Anda yang berbeda berdasarkan permintaan tulis atau baca atau dalam database yang digunaka jika pengguna "usera" hash ke "[table / database] foo" untuk semua permintaan tulis.

Pemisahan masalah adalah teman Anda, memilih kapan dan di mana melakukannya adalah keputusan arsitektur dan sedikit seni. Hindari penggabungan ketat dari apa pun yang akan berkembang memiliki persyaratan yang sangat berbeda. Ada banyak alasan lain untuk menjaga hal-hal terpisah yaitu menyederhanakan pengujian, penyebaran perubahan, keamanan, penggunaan kembali, fleksibilitas dll.

Harry
sumber
Saya mengerti dari mana Anda berasal, dan saya tidak setuju dengan apa pun yang Anda katakan, tetapi ini adalah masalah kecil saat ini. Proyek tersebut adalah masalah pribadi, dan sebagian besar masalah saya dengan model saya saat ini berasal dari insting programmer saya untuk menghindari penggabungan yang ketat, tetapi saya benar-benar seorang pemula untuk pengembangan sisi-server yang kompleks, jadi pada akhirnya ini sedikit di atas kepalaku. Namun, +1 untuk apa yang menurut saya merupakan saran yang baik, meskipun saya mungkin tidak sepenuhnya mengikuti untuk proyek ini.
Alexis King
Jika apa yang Anda lakukan akan tetap kecil maka saya akan membuatnya sesederhana mungkin. Prinsip yang baik untuk diikuti di sini adalah YAGNI .
Harry
1

Saya berasumsi bahwa ketika Anda mengatakan "halaman itu sendiri" yang Anda maksud adalah file sumber PHP yang secara dinamis menghasilkan HTML.

Jangan meminta basis data dan menghasilkan HTML dalam file sumber yang sama.

File sumber tempat Anda query database bukan "halaman", meskipun itu file sumber PHP.

Dalam file sumber PHP di mana Anda secara dinamis membuat HTML Anda hanya membuat panggilan ke fungsi-fungsi yang didefinisikan dalam file sumber PHP di mana database diakses.

Tulains Córdova
sumber
0

Pola yang saya gunakan untuk sebagian besar proyek skala menengah adalah sebagai berikut:

  • Semua kueri SQL dipisahkan dari kode sisi server, di lokasi yang terpisah.

    Bekerja dengan C #, itu berarti menggunakan kelas parsial, yaitu menempatkan kueri dalam file terpisah, mengingat bahwa kueri tersebut akan dapat diakses dari satu kelas (lihat kode di bawah).

  • Kueri SQL itu adalah konstanta . Ini penting, karena itu mencegah godaan membangun query SQL on the fly (sehingga meningkatkan risiko injeksi SQL dan pada saat yang sama membuatnya lebih sulit untuk meninjau permintaan nanti).

Pendekatan saat ini dalam C #

Contoh:

// 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.
    }
}

Pendekatan ini memiliki manfaat memiliki kueri dalam file terpisah. Ini memungkinkan DBA untuk meninjau dan memodifikasi / mengoptimalkan kueri dan mengkomit file yang dimodifikasi ke kontrol sumber tanpa bertentangan dengan komitmen yang dibuat oleh pengembang.

Manfaat terkait adalah bahwa kontrol sumber dapat dikonfigurasi dengan cara membatasi akses DBA hanya untuk file-file yang berisi pertanyaan, dan menolak akses ke seluruh kode.

Apakah mungkin dilakukan di PHP?

PHP tidak memiliki kelas parsial dan kelas dalam, sehingga tidak dapat diimplementasikan dalam PHP.

Anda dapat membuat file terpisah dengan kelas statis terpisah ( DemoQueries) yang berisi konstanta, mengingat kelas akan dapat diakses dari mana saja. Selain itu, untuk menghindari polusi ruang lingkup global, Anda dapat menempatkan semua kelas kueri di namespace khusus. Ini akan membuat sintaks yang agak verbose, tapi saya ragu Anda bisa menghindari verbositas:

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.
    }
}
Arseni Mourzenko
sumber