در این قسمت از سری آموزشی کار با رابط PDO در مورد دو موضوع صحبت می کنیم؛ ابتدا مشکلی که ممکن است هنگام استفاده از دستور LIMIT پیش بیاید و سپس در رابطه با تراکنش ها.
اگر در حالت emulation mode باشید (در حالت پیش فرض emulation mode فعال است)، PDO به جای آن که داده ها را به placeholder ها بفرستد، placeholder ها را مستقیما با داده ها تعویض می کند. از طرفی PDO در هنگام استفاده از آرایه ها در ()execute تمام پارامتر ها را رشته فرض می کند. نتیجه ی این دو مسئله این است که دستور ?,? LIMIT تبدیل به دستور '10', '10' LIMIT می شود و از طرفی همه می دانیم که این ساختار برای یک کوئری اشتباه است و در نتیجه کوئری اجرا نخواهد شد.
دو راه حل برای این مشکل وجود دارد:
اولین راه حل غیر فعال کردن emulation mode است. این کار مشکلی ایجاد نمی کند چرا که MySQL می تواند به تنهایی placeholder ها را مدیریت کند. برای انجام این کار کد زیر را اجرا کنید:
$conn->setAttribute( PDO::ATTR_EMULATE_PREPARES, false );
در این حالت پارامتر ها می توانند در ()execute بمانند:
$conn->setAttribute( PDO::ATTR_EMULATE_PREPARES, false );
$stmt = $pdo->prepare('SELECT * FROM table LIMIT ?, ?');
$stmt->execute([$offset, $limit]);
$data = $stmt->fetchAll();
راه حل دوم این مشکل:
در این روش باید پارامتر ها را به صورت دستی و صریح bind کرده و نوع آن ها را نیز تعیین کنید. به مثال زیر توجه کنید:
$stmt = $pdo->prepare('SELECT * FROM table LIMIT ?, ?');
$stmt->bindParam(1, $offset,PDO::PARAM_INT);
$stmt->bindParam(2, $limit,PDO::PARAM_INT);
$stmt->execute();
$data = $stmt->fetchAll();
در کد بالا خط دوم و سوم ساختار زیر را دارند:
$stmt->bindParam(1, $offset,PDO::PARAM_INT);
کاری که این خطوط انجام می دهند این است که مقدار ها را به صورت دستی به پارامتر ها bind می کنند. ما این مسئله را در قسمت های قبلی توضیح داده ایم اما چیزی که شاید برای شما تازگی داشته باشد PDO::PARAM_INT است. این دستور چیز عجیبی نیست و تنها کار آن این است که نوع داده ی ما را به صورت دستی تعیین کند (مثلا رشته باشد یا عدد - بالاتر اشاره کردیم که چرا مجبور به این کار هستیم).
نکته ای عجیب در مورد PDO::PARAM_INT : این دستور به دلایل عجیبی، type casting (تغییر نوع داده - مثلا از رشته به عدد) را به صورت حتمی اعمال و overwrite نمی کند! بنابراین استفاده از آن روی عددی که از نوع رشته ای است باعث برخوردن به خطای زیر می شود:
$stmt = $pdo->prepare("SELECT 1 LIMIT ?");
$stmt->bindValue(1, "1", PDO::PARAM_INT);
$stmt->execute();
اما اگر "1" را به 1 تبدیل کنید همه چیز به درستی پیش خواهد رفت.
برای اجرای موفق تراکنش ها، باید مطمئن شوید که حالت گزارش خطا روی exceptions تنظیم شده باشد و از سه متد زیر نیز استفاده کنید:
()beginTransaction برای شروع تراکنش()commit برای اجرا و کامل کردن تراکنش()rollback برای لغو کردن تمام تغییراتی که از زمان تراکنش ایجاد شده اند.Exception ها برای تراکنش ها عنصری حیاتی به حساب می آیند چرا که می توانند caught شوند. بنابراین اگر کوئری به خطا برخورد کند، اجرا متوقف شده و مستقیما به بلوکه کد catch (قسمت از سورس کد که در آن جا exception ها را catch می کنیم) منتقل می شود و در نتیجه تمام تراکنش به اصطلاح برنامه نویسان rollback می شود، یعنی به حالت قبلی و اولیه ی خود بر میگردد. کلمه ی rollback در لغت به معنی "برگشتن یا برگشت داده شدن" است اما در اصطلاح برنامه نویسی و در تراکنش ها به معنی لغو شدن تراکنش و "برگشتن" همه چیز به حالت قبل از شروع تراکنش است.
البته ممکن است در زمینه های مختلف معنی این کلمه کمی تغییر کند (مثلا بازگشت به عقب برای ایجاد اصلاحات و ...) اما به طور کلی معنی "برگشت" را در تمام حالات در خود خواهد داشت.
بنابراین یک مثال عادی به شکل زیر خواهد بود:
try {
$pdo->beginTransaction();
$stmt = $pdo->prepare("INSERT INTO users (name) VALUES (?)");
foreach (['Joe','Ben'] as $name)
{
$stmt->execute([$name]);
}
$pdo->commit();
}catch (Exception $e){
$pdo->rollback();
throw $e;
}
نکات مهم و خلاصه در مورد تراکنش ها:
PDO::ERRMODE_EXCEPTION تنظیم شده باشد.نکته: برای اینکه بدانید در حال حاضر سرور شما از چه موتور های ذخیره سازی پشتیبانی می کنید، می توانید از دستور SHOW ENGINES استفاده کنید. نمونه ای از استفاده از این دستور در مثال زیر آمده است:
mysql> SHOW ENGINES\G
*************************** 1. row ***************************
Engine: PERFORMANCE_SCHEMA
Support: YES
Comment: Performance Schema
Transactions: NO
XA: NO
Savepoints: NO
*************************** 2. row ***************************
Engine: InnoDB
Support: DEFAULT
Comment: Supports transactions, row-level locking, and foreign keys
Transactions: YES
XA: YES
Savepoints: YES
*************************** 3. row ***************************
Engine: MRG_MYISAM
Support: YES
Comment: Collection of identical MyISAM tables
Transactions: NO
XA: NO
Savepoints: NO
*************************** 4. row ***************************
Engine: BLACKHOLE
Support: YES
Comment: /dev/null storage engine (anything you write to it disappears)
Transactions: NO
XA: NO
Savepoints: NO
*************************** 5. row ***************************
Engine: MyISAM
Support: YES
Comment: MyISAM storage engine
Transactions: NO
XA: NO
Savepoints: NO
...
در کد های خروجی مقابل عبارت Transactions، یا کلمه ی NO و یا کلمه ی YES قرار خواهد داشت که قابلیت پشتیبانی از تراکنش را معلوم می کند.
امیدوارم از این مقاله استفاده کرده باشید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.