कुछ दिन पहले, मैं एक फ्लैकी टेस्ट को ठीक कर रहा था, और यह पता चला कि मुझे अपने कारखाने के भीतर कुछ अद्वितीय और वैध मूल्यों की आवश्यकता है। Laravel FakerPHP को लपेटता है, जिसे हम आमतौर पर fake()
हेल्पर के माध्यम से एक्सेस करते हैं। FakerPHP valid()
और unique()
जैसे संशोधकों के साथ आता है, लेकिन आप एक समय में केवल एक का उपयोग कर सकते हैं, इसलिए आप fake()->unique()->valid()
नहीं कर सकते हैं, जो वास्तव में मुझे चाहिए था।
इससे मुझे लगा कि अगर हम अपना खुद का संशोधक बनाना चाहें तो क्या होगा? उदाहरण के लिए, uniqueAndValid()
या कोई अन्य संशोधक। हम फ्रेमवर्क का विस्तार कैसे कर सकते हैं?
मैं अपनी विचारधारा को त्याग दूँगा।
किसी भी अति-इंजीनियर्ड समाधान में कूदने से पहले, मैं हमेशा यह जांचना चाहता हूं कि क्या कोई सरल विकल्प है और मैं किससे निपट रहा हूं। तो, आइए fake()
हेल्पर पर एक नज़र डालें:
function fake($locale = null) { if (app()->bound('config')) { $locale ??= app('config')->get('app.faker_locale'); } $locale ??= 'en_US'; $abstract = \Faker\Generator::class.':'.$locale; if (! app()->bound($abstract)) { app()->singleton($abstract, fn () => \Faker\Factory::create($locale)); } return app()->make($abstract); }
कोड को पढ़ते हुए, हम देख सकते हैं कि Laravel एक सिंगलटन को कंटेनर से बांधता है। हालाँकि, अगर हम एब्सट्रैक्ट का निरीक्षण करते हैं, तो यह एक नियमित क्लास है जो किसी भी इंटरफ़ेस को लागू नहीं करता है, और ऑब्जेक्ट को फ़ैक्टरी के माध्यम से बनाया जाता है। यह चीजों को जटिल बनाता है। क्यों?
क्योंकि अगर यह एक इंटरफ़ेस होता, तो हम बस एक नया क्लास बना सकते थे जो बेस \Faker\Generator
क्लास को एक्सटेंड करता, कुछ नई सुविधाएँ जोड़ता और इसे कंटेनर से फिर से जोड़ता। लेकिन हमारे पास यह सुविधा नहीं है।
इसमें एक फैक्ट्री शामिल है। इसका मतलब है कि यह कोई सरल इंस्टेंशिएशन नहीं है; इसमें कुछ तर्क चल रहे हैं। इस मामले में, फैक्ट्री कुछ प्रदाता (फोन नंबर, टेक्स्ट, यूजरएजेंट, आदि) जोड़ती है। इसलिए, भले ही हम रीबाइंड करने की कोशिश करें, हमें फैक्ट्री का उपयोग करना होगा, जो मूल \Faker\Generator
लौटाएगा।
समाधान 🤔? कोई सोच सकता है, "हमें अपना खुद का कारखाना बनाने से क्या रोक रहा है जो बिंदु 1 में उल्लिखित नए जनरेटर को लौटाता है?" खैर, कुछ भी नहीं, हम ऐसा कर सकते हैं, लेकिन हम नहीं करेंगे! हम कई कारणों से एक फ्रेमवर्क का उपयोग करते हैं, उनमें से एक अपडेट है। अगर FakerPHP कोई नया प्रदाता जोड़ता है या कोई बड़ा अपग्रेड करता है तो क्या होगा? Laravel कोड को समायोजित करेगा, और जिन लोगों ने कोई बदलाव नहीं किया है, उन्हें कुछ भी नज़र नहीं आएगा। हालाँकि, हम बाहर रह जाएँगे, और हमारा कोड टूट भी सकता है (सबसे अधिक संभावना है)। तो, हाँ, हम इतना आगे नहीं जाना चाहते।
अब जबकि हमने बुनियादी विकल्पों का पता लगा लिया है, हम डिज़ाइन पैटर्न जैसे अधिक उन्नत विकल्पों के बारे में सोचना शुरू कर सकते हैं। हमें सटीक कार्यान्वयन की आवश्यकता नहीं है, बस हमारी समस्या से परिचित कुछ चाहिए। यही कारण है कि मैं हमेशा कहता हूं कि उन्हें जानना अच्छा है। इस मामले में, हम पुराने को बनाए रखते हुए नई सुविधाएँ जोड़कर Generator
क्लास को "सजा" सकते हैं। अच्छा लगता है? आइए देखें कैसे!
सबसे पहले, आइए एक नया क्लास बनाएं, FakerGenerator
:
<?php namespace App\Support; use Closure; use Faker\Generator; use Illuminate\Support\Traits\ForwardsCalls; class FakerGenerator { use ForwardsCalls; public function __construct(private readonly Generator $generator) { } public function uniqueAndValid(Closure $validator = null): UniqueAndValidGenerator { return new UniqueAndValidGenerator($this->generator, $validator); } public function __call($method, $parameters): mixed { return $this->forwardCallTo($this->generator, $method, $parameters); } }
यह हमारा "डेकोरेटर" (कुछ इस तरह) होगा। यह एक सरल वर्ग है जो बेस Generator
निर्भरता के रूप में अपेक्षा करता है और एक नया संशोधक, uniqueAndValid()
प्रस्तुत करता है। यह Laravel से ForwardsCalls
विशेषता का भी उपयोग करता है, जो इसे बेस ऑब्जेक्ट पर कॉल को प्रॉक्सी करने की अनुमति देता है।
इस विशेषता में दो विधियाँ हैं:
forwardCallTo
औरforwardDecoratedCallTo
। जब आप सजाए गए ऑब्जेक्ट पर विधियों को श्रृंखलाबद्ध करना चाहते हैं तो बाद वाले का उपयोग करें। हमारे मामले में, हमारे पास हमेशा एक ही कॉल होगी।
हमें UniqueAndValidGenerator
को भी लागू करना होगा, जो कि कस्टम संशोधक है, लेकिन यह लेख का मुद्दा नहीं है। यदि आप कार्यान्वयन में रुचि रखते हैं, तो यह वर्ग मूल रूप से ValidGenerator और UniqueGenerator का मिश्रण है जो FakerPHP के साथ आता है, आप इसे यहाँ पा सकते हैं।
अब, AppServiceProvider
में फ्रेमवर्क का विस्तार करते हैं:
<?php namespace App\Providers; use Closure; use Faker\Generator; use App\Support\FakerGenerator; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { public function register(): void { $this->app->extend( $this->fakerAbstractName(), fn (Generator $base) => new FakerGenerator($base) ); } private function fakerAbstractName(): string { // This is important, it matches the name bound by the fake() helper return Generator::class . ':' . app('config')->get('app.faker_locale'); } }
extend()
विधि यह जाँचती है कि दिए गए नाम से मेल खाने वाला कोई सार कंटेनर से बंधा हुआ है या नहीं। यदि ऐसा है, तो यह क्लोजर के परिणाम के साथ इसके मान को ओवरराइड करता है, एक नज़र डालें:
// Laravel: src/Illuminate/Container/Container.php public function extend($abstract, Closure $closure) { $abstract = $this->getAlias($abstract); if (isset($this->instances[$abstract])) { // You are interested here $this->instances[$abstract] = $closure($this->instances[$abstract], $this); $this->rebound($abstract); } else { $this->extenders[$abstract][] = $closure; if ($this->resolved($abstract)) { $this->rebound($abstract); } } }
इसीलिए हमने fakerAbstractName()
विधि को परिभाषित किया है, जो कंटेनर में fake()
हेल्पर बाइंड के समान नाम उत्पन्न करता है।
यदि आपने उपरोक्त कोड को मिस कर दिया है तो पुनः जांच लें, मैंने एक टिप्पणी छोड़ दी है।
अब, जब भी हम fake()
कॉल करेंगे, FakerGenerator
का एक इंस्टेंस वापस आ जाएगा, और हमारे पास हमारे द्वारा शुरू किए गए कस्टम मॉडिफायर तक पहुंच होगी। हर बार जब हम किसी ऐसे कॉल को इनवोक करेंगे जो FakerGenerator
क्लास पर मौजूद नहीं है, __call()
ट्रिगर हो जाएगा, और यह forwardCallTo()
विधि का उपयोग करके बेस Generator
को प्रॉक्सी करेगा।
बस! मैं अंततः fake()->uniqueAndValid()->randomElement()
कर सकता हूँ, और यह बहुत बढ़िया काम करता है!
निष्कर्ष निकालने से पहले, मैं यह बताना चाहता हूँ कि यह शुद्ध डेकोरेटर पैटर्न नहीं है। हालाँकि, पैटर्न पवित्र ग्रंथ नहीं हैं; उन्हें अपनी ज़रूरतों के हिसाब से बदलें और समस्या का समाधान करें।
फ़्रेमवर्क अविश्वसनीय रूप से सहायक होते हैं, और Laravel में बहुत सारी अंतर्निहित विशेषताएं होती हैं। हालाँकि, वे आपकी परियोजनाओं में सभी एज केस को कवर नहीं कर सकते हैं, और कभी-कभी, आप एक मृत अंत पर पहुँच सकते हैं। जब ऐसा होता है, तो आप हमेशा फ़्रेमवर्क का विस्तार कर सकते हैं। हमने देखा है कि यह कितना सरल है, और मुझे आशा है कि आपने मुख्य विचार को समझ लिया है, जो केवल इस फ़ेकर उदाहरण से परे लागू होता है।
हमेशा सरल तरीके से शुरुआत करें और समस्या का सबसे सरल समाधान खोजें। जटिलता तब आएगी जब इसकी आवश्यकता होगी, इसलिए यदि बुनियादी विरासत काम करती है, तो डेकोरेटर या किसी अन्य चीज़ को लागू करने की कोई आवश्यकता नहीं है। जब आप फ्रेमवर्क का विस्तार करते हैं, तो सुनिश्चित करें कि आप बहुत दूर न जाएं, जहां नुकसान लाभ से अधिक हो। आप फ्रेमवर्क के किसी हिस्से को अपने दम पर बनाए रखना नहीं चाहेंगे।