نوشتن ماژول در دروپال 7 - بخش شش

By کوشا حسینی, 1 اکتبر, 2013

ماژول نویسی دروپال بخش یک
ماژول نویسی دروپال بخش دو
ماژول نویسی دروپال بخش سه
ماژول نویسی دروپال بخش چهار
ماژول نویسی دروپال بخش پنج
ماژول نویسی دروپال بخش هفت
ماژول نویسی دروپال بخش هشت

در بخش قبل متوجه شدیم که db_insert وجود داده‌های قبلی رو بررسی نمی‌کنه، پس باید این کار رو دستی انجام بدیم. برای این کار دو راه وجود داره: استفاده از db_select یا استفاده از db_query. تابع db_select، شیئی رو به ما برمی‌گرداند که می‌تونستیم شرایط(condition) یا joinها یا سایر قابلیت‌های پایگاه داده رو به صورت کدهای PHP تنظیم کنیم. اما مشکلی که در تایع db_select وجود داره، بازدهی پایین‌تر این تابع هست. برای انجام queryهای ساده‌تر، می‌تونیم از تابع db_query استفاده کنیم. برای اجرای db_query نیاز به نوشتن دستی کد نهایی SQL داریم که مزیت‌هایی که قبلا گفتیم رو نداره. پس همیشه باید برای انتخاب یکی از این دو ابزار سادگی کار و یا بازدهی بیشتر رو در نظر بگیریم.

بعد از اجرای query مورد نظر، با بررسی خالی بودن db_select یا db_query متوجه می‌شیم داده‌ای وجود نداره و در صورتی که این توابع یک user_id یا uid که قبلا در جدول پایگاه داده ذخیره کردیم برگرداندند، می‌فهمیم باید اطلاعات کدام سطر جدول رو به روز کنیم. به این صورت:

  $uid = $GLOBALS['user']->uid;

  if (isset($form_state['clicked_button']) && $form_state['clicked_button']['#name'] == 'op') {
    $v = $form_state['values'];
    $result = _my_calculator_calculate($v['value_1'], $v['value_2'], $v['operation']);

    $fields = array('uid' => $uid,  'result' => $result);

    // First check to see if there is already anything in DB.
    $check = db_query('SELECT rid FROM {my_calculator_result} WHERE uid=:uid LIMIT 0,1',
        array(':uid' => $uid));
    $check = $check->fetchAssoc();
    
    if(empty($check)) {
      $query = db_insert('my_calculator_result')
        ->fields($fields);
        ->execute();
    }
    else {
      $query = db_update('my_calculator_result')
        ->fields($fields)
        ->condition('rid', $check['rid'])
        ->execute();
    }
  }
  else {
    $query = db_select('my_calculator_result', 'mcr')
      ->condition('mcr.uid', $uid)
      ->fields('mcr', array('result'))
      ->range(0, 1)
      ->execute();

    $result = $query->fetchAssoc();
    $result = $result['result'];
  }

توضیحات:

  • متغیر $GLOBALS در PHP آرایه‌ایست از همه‌ی متغیرهای سراسری. در دروپال، یک متغیر سراسری به اسم user وجود داره، که در این آرایه اندیس 'user' هست. خود user شیئی از کلاس stdClass هست. با گرفتن یک dpm از $GLOBALS میتونید همه‌ی متغیرهای سراسری موجود در دروپال و با dpm از اندیس کاربر (user) می‌تونید ببینید این شیئ حاوی چه اطلاعاتیه. چیزی که ما نیاز داشتیم، شناسه‌ی کاربر یا uid اون بود. توجه کنید که uid برای کاربران ناشناس همیشه 0 هست. در php راه دیگری هم برای دسترسی به متغیرهای موجود در $GLOBALS وجود داره: استفاده از اعلان global به این صورت: global $user که بعدا می‌تونیم به uid به این صورت دسترسی پیدا کنیم:x $user->uid.
  • در عبارت if اول، یعنی در صورتی که فرم حاوی مقادیری وارد شده توسط کاربر بود، ابتدا بررسی می‌کنیم که در پایگاه داده اطلاعاتی توسط این کاربر ذخیره شده یا نه. برای اینکار از db_query استفاده کردیم. نکته‌ی مهمی در استفاده از این تابع وجود داره برای جلوگیری از sql injection یا طزریق sql، شناسه‌ی کاربر رو به صورت place holder در عبارت SQL مشخص کردیم، و در آرایه‌ای به عنوان آرگومان دوم db_query، مقدار این place_holder رو تعیین کردیم. place_holder ها برای queryها با علامت دونقطه «:» و کلمه‌ای به عنوان نام place_holder تعیین می‌شن.
  • نام جدولی که در عبارت SQL ارسالی به db_query ذکر کردیم درون آکولاد {} قرار گرفت. دلیل این کار این بود که جداول دروپال در پایگاه داده می‌تونن دارای پیشوند باشن (اگر به یاد داشته باشید، در طول فرایند نصب، دروپال پیشوند جداول رو در حین تعیین نام پایگاه داده از شما می‌پرسه که می‌تونید اون رو خالی بگذارید). با این کار به دروپال فرصت می‌دیم این پیشوند رو به نام جداول اضافه کنه.
  • با عبارت LIMIT 0,1 مجموعه‌ی جواب رو به حداکثر یک جواب محدود کردیم، پس بعد از اجرای db_query، اگر نتیجه‌ای در پایگاه داده پیدا شود، در آرایه‌ای قرار خواهد گرفت که شامل یک مقدار خواهد بود، اندیس این آرایه همان rid و مقدار این اندیس، سطر مربوط به uid تعیین شده است. اگر نتیجه‌ای پیدا نشد، $check برابر با FALSE خواهد بود.
  • در بخش بعد، اگر $check خالی بود، طبق روال قبل پیش میریم اما اگر حاوی مقدار بود، معلوم میشه باید سطری در پایگاه داده به روز بشه. برای این کار از تابع db_update استفاده می‌کنیم. دستور db_update مشابه db_insert هست. اما باید همیشه یادمون باشه اگر condition رو تعیین نکنیم همه‌ی سطرهای جدول با مقدار مشخص شده به روز می‌شن! در نهایت هم $query که db_uodate به اون اختصاص پیدا کرده حاوی تعداد سطرهای به روز شده خواهد بود.
  • در db_update از همان rid پیدا شده برای به روز رسانی استفاده کردیم اما لزومی نداشت، می‌تونستیم از همان uid استفاده کنیم، اما استفاده از rid به ما اجازه‌ی توسعه راحت‌تر ماژول در آینده رو می‌ده.
  • در هیچ یک از متدهای condition بالا، آرگومان سوم که عملگر رو تعیین میکنه، نوشته نشد، چون پیش‌فرض اون عملگر تساوی «=» برای مقادیر مشخص و عملگر «IN» برای آرایه‌هاست پس عملگر تساوی به صورت خودکار برای ما انتخاب میشه.

کاری که باید انجام بدید حذف مقادیر تکراری برای یک کاربر با uid مشخص از پایگاه داده است که به خاطر باگ موجود در کدهای قبلی به وجود آمده بود. یک راه نصب مجدد ماژول با دستور drush dre my_calculator در طورت نصب بودن devel و راه دیگر، خالی کردن (truncate table my_calculator_result) جدول ماژول از پایگاه داده است.

اما راه آسان‌تری هم برای انجام کدهای بالا با استفاده از db_merge وجود داره. این تابع، وظایف مربوط به «بررسی و به روز رسانی در صورت وجود داشتن رکوردهای قبلی و در غیر اسن صورت ایجاد رکورد جدید» رو در یک تابع خلاصه می‌کنه. پس کد بالا به این صورت تغییر پیدا می‌کنه.

  $uid = $GLOBALS['user']->uid;

  if (isset($form_state['clicked_button']) && $form_state['clicked_button']['#name'] == 'op') {
    $v = $form_state['values'];
    $result = _my_calculator_calculate($v['value_1'], $v['value_2'], $v['operation']);

    $fields = array('uid' => $uid,  'result' => $result);

    db_merge('my_calculator_result')
      ->key('uid' => $uid)
      ->fields($fields)
      ->execute();
  }
  else {
    $query = db_select('my_calculator_result', 'mcr')
      ->condition('mcr.uid', $uid, '=')
      ->fields('mcr', array('result'))
      ->range(0, 1)
      ->execute();

    $result = $query->fetchAssoc();
    $result = $result['result'];
  }

توضیحات:

  • در استفاده از db_merge، به جای تعیین condition، از متد key برای تعیین اینکه آیا رکوردی از قبل در پایگاه داده وجود داشته یا نه استفاده می‌کنیم. آرگومان متد key یک آرایه‌ی associative از کلیدها و مقادیرشون هست.
  • توصیه میکنم صفحه‌ی این تابع در دروپال رو مطالعه کنید تا با سایر قابلیت‌های این تابع هم آشنا بشید. مثلا این تابع می‌تونه در صورتی که رکوردی از قبل وجود داشت فقط یک فیلد از این رکورد رو به روز رسانی کنه و به بقیه فیلدها کاری نداشته باشه.
  • متد MERGE در پایگاه داده‌های معمول در واقع وجود نداره (یا دارای دستورهای عجیب و غریبه) و دروپال با db_merge این دستور SQL رو با دستورهای موجود شبیه‌سازی می‌کنه.

ادامه دارد...

شاد و موفق باشید.

تگ های مطلب
دسته بندی مطلب