แบบสอบถามฐานข้อมูลควรถูกแยกออกจากหน้าตัวเองหรือไม่?


10

เมื่อเขียนการสร้างหน้าใน PHP ฉันมักจะพบว่าตัวเองกำลังเขียนไฟล์ที่เกลื่อนไปด้วยแบบสอบถามฐานข้อมูล ตัวอย่างเช่นฉันอาจมีแบบสอบถามเพื่อดึงข้อมูลบางอย่างเกี่ยวกับการโพสต์โดยตรงจากฐานข้อมูลเพื่อแสดงบนหน้าเช่นนี้:

$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

ข้อความค้นหาแบบครั้งเดียวที่รวดเร็วเหล่านี้มักจะเล็ก แต่บางครั้งฉันก็ลงท้ายด้วยรหัสการโต้ตอบของฐานข้อมูลขนาดใหญ่ซึ่งเริ่มดูยุ่งเหยิง

ในบางกรณีฉันได้แก้ไขปัญหานี้ด้วยการสร้างไลบรารีฟังก์ชันที่ใช้งานง่ายเพื่อจัดการคิวรี db ที่เกี่ยวข้องกับการโพสต์ของฉันทำให้บล็อกโค้ดนั้นสั้นลงง่ายๆ

$content = post_get_content($id);

และนั่นก็ยอดเยี่ยม หรืออย่างน้อยก็จนกว่าฉันจะต้องทำอย่างอื่น บางทีฉันอาจต้องได้รับห้าโพสต์ล่าสุดเพื่อแสดงในรายการ ฉันสามารถเพิ่มฟังก์ชั่นอื่นได้เสมอ:

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

แต่นั่นก็จบลงด้วยการใช้SELECT *แบบสอบถามซึ่งโดยปกติฉันไม่ต้องการอะไรมากนัก แต่มักจะซับซ้อนเกินกว่าจะสรุปได้พอสมควร ในที่สุดฉันก็จบลงด้วยห้องสมุดขนาดใหญ่ของฟังก์ชั่นการโต้ตอบฐานข้อมูลสำหรับทุกกรณีการใช้งานหรือชุดของแบบสอบถามที่ยุ่งเหยิงภายในโค้ดของทุกหน้า และแม้กระทั่งเมื่อฉันสร้างห้องสมุดเหล่านี้ฉันจะพบว่าตัวเองต้องเข้าร่วมกลุ่มเล็ก ๆ ที่ฉันไม่เคยใช้มาก่อนและฉันก็จำเป็นต้องเขียนฟังก์ชั่นพิเศษอื่น ๆ เพื่อทำงาน

แน่นอนว่าฉันสามารถใช้ฟังก์ชั่นสำหรับกรณีการใช้งานทั่วไปและแบบสอบถามสำหรับการโต้ตอบเฉพาะ แต่ทันทีที่ฉันเริ่มเขียนแบบสอบถามแบบดิบฉันจะเริ่มย้อนกลับไปใช้การเข้าถึงโดยตรงสำหรับทุกสิ่ง ไม่เช่นนั้นหรือฉันจะขี้เกียจและจะเริ่มทำสิ่งต่าง ๆ ในลูป PHP ที่ควรจะทำโดยตรงในแบบสอบถาม MySQL ต่อไป

ฉันอยากจะขอให้ผู้ที่มีประสบการณ์เกี่ยวกับการเขียนแอปพลิเคชันอินเทอร์เน็ตมากขึ้น: การเพิ่มความสามารถในการบำรุงรักษานั้นคุ้มค่ากับโค้ดพิเศษและความไร้ประสิทธิภาพที่เป็นไปได้ หรือเพียงแค่ใช้สตริงคำสั่งโดยตรงเป็นวิธีที่ยอมรับได้สำหรับจัดการกับการโต้ตอบของฐานข้อมูล?


บางทีคุณสามารถใช้ขั้นตอนการจัดเก็บเพื่อ "ห่อ" ยุ่งselect- s คุณจะต้องเรียกขั้นตอนดังกล่าวด้วยพารามิเตอร์บางอย่างที่คุณต้องการ
k102

คำตอบ:


7

เมื่อคุณมีฟังก์ชั่นแบบสอบถามพิเศษมากเกินไปคุณสามารถลองแบ่งออกเป็นบิตคอมโพสิต ตัวอย่างเช่น

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

นอกจากนี้ยังมีลำดับชั้นของระดับนามธรรมที่คุณอาจพบว่ามีประโยชน์ที่ควรทราบ คุณมี

  1. mysql API
  2. ฟังก์ชั่น mysql ของคุณเช่น select ("select * จากโพสต์ที่ foo = bar"); หรืออาจจะแต่งขึ้นเป็นselect("posts")->where("foo = bar")->first(5)
  3. ฟังก์ชันที่เฉพาะเจาะจงสำหรับโดเมนแอปพลิเคชันของคุณ posts()->joinWithComments()
  4. ฟังก์ชันที่เฉพาะเจาะจงสำหรับหน้าใดหน้าหนึ่งเช่น commentsToBeReviewed($currentUser)

มันจ่ายมากในแง่ของความง่ายในการบำรุงรักษาเพื่อเคารพลำดับนามธรรมนี้ สคริปต์หน้าควรใช้เฉพาะระดับ 4 ฟังก์ชั่นระดับ 4 ควรเขียนในรูปแบบของฟังก์ชันระดับ 3 และอื่น ๆ เป็นความจริงที่ว่าจะต้องใช้เวลาอีกเล็กน้อย แต่จะช่วยให้ค่าใช้จ่ายในการบำรุงรักษาของคุณคงที่ตลอดเวลา (ตรงข้ามกับ "โอ้วววววพวกเขาต้องการการเปลี่ยนแปลงอีกครั้ง !!!")


2
+1 ไวยากรณ์นี้เป็นหลักสร้าง ORM ของคุณเอง หากคุณกังวลเกี่ยวกับการเข้าถึงฐานข้อมูลที่ซับซ้อนและไม่ต้องการเสียเวลากับรายละเอียดมากนักฉันขอแนะนำให้ใช้เฟรมเวิร์กสำหรับเว็บที่พัฒนาแล้ว (เช่นCodeIgniter ) ซึ่งคิดเรื่องนี้แล้ว หรืออย่างน้อยก็ลองใช้มันเพื่อดูว่ามันให้น้ำตาลซินแทติกติกแบบไหนที่คล้ายกับที่ xpmatteo แสดงให้เห็น
Hartley Brody

5

การแยกข้อกังวลเป็นหลักการที่ควรอ่านอ่านบทความวิกิพีเดีย

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

หลักการที่ควรค่าแก่การอ่านอีกข้อหนึ่งคือการมีเพศสัมพันธ์:

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

คุณมีข้อกังวลที่แตกต่างกันสองข้อข้อที่หนึ่งคือการจัดการข้อมูลจากฐานข้อมูลและข้อสองคือการเรนเดอร์ข้อมูลนั้น ในแอพพลิเคชั่นที่เรียบง่ายจริงๆอาจมีความกังวลเล็กน้อยคุณได้เชื่อมโยงการเข้าถึงฐานข้อมูลและเลเยอร์การจัดการเข้ากับเลเยอร์การแสดงผลของคุณอย่างแน่นหนา แต่สำหรับแอปขนาดเล็กนี่ไม่ใช่เรื่องใหญ่ ปัญหาคือเว็บแอปพลิเคชั่นมีแนวโน้มที่จะพัฒนาขึ้นและหากคุณต้องการเพิ่มขนาดแอปพลิเคชันเว็บไม่ว่าจะเป็นประสิทธิภาพหรือฟังก์ชันการทำงานคุณก็พบปัญหาบางอย่าง

ให้บอกว่าคุณกำลังสร้างเว็บเพจของความคิดเห็นที่ผู้ใช้สร้างขึ้น พร้อมกับเจ้านายที่มีผมแหลมและขอให้คุณเริ่มสนับสนุนแอพ Native เช่น iPhone / Android ฯลฯ เราต้องการเอาต์พุต JSON บางส่วนตอนนี้คุณต้องดึงรหัสการแสดงผลออกที่สร้าง HTML เมื่อคุณทำสิ่งนี้ตอนนี้คุณมีไลบรารีการเข้าถึงข้อมูลที่มีเอ็นจิ้นการเรนเดอร์สองตัวและทุกอย่างเรียบร้อยแล้ว คุณอาจมีการจัดการเพื่อแยกทุกอย่างเช่นตรรกะทางธุรกิจจากการแสดงผล

หัวหน้าพร้อมมาและบอกคุณว่าเขามีลูกค้าที่ต้องการแสดงโพสต์บนเว็บไซต์ของพวกเขาพวกเขาต้องการ XML และพวกเขาต้องการประมาณ 5000 คำขอต่อวินาทีประสิทธิภาพสูงสุด ตอนนี้คุณต้องสร้าง XML / JSON / HTML คุณสามารถแยกการเรนเดอร์ออกมาอีกครั้งเหมือนเมื่อก่อน อย่างไรก็ตามตอนนี้คุณต้องเพิ่มเซิร์ฟเวอร์ 100 เครื่องเพื่อให้ได้ประสิทธิภาพที่คุณต้องการอย่างสะดวกสบาย ตอนนี้ฐานข้อมูลของคุณกำลังได้รับผลกระทบจากเซิร์ฟเวอร์ 100 เครื่องที่มีการเชื่อมต่อหลายสิบเซิร์ฟเวอร์ต่อเซิร์ฟเวอร์ซึ่งแต่ละแอพพลิเคชั่นจะมีความต้องการที่แตกต่างกันและมีการค้นหาที่แตกต่างกัน แต่ฉันจะไม่ไปที่นั่น ตอนนี้คุณต้องเพิ่มประสิทธิภาพการทำงานแต่ละแอพมีข้อกำหนดการแคชที่แตกต่างกันเช่นความกังวลที่ต่างกัน คุณสามารถลองและจัดการสิ่งนี้ในเลเยอร์คู่ที่แน่นหนาเช่นการเข้าถึงฐานข้อมูล / ตรรกะทางธุรกิจ / เลเยอร์การแสดงผลของคุณ ความกังวลของแต่ละเลเยอร์ตอนนี้เริ่มที่จะเข้าหากันเช่นความต้องการแคชของข้อมูลจากฐานข้อมูลอาจแตกต่างจากเลเยอร์การแสดงผลตรรกะที่คุณมีในเลเยอร์ธุรกิจมีแนวโน้มที่จะตกอยู่ใน SQL คือย้ายไปข้างหลังหรืออาจตกไปที่เลเยอร์การเรนเดอร์นี่เป็นหนึ่งในปัญหาที่ใหญ่ที่สุดที่ฉันเคยเห็นเมื่อมีทุกอย่างในเลเยอร์เดียวมันเหมือนกับการเทคอนกรีตเสริมเหล็กในแอปพลิเคชันของคุณและไม่ใช่วิธีที่ดี

มีวิธีมาตรฐานในการแก้ไขปัญหาประเภทนี้เช่นการแคช HTTP ของบริการเว็บ (squid / yts ฯลฯ ) การแคชระดับแอปพลิเคชันภายในเว็บเซอร์วิสมีอะไรบางอย่างเช่น memcached / redis คุณจะพบปัญหาในขณะที่คุณเริ่มขยายฐานข้อมูลของคุณเช่นโฮสต์การอ่านหลายรายการและข้อมูลหลักหนึ่งรายการหรือส่งข้อมูลไปยังโฮสต์ทั้งหมด คุณไม่ต้องการ 100 โฮสต์ที่จัดการการเชื่อมต่อกับฐานข้อมูลของคุณที่แตกต่างกันไปตามคำขอการเขียนหรือการอ่านหรือในฐานข้อมูลที่แตกหักหากผู้ใช้“ usera” แฮ็ชเป็น“ [table / database] foo” สำหรับคำขอเขียนทั้งหมด

การแยกข้อกังวลออกมาคือเพื่อนของคุณการเลือกเวลาและสถานที่ที่จะทำคือการตัดสินใจด้านสถาปัตยกรรมและศิลปะ หลีกเลี่ยงการมีเพศสัมพันธ์อย่างแน่นหนาของสิ่งที่จะพัฒนาให้มีข้อกำหนดที่แตกต่าง มีเหตุผลหลายประการที่ทำให้แยกสิ่งต่าง ๆ เช่นทำให้การทดสอบง่ายขึ้นการปรับใช้การเปลี่ยนแปลงความปลอดภัยการใช้ซ้ำความยืดหยุ่นเป็นต้น


ฉันเข้าใจว่าคุณมาจากไหนและฉันไม่เห็นด้วยกับสิ่งที่คุณพูด แต่ตอนนี้เป็นเรื่องเล็กน้อย โครงการที่เป็นปัญหานั้นเป็นเรื่องส่วนตัวและปัญหาส่วนใหญ่ของฉันกับโมเดลปัจจุบันของฉันมาจากสัญชาตญาณของโปรแกรมเมอร์ของฉันเพื่อหลีกเลี่ยงการแต่งงานกันอย่างแน่นหนา แต่จริงๆแล้วฉันเป็นมือใหม่ในการพัฒนาฝั่งเซิร์ฟเวอร์ที่ซับซ้อน เหนือหัวของฉัน ถึงกระนั้น +1 สำหรับสิ่งที่ดูเหมือนว่าฉันจะเป็นคำแนะนำที่ดีแม้ว่าฉันอาจไม่ปฏิบัติตามมันทั้งหมดสำหรับโครงการนี้
Alexis King

หากสิ่งที่คุณกำลังทำอยู่ยังคงมีขนาดเล็กอยู่ฉันก็จะทำให้มันง่ายที่สุด หลักการที่ดีที่จะทำตามที่นี่เป็นYAGNI
Harry

1

ฉันคิดว่าเมื่อคุณพูดว่า "หน้าตัวเอง" คุณหมายถึงไฟล์ต้นฉบับ PHP ที่สร้าง HTML แบบไดนามิก

อย่าสืบค้นฐานข้อมูลและสร้าง HTML ในไฟล์ต้นฉบับเดียวกัน

ไฟล์ต้นฉบับที่คุณสืบค้นฐานข้อมูลไม่ใช่ "หน้า" แม้ว่าจะเป็นไฟล์ต้นฉบับ PHP ก็ตาม

ในไฟล์ต้นฉบับ PHP ที่คุณสร้าง HTML แบบไดนามิกคุณเพียงแค่โทรไปยังฟังก์ชั่นที่กำหนดไว้ในไฟล์ต้นฉบับ PHP ที่มีการเข้าถึงฐานข้อมูล


0

รูปแบบที่ฉันใช้สำหรับโครงการขนาดกลางส่วนใหญ่เป็นรูปแบบต่อไปนี้:

  • แบบสอบถาม SQL ทั้งหมดจะถูกแยกออกจากรหัสฝั่งเซิร์ฟเวอร์ในตำแหน่งที่ตั้งอื่น

    การทำงานกับ C # หมายถึงการใช้คลาสบางส่วนเช่นการใส่คิวรี่ในไฟล์แยกต่างหากเนื่องจากคิวรีเหล่านั้นจะสามารถเข้าถึงได้จากคลาสเดียว (ดูรหัสด้านล่าง)

  • ผู้แบบสอบถาม SQL มีค่าคงที่ สิ่งนี้มีความสำคัญเนื่องจากช่วยป้องกันการล่อลวงการสร้างคิวรี SQL ในขณะเดินทาง (ซึ่งเป็นการเพิ่มความเสี่ยงของการฉีด SQL และในเวลาเดียวกันทำให้ยากต่อการตรวจสอบคิวรีในภายหลัง)

วิธีการปัจจุบันใน C #

ตัวอย่าง:

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

วิธีนี้มีประโยชน์ในการมีคิวรีอยู่ในไฟล์แยกต่างหาก สิ่งนี้จะช่วยให้ DBA ตรวจสอบและแก้ไข / ปรับให้เหมาะสมกับคิวรีและคอมมิทไฟล์ที่แก้ไขแล้วเพื่อควบคุมแหล่งที่มาโดยไม่ขัดแย้งกับข้อผูกมัดที่นักพัฒนาทำขึ้น

ประโยชน์ที่เกี่ยวข้องคือการควบคุมแหล่งที่มาอาจถูกกำหนดค่าในวิธีการ จำกัด การเข้าถึง DBA เฉพาะไฟล์ที่มีการสืบค้นและปฏิเสธการเข้าถึงส่วนที่เหลือของรหัส

เป็นไปได้ที่จะทำใน PHP?

PHP ไม่มีทั้งคลาสบางส่วนและคลาสภายในดังนั้นไม่สามารถใช้งานใน PHP ได้

คุณสามารถสร้างไฟล์แยกต่างหากพร้อมกับสแตติกคลาส ( DemoQueries) ที่มีค่าคงที่โดยที่คลาสจะสามารถเข้าถึงได้จากทุกที่ นอกจากนี้เพื่อหลีกเลี่ยงการสร้างมลภาวะขอบเขตโกลบอลคุณสามารถใส่คลาสเคียวรีทั้งหมดในเนมสเปซเฉพาะ สิ่งนี้จะสร้างไวยากรณ์ที่ค่อนข้าง verbose แต่ฉันสงสัยว่าคุณสามารถหลีกเลี่ยง verbosity:

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.
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.