ใน MVC สามารถเรียกค้นข้อมูลพื้นฐานจาก Model ได้หรือไม่ในมุมมอง?


10

ด้วยแนวคิดของ'ตัวควบคุมผอมแบบจำลองไขมัน'และการยอมรับทั่วไปที่ Views สามารถเรียกใช้โมเดลโดยตรงเมื่อต้องการข้อมูลเพื่อการส่งออกเราควรพิจารณาการจัดการส่วน 'รับและแสดง' ของคำขอภายใน Views ไม่ใช่ตัวควบคุม ตัวอย่างเช่น (พยายามเก็บรหัสที่ค่อนข้างทั่วไป):

ตัวควบคุม

<?php

class Invoice extends Base_Controller {

    /**
     * Get all the invoices for this month
     */

    public function current_month() {

        // as there's no user input let's keep the controller very skinny,
        // DON'T get data from the Model here, just load the view

        $this->load->view('invoice/current_month');

    }

}

ดู

<?php

// directly retrieve current month invoices here

$invoices = $this->invoice_model->get_current_month();

// get some other display-only data, e.g. a list of users for a separate list somewhere on the page

$users = $this->user_model->get_users();

?>

<h1>This month's invoices</h1>

<ul>
<?php foreach ($invoices as $invoice) { ?>

<li><?php echo $invoice['ref']; ?></li>

<?php } ?>
</ul>

สำหรับฉันแล้วนี่ทำให้อย่างน้อยก็มีเหตุผลบางอย่างในกรณีที่คำขอเป็นเพียงแค่มุมมอง เหตุใดผู้ควบคุมจึงต้องรวบรวมและส่งผ่านข้อมูลไปยังมุมมองเมื่อมันสามารถเรียกคืนได้เอง สิ่งนี้ทำให้ตัวควบคุมเปิดอยู่สำหรับการประมวลผล 'ระดับแอปพลิเคชัน' เท่านั้น (เช่นการจัดการคำขอ GET / POST การจัดการสิทธิ์การเข้าถึงและการอนุญาตเป็นต้น) รวมถึงการรักษาแบบจำลองที่ใช้ซ้ำได้และสิ่งดีๆอื่น ๆ

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

นี่เป็นวิธีที่ถูกต้องในการพัฒนาแอปพลิเคชัน MVC หรือไม่ หรือฉันมองส่วนสำคัญของบทบาทที่ผู้ควบคุมควรเล่น?

คำตอบ:


17

ใช่มันสามารถทำได้ในทางเทคนิค ไม่ไม่ควรทำ และใช่คุณพลาดสิ่งที่คอนโทรลเลอร์มีไว้

คอนโทรลเลอร์อยู่ที่นั่นเพื่อแยกมุมมองออกจากโมเดล Decoupling นั้นมีประโยชน์เพราะคุณควรมองที่รหัส View เป็นรหัสที่เกือบจะทิ้ง เมื่อเทคโนโลยี UI ของคุณมีการเปลี่ยนแปลงคุณต้องการลดการทำงานซ้ำที่จำเป็นในการสร้างมุมมองใหม่ คอนโทรลเลอร์เปิดใช้งานการถอดรหัสและให้สถานที่สำหรับรหัสของคุณที่จะถ่ายทอดสดผ่านเทคโนโลยี UI

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

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


1
คำตอบที่ดีขอบคุณ ขยายสถานการณ์ 'มองไปข้างหน้า' ของคุณเล็กน้อย หากมีข้อมูลทั่วไปในหน้าเว็บที่แยกจากสิ่งที่ขอ (เช่นผู้ใช้กำลังดูผลิตภัณฑ์เฉพาะรายการทั่วไปของ 'ข้อเสนอพิเศษล่าสุด' จะแสดงที่ด้านข้าง) วิธี / การโทรควรoffers_model->get_latest()จะทำอย่างไร? การเพิ่มสิ่งนี้ลงในทุก ๆ วิธีในคอนโทรลเลอร์ (อย่างที่ฉันเคยทำมาก่อนหน้านี้โง่ ๆ ) ดูเหมือนจะเกินความจริงและยกเลิกการอบแห้งอย่างชัดเจน
Adam Westbrook

2
@AdamWestbrook มาดู MVVM ส่วน ViewModel ที่สามารถแก้ไขปัญหานี้ได้ คุณสามารถเพิ่มoffers_model->get_latest()ไปยังProductViewModelชั้นฐานหรือสิ่งที่คล้ายกัน
Zachary Yates

1
เยี่ยมมากฉันจะดู MVVM แล้วขอบคุณอีกครั้ง
Adam Westbrook

คำตอบที่ดีมากจะทำให้ติดดาวนี้อย่างเด็ดขาด โดยส่วนตัวแล้วฉันยังเป็นแฟนตัวยงของ MVVM :)
Benjamin Gruenbaum

@BenjaminGruenbaum คุณใช้ MVVM ใน PHP หรือไม่? ถ้าเป็นเช่นนั้นคุณใช้กรอบงานเฉพาะสำหรับมันหรือไม่?
Adam Westbrook

6

ด้วยแนวคิดของ 'คอนโทรลเลอร์บางรุ่นไขมัน' และการยอมรับทั่วไปที่ Views สามารถเรียกใช้โมเดลโดยตรงเมื่อต้องการข้อมูลสำหรับเอาต์พุต

ไม่มันไม่ถูกต้อง ดูไม่สามารถโทรหารุ่นโดยตรง มุมมองไม่ควรมีการเข้าถึงวัตถุโมเดลยกเว้นว่าด้วยเหตุผลบางประการโปรแกรมเมอร์ได้เปิดเผยวัตถุเหล่านั้นไปยังมุมมอง

ควรพิจารณาการจัดการส่วน 'รับและแสดง' ของคำขอภายในมุมมองไม่ใช่ตัวควบคุมหรือไม่

โดยทั่วไปจะลบผู้ควบคุมและกำจัดจุดที่มีอยู่

เหตุใดผู้ควบคุมจึงต้องรวบรวมและส่งผ่านข้อมูลไปยังมุมมองเมื่อมันสามารถเรียกคืนได้เอง

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

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

เลขที่

คอนโทรลเลอร์จะตรวจสอบว่าข้อมูล POSTed นั้นถูกต้องหรือไม่จากนั้นจะส่งข้อมูลนี้เป็นตัวเลือกไปยังโมเดลซึ่งจะทำการสืบค้นแหล่งข้อมูลและส่งคืนข้อมูลและคอนโทรลเลอร์จะส่งข้อมูลนั้นไปยังมุมมอง

นี่เป็นวิธีที่ถูกต้องในการพัฒนาแอปพลิเคชัน MVC หรือไม่ หรือฉันมองส่วนสำคัญของบทบาทที่ผู้ควบคุมควรเล่น?

ตัวควบคุมทำงานเป็นตัวจัดการการร้องขอจากเบราว์เซอร์ โปรแกรมเลือกจ่ายงานจะส่งคำขอไปยังการกระทำของคอนโทรลเลอร์ซึ่งจะกระจายคำขอไปยังรุ่นต่างๆ รุ่นประกอบด้วยตรรกะทางธุรกิจทั้งหมด (นี่คือส่วนที่อ้วน) และให้ข้อมูลกลับไปที่ตัวควบคุม คอนโทรลเลอร์สามารถปรับและปรับข้อมูลให้ง่ายขึ้นเพื่อให้มุมมองนำเสนอได้ง่ายขึ้น

จุดของมุมมองคือการแยกโครงสร้างและการพึ่งพาระหว่างการนำเสนอของ HTML และแหล่งข้อมูล ในขณะนี้อาจเป็นเรื่องยาก จำนวนการดูไม่แสดงข้อมูลที่มาจากแบบจำลองโดยตรง ตัวควบคุมมักจะเพิ่มข้อมูลพิเศษที่เกี่ยวข้อง

ฉันแน่ใจว่ามีบทเรียนมากมายใน MVC ฉันขอแนะนำให้อ่านบางอย่าง


ขอบคุณแม็ตธิว เพื่อความกระจ่างแจ้งจนถึงตอนนี้ฉันแยกการดูและรุ่นกับตัวควบคุมตามที่อ่านและแนะนำเสมอ อย่างไรก็ตามตั้งแต่เริ่มอ่านเพื่อรักษาตัวควบคุม 'ผอม' ฉันแค่สงสัยว่าสิ่งที่ควร / อาจถูกย้ายออกจากพวกเขาดูเหมือนว่ากระบวนการคิดที่นำฉันไปสู่คำถามนี้เป็นขั้นตอนที่หนึ่งหรือสองเกินไป!
Adam Westbrook

เมื่อคุณเริ่มใช้ตัวควบคุมหลายรุ่น ความต้องการของพวกเขาที่จะอ้วนนั้นชัดเจนมาก เมื่อ View เริ่มมี PHP จำนวนมากคุณจะรู้ว่าคอนโทรลเลอร์ของคุณมีความบาง เมื่อผู้ควบคุมของคุณอ้วนมาก เป็นการยากที่จะให้ตัวควบคุมอื่นทำงานในลักษณะเดียวกัน (ตัวอย่างเช่นการเพิ่มบริการ API)
เปิดใช้งานใหม่

3

ฉันพบคำถามของคุณน่าสนใจมากเพราะฉันพบปัญหาเดียวกันขณะเรียนรู้ Python เมื่อเร็ว ๆ นี้

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

MVC

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

Model-View-Controller เป็นรูปแบบการออกแบบพื้นฐานสำหรับการแยกตรรกะส่วนติดต่อผู้ใช้จากตรรกะทางธุรกิจ น่าเสียดายที่ความนิยมของรูปแบบส่งผลให้มีคำอธิบายที่ผิดพลาดจำนวนมาก โดยเฉพาะอย่างยิ่งคำว่า "คอนโทรลเลอร์" ถูกนำมาใช้เพื่อหมายถึงสิ่งต่าง ๆ ในบริบทที่แตกต่างกัน โชคดีที่การปรากฎตัวของเว็บแอปพลิเคชันได้ช่วยแก้ไขความคลุมเครือบางอย่างเนื่องจากการแยกระหว่างมุมมองและตัวควบคุมนั้นชัดเจนมาก

ในการเขียนโปรแกรมประยุกต์ใน Smalltalk-80: วิธีการใช้ Model-View-Controller (MVC) [Burbeck92] Steve Burbeck อธิบายการเปลี่ยนแปลงของ MVC สองรูปแบบ: โมเดลเชิงรับและตัวแบบที่ใช้งานอยู่

รูปแบบแฝงถูกใช้เมื่อคอนโทรลเลอร์หนึ่งควบคุมโมเดลนั้นโดยเฉพาะ คอนโทรลเลอร์ปรับเปลี่ยนโมเดลจากนั้นแจ้งมุมมองว่าโมเดลมีการเปลี่ยนแปลงและควรรีเฟรช (ดูรูปที่ 2) โมเดลในสถานการณ์นี้ไม่ขึ้นอยู่กับมุมมองและคอนโทรลเลอร์ซึ่งหมายความว่าไม่มีวิธีสำหรับโมเดลที่จะรายงานการเปลี่ยนแปลงในสถานะของมัน โปรโตคอล HTTP เป็นตัวอย่างของสิ่งนี้ ไม่มีวิธีที่ง่ายในเบราว์เซอร์เพื่อรับการอัพเดทแบบอะซิงโครนัสจากเซิร์ฟเวอร์ เบราว์เซอร์แสดงมุมมองและตอบกลับการป้อนข้อมูลของผู้ใช้ แต่ไม่พบการเปลี่ยนแปลงในข้อมูลบนเซิร์ฟเวอร์ เฉพาะเมื่อผู้ใช้ร้องขอการรีเฟรชอย่างชัดเจนเซิร์ฟเวอร์จะทำการสอบสวนเพื่อทำการเปลี่ยนแปลง

MVC - รูปแบบ Passive

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

ข้อความเต็มของบทความที่นี่


ถูกต้องและสิ่งอื่น ๆ ที่เพิ่มความสับสนคือไคลเอนต์เซิร์ฟเวอร์ซึ่ง SmallTalk MVC ดั้งเดิมไม่ได้มีไว้สำหรับ ในไคลเอนต์ - เซิร์ฟเวอร์ (เช่น javascript) มีโมเดลที่มุ่งเน้นการนำเสนอมุมมองและตัวควบคุมบนไคลเอนต์และมุมมองที่มุ่งเน้นโดเมนและตัวควบคุมบนเซิร์ฟเวอร์ นอกจากนี้บางครั้งเราต้องการให้มุมมองโดเมนมีความคงทนซึ่งหมายความว่าพารามิเตอร์มุมมองเป็นแบบจำลองของตัวเองซึ่งไม่จำเป็นต้องเป็นส่วนหนึ่งของแบบจำลองโดเมน
Erik Eidt

ขอบคุณสำหรับลิงค์ฉันรู้ว่าฉันไม่ได้คิดมากเรื่องนี้! นี่คือสิ่งที่ฉันจะออกไปก่อนที่ฉันจะใช้ความคิดสักหน่อยถ้าตราบใดที่โมเดลไม่ได้ขึ้นอยู่กับสิ่งใดมันมีความสำคัญต่อการเข้าถึงที่ / อย่างไร? ฉันยังไม่ได้ตัดสินใจว่าจะพัฒนาวิธีการต่อไปอย่างไร แต่สิ่งนี้จะช่วยได้แน่นอน
Adam Westbrook

1

อีกสิ่งที่ควรพิจารณาคือคุณดูเหมือนจะโหลดอัตโนมัติuser_modelและinvoice_modelอนุญาตให้มุมมองเข้าถึงพวกเขา เพื่อให้ทำงานได้อย่างน่าเชื่อถือคุณอาจโหลดแบบจำลองทั้งหมดของคุณโดยอัตโนมัติ(เพราะ$this->load->model()ดูผิดในมุมมองใช่ไหม ... )

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

ดูเหมือนว่า CodeIgniter ฉันพัฒนา CI มาหลายครั้งแล้วและฉันสามารถแบ่งปันจากประสบการณ์ส่วนตัวที่คุณไม่ต้องการโหลดอัตโนมัติมากกว่าที่คุณต้องการจริงๆ ลองเพิ่ม$this->output->enable_profiler(TRUE);ในคอนสตรัคเตอร์และซอที่มีออโต้โหลด (รวมถึงผู้ช่วยเช่นdatabase): คุณอาจเห็นการเปลี่ยนแปลงที่สำคัญในเวลาโหลดและการดำเนินการ แต่โดยเฉพาะอย่างยิ่งในการจัดสรรหน่วยความจำ


1
จุดที่ดีคุณพูดถูกนี่คือพื้นฐานของ CI แม้ว่าฉันจะลบไวยากรณ์บางส่วนออกเพื่อความชัดเจน ฉันเคยติดนิสัย 'autoloading' ทุกอย่างมาเป็นเวลานานและด้วยเหตุผล DRY ดูเหมือนว่าจะบ้าเหมือนกันload->modelมากในตัวควบคุมและวิธีการส่วนใหญ่ การไม่ใช้ฟังก์ชั่น autoload ที่เหมาะสมเป็นหนึ่งในสิ่งที่ฉันไม่ชอบมากที่สุดเกี่ยวกับความเข้ากันได้แบบย้อนหลังของ CI แต่เป็นการสนทนาอื่น ๆ ทั้งหมด ...
Adam Westbrook

0

คำตอบสั้น ๆ คือรูปแบบตัวอย่างโค้ดของคุณนั้นใช้งานง่าย ดูเหมือนว่านี่จะเป็นวิธี "ง่ายในใจ" ที่จะไป


ปัญหา # 1

คุณModelและViewวัตถุจะถูกรวมเข้าด้วยกันอย่างแน่นหนา

หากคุณต้องเพิ่มหรือลบวิธีการในModelนั้นคุณอาจต้องเปลี่ยนวิธีการViewตาม

พื้นฐาน MVC มาจากรูปแบบคำสั่งและผู้สังเกตการณ์ คุณต้องการ'โมเดล' อิสระที่จัดการผ่านอินเตอร์เฟส / APIที่Controllerสามารถเชื่อมต่อได้ (เช่นการมอบหมาย)

บ่อยครั้งที่นี้หมายถึงการฉีด ModelและViewกรณีเป็นและเก็บไว้เป็นสมบัติของกล่าวว่าController Controllerจากนั้นใช้วิธีการController(เช่นคำสั่ง) เป็นพื้นที่ทำงานส่งผ่านข้อมูลไปยัง View จาก Model ( หลังจาก `รุ่นได้เสร็จสิ้นการอัปเดตสถานะแอปพลิเคชัน )

ข้อมูลที่ส่งผ่าน (อาร์เรย์วัตถุ iterable, อะไรก็ตาม) ช่วยให้การมีเพศสัมพันธ์ระหว่างModelและViewกรณีหลวม หากคุณฉีดModelอินสแตนซ์เข้าไปในViewดูปัญหา # 1 ด้านบน

โปรดจำไว้ว่าViewsอาจจะเป็น HTML, JSON, ข้อความ, XML, ส่วนหัว HTTP, YAML หรือเกือบทุกอย่างต่อไปนี้วิธีการถ่ายโอนการเป็นตัวแทนของรัฐ (REST)

ดังนั้นกุญแจสำคัญในการทำความเข้าใจวิธีการจัดการความสัมพันธ์ระหว่างModelและViewsคือการเห็นความสัมพันธ์สำหรับสิ่งที่มันเป็นหนึ่งต่อหลายคน (อาจ)! นี่คือรูปแบบการสังเกตการณ์ที่ออกแบบมาเพื่อให้สำเร็จ

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

ดังนั้นถ้าคุณมีหนึ่งModelและหลาย ๆViewsที่อาจเกิดขึ้นอาการปวดหัวของการปรับปรุงทั้งหมดViews'รหัสการดำเนินงานเพราะคุณมีการเปลี่ยนแปลงบางสิ่งบางอย่างในModel'sAPI / วิธีการตอนนี้กลายเป็นเฉียบพลัน

ส่งผ่านข้อมูลไป Views , ไม่ได้กรณีของ Models

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