نویسنده: حمید حق دوست

  • کار با بیش از یک دیتابیس در لاراول ۵

    بعضی وقتا نیازه که شما با بیش از یک دیتابیس در ارتباط باشید در لاراول ، مثلا ممکنه برای هر وبلاگی که توی سیستم وبلاگ دهی شما ایجاد میشه یک دیتابیس درست کنید(البته فک نکنم تو این مورد ، این کار معقول باشه) اونجا نیاز هست که در اولین قدم توی فایل config/database.php  کانکشن مربوط به این کار رو ایجاد کنید. بصورت پیشفرض ۴ کانکشن در لاراول ۵ تعریف شده که میتونید شما هم کانکشن دیگه ای برای استفاده تعریف کنید.

    تعریف کانکشن برای اتصال به بیش از یک دیتابیس

    'mysql2' => [
                'driver'    => 'mysql',
                'host'      => env('DB_EXT_HOST', 'localhost'),
                'database'  => env('DB_EXT_DATABASE', 'db2'),
                'username'  => env('DB_EXT_USERNAME', 'username'),
                'password'  => env('DB_EXT_PASSWORD', 'password'),
                'charset'   => 'utf8',
                'collation' => 'utf8_unicode_ci',
                'prefix'    => '',
                'strict'    => false,
            ],

    توی کد بالا ما کانکشن mysql2  رو تعریف کردیم. دقت داشته باشید که کد بالا باید درون connectios تعریف کنید. همچنین توی ریشه اصلی پروژه و توی فایل .env  هم کانکشن باید مشخص شه… اون فایل رو ادیت کنید و مقادیر دلخواهتون رو اضافه کنید :

    DB_EXT_HOST=localhost
    DB_EXT_DATABASE=db2
    DB_EXT_USERNAME=username
    DB_EXT_PASSWORD=password

    حالا کانکشن های شما آماده هستند و میتونید به روشی که در ادامه میگم از اونها استفاده کنید.

    تغییر کانکشن پیشفرض در لاراول

    برای اینکه در کل پروژه کانکشن پیشفرض رو تغییر بدیم ، در فایل config/database.php  مقدار ‘default’ => ‘mysql’,  رو به ‘default’ => ‘mysql2’,  تغییر میدیم. در این صورت در کل پروژه شما mysql2 بعنوان کانکشن دیفالت استفاده خواهد شد.

    استفاده از یک کانکشن در یک مدل خاص

    اما گاهی لازمه که در یک مدل از یک کانکشن خاص استفاده بشه ، برای انجام این کار به روش زیر عمل میکنیم :

    <?php
    class SomeModel extends Eloquent {
     
        protected $connection = 'mysql2';
     
    }

    از این پس تمام اتصالاتی که با مدل بالا به دیتابیس وصل میشه با کانکشن mysql2 خواهد بود.

    تغییر کانکشن در کنترلر

    شاید بخواید جزئی تر شید و بخواید درون یک کنترلر خاص کانکشن رو عوض کنید . برای اینکار به راحتی از روش زیر اشتفاده کنید :

    <?php
     
    class SomeController extends BaseController {
     
        public function someMethod()
        {
            $someModel = new SomeModel;
     
            $someModel->setConnection('mysql_2');
     
            $something = $someModel->find(1);
     
            return $something;
        }
     
    }

    همونطور که میبینید با استفاده از متد setConnection()  این کار شدنیه…

    و استفاده در Query Builder :

    برای استفاده از کانکشن دیگه توی کوئوری بیلدر هم بصورت زیر عمل میکنیم :

    	
    $users = DB::connection('mysql2')->select(...);

    همونطور که میبینید لاراول بصورت خیلی شیک و تمیز کار شما رو راه میندازه و نیاز به اضافه کاری نیست.

    امیدوارم به دردتون خورده باشم 🙂

  • آموزش و کاربرد استفاده IOC container در Laravel

    امروز بحث رو اندکی تخصصی تر میکنیم و میریم سمت container قدرتمند لاراول. یکی از مشخصه هایی که لاراول رو از سایر فریمورک های PHP جدا میکنه ، همین IOC هست. Dependency injection  یا DI یا تزریق نیازمندیها یک موضوع مهم در همه زبانهای شئ گراست. اینکه شما سایر کلاسها رو در کلاس خودتون استفاده کنید خیلی مهمه.

    اما IOC کانتینر چیست؟

    برای اینکه از یک کلاس در کدمون استفاده کنیم باید از اون نمونه بگیریم. یه چیزی مثل کد زیر :

    $myInstance = new MyClass();
    
    $myInstance->method();

    حتما بارها شده که مقدار Request $request رو بعنوان ورودی به یک تابع در کنترلر میفرستید. سوالی که پیش میاد اینه که بدون نمونه گرفتن از کلاس Request چطور PHP اونو میشناسه؟ اینجاست که کاربرد IOC مشخص میشه. لاراول با کانتینرش شرایطی رو مهیا کرده که شما به راحتی با آبجکت ها کار کنید و ازشون نمونه بگیرید. لاراول وقتی کدی مثل FOO $foo رو میبینه دنبال کلاس FOO میگرده که از اون نمونه بگیره به شرطی که کلاس FOO به کانتینر معرفی شده باشه قبلا. این خیلی خوبه. شما بدون اینکه از کلاس نمونه بگیرید نمونه گرفته میشه. بعضی وقتا پیش میاد که شما میخواید یک ریپازیتوری بنویسید که دیگران هم بتونن ازش استفاده کنن. اینجا نیاز هست که کلاس خودتون رو به لاراول برای نمونه گیری معرفی کنید و شما باید بصورت دستی کلاس رو به IOC بچسبونید یا به اصطلاح bind کنید.

    نحوه ی bind یک کلاس به IOC در لاراول

    ساده ترین روش بایند کردن کلاس به ioc استفاده از فساد App هست که باهاش میتونید هر جایی در برنامه یه کلاس رو به کانتینر بایند کنید. (هر چند بهترین جا برای بایند کردن استفاده از service provider ها هست)

    App::bind('Hamid\Repositories\User\UserRepository', function($app)
    {
        return new EloquentUserRepository( new User );
    });

    در کد بالا EloquentUserRepository رو چسبوندیم به کانتینر و از این به بعد لاراول کلاس EloquentUserRepository رو میشناسه همه جا و resolve اش میکنه.

    حتما در مورد فساد ها در لاراول اطلاعاتی دارید. همه ی کلاسهایی که در فساد ها ازشون استفاده میکنیم قبلا به کانتینر بایند شدن. مثلا در زیر بخشی فایل Illuminate/Support/Facades/Auth.php رو میبینید که مربوط به فساد Auth هست که هر روز ازش استفاده میکنیم:

    class Auth extends Facade
    {
        /**
         * Get the registered name of the component.
         *
         * @return string
         */
        protected static function getFacadeAccessor()
        {
            return 'auth';
        }

    همونطور که میبینید متد استاتیک getFacadeAccessor داره یه رشته رو برمیگردونه که در فایل Illuminate/Auth/AuthServiceProvider.php بصورت زیر به کانتینر بایند شده:

    $this->app->singleton('auth', function ($app) {
         return new AuthManager($app);
    });

  • مفهوم و کاربرد اینترفیس – interface در PHP

    قبلا در مورد کلاسهای abstract در PHP مطلبی نوشتم ، کلاسهای انتزاعی یا abstract کلاسهایی هستند که قابل نمونه گیری نیستند و میتونیم درون کلاسهای انتزاعی متد هایی بنویسیم و در کلاسهایی که از اون مشتق شدن ازش استفاده کنیم. اینترفیس ها دقیقا مثل کلاسهای انتزاعی هستند ، با این تفاوت که در اینترفیس ها هیچ متدی نمیتونه دارای بدنه باشه. اولش خیلی عجیب به نظر میرسه… وقتی متدها بدنه نداشته باشن ، به چه دردی میخودن؟

    به خاطر داشته باشید که شما با تعریف یک متد در کلاس abstract نویسنده کلاسهای دیگه (که از این کلاس مشتق شدن) رو وادار نمیکردید که متدهای کلاس پدر رو بازنویسی کنه. ولی در اینترفیس ها اینطور هست، یعنی هر کلاسی که یک اینرفیس رو implement کنه ، باید تمام متدهای کلاس پدر رو بازنویسی کنه.

    مثلا شما میخواید کلاسی بسازید که یک تگ html رو رندر کنه و برای این کار نیاز هست که به تگ ، id و class داده بشه. شما میاید یه اینترفیس میسازید و درون اون دو تابع setID  و setClass  رو مینویسید. از اون به بعد هر کسی که کلاس خودش رو از اینترفیس شما مشتق کرد مجبوره که دو تا تابع رو بنویسه و این از خطای برنامه جلوگیری میکنه. شاید در پروژه های کوچیک زیاد به درد نخوره ، ولی پروژه هایی که تعداد زیادی توسعه دهنده روشون کار میکنند ، برای پیاده سازی design pattern ها در اکثر اوقات باید از اینترفیس ها استفاده کنن.

    نحوه ی تعریف interface ها در PHP

    برای تعریف اینترفیس ها از کلمه کلیدی interface استفاده میکنیم :

    interface abc
    {
        public function xyz($b);
    }

    در مثال بالا کلاسها رو مجبور میکنیم که متد xyz رو بنویسن. برای ارث بردن یا به اصطلاح پیاده کردن یک اینترفیس از کلمه کلیدی implements استفاده میکنیم:

    class test implements abc
    {
       public function xyz($b)
       {
           // The body of your function...
       }
    }

    نکته قابل توجه اینه که تمام متد ها و پراپرتی هایی که درون یک اینترفیس تعریف میشن باید بصورت عمومی یا public  تعریف بشن ، در غیر اینصورت با اررور مواجه میشیم.

    یک نکته دیگه اینکه یک کلاس میتونه بیش از یک اینترفیس رو implement کنه و همچنین یک اینترفیس میتونه از چند اینترفیس دیگه مشتق بشه.

    نوشتن کدهای Open-Closed با استفاده از اینترفیس ها

    ما در مفاهیم SOLID همیشه تشویق میشیم که کدهامون رو open-closed بنویسیم. ینی کدهامون برای تغییر بسته باشن و جوری کد بزنیم که کلاسها رو هی نیایم دستکاری کنیم همینطور باید کد رو جوری بنویسیم که به راحتی extendable باشه. دلیل این هم شاید برمیگرده به اینکه ریفکتور کردن کدهای اینجوری سخته و باعث بروز مشکلات میشه. من در ادامه یه مثال میارم و روی اون توضیح میدم:

    <?php
    
    interface DriverContract
    {
        public function connect();
        public function run();
        public function get();
    }
    
    
    class MySQL implements DriverContract
    {
        public function connect()
        {
            echo "Connecting to MySQL...\n";
        }
        public function run()
        {
            echo "Running MySQL...\n";
        }
        public function get()
        {
            echo "Getting MySQL...\n";
        }
    }
    
    class MongoDB implements DriverContract
    {
        public function connect()
        {
            echo "Connecting to MongoDB...\n";
        }
        public function run()
        {
            echo "Running MongoDB...\n";
        }
        public function get()
        {
            echo "Getting MongoDB...\n";
        }
    }
    
    
    function consume(DriverContract $driver)
    {
        $driver->connect();
        $driver->run();
        $driver->get();
    }
    
    consume(new MySQL());
    

    یه راه ابتدای برای نوشتن این کد اینه که بگیم اگه درایور فلان بود اینجوری کانکت شو و اگه اونیکی بود اونجوری! ولی مشکلی که پیش میاد هر بار که درایور اضافه میشه من باید بیام ایف هام رو دستکاری کنم. با این مدل کد نوشتن من انتخاب درایور رو به کانسومر واگذار کردم و در واقع پیچیدگی کد رو بردم به لایه های بالاتر و همینطور در صورت نیاز به اضافه کردن درایور دیگر، به راحتی فقط کلاسش رو عوض میکنیم و نمیریم کد قبلی رو دستکاری کنیم. در واقع ما با استفاده از یک اینترفیس اون پشت رو از دید کاربر معمولی قایم کردیم و کاربر میدونه که یه درایور رو بگیره و استفاده کنه. مثلا در فریمورک لاراول ما وقتی model binding انجام میدیم مهم نیس که اون مدل با چی ساخته شده! مهم اینه که فانکشن هایی که هر مدل میخواد رو ما داخلش داریم و اینجوریه که دیتابیس رو عوض میکنیم هیچ مشکلی پیش نمیاد. پس کد برای گسترش باز نگه داشته شده و هر چنتا درایور بخوایم میتونیم اضافه کنیم ولی برای تغییر کلوزد هست و کدهای قبلی دستکاری نمیشن.

    کدهای Testable با کمک interface ها

    اگه با مفاهیم تست نویسی و بخصوص mocking آشنا نیستید شاید این بخش یه خورده گنگ باشه واستون. ولی به هر حال در کد بالا میتونستیم بجای inject کردن اینترفیس مستقیما یک کلاس رو اینجکت کنیم و در ظاهر خیلی اتفاق بدی نمیفتاد. ولی مشکل اصلی زمانی پیش میاد که شما میخواید بدون تغییر در کد یا گرفتن ورودی اضافه کدتون رو تست کنید. اونجا برای تست تابع consume باید حتما ورودی رو از نوع مثلا MySQL میدادید که در این صورت نمیشد یه کلاس فیک برای تست نوشت ولی الان خیلی راحت میتونیم هر نمونه ای که اینترفیس رو پیاده کرده رو به عنوان ورودی به تابع بدیم و اون رو تست کنیم.

    در پایان من فرصت نکردم متن رو ریویو کنم اگه مشکلی بود ببخشید و توی کامنت ها بگید که اصلاح کنم 🙂

  • کلاسهای abstract یا انتزاعی در PHP

    کلاسهای abstract در اکثر زبان های شئ گرا وجود دارند. کلاسهایی هستند که قابل نمونه گیری نیستند ، و سایر کلاسها فقط میتوانند از آنها ارث بری کنند.

    فرض کنید که شما یک کلاس animal دارید و میخواهید همه حیوانات از آن کلاس یا شئ مشتق شوند ، خود کلاس animal به تنهایی قابل نمونه گیری نیست. چون ما موجود حیون نداریم و مثلا گربه با به ارث بردن بخشی از ویژگی های حیوان به موجودیت میرسد . پس دلیل اینکه اسم این نوع کلاسها را abstract گذاشته اند هم قابل درک هست.

    کاربرد کلاسهای abstract

    حتما الان این سوال براتون پیش اومده که ” چه کاریه از abstract استفاده کنیم ؟ کلاس معمولی درست میکنیم ، ازش ارثم میبریم 🙂 “

    سوال به جایی هم هست ، در پروژه های بزرگ برای خوانایی برنامه از abstract ها استفاده میکنند . شما وقتی یک کلاس abstract رو میبینید، خیلی راحت میفهمید که این تابع قابل نمونه گیری نیست و یک کلاس برای ارث برده شدن هست. در پروژه های کوچیک این مسئله زیاد خودش رو نشون نمیده ولی در پروژه های بزرگ خیلی کاربرد داره. مثلا همین فریمورک لاراول خودمون خیلی جاها از همین نوع کلاسها استفاده کرده.

    کاربرد دیگه ی کلاسهای abstract این هست که شما با این نوع کلاسها میتونید نویسنده های کلاسهای فرزند رو وادار کنید که برخی توابع رو بازنویسی کنند (در ادامه بیشتر متوجه این موضوع میشید).

    نحوه ی تعریف یک کلاس abstract

    برای تعریف یک کلاس انتزاعی از کلمه کلیدی abstract قبل از class  استفاده میکنیم :

    abstract class Animal
    {
        public $name;
        public $age;
        
        public function Describe()
        {
            return $this->name . ", " . $this->age . " years old";    
        }
        
        abstract public function Greet();
    }

    الان اگه شما از کلاس بالا نمونه بگیرید با اررور مواجه میشید. طبیعی هم هست دیگه 🙂

    یادتون باشه درون کلاس abstract تون میتونید متد هایی رو تعریف کنید و از اونها استفاده کنید و اون متد ها میتونن abstract باشن یا نه…

    اگه متدی رو از نوع abstract تعریف کردید ، توسعه دهنده های دیگه رو مجبور میکنید : در صورتی که از کلاس شما کلاسی رو به ارث بردن ، حتما نابع رو دوباره بنویسن. مثلا هر کلاسی که از کلاس بالا مشتق بشه ، باید متد Greet رو دوباره بنویسه.

    و یادتون باشه که متد های انتزاعی فقط درون کلاسهای انتزاعی قابل نوشته شدن هستند.

    کلاس زیر از کلاس بالا مشتق شده :

    class Dog extends Animal
    {
        public function Greet()
        {
            return "Woof!";    
        }
        
        public function Describe()
        {
            return parent::Describe() . ", and I'm a dog!";    
        }
    }

    همونطور که میبینید ما مجبور بودیم که متد Greet() رو بازنویسی کنیم. حالا میتونیم از کلاس Dog بصورت زیر نمونه بگیریم :

    $animal = new Dog();
    $animal->name = "Bob";
    $animal->age = 7;
    echo $animal->Describe();
    echo $animal->Greet();

    کاربرد متدهای Abstract در دیزاین پترن template method

    دیزاین پترن ها یا الگوهای طراحی نرم افزار الگوهایی مشخص و روتین هستند که بین برنامه نویس ها شناخته شده هستن و یجورایی توسعه دهنده ها میتونن آدرس بدن که فلان کد رو چجوری توسعه دادن بدون اینکه نیاز باشه جزییات رو بگن. یکی از این دیزاین پترن ها template method هست. یه تایم هایی هست که شما کارای تکراری زیادی توی دو تا کلاس دارید و میخواید از اون تکرار جلوگیری کنید. مثلا حالت زیر رو در نظر بگیرید:

    <?php
    
    
    class GatewayPay {
        public function changeOrderStatus(){
            var_dump('change order status');
        }
        public function goToGateway(){
            var_dump('redirecting to gateway.');
        }
    }
    
    class WalletPay {
        public function changeOrderStatus()
        {
            var_dump('change order status');
        }
        public function changeUserCredit(){
            var_dump('changing user credit.');
        }
    }

    همونطور که میبینید در این کد متد اول در هر دو کلاس عینا تکرار شده. برای خلاص شدن از شر کد تکراری بالا میشه اینطور کار کرد:

    <?php
    
    abstract class Pay
    {
        public function changeOrderStatus()
        {
            var_dump('change order status');
        }
    
        abstract public function finalize();
    }
    
    
    class GatewayPay implements Pay {
    
        public function finalize(){
            var_dump('redirecting to gateway.');
        }
    }
    
    class WalletPay implements Pay {
    
        public function finalize(){
            var_dump('changing user credit.');
        }
    }
    

    میبینید که متد تکراری رو بردیم بالا توی کلاس دیگه و متدهای دیگه رو هم نام کردیم و کارهای غیر تکراری رو توشون گذاشتیم. اگه حرفامو متوجه شدید بهتون تبریک میگم. شما یک دیزاین پترن به اسم Template Method رو به همین آسونی یاد گرفتید 🙂 جزییات بیشتر رو توی سایت زیر بخونید:

    https://refactoring.guru/design-patterns/template-method

  • سلام دنیا!

    سلام دنیا ،  این نخستین نوشته‌‌ی من است. من حمید حق دوست هستم، برنامه نویس وب و الان ساکن شهر زنجان هستم. خیلی دوست داشتم وبلاگی داشته باشم که توش بنویسم . قبلا توی w3team.ir مینوشتم و به دلایل خاصی نتونستم ادامش بدم و حالا تصمیم دارم همینجا ادامه بدم. خیلی علاقمند به تکنولوژی و البته برنامه نویسی هستم ، فراز و نشیبی که توی زندگیم بوده هم نتونسته از علاقم به این مدل زندگی کم کنه و بیشتر هم شده علاقم به دنیای عجیب برنامه نویسی.

    از سی ام اس وردپرس برای کارم استفاده کردم ، چون فکر میکنم کارم رو راه بندازه . امیدوارم بعد از این مطلب به درد نخورم ، مطالبی بنویسم که به دردتون بخوره. مطالبی که گره از کار کسی باز کنه . مطالبی که شاید به یکی مثل خودم کمک کنه که بتونه از طریق وب فارسی هم مطلب خوب بخونه

    پایان