Yii का उचित उपयोग

प्रविष्टि


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

डिजाइन और अवधारणाओं


पाठ में, "नियंत्रक" और "मॉडल" की अवधारणाएं दो संदर्भों में दिखाई देंगी: एमवीसी और वाईआईआई, इस पर ध्यान दें। गैर-स्पष्ट स्थानों में, मैं बताऊंगा कि मैं किस संदर्भ का उपयोग करता हूं।
एक दृश्य MVC के संदर्भ में एक दृश्य है।
"देखें" व्यू फ़ोल्डर से एक फ़ाइल है।
मैं बड़े अक्षरों में पैटर्न को उजागर करूंगा।

चलो चलते हैं!



Yii एक बहुत लचीला ढांचा है। यह कुछ डेवलपर्स के लिए अपने कोड को संरचित करने के बारे में चिंता नहीं करना संभव बनाता है, जो हमेशा बगों का एक गुच्छा और जटिल रीफैक्टरिंग की ओर जाता है। हालाँकि, Yii का इससे कोई लेना-देना नहीं है - अक्सर एमवीसी सिद्धांत की एक गलतफहमी के साथ पहले से ही समस्याएं शुरू हो जाती हैं।

इसलिए, इस पोस्ट में मैं YVC के संदर्भ में MVC की मूल बातें, और इसके C और V को कवर करूंगा। एम अक्षर एक अलग जटिल विषय है जो इसके पद का हकदार है। सभी कोड उदाहरण सामान्य होंगे, लेकिन सिद्धांतों के सार को दर्शाते हैं।

MVC


एमवीसी एक उत्कृष्ट डिजाइन सिद्धांत है जो कई समस्याओं से बचने में मदद करता है। मेरी राय में, इस डिजाइन पैटर्न के बारे में आवश्यक-पर्याप्त ज्ञान एक विकिपीडिया लेख से चमकाया जा सकता है।

दुर्भाग्य से, मैंने एक से अधिक बार देखा है कि अभिव्यक्ति "Yii एक MVC फ्रेमवर्क है" को बहुत अधिक शब्दशः लिया गया था (अर्थात, M एक CModel , C एक CController , V views फ़ोल्डर से एक views ), जो हमें सिद्धांत को समझने से दूर है। । यह बहुत सारी त्रुटियां उत्पन्न करता है, उदाहरण के लिए, जब दृश्य के लिए सभी आवश्यक डेटा नियंत्रक में चुने जाते हैं, या जब व्यापार तर्क के टुकड़े नियंत्रक को स्थानांतरित किए जाते हैं।

नियंत्रक ("C") अनुप्रयोग का परिचालन स्तर है। CContrller वर्ग के साथ इसे भ्रमित न करें। CContrller पास कई जिम्मेदारियां हैं। MVC में, "नियंत्रक" की अवधारणा मुख्य रूप से CController' की एक कार्रवाई है। यदि कोई ऑपरेशन किसी ऑब्जेक्ट पर किया जाता है, तो नियंत्रक को यह जानने की आवश्यकता नहीं है कि यह ऑपरेशन कैसे किया जाए - यह कार्य "M" है। किसी ऑब्जेक्ट को प्रदर्शित करने के मामले में, उसे यह जानने की आवश्यकता नहीं है कि ऑब्जेक्ट को कैसे प्रदर्शित किया जाए - यह कार्य "V" है। वास्तव में, नियंत्रक को केवल वांछित वस्तु (ओं) को लेना चाहिए, और यह बताना चाहिए कि उन्हें क्या करना है।

मॉडल ("एम") आवेदन के व्यावसायिक तर्क का स्तर है। वाईवी में एक मॉडल की अवधारणा को एमवीसी में एक मॉडल की अवधारणा के साथ जोड़ना खतरनाक है। एक मॉडल केवल इकाई वर्ग (आमतौर पर एक CModel ) नहीं है। इसमें शामिल हैं, उदाहरण के लिए, विशेष CValidator सत्यापनकर्ता, या सेवा (यदि वे व्यावसायिक तर्क प्रदर्शित करते हैं), रिपोर्ट, और बहुत कुछ। मॉडल को इसका उपयोग करने वाले नियंत्रकों या मैपिंग के बारे में कुछ भी जानने की आवश्यकता नहीं है। इसमें केवल व्यावसायिक तर्क होते हैं और इससे अधिक कुछ नहीं।

प्रस्तुति ("वी") - प्रदर्शन स्तर। आपको इसे केवल एक php फ़ाइल के रूप में प्रदर्शित नहीं करना चाहिए (हालाँकि, एक नियम के रूप में, यह है)। उसके पास अपना, कभी-कभी बहुत जटिल, तर्क है। और अगर किसी ऑब्जेक्ट को प्रदर्शित करने के लिए हमें कुछ विशिष्ट डेटा की आवश्यकता होती है, उदाहरण के लिए, भाषाओं की सूची या कुछ और, यह स्तर उन्हें अनुरोध करना चाहिए। दुर्भाग्य से, Yii में, आप किसी भी विशेष वर्ग (जब तक कि CWidget , आदि का उपयोग नहीं करते हैं) के साथ एक दृश्य नहीं जोड़ सकते हैं, जिसमें प्रदर्शन तर्क होगा। लेकिन अपने आप को लागू करना आसान है (शायद ही कभी, लेकिन कभी-कभी बेहद उपयोगी)।

Yii स्वयं हमें इन तीनों स्तरों के लिए एक बुनियादी ढाँचा प्रदान करता है।

आम एमवीसी त्रुटियां



यहाँ कुछ गलतियाँ हैं। ये उदाहरण अत्यंत अतिरंजित हैं, लेकिन वे सार को दर्शाते हैं। एक बड़े अनुप्रयोग पैमाने पर, ये त्रुटियां भयावह समस्याओं में बढ़ती हैं।

1. मान लीजिए कि हमें उपयोगकर्ता को उसकी पोस्ट के साथ प्रदर्शित करने की आवश्यकता है। एक सामान्य क्रिया कुछ इस तरह दिखाई देती है:

  public function actionUserView($id) { $user = User::model()->findByPk($id); $posts = Post::model()->findAllByAttributes(['user_id' => $user->id]); $this->render('userWithPosts', [ 'user' => $user, 'posts' => $posts ]); } 

यह एक गलती है। नियंत्रक को यह जानने की आवश्यकता नहीं है कि उपयोगकर्ता को कैसे प्रदर्शित किया जाएगा। उसे उपयोगकर्ता को खोजना होगा, और उसे बताना होगा "इस दृश्य के साथ दिखाएं।" यहाँ हम कंट्रोलर में डिस्प्ले लॉजिक का हिस्सा निकालते हैं (अर्थात्, ज्ञान कि उसे पदों की आवश्यकता है)।

समस्या यह है कि यदि आप उदाहरण के रूप में करते हैं, तो आप कोड के पुन: उपयोग के बारे में भूल सकते हैं और व्यापक दोहराव को पकड़ सकते हैं।

जहां भी हम इस दृश्य का उपयोग करना चाहते हैं, हमें पदों की सूची को उसमें स्थानांतरित करना होगा, जिसका अर्थ है कि हर जगह हमें उन्हें अग्रिम रूप से चुनना होगा - कोड दोहराव।

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

  public function actions() { return [ 'showWithPost' => [ 'class' => UserViewAction::class, 'view' => 'withPost' ], 'showWithoutPost' => [ 'class' => UserViewAction::class, 'view' => 'withoutPost' ], 'showAnythingUserView' => [ 'class' => UserViewAction::class, 'view' => 'anythingUserView' ] ]; } 


यदि आप नियंत्रक और प्रदर्शन में हस्तक्षेप करते हैं - यह संभव नहीं है।

यह त्रुटि केवल कोड का दोहराव बनाती है। दूसरी गलती के बहुत अधिक भयावह परिणाम हैं।

2. मान लीजिए कि हमें समाचार को संग्रह में स्थानांतरित करने की आवश्यकता है। यह status फ़ील्ड सेट करके किया जाता है। कार्रवाई देखें:

  public function actionArchiveNews($id) { $news = News::model()->findByPk($id); $news->status = News::STATUS_ARCHIVE; $news->save(); } 


इस उदाहरण की त्रुटि यह है कि हम व्यापार तर्क को नियंत्रक में स्थानांतरित करते हैं। इससे कोड का पुन: उपयोग करने में असमर्थता हो जाती है (मैं नीचे क्यों बताऊंगा), लेकिन यह दूसरी समस्या की तुलना में केवल एक ट्रिफ़ल है: अगर हम संग्रह को स्थानांतरित करने के तरीके को बदलते हैं तो क्या होगा? उदाहरण के लिए, स्थिति को बदलने के बजाय, हम inArchive क्षेत्र में true असाइन करेंगे? और यह कार्रवाई आवेदन के कई स्थानों पर की जाएगी? और यह खबर नहीं है, लेकिन $ 10 मिलियन का लेनदेन है?

उदाहरण में, इन स्थानों को ढूंढना आसान है - बस STATUS_ARCHIVE स्थिर के लिए Find Usage । लेकिन अगर आपने "status = 'archive'" अनुरोध की मदद से "status = 'archive'" है "status = 'archive'" ढूंढना ज्यादा मुश्किल है, क्योंकि एक अतिरिक्त जगह की भी जरूरत है और आपको यह लाइन नहीं मिलेगी।

व्यापार तर्क हमेशा मॉडल में रहना चाहिए। यहां हमें सार में एक अलग विधि को एकल करना चाहिए, जो कि समाचार को एक संग्रह (या किसी तरह से अलग, लेकिन व्यावसायिक तर्क की परत में) में अनुवाद करता है। यह उदाहरण अत्यंत अतिरंजित है, कुछ इसी तरह की गलती करते हैं।

लेकिन पहली त्रुटि से उदाहरण में, यह समस्या भी है, बहुत कम स्पष्ट:

  $posts = Post::model()->findAllByAttributes(['user_id' => $user->id]); 


यह जानना कि Post और User कैसे जुड़े हैं, यह भी आवेदन का व्यावसायिक तर्क है। इसलिए, यह स्ट्रिंग नियंत्रक या दृश्य में नहीं होनी चाहिए। यहां, सही समाधान User लिए रिले, या Post लिए स्कोप का उपयोग करना होगा:

  //  $posts = $user->posts; //  $posts = Post::model()->forUser($user)->findAll(); 


मैजिक कैविएशन


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

आइए देखें कि आप उपर्युक्त उदाहरणों से UserViewAction कैसे लागू कर सकते हैं:

 class UserViewAction extends CAction { /** * @var string view for render */ public $view; /** * @param $id string user id * @throws CHttpException */ public function run($id) { $user = User::model()->findByPk($id); if(!$user) throw new CHttpException(HttpResponse::STATUS_NOT_FOUND, "User not found"); $this->controller->render($this->view, $user); } } 


अब हम एक्शन कॉन्फिग में कोई भी दृश्य सेट कर सकते हैं। यह कोड पुन: प्रयोज्य का एक अच्छा उदाहरण है, लेकिन यह सही नहीं है। हम कोड को संशोधित करते हैं ताकि यह न केवल User मॉडल के साथ काम करे, बल्कि किसी भी CActiveRecord वंशज के साथ:

 class ModelViewAction extends CAction { /** * @var string model class for action */ public $modelClass; /** * @var string view for render */ public $view; /** * @param $id string model id * @throws CHttpException */ public function run($id) { $model = CActiveRecord::model($this->modelClass)->findByPk($id); if(!$model) throw new CHttpException(HttpResponse::STATUS_NOT_FOUND, "{$this->modelClass} not found"); $this->controller->render($this->view, $model); } } 


वास्तव में, हमने हार्ड-कोडित User वर्ग को विन्यास योग्य संपत्ति $modelClass साथ बदल दिया $modelClass नतीजतन, हमें एक ऐसी कार्रवाई मिली, जिसका उपयोग किसी भी दृश्य का उपयोग करके किसी भी मॉडल को प्रदर्शित करने के लिए किया जा सकता है।

पहली नज़र में, यह लचीला नहीं है, लेकिन सामान्य सिद्धांत को समझने के लिए यह सिर्फ एक उदाहरण है। PHP एक बहुत ही लचीली भाषा है, और यह हमें रचनात्मकता के लिए जगह देती है:



इसी तरह के कार्यों को लगभग किसी भी कार्रवाई के लिए लिखा जा सकता है: सीआरयूडी, सत्यापन, व्यवसाय संचालन, संबंधित वस्तुओं के साथ काम करना, आदि।

वास्तव में, यह 30-40 ऐसे कार्यों को लिखने के लिए पर्याप्त है, जो नियंत्रक कोड का 90% (स्वाभाविक रूप से, यदि आप मॉडल, दृश्य और नियंत्रक को अलग करते हैं) को कवर करेंगे। सबसे सुखद प्लस, निश्चित रूप से, बग की संख्या में कमी है, क्योंकि परीक्षण लिखने के लिए बहुत कम कोड + आसान है + जब सैकड़ों स्थानों पर कार्रवाई का उपयोग किया जाता है तो वे बहुत तेजी से पॉप होते हैं।

अद्यतन के लिए क्रिया उदाहरण


मैं आपको कुछ और उदाहरण देता हूं। यहाँ अद्यतन पर कार्रवाई है

 class ARUpdateAction extends CAction { /** * @var string update view */ public $view = 'update'; /** * @var string model class */ public $modelClass; /** * @var string model scenario */ public $modelScenario = 'update'; /** * @var string|array url for return after success update */ public $returnUrl; /** * @param $id string|int|array model id * @throws CHttpException */ public function run($id) { $model = CActiveRecord::model($this->modelClass)->findByPk($id); if($model === null) throw new CHttpException(HttpResponse::STATUS_NOT_FOUND, "{$this->modelClass} not found"); $model->setScenario($this->modelScenario); if($data = Yii::app()->request->getDataForModel($model)) { $model->setAttributes($data); if($model->save()) Yii::app()->request->redirect($this->returnUrl); else Yii::app()->response->setStatus(HttpResponse::STATUS_UNVALIDATE_DATA); } $this->controller->render($this->view, $model); } } 


मैंने CRUD gii से उसका कोड लिया, और इसे थोड़ा सा काम किया। पुन: प्रयोज्य के लिए $modelClass संपत्ति को पेश करने के अलावा, यह कई अन्य महत्वपूर्ण बिंदुओं द्वारा पूरक है:



स्वाभाविक रूप से, यह नियंत्रक वास्तविक की तुलना में बहुत सरल है:



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

मेरी राय में, कलाकारों को निष्पादित करने के लिए सबसे अच्छी जगह Yii::app()->request->getDataForModel($model) विधि है। फ़ील्ड प्रकार प्राप्त करने के कई तरीके हैं, मेरे लिए सबसे आकर्षक हैं:



किसी भी स्थिति में, हम IAttributesTypes इंटरफ़ेस बना सकते हैं जहाँ getAttributesTypes विधि को परिभाषित करने के लिए, और HttpRequest::getDataForModel को public getDataForModel(IAttributesTypes $model) रूप में घोषित करें। और प्रत्येक वर्ग को यह निर्धारित करने दें कि वह किस प्रकार इंटरफ़ेस को लागू करता है।

सूची के लिए क्रिया उदाहरण


शायद यह सबसे जटिल उदाहरण है, मैं इसे कक्षाओं के बीच जिम्मेदारियों के विभाजन को दिखाने के लिए दूंगा:

 class MongoListAction extends CAction { /** * @var string view for action */ public $view = 'list'; /** * @var array|EMongoCriteria predefined criteria */ public $criteria = []; /** * @var string model class */ public $modelClass; /** * @var string scenario for models */ public $modelScenario = 'list'; /** * @var array dataProvider config */ public $dataProviderConfig = []; /** * @var string dataProvuder class */ public $dataProviderClass = 'EMongoDocumentDataProvider'; /** * @var string filter class */ public $filterClass; /** * @var string filter scenario */ public $filterScenario = 'search'; /** * */ public function run() { //            /** @var $filter EMongoDocument */ $filterClass = $this->filterClass ? $this->filterClass : $this->modelClass; $filter = new $filterClass($this->filterScenario); $filter->unsetAttributes(); if($data = Yii::app()->request->getDataForModel($filter)) $filter->setAttributes($data); $filter->search(); //    ,            //        $filter->getDbCriteria()->mergeWith($this->criteria); //    .     yiimongodbsuite     //    (   - ) /** @var $dataProvider EMongoDocumentDataProvider */ $dataProviderClass = $this->dataProviderClass; $dataProvider = new $dataProviderClass($filter, $this->dataProviderConfig); //     .    ,         self::setScenario($dataProvider->getData(), $this->modelScenario); //   $this->controller->render($this->view, [ 'dataProvider' => $dataProvider, 'filter' => $filter ]); } } 


और इसके उपयोग का एक उदाहरण, निष्क्रिय उपयोगकर्ताओं को प्रदर्शित करना:

  public function actions() { return [ 'unactive' => [ 'class' => MongoListAction::class, 'modelClass' => User::class, 'criteria' => ['scope' => User::SCOPE_UNACTIVE], 'dataProviderClass' => UserDataProvider::class ], ]; } 


कार्य का तर्क सरल है: हम फ़िल्टरिंग मानदंड प्राप्त करते हैं, एक डेटा प्रदाता और प्रदर्शन करते हैं।

फिल्टर:

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

लेकिन फ़िल्टर का एकमात्र उद्देश्य नमूनाकरण के लिए मापदंड प्राप्त करना है। उदाहरण में फ़िल्टर का कार्यान्वयन पूरी तरह से सफल नहीं है। तथ्य यह है कि फ़िल्टर वर्ग ( $filterClass का उपयोग करके) सेट करने की क्षमता के बावजूद, यह अभी भी इसका मतलब है कि यह एक Model । यह कॉल के तरीकों से $filter->unsetAttributes() और $filter->search() , जो मॉडल में निहित हैं।

केवल एक चीज की जरूरत है कि इनपुट प्राप्त किया जाए और इसे EMongoCriteria को दिया EMongoCriteria । बस इस इंटरफ़ेस को लागू करने की आवश्यकता है:

 interface IMongoDataFilter { /** * @param array $data * @return mixed */ public function setFilterAttributes(array $data); /** * @return EMongoCriteria */ public function getFilterCriteria(); } 


मैंने विधियों के नामों में एक Filter डाला ताकि कार्यान्वयन कक्षा में setAttributes और getDbCriteria की घोषणा पर निर्भर न हो। एक फिल्टर के रूप में मॉडल का उपयोग करने के लिए, एक सरल लक्षण लिखना सबसे अच्छा है:

 trait MongoDataFilterTrait { /** * @param array $data * @return mixed */ public function setFilterAttributes(array $data) { $this->unsetAttributes(); $this->setAttrubites($data); } /** * @return EMongoCriteria */ public function getFilterCriteria() { if($this->validate()) $this->search(); return $this->getDbCriteria(); } } 


इंटरफ़ेस का उपयोग करने के लिए कार्रवाई को IMongoDataFilter , हम IMongoDataFilter इंटरफ़ेस को लागू करने वाले किसी भी वर्ग का उपयोग कर सकते हैं, अगर यह एक मॉडल या कुछ और है तो कोई बात नहीं।

डेटा प्रदाता:
सभी जो आवश्यक डेटा के नमूने के तर्क की चिंता करते हैं - डेटा प्रदाता इसके लिए जिम्मेदार है। कभी-कभी इसमें काफी जटिल तर्क भी होते हैं, इसलिए यह $dataProviderClass का उपयोग करके अपनी कक्षा को कॉन्फ़िगर करने के लिए समझ में आता है।

उदाहरण के लिए, yiimongodbsuite एक्सटेंशन के मामले में, जिसमें रिले का वर्णन करने का कोई तरीका नहीं है, हमें उन्हें मैन्युअल रूप से लोड करने की आवश्यकता है। (वास्तव में, इस एक्सटेंशन को जोड़ना बेहतर है, लेकिन उदाहरण अच्छा है)

लोडिंग लॉजिक को कुछ REPOSITORY वर्ग में भी रखा जा सकता है, लेकिन यदि रिले के साथ डेटा को वापस करने के लिए किसी विशेष डेटा प्रदाता की जिम्मेदारी है, तो डेटा प्रदाता को REPOSITORY लोडर विधि को कॉल करना होगा। मैं नीचे डेटा प्रदाताओं की पुन: प्रयोज्यता के बारे में लिखूंगा।

कार्रवाई का उपयोग करने के लिए मानदंड:


मैं एक बार फिर सबसे "बग-जनरेट" समस्या पर ध्यान आकर्षित करना चाहता हूं:

प्रदर्शित करने की आवश्यकता का ज्ञान (इस मामले में, निष्क्रिय उपयोगकर्ताओं) नियंत्रक का ज्ञान है। लेकिन एक निष्क्रिय उपयोगकर्ता द्वारा निर्धारित मानदंडों के बारे में ज्ञान मॉडल का ज्ञान है।

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

वास्तव में, गुंजाइश विशिष्टताओं का एक "टुकड़ा" है। आप आसानी से विनिर्देशों के साथ काम करने के लिए कार्रवाई को फिर से लिख सकते हैं। हालांकि, यह केवल जटिल अनुप्रयोगों में मांग में है। ज्यादातर मामलों में, गुंजाइश सही समाधान है।

नियंत्रक और दृश्य के पृथक्करण के बारे में:


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


मॉडल क्लास के साथ एक्शन बंडल


ज्यादातर मामलों में, नियंत्रक (अर्थात् CController ) एक वर्ग के साथ काम करता है (उदाहरण के लिए, User साथ)। इस मामले में, मॉडल वर्ग को निर्दिष्ट करने के लिए प्रत्येक कार्रवाई की कोई विशेष आवश्यकता नहीं है - नियंत्रक में इसे निर्दिष्ट करना आसान है। लेकिन कार्रवाई में, इस अवसर को छोड़ दिया जाना चाहिए।
इस स्थिति को हल करने के लिए, एक्शन में आपको $ मॉडलक्लास के लिए गेट्टर और सेटर रजिस्टर करना होगा। गटर इस तरह दिखेगा:

 public function getModelClass() { if($this->_modelClass === null) { if($this->controller instanceof IModelController && ($modelClass = $this->controller->getModelClass())) $this->_modelClass = $modelClass; else throw new CException('Model class must be setted'); } return $this->_modelClass; } 


सिद्धांत रूप में, आप मानक CRUD के लिए एक नियंत्रक रिक्त भी बना सकते हैं:

 /** * Class BaseARController */ abstract class BaseARController extends Controller implements IModelController { /** * @return string model class */ public abstract function getModelClass(); /** * @return array default actions */ public function actions() { return [ 'list' => ARListAction::class, 'view' => ARViewAction::class, 'create' => ARCreateAction::class, 'update' => ARUpdateAction::class, 'delete' => ARDeleteAction::class, ]; } } 


अब हम कई लाइनों में एक CRUD नियंत्रक कर सकते हैं:

 class UserController extends BaseARController { /** * @return string model class */ public function getModelClass() { return User::class; } } 


नियंत्रक सारांश


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

विचार


Yii इसे बनाने के लिए हमें अवसंरचना प्रदान करता है। ये CWidget , CGridColumn , CGridView , Menu और बहुत कुछ हैं। इस सभी का उपयोग, विस्तार, फिर से लिखने से डरो मत।

दस्तावेज़ीकरण पढ़कर यह सब सीखना आसान है, लेकिन मैं कुछ और स्पष्ट करना चाहता हूं।

मैंने ऊपर उल्लेख किया है कि नियंत्रक को यह जानने की आवश्यकता नहीं है कि इकाई कैसे प्रदर्शित होगी, इसलिए इसमें दृश्य के लिए डेटा का चयन करने के लिए कोड नहीं होना चाहिए। , — . Yii , $this .

लेकिन ऐसा है नहीं। . ? .

: , . , .

, -. ( Client ), ( Address ) ( Order ). .


, , , .

, «». CController::render ( $this ). — , . . , Yii , «» , , CController::$clips .

- — . , .

«» MVC .

. , .
. Clients $client->adress $client->getOrders()
. .


, . , .

, — :
 $this->widget('zii.widgets.CDetailView', [ 'model' => $client, 'attributes' => [ 'fio', 'age', 'address.city', 'adress.street' ] ]); foreach($client->orders as $order) { $this->widget('zii.widgets.CDetailView', [ 'model' => $order, 'attributes' => [ 'date', 'sum', 'status', ] ]); } 


, , :

  $this->renderPartial('_client', $client); $this->renderPartial('_address', $client->address); $this->renderPartial('_orders', $client->orders); 


, — , .
. , Order — -. EMongoDocumentDataProvider :

  $this->widget('zii.widgets.grid.CGridView', [ 'dataProvider' => new EMongoDocumentDataProvider((new Order())->forClient($client)), 'columns' => ['date', 'sum', 'status'] ]); 


- . : , Client User ( forClient ), .

, , , — - — ? , — , , .

— , . — , — .


.

, Client , Address OrderMongoDB . , $client->address . , .

100 , $client->address — 100 — . .

AR , , . MongoDB (, yiimongodbsuite ) .

-. , , .

- :
 class ClientsDataProvider extends EMongoDocumentDataProvider { /** * @param bool $refresh * @return array */ public function getData($refresh=false) { if($this->_data === null || $refresh) { $this->_data=$this->fetchData(); //   id  $addressIds = []; foreach($this->_data as $client) $addressIds[] = $client->addressId; //   $adresses = Address::model()->findAllByPk($addressIds); ...          .... } return $this->_data; } } 


2 :



— , .

, - :

 class ClientsDataProvider extends EMongoDocumentDataProvider { /** * @param bool $refresh * @return array */ public function getData($refresh=false) { if($this->_data === null || $refresh) { $this->_data=$this->fetchData(); Client::loadAddresses($this->_data); } return $this->_data; } } 


.

«»:
Client , Address . , Client. . , , , — -. , . Client , . .


-


- ( ). 2 : , , .

- -, .
.

$this


, . , CController , . . ( , ), (- - ) - . यह सही नहीं है। .

निष्कर्ष


— . , .

— . , , , « ».

— — , . — , .

, . . — .

अंतभाषण


, « », . . — , .

PHP . Yii . — . PHP Yii — .

, MVC, - , , , , — PHP. Yii. , . — .

.

सभी के लिए अच्छा है।

Source: https://habr.com/ru/post/In211739/


All Articles