اگر ارتباط با دیتابیس از دست بره، چه منطقی رو پیاده سازی کنیم؟

پرسیده شده
فعالیت 1487 روز پیش
دیده شده 767 بار
0

سلام،

توی PHP، به هر دلیلی اگر ارتباط با دیتابیس از دست بره، چه فرآیندی رو باید اجرا کنیم؟

بعنوان مثال توی فریمورک لاراول وقتی کانکشن با دیتابیس برقرار نیست، یک QueryException با پیغام Connection refused ایجاد میشه. در این حالت لاجیک کد به چه صورت پیاده سازی میشه؟! آیا باید در هر تکه از کد که قرار هست یک رکورد ایجاد یا ویرایش بشه از بلوک try catch استفاده کنیم؟

 

لینک مقاله خوبی هم سراغ دارید ممنون میشم به اشتراک بذارید.

فایل پیوست

رسول طیبی‌راد
رسول طیبی‌راد

20 فروردین 99

2

مورد تایید استاد

حذف شده

سلام مجدد، ببینید از استفاده ی try/catch که نمیشه جلوگیری کرد، حتی فریمورک های بزرگ و میکروفریمورک هارو هم که ببینید مثلا متدهایی دارن با اسم هایی چون init یا run یا render یا bootstrap یا ... با اینکه Error Handler و Exception Handler رو برای فریمورک پیاده سازی کردن ولی باز هم میبینیم از بلاک های try/catch در جاهای مختلف استفاده شده.

در بعضی جاها قبل از throw یا پرتاب یک اکسپشن، ممکنه نیاز باشه اگر اکسپشنی رخ داد، فایلی یا اتصالی رو close کنن یا ممکنه بخوان لاگ بگیرن ازون موقعیت و ... و بعد ازون throw کنن، بنابراین اون تکه کدی که احتمال بروز اکسپشن رو میدن، داخل بلاک try/catch میذارن و در catch عملیاتشونو انجام میدن و بعد throw میکنن یک exception رو.

 

اما خلاصش برای اینکه از try/catch های متعدد جلوگیری کنید، میتونید از set_exception_handler استفاده کنید و یک callback بعنوان پارامتر بدید تا مشخص کنید که در مواجهه با یک اکسپشن چه رفتاری رو انجام بده.

بعد از اجرا callback برنامه terminate میشه. در اینجا خطاهایی که پرتاب میشه و همچنین بلاک catch ای  برای گرفتن  خطا وجود نداره، اصطلاحاً(Uncaught Exception) توسط callback خونده میشه.

مثالی از داکیومنت PHP:

<?php
function exception_handler($exception) {
  echo "Uncaught exception: " , $exception->getMessage(), "\n";
}

set_exception_handler('exception_handler');

throw new Exception('Uncaught Exception');
echo "Not Executed\n";

 

هرجایی از برنامه اکسپنی رخ بده(مثل اکسپشن های دیتابیس) اینجا مدیریت میشه. میتونید Custom Exception هم بنویسید. کافیه هر کجا از برنامه که نیاز بود، throw new MyCustomException رو فراخوانی کنیم.

باید کمی دستتون اومده باشه.

 

فریمورک لاراول به این شکل بود که در متد bootstrap از کلاس HandleExceptions اومده:

error_reporting(-1);

set_error_handler([$this, 'handleError']);

set_exception_handler([$this, 'handleException']);

register_shutdown_function([$this, 'handleShutdown']);

if (! $app->environment('testing')) {
    ini_set('display_errors', 'Off');
}

error handler رو خودش مدیریت کرده تا خطاهایی مثل notice و warning رو بصورت exception تولید کنه. داخل متد errorHandle دستور throw new ErrorException اومده.

 

 

و در داخل متد handleException:

if (!$e instanceof Exception) {
    $e = new FatalThrowableError($e); // catch fatal error
}

try {
    
} catch (Exception $e) {
    
}

if ($this->app->runningInConsole()) {
    $this->renderForConsole($e);
} else {
    $this->renderHttpResponse($e);
}

 

register_shutdown_function هم بعد از اتمام اسکریپت فراخوانی میشه.

 

در YiiFramework هم شبیه به Laravel به شکل زیر:

public function register()
{
    ini_set('display_errors', false);


    set_exception_handler([$this, 'handleException']);


    if (defined('HHVM_VERSION')) {
        set_error_handler([$this, 'handleHhvmError']);
    } else {
        

        set_error_handler([$this, 'handleError']);


    }
    if ($this->memoryReserveSize > 0) {
        $this->_memoryReserve = str_repeat('x', $this->memoryReserveSize);
    }


    register_shutdown_function([$this, 'handleFatalError']);
}

 

البته مسئله ی مدیریت اکسپشن ها در این فریمورک ها خلاصه به این چند مورد نمیشه و به قول استاد آوند، event های زیادی بررسی میشه و مخصوصا بجز اینهایی که اوردم، داخل متد ها مخصوصا متدهای کار با دیتابیس try/catch های متعددی اومده که بخاطر بزرگی پروژه فعلا از درک من خارجه! :دی

 

در مورد دیتابیس هم PDO اگر مشکلی پیش بیاد، اکسپشن تولید میکنه ولی اگر میخواید خیلی ریزتر وارد مسئله بشید Custom Exception بسازید مثلا class CustomException extends Exception و در موقعیت مناسب throw کنید.

 

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

فایل پیوست

محسن موحد

توسط

محسن موحد

20 فروردین 99

1
حذف شده

سلام.

در مورد دیتابیس زمانیکه کانکشن PDO رو میسازیم Error Mode ست میکنیم:

$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

یعنی اگر مشکلی برای کانکشن و ارتباط هامون با دیتابیس اتفاق بیوفته، حتما یک PDOException اتفاق میوفته.

این اکسپشن دنبال بلاک catch مخصوصش میگرده و اگر وجود نداشت یک fatal error اتفاق میوفته.

بجای اینکه تمام تکه کدهارو try/catch اضافه کنید، پروژه رو بصورتی پیاده کنید که تمام هندل request هاف response ها و ... پروژه در یک try/catch اتفاق بیوفته و Custom Exception هایی بسازید که با if/else بعضی قسمت هارو میتونید چک کنید و throw new Excepton یا throw new CustomException یا حتی با توجه به مثال شما throw new QueryException قرار بدید.

در مورد if/else یه مثال میزنم، چک میکنم if آدرسی که کاربر خواسته باز کنه در سایت وجود نداشت، throw new HttpException و ...

قطعا در بعضی از تکه کدها مجددا از try/catch نیازه که استفاده کنید تا مثلا اگر تکه کدی exception ای تولید شد، داخل catch، مثلا یک لاگ یا هر کاری دیگه بسته به موقعیت کد و فکر برنامه نویس، اتفاق بیوفته و بعد از اون throw new MyException کند و ...

میتونید تمام exception ها و حتی fatal error ها رو خودتون مدیریت و customize کنید.

اینایی که گفتم یک کلیتی از هندل کردن exception ها بود که فک میکنم در PHP پیشرفته با یادگیریه شی گرایی و مخصوصا در سرفصل میکروفریمورک این مباحث جا میوفته.

فایل پیوست

محسن موحد

توسط

محسن موحد

20 فروردین 99

حذف شده
ممنون از پاسخ شما دوست عزیز، بله درسته، با کلیتی که گفتید آشنا هستم. با شما موافقم که باید تمامی فرآیندهایی که ممکنه منجر به تولید خطا بشن رو حتی الامکان در یک بلوک try-catch مدیریت کنیم. اما من بطور ویژه با مسئله از دست رفتن ارتباط با دیتابیس مشکل دارم. چون چیزی که میخام در موردش حرف بزنم طولانیه، یه پاسخ جدید اضافه میکنم ممنون میشم دیدگاه خودتون رو منتقل کنید.
رسول طیبی‌راد

20 فروردین 99

0
حذف شده

در پاسخ به دوست گرامی @محسن موحد:

بنظرم بطور کلی برای انجام عملیات CRUD بهتر هست از الگوی Repository Pattern استفاده بکنیم. یعنی اینکه تمامی موجودیت های برنامه شامل یوزر، محصول، سفارش، ... از یک BaseRepository ارث بری کنند و در این کلاس خطاهای احتمالی رو در چند بلوک try-catch مدیریت کنیم.


مثال (در لاراول):

interface RepositoryInterface
{
    public function find($someArgiments);

    // other needed methods ...
}

 

و برای BaseRepository:

class BaseRepository implements RepositoryInterface
{
    protected $model;

    public function find($someArguments)
    {
        try
        {
            // do stuff with $someArguments ...
            return $this->model::find($ID);
        } catch(Exception $e) {
            Notification::send($e); // To developers stack ...
            return back()->with($message);
        }
    }

    // Implement other methods ...
}

 

و در نهایت، UserRepository از کلاس Base ارث بری کنه و توی سطح کنترلرها یا سرویس ها دیگه از try-catch استفاده نکنیم..

 

آیا این لاجیک Clean Code محسوب میشه؟ اگر نه چه تغییری لازمه تا بتونیم خطاهای ارتباط با دیتابیس رو در یک جا متمرکز کنیم؟

 

ممنون از همراهی..

فایل پیوست

رسول طیبی‌راد

توسط

رسول طیبی‌راد

20 فروردین 99

1
حذف شده

مبحثی که شما فرمودید خیلی جلوتر از بحث دوره هست فعلا.

لاراول امکانات و ابزار زیادی برای این موارد داره و این امکان وجود داره اونجا که در هنگام رخداد یه اتفاق خاص (مثل از دست رفتن کانکشت دیتابیس) یه ایونتی فایر شه و به اون ایونت پاسخ بدیم.

اینجا توی php چیز خاصی نیست مگه اینکه همچین چیزی رو پیاده سازی کنیم.

محل استفاده از الگوهای طراحی هم جای بحث زیادی داره که ایشالله به سرفصل الگوهای طراحی برسیم در موردش صحبت میکنم حتما.

فایل پیوست

لقمان آوند

توسط

لقمان آوند

20 فروردین 99

حذف شده
ممنون آقای دکتر آوند، بله میدونم، سوالم یذره زیادی جلوتر از مباحث دوره بوده..ببخشید دیگه! فقط اینکه برای فایر کردن یک event باز هم نیاز هست اونو توی بلوک catch فایر کنیم..ولی من دنبال راه حلی هستم که تا حد امکان این بلوک های try-catch مربوط به دیتابیس رو در یکجا متمرکز کنیم..
رسول طیبی‌راد

20 فروردین 99

حذف شده
در هر حال هر جایی که exception احتمال داره رخ بده یکی از راهکار ها try...catch هست. البته ابزارهایی برای مانیتور کردن خطاهای نرما فزار وجود داره که خودش واچ میکنه روی کل سیستم و خطاها رو جمع می کنه. خود ما در همین آکادمی داریم ازش استفاده می کنیم. حتما sentry.io رو ببینید.
لقمان آوند

22 فروردین 99