MVC में एक मॉडल को कैसे संरचित किया जाना चाहिए? [बन्द है]


551

मुझे एमवीसी ढांचे पर समझ आ रही है और मुझे अक्सर आश्चर्य होता है कि मॉडल में कितना कोड होना चाहिए। मेरे पास एक डेटा एक्सेस क्लास है जिसमें इस तरह के तरीके हैं:

public function CheckUsername($connection, $username)
{
    try
    {
        $data = array();
        $data['Username'] = $username;

        //// SQL
        $sql = "SELECT Username FROM" . $this->usersTableName . " WHERE Username = :Username";

        //// Execute statement
        return $this->ExecuteObject($connection, $sql, $data);
    }
    catch(Exception $e)
    {
        throw $e;
    }
}

मेरा मॉडल एक इकाई वर्ग है जो डेटाबेस तालिका में मैप किया जाता है।

क्या मॉडल ऑब्जेक्ट में सभी डेटाबेस मैप किए गए गुणों के साथ-साथ ऊपर दिए गए कोड हैं या क्या उस कोड को अलग करना ठीक है जो वास्तव में डेटाबेस काम करता है?

क्या मैं चार परतें खत्म कर दूंगा?


133
आप उन्हें फिर से फेंकने के लिए अपवाद क्यों पकड़ रहे हैं?
बेली पार्कर

9
@ एलियास वान ओटगेम: आप इस बिंदु से चूक गए। इस मामले में उन्हें पकड़ना व्यर्थ है।
कारोली होरवाथ

4
@ एलियास वान ओटगेम: हुह? यदि यह पुनर्खरीद के साथ काम करता है, तो इसका मतलब है कि एक ऊपरी परत अपवाद को पकड़ती है। लेकिन अगर वहाँ एक है, तो यह उस निरर्थक पुनर्खरीद के बिना इसे पकड़ लिया होगा ... (यदि आप अभी भी इसे प्राप्त नहीं करते हैं, तो कृपया एक छोटे परीक्षण कोड का मजाक
उड़ाएं

3
@ एलियास वान ओटेगेम: मुझे पता नहीं है कि आप किस बारे में बात कर रहे हैं, एक विशिष्ट परत पर एक अपवाद को संभालना नहीं है इसका मतलब यह नहीं है कि यह ऐप को रोक देगा। कृपया निर्माण करें (या अधिक सटीक: निर्माण करने में विफल) एक कोड उदाहरण है जहां उस पुनर्विचार आवश्यक है। आइए, इस अपमानजनक बातचीत को रोकें
Kroly Horvath

6
@drrcknlsn: यह एक मान्य तर्क है, लेकिन उस मामले में कम से कम उस अपवाद को पकड़ लें जिसे आप फेंकने की उम्मीद करते हैं, जेनेरिक के Exceptionपास बहुत अधिक दस्तावेज़ीकरण मूल्य नहीं है। व्यक्तिगत रूप से अगर मैं उस सड़क पर उतर गया तो मैं PHPDoc @exception, या कुछ इसी तरह के तंत्र को चुनूंगा , इसलिए यह उत्पन्न दस्तावेज में दिखाई देता है।
कारोली होर्वाथ

जवाबों:


903

अस्वीकरण: निम्नलिखित विवरण है कि मैं PHP आधारित वेब अनुप्रयोगों के संदर्भ में एमवीसी जैसे पैटर्न को कैसे समझता हूं। सभी बाहरी लिंक जो सामग्री में उपयोग किए जाते हैं, वे शब्दों और अवधारणाओं को समझाने के लिए हैं, कि इस विषय पर मेरी अपनी विश्वसनीयता का अनुमान लगाने के लिए।

पहली बात जो मुझे स्पष्ट करनी चाहिए वह है: मॉडल एक परत है

दूसरा: शास्त्रीय एमवीसी और वेब विकास में हम क्या उपयोग करते हैं, के बीच अंतर है । यहाँ मैंने एक पुराना उत्तर लिखा है, जो संक्षेप में बताता है कि वे कैसे भिन्न हैं।

एक मॉडल क्या नहीं है:

मॉडल एक वर्ग या कोई एकल वस्तु नहीं है। यह एक बहुत ही सामान्य गलती है (मैंने भी किया था, हालांकि मूल उत्तर तब लिखा गया था जब मैंने अन्यथा सीखना शुरू किया था) , क्योंकि अधिकांश रूपरेखा इस गलत धारणा को बनाए रखती हैं।

न तो यह ऑब्जेक्ट-रिलेशनल मैपिंग तकनीक (ओआरएम) है और न ही डेटाबेस टेबलों का अमूर्तन है। जो कोई भी आपको अन्यथा बताता है, वह सबसे अधिक संभावना है कि वह किसी अन्य ब्रांड-ओआरएम या पूरे ढांचे को 'बेचने' की कोशिश कर रहा है ।

एक मॉडल क्या है:

उचित MVC अनुकूलन में, एम सभी डोमेन व्यापार तर्क होता है और मॉडल परत है ज्यादातर संरचनाओं के तीन प्रकार से बनाया गया:

  • डोमेन ऑब्जेक्ट

    एक डोमेन ऑब्जेक्ट विशुद्ध रूप से डोमेन जानकारी का एक तार्किक कंटेनर है; यह आमतौर पर समस्या क्षेत्र में तार्किक इकाई का प्रतिनिधित्व करता है। आमतौर पर व्यावसायिक तर्क के रूप में संदर्भित किया जाता है

    यह वह जगह होगी जहां आप चालान भेजने से पहले डेटा को मान्य करने या किसी ऑर्डर की कुल लागत की गणना करने के तरीके को परिभाषित करेंगे। उसी समय, डोमेन ऑब्जेक्ट्स स्टोरेज से पूरी तरह से अनजान हैं - न तो जहां से (एसक्यूएल डेटाबेस, रीस्ट एपीआई, टेक्स्ट फाइल, आदि) और न ही यहां तक ​​कि अगर वे सहेजे गए या पुनर्प्राप्त किए जाते हैं।

  • डेटा मैपर

    ये ऑब्जेक्ट केवल संग्रहण के लिए जिम्मेदार हैं। यदि आप किसी डेटाबेस में जानकारी संग्रहीत करते हैं, तो यह वह जगह होगी जहां SQL रहता है। या हो सकता है कि आप डेटा स्टोर करने के लिए एक XML फ़ाइल का उपयोग करें, और आपका डेटा मैपर XML फ़ाइलों से और उससे पार्स कर रहा हो।

  • सेवाएं

    आप उन्हें "उच्च स्तरीय डोमेन ऑब्जेक्ट्स" के रूप में सोच सकते हैं, लेकिन व्यावसायिक तर्क के बजाय, डोमेन ऑब्जेक्ट्स और मैपर्स के बीच बातचीत के लिए सेवाएँ जिम्मेदार हैं । ये संरचनाएं डोमेन व्यवसाय तर्क के साथ बातचीत के लिए एक "सार्वजनिक" इंटरफ़ेस बनाती हैं। आप उनसे बच सकते हैं, लेकिन कुछ डोमेन लॉजिक को कंट्रोलर्स में लीक करने के दंड पर ।

    ACL कार्यान्वयन प्रश्न में इस विषय से संबंधित उत्तर है - यह उपयोगी हो सकता है।

मॉडल लेयर और MVC ट्रायड के अन्य भागों के बीच संचार सेवा के माध्यम से ही होना चाहिए । स्पष्ट पृथक्करण के कुछ अतिरिक्त लाभ हैं:

  • यह एकल जिम्मेदारी सिद्धांत (एसआरपी) को लागू करने में मदद करता है
  • तर्क में बदलाव होने पर अतिरिक्त। विग्लिंग रूम ’प्रदान करता है
  • नियंत्रक को यथासंभव सरल रखता है
  • एक स्पष्ट खाका देता है, अगर आपको कभी बाहरी एपीआई की आवश्यकता होती है

 

एक मॉडल के साथ बातचीत कैसे करें?

आवश्यक शर्तें: घड़ी व्याख्यान "ग्लोबल स्टेट एंड सिंग्लेट्सन" और "डोंट लुक फॉर थिंग्स!" स्वच्छ संहिता वार्ता से।

सेवा उदाहरणों के लिए पहुँच प्राप्त करना

इन सेवाओं तक पहुँचने के लिए व्यू और कंट्रोलर इंस्टेंस (जिसे आप कॉल कर सकते हैं: "UI लेयर") दोनों के लिए, दो सामान्य दृष्टिकोण हैं:

  1. आप अपने विचारों और नियंत्रकों के निर्माणकर्ताओं में आवश्यक सेवाओं को सीधे इंजेक्ट कर सकते हैं, अधिमानतः डि कंटेनर का उपयोग करके।
  2. अपने सभी विचारों और नियंत्रकों के लिए अनिवार्य निर्भरता के रूप में सेवाओं के लिए एक कारखाने का उपयोग करना।

जैसा कि आप संदेह कर सकते हैं, डि कंटेनर एक बहुत अधिक सुरुचिपूर्ण समाधान है (जबकि शुरुआत के लिए सबसे आसान नहीं है)। दो पुस्तकालयों, कि मैं इस कार्यक्षमता के लिए विचार करने की सलाह देता हूं Syfmony के स्टैंडअलोन निर्भरता Injection घटक या Auryn होगा

एक कारखाने और DI कंटेनर का उपयोग करने वाले दोनों समाधान आपको दिए गए अनुरोध-प्रतिक्रिया चक्र के लिए चयनित नियंत्रक और दृश्य के बीच साझा किए जाने वाले विभिन्न सर्वरों के उदाहरणों को साझा करने देंगे।

मॉडल के राज्य का परिवर्तन

अब जब आप नियंत्रकों में मॉडल परत तक पहुंच सकते हैं, तो आपको वास्तव में उनका उपयोग करना शुरू करना होगा:

public function postLogin(Request $request)
{
    $email = $request->get('email');
    $identity = $this->identification->findIdentityByEmailAddress($email);
    $this->identification->loginWithPassword(
        $identity,
        $request->get('password')
    );
}

आपके नियंत्रकों के पास एक बहुत ही स्पष्ट कार्य है: उपयोगकर्ता इनपुट लें और इस इनपुट के आधार पर, व्यापार तर्क की वर्तमान स्थिति को बदलें। इस उदाहरण में जिन राज्यों के बीच परिवर्तन किया गया है वे हैं "अनाम उपयोगकर्ता" और "लॉग इन उपयोगकर्ता"।

उपयोगकर्ता के इनपुट को मान्य करने के लिए नियंत्रक जिम्मेदार नहीं है, क्योंकि यह व्यावसायिक नियमों का हिस्सा है और नियंत्रक निश्चित रूप से एसक्यूएल प्रश्नों को कॉल नहीं कर रहा है, जैसे कि आप यहां या यहां क्या देखेंगे (कृपया उन पर नफरत न करें, वे गुमराह हैं, बुराई नहीं)।

उपयोगकर्ता को राज्य-परिवर्तन दिखा रहा है।

ठीक है, उपयोगकर्ता लॉग इन (या विफल) है। अब क्या? कहा उपयोगकर्ता अभी भी इससे अनजान है। इसलिए आपको वास्तव में प्रतिक्रिया देने की जरूरत है और यह एक दृश्य की जिम्मेदारी है।

public function postLogin()
{
    $path = '/login';
    if ($this->identification->isUserLoggedIn()) {
        $path = '/dashboard';
    }
    return new RedirectResponse($path); 
}

इस मामले में, दृश्य ने मॉडल परत की वर्तमान स्थिति के आधार पर दो संभावित प्रतिक्रियाओं में से एक का उत्पादन किया। एक अलग उपयोग-मामले के लिए आपको "लेख के वर्तमान चयनित" जैसे कुछ के आधार पर प्रस्तुत करने के लिए अलग-अलग टेम्पलेट चुनने का दृश्य होगा।

प्रस्तुति परत वास्तव में काफी विस्तृत हो सकती है, जैसा कि यहां वर्णित है: PHP में एमवीसी व्यूज़ को समझना

लेकिन मैं सिर्फ एक REST API बना रहा हूँ!

बेशक, ऐसी स्थितियां हैं, जब यह एक ओवरकिल है।

एमवीसी, सिर्फ़ सेपरेशन थ्योरी के अलगाव का एक ठोस समाधान है । MVC उपयोगकर्ता इंटरफ़ेस को व्यावसायिक तर्क से अलग करता है, और UI में इसे उपयोगकर्ता इनपुट और प्रस्तुति से अलग करता है। यह महत्वपूर्ण है। जबकि अक्सर लोग इसे "त्रय" के रूप में वर्णित करते हैं, यह वास्तव में तीन स्वतंत्र भागों से नहीं बना है। संरचना इस तरह से अधिक है:

एमवीसी अलगाव

इसका मतलब यह है कि, जब आपकी प्रस्तुति परत का तर्क किसी के पास नहीं है, तो व्यावहारिक दृष्टिकोण उन्हें एकल परत के रूप में रखना है। यह मॉडल परत के कुछ पहलुओं को भी काफी हद तक सरल कर सकता है।

इस दृष्टिकोण का उपयोग करके लॉगिन उदाहरण (एक एपीआई के लिए) के रूप में लिखा जा सकता है:

public function postLogin(Request $request)
{
    $email = $request->get('email');
    $data = [
        'status' => 'ok',
    ];
    try {
        $identity = $this->identification->findIdentityByEmailAddress($email);
        $token = $this->identification->loginWithPassword(
            $identity,
            $request->get('password')
        );
    } catch (FailedIdentification $exception) {
        $data = [
            'status' => 'error',
            'message' => 'Login failed!',
        ]
    }

    return new JsonResponse($data);
}

हालांकि यह स्थायी नहीं है, जब आपके पास प्रतिक्रिया निकाय को प्रस्तुत करने के लिए जटिल तर्क होते हैं, तो यह सरलीकरण अधिक तुच्छ परिदृश्यों के लिए बहुत उपयोगी है। लेकिन चेतावनी दी जाए , यह दृष्टिकोण एक बुरा सपना बन जाएगा, जब जटिल प्रस्तुति तर्क के साथ बड़े कोडबेस में उपयोग करने का प्रयास किया जाएगा।

 

मॉडल का निर्माण कैसे करें?

चूंकि एक भी "मॉडल" वर्ग नहीं है (जैसा कि ऊपर बताया गया है), आप वास्तव में "मॉडल का निर्माण नहीं करते हैं"। इसके बजाय आप सेवाओं को बनाने से शुरू करते हैं, जो कुछ निश्चित तरीकों का प्रदर्शन करने में सक्षम हैं। और फिर डोमेन ऑब्जेक्ट्स और मैपर्स को लागू करें ।

सेवा पद्धति का एक उदाहरण:

ऊपर के दोनों तरीकों में पहचान सेवा के लिए यह लॉगिन विधि थी। यह वास्तव में कैसा दिखेगा। मैं लाइब्रेरी से उसी कार्यक्षमता का थोड़ा संशोधित संस्करण उपयोग कर रहा हूं , जो मैंने लिखा है .. क्योंकि मैं आलसी हूं:

public function loginWithPassword(Identity $identity, string $password): string
{
    if ($identity->matchPassword($password) === false) {
        $this->logWrongPasswordNotice($identity, [
            'email' => $identity->getEmailAddress(),
            'key' => $password, // this is the wrong password
        ]);

        throw new PasswordMismatch;
    }

    $identity->setPassword($password);
    $this->updateIdentityOnUse($identity);
    $cookie = $this->createCookieIdentity($identity);

    $this->logger->info('login successful', [
        'input' => [
            'email' => $identity->getEmailAddress(),
        ],
        'user' => [
            'account' => $identity->getAccountId(),
            'identity' => $identity->getId(),
        ],
    ]);

    return $cookie->getToken();
}

जैसा कि आप देख सकते हैं, अमूर्तता के इस स्तर पर, इस बात का कोई संकेत नहीं है कि डेटा कहां से लाया गया था। यह एक डेटाबेस हो सकता है, लेकिन यह भी परीक्षण के प्रयोजनों के लिए सिर्फ एक नकली वस्तु हो सकता है। यहां तक ​​कि डेटा मैपर्स, जो वास्तव में इसके लिए उपयोग किए जाते हैं, privateइस सेवा के तरीकों में दूर छिपे हुए हैं ।

private function changeIdentityStatus(Entity\Identity $identity, int $status)
{
    $identity->setStatus($status);
    $identity->setLastUsed(time());
    $mapper = $this->mapperFactory->create(Mapper\Identity::class);
    $mapper->store($identity);
}

मैपर बनाने के तरीके

दृढ़ता के अमूर्त को लागू करने के लिए, सबसे लचीले दृष्टिकोण पर कस्टम डेटा मैपर्स बनाना है ।

मैपर आरेख

प्रेषक: पोइया पुस्तक

व्यवहार में उन्हें विशिष्ट वर्गों या सुपरक्लास के साथ बातचीत के लिए लागू किया जाता है। कहते हैं कि आपके पास Customerऔर Adminआपके कोड में (दोनों एक Userसुपरक्लास से विरासत में ) हैं। दोनों का अलग-अलग मिलान मैपर होगा, क्योंकि उनमें अलग-अलग क्षेत्र होते हैं। लेकिन आप साझा और आमतौर पर उपयोग किए जाने वाले कार्यों के साथ भी समाप्त हो जाएंगे। उदाहरण के लिए: "अंतिम बार ऑनलाइन देखा गया" समय अपडेट करना । और मौजूदा मैपर को अधिक जटिल बनाने के बजाय, अधिक व्यावहारिक दृष्टिकोण के लिए एक सामान्य "उपयोगकर्ता मैपर" होना चाहिए, जो केवल उस टाइमस्टैम्प को अपडेट करता है।

कुछ अतिरिक्त टिप्पणियां:

  1. डेटाबेस टेबल और मॉडल

    हालांकि कभी-कभी एक डेटाबेस टेबल, डोमेन ऑब्जेक्ट और मैपर के बीच 1: 1: 1 संबंध होता है , बड़ी परियोजनाओं में यह आपकी अपेक्षा से कम आम हो सकता है:

    • किसी एकल डोमेन ऑब्जेक्ट द्वारा उपयोग की जाने वाली जानकारी को विभिन्न तालिकाओं से मैप किया जा सकता है, जबकि ऑब्जेक्ट का डेटाबेस में कोई दृढ़ता नहीं है।

      उदाहरण: यदि आप एक मासिक रिपोर्ट उत्पन्न कर रहे हैं। यह विभिन्न तालिकाओं से जानकारी एकत्र करेगा, लेकिन MonthlyReportडेटाबेस में कोई जादुई तालिका नहीं है।

    • एक एकल मैपर कई तालिकाओं को प्रभावित कर सकता है।

      उदाहरण: जब आप Userऑब्जेक्ट से डेटा स्टोर कर रहे हैं , तो इस डोमेन ऑब्जेक्ट में अन्य डोमेन ऑब्जेक्ट - Groupइंस्टेंस के संग्रह हो सकते हैं । यदि आप उन्हें बदल देते हैं और डेटा संग्रहीत करते हैं User, तो डेटा मैपर को कई तालिकाओं में प्रविष्टियों को अपडेट और / या सम्मिलित करना होगा।

    • किसी एकल डोमेन ऑब्जेक्ट का डेटा एक से अधिक तालिका में संग्रहीत किया जाता है।

      उदाहरण: बड़ी प्रणालियों में (सोचें: एक मध्यम आकार का सामाजिक नेटवर्क), यह उपयोगकर्ता प्रमाणीकरण डेटा और अक्सर-एक्सेस किए गए डेटा को सामग्री के बड़े हिस्से से अलग करके संग्रहीत करने के लिए व्यावहारिक हो सकता है, जिसकी शायद ही कभी आवश्यकता होती है। उस स्थिति में आपके पास अभी भी एक एकल Userवर्ग हो सकता है , लेकिन इसमें मौजूद जानकारी इस बात पर निर्भर करेगी कि क्या पूरा विवरण प्राप्त किया गया था।

    • प्रत्येक डोमेन ऑब्जेक्ट के लिए एक से अधिक मैपर हो सकते हैं

      उदाहरण: आपके पास सार्वजनिक-सामना और प्रबंधन सॉफ़्टवेयर दोनों के लिए साझा कोडबेड के साथ एक समाचार साइट है। लेकिन, जबकि दोनों इंटरफेस एक ही Articleवर्ग का उपयोग करते हैं , प्रबंधन को इसमें बहुत अधिक जानकारी की आवश्यकता होती है। इस मामले में आपके पास दो अलग-अलग मैपर्स होंगे: "आंतरिक" और "बाहरी"। प्रत्येक अलग-अलग प्रश्न करते हैं, या यहां तक ​​कि अलग-अलग डेटाबेस (जैसे मास्टर या दास) का उपयोग करते हैं।

  2. एक दृश्य एक टेम्पलेट नहीं है

    एमवीसी में उदाहरण देखें (यदि आप पैटर्न के एमवीपी भिन्नता का उपयोग नहीं कर रहे हैं) प्रस्तुति तर्क के लिए जिम्मेदार हैं। इसका मतलब यह है कि प्रत्येक दृश्य आमतौर पर कम से कम कुछ टेम्पलेट्स को टटोलता है। यह मॉडल लेयर से डेटा प्राप्त करता है और फिर, प्राप्त जानकारी के आधार पर, एक टेम्पलेट चुनता है और मान सेट करता है।

    इससे आपको जो लाभ होता है, उसमें से एक लाभ पुन: प्रयोज्य है। यदि आप एक ListViewवर्ग बनाते हैं , तो, अच्छी तरह से लिखे गए कोड के साथ, आप उसी वर्ग को एक लेख के नीचे उपयोगकर्ता-सूची और टिप्पणियों की प्रस्तुति सौंप सकते हैं। क्योंकि वे दोनों एक ही प्रस्तुति तर्क हैं। आप बस टेम्पलेट्स स्विच करें।

    आप मूल PHP टेम्प्लेट का उपयोग कर सकते हैं या कुछ तृतीय-पक्ष टेंपलेटिंग इंजन का उपयोग कर सकते हैं । कुछ तृतीय-पक्ष लाइब्रेरी भी हो सकती हैं, जो व्यू इंस्टेंस को पूरी तरह से बदलने में सक्षम हैं ।

  3. उत्तर के पुराने संस्करण के बारे में क्या?

    एकमात्र बड़ा बदलाव यह है कि पुराने संस्करण में जिसे मॉडल कहा जाता है , वह वास्तव में एक सेवा है । बाकी "पुस्तकालय सादृश्य" बहुत अच्छी तरह से रखता है।

    एकमात्र दोष जो मैं देखता हूं कि यह वास्तव में अजीब पुस्तकालय होगा, क्योंकि यह आपको पुस्तक से जानकारी लौटाएगा, लेकिन आपको पुस्तक को खुद को छूने नहीं देगा, क्योंकि अन्यथा अमूर्तता "लीक" करना शुरू कर देगी। मुझे अधिक उपयुक्त सादृश्य के बारे में सोचना पड़ सकता है।

  4. दृश्य और नियंत्रक उदाहरणों के बीच क्या संबंध है ?

    MVC संरचना दो परतों से बना है: ui और मॉडल। UI परत में मुख्य संरचनाएं दृश्य और नियंत्रक हैं।

    जब आप MVC डिज़ाइन पैटर्न का उपयोग करने वाली वेबसाइटों के साथ काम कर रहे हैं, तो सबसे अच्छा तरीका यह है कि विचारों और नियंत्रकों के बीच 1: 1 संबंध हो। प्रत्येक दृश्य आपकी वेबसाइट में एक पूरे पृष्ठ का प्रतिनिधित्व करता है और इसमें उस विशेष दृश्य के लिए आने वाले सभी अनुरोधों को संभालने के लिए एक समर्पित नियंत्रक है।

    उदाहरण के लिए, एक खुले लेख का प्रतिनिधित्व करने के लिए, आपके पास \Application\Controller\Documentऔर होगा \Application\View\Document। इसमें UI परत के लिए सभी मुख्य कार्यक्षमता शामिल होगी, जब यह लेखों से निपटने की बात आती है (निश्चित रूप से आपके पास कुछ XHR घटक हो सकते हैं जो सीधे लेख से संबंधित नहीं हैं)


4
@Rinzler, आप देखेंगे कि उस लिंक में कहीं भी, मॉडल के बारे में कुछ भी नहीं कहा गया है (एक टिप्पणी को छोड़कर)। यह केवल "ऑब्जेक्ट-ओरिएंटेड इंटरफ़ेस टू डेटाबेस टेबल" है । यदि आप इसे मॉडल जैसी चीज़ में ढालने की कोशिश करते हैं, तो आप SRP और LSP का उल्लंघन करते हैं ।
tereško

8
@hafichuk केवल स्थितियों, जब यह उचित है कि ActiveRecord पैटर्न को नियत करना प्रोटोटाइप के लिए है। जब आप उस कोड को लिखना शुरू करते हैं जो उत्पादन के लिए होता है, तो यह एक विरोधी पैटर्न बन जाता है, क्योंकि यह भंडारण और व्यावसायिक तर्क को मिलाता है। और चूंकि मॉडल लेयर अन्य MVC भागों से पूरी तरह से अनजान है। यह मूल पैटर्न पर भिन्नता के आधार पर नहीं बदलता है । एमवीवीएम का उपयोग करते समय भी। कोई "कई मॉडल" नहीं हैं और वे किसी भी चीज़ के लिए मैप नहीं किए जाते हैं। मॉडल एक परत है।
tereško

3
लघु संस्करण - मॉडल डेटा संरचनाएं हैं
एडी बी

9
खैर यह देखते हुए कि उन्होंने एमवीसी का आविष्कार किया लेख में कुछ योग्यता हो सकती है।
एडी बी

3
... या यहां तक ​​कि सिर्फ कार्यों का एक सेट। MVC को OOP शैली में लागू करने की आवश्यकता नहीं है, हालांकि यह ज्यादातर उसी तरह से लागू किया जाता है। सबसे महत्वपूर्ण बात यह है कि परतों को अलग करना और सही डेटा और नियंत्रण प्रवाह स्थापित करना है
hek2mgl

37

व्यवसाय तर्क जो सब कुछ एक मॉडल में है, चाहे वह डेटाबेस क्वेरी, गणना, एक आरईएसटी कॉल, आदि हो।

आप मॉडल में डेटा का उपयोग स्वयं कर सकते हैं, एमवीसी पैटर्न आपको ऐसा करने से प्रतिबंधित नहीं करता है। आप इसे सेवाओं, मैपर्स और क्या नहीं के साथ चीनी कोट कर सकते हैं, लेकिन एक मॉडल की वास्तविक परिभाषा एक परत है जो व्यापार तर्क को संभालती है, अधिक कुछ नहीं, कुछ भी कम नहीं। यदि आप चाहते हैं तो यह एक वर्ग, एक फ़ंक्शन, या गज़िल वस्तुओं के साथ एक पूर्ण मॉड्यूल हो सकता है।

हमेशा एक अलग वस्तु रखना आसान होता है जो वास्तव में डेटाबेस प्रश्नों को सीधे मॉडल में निष्पादित करने के बजाय निष्पादित करता है: यह विशेष रूप से तब काम में आएगा जब यूनिट परीक्षण (आपके मॉडल में मॉक डेटाबेस निर्भरता को इंजेक्ट करने की सहजता के कारण):

class Database {
   protected $_conn;

   public function __construct($connection) {
       $this->_conn = $connection;
   }

   public function ExecuteObject($sql, $data) {
       // stuff
   }
}

abstract class Model {
   protected $_db;

   public function __construct(Database $db) {
       $this->_db = $db;
   }
}

class User extends Model {
   public function CheckUsername($username) {
       // ...
       $sql = "SELECT Username FROM" . $this->usersTableName . " WHERE ...";
       return $this->_db->ExecuteObject($sql, $data);
   }
}

$db = new Database($conn);
$model = new User($db);
$model->CheckUsername('foo');

इसके अलावा, PHP में, आपको शायद ही कभी अपवादों को पकड़ने / पुनर्विचार करने की आवश्यकता होती है, क्योंकि बैकट्रेस संरक्षित है, खासकर आपके उदाहरण जैसे मामले में। बस अपवाद को फेंक दिया जाए और नियंत्रक के बजाय इसे पकड़ लें।


मेरी संरचना बहुत समान है, मुझे लगता है कि मैं इसे थोड़ा और अलग करता हूं। कारण मैं कनेक्शन के आसपास से गुजर रहा था क्योंकि मुझे लेन-देन में हिस्सा चलाने की आवश्यकता थी। मैं एक उपयोगकर्ता जोड़ना चाहता था और फिर उपयोगकर्ता को एक भूमिका में जोड़ना चाहता था, लेकिन यदि कोई विफल हुआ तो भूमिका वापस करें। एक ही रास्ता है कि मैं हल कर सकता हूं कि कनेक्शन पास करना था।
डायटपिक्सल

10
-1: यह पूरी तरह से गलत होने के लिए भी होता है। मॉडल एक तालिका के लिए एक अमूर्त नहीं है।
tereško

1
Userवर्ग मूल रूप से मॉडल फैली हुई है, लेकिन एक वस्तु itsn't। उपयोगकर्ता को एक ऑब्जेक्ट होना चाहिए और जैसे गुण हैं: आईडी, नाम ... आप Userकक्षा को तैनात कर रहे हैं एक सहायक है।
टॉमस्वायर

1
मुझे लगता है कि आप एमवीसी को समझते हैं लेकिन यह नहीं समझते कि ओओपी क्या है। इस परिदृश्य में, जैसा कि मैंने कहा, Userएक वस्तु के लिए खड़ा है, और इसमें उपयोगकर्ता के गुण होने चाहिए, न कि तरीके जैसे CheckUsername, यदि आप नई Userवस्तु बनाना चाहते हैं तो आपको क्या करना चाहिए ? new User($db)
टॉमसॉयर

@TomSawyer OOP का मतलब यह नहीं है कि वस्तुओं के लिए गुण होना आवश्यक है। आप जो वर्णन कर रहे हैं, वह एक डिज़ाइन पैटर्न है, जो प्रश्न के लिए अप्रासंगिक है या उस प्रश्न का उत्तर है। OOP एक भाषा मॉडल है, न कि एक डिज़ाइन पैटर्न।
netcoder

20

वेब- "एमवीसी" में आप जो चाहें कर सकते हैं।

मूल अवधारणा (1) ने मॉडल को व्यावसायिक तर्क के रूप में वर्णित किया। इसे एप्लिकेशन स्थिति का प्रतिनिधित्व करना चाहिए और कुछ डेटा संगति को लागू करना चाहिए। उस दृष्टिकोण को अक्सर "वसा मॉडल" के रूप में वर्णित किया जाता है।

अधिकांश PHP फ्रेमवर्क एक अधिक उथले दृष्टिकोण का पालन करते हैं, जहां मॉडल सिर्फ एक डेटाबेस इंटरफ़ेस है। लेकिन बहुत कम से कम इन मॉडलों को अभी भी आने वाले डेटा और संबंधों को मान्य करना चाहिए।

किसी भी तरह से, यदि आप SQL सामान या डेटाबेस कॉल को किसी अन्य परत में अलग करते हैं तो आप बहुत दूर नहीं हैं। इस तरह से आपको केवल वास्तविक डेटा / व्यवहार के साथ खुद को चिंता करने की आवश्यकता है, वास्तविक भंडारण एपीआई के साथ नहीं। (हालांकि यह अति करने के लिए अनुचित नहीं है। यदि आप आगे डिज़ाइन नहीं किए गए थे तो आप कभी भी डेटाबेस बैकएंड को फ़िलेस्टोरेज से बदलने में सक्षम नहीं होंगे।)


8
लिंक अमान्य है (404)
Kyslik


6

अधिकांशतः अधिकतर अनुप्रयोगों में डेटा, डिस्प्ले और प्रोसेसिंग पार्ट होता है और हम बस उन सभी अक्षरों को डालते हैं M, Vऔर C

मॉडल ( M) -> गुण है कि आवेदन की स्थिति रखती है और यह Vऔर के बारे में किसी भी बात को पता नहीं है C

देखें ( V) -> आवेदन के लिए प्रदर्शित करने का प्रारूप है और केवल इस बारे में जानता है कि इस पर मॉडल कैसे-कैसे पचता है और इसके बारे में परेशान नहीं करता है C

नियंत्रक ( C) ----> प्रसंस्करण आवेदन के हिस्से है और एम और वी के बीच तारों के रूप में कार्य और यह दोनों पर निर्भर करता है M, Vके विपरीत MऔरV

कुल मिलाकर प्रत्येक के बीच चिंता का विषय है। भविष्य में किसी भी परिवर्तन या संवर्द्धन को बहुत आसानी से जोड़ा जा सकता है।


0

मेरे मामले में मेरे पास एक डेटाबेस वर्ग है जो सभी प्रत्यक्ष डेटाबेस इंटरैक्शन जैसे कि क्वेरी करना, लाना और इस तरह संभालना है। इसलिए अगर मुझे अपने डेटाबेस को MySQL से PostgreSQL में बदलना पड़ा तो कोई समस्या नहीं होगी। इसलिए उस अतिरिक्त परत को जोड़ना उपयोगी हो सकता है।

प्रत्येक तालिका का अपना वर्ग हो सकता है और उसके विशिष्ट तरीके हो सकते हैं, लेकिन वास्तव में डेटा प्राप्त करने के लिए, यह डेटाबेस वर्ग को संभालने देता है:

फ़ाइल Database.php

class Database {
    private static $connection;
    private static $current_query;
    ...

    public static function query($sql) {
        if (!self::$connection){
            self::open_connection();
        }
        self::$current_query = $sql;
        $result = mysql_query($sql,self::$connection);

        if (!$result){
            self::close_connection();
            // throw custom error
            // The query failed for some reason. here is query :: self::$current_query
            $error = new Error(2,"There is an Error in the query.\n<b>Query:</b>\n{$sql}\n");
            $error->handleError();
        }
        return $result;
    }
 ....

    public static function find_by_sql($sql){
        if (!is_string($sql))
            return false;

        $result_set = self::query($sql);
        $obj_arr = array();
        while ($row = self::fetch_array($result_set))
        {
            $obj_arr[] = self::instantiate($row);
        }
        return $obj_arr;
    }
}

टेबल ऑब्जेक्ट क्लासएल

class DomainPeer extends Database {

    public static function getDomainInfoList() {
        $sql = 'SELECT ';
        $sql .='d.`id`,';
        $sql .='d.`name`,';
        $sql .='d.`shortName`,';
        $sql .='d.`created_at`,';
        $sql .='d.`updated_at`,';
        $sql .='count(q.id) as queries ';
        $sql .='FROM `domains` d ';
        $sql .='LEFT JOIN queries q on q.domainId = d.id ';
        $sql .='GROUP BY d.id';
        return self::find_by_sql($sql);
    }

    ....
}

मुझे आशा है कि यह उदाहरण आपको एक अच्छी संरचना बनाने में मदद करता है।


12
"तो अगर मुझे अपने डेटाबेस को MySQL से PostgreSQL में बदलना पड़ा तो कोई समस्या नहीं होगी।" उपरोक्त कोड के साथ Uhhhmmm आपको कुछ भी बदलने में भारी समस्या होगी।
PeeHaa

मुझे लगता है कि मेरा उत्तर संपादित होने के बाद कम और कम समझ में आता है, और जैसे-जैसे समय बीतता है। लेकिन यह यहाँ रहना चाहिए
Ibu

2
Databaseउदाहरण में एक वर्ग नहीं है। यह कार्यों के लिए सिर्फ एक आवरण है। इसके अलावा, आप एक वस्तु के बिना "टेबल ऑब्जेक्ट क्लास" कैसे कर सकते हैं?
tereško

2
@ tereško मैंने आपके कई पोस्ट पढ़े हैं और वे बहुत अच्छे हैं। लेकिन, मुझे अध्ययन करने के लिए कहीं भी कोई पूर्ण ढांचा नहीं मिला। क्या आप एक को जानते हैं कि "यह सही है"? या कम से कम एक ऐसा है जो आपको और कुछ अन्य लोगों को एसओ पर पसंद करता है? धन्यवाद।
जॉनी

मुझे देर हो सकती है, लेकिन मैं यह बताना चाहूंगा कि पीडीओ भविष्य में बदलाव की सुविधा के लिए डीबी 'लेयर' बनाने के मुद्दे को हल करता है।
मैथ्यू गोलार्ट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.