در این قسمت از سری آموزشی کار با رابط 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 قرار خواهد داشت که قابلیت پشتیبانی از تراکنش را معلوم می کند.
امیدوارم از این مقاله استفاده کرده باشید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.