روش های کوئری نویسی سفارشی(Custom Query) در وردپرس

کوئری نویسی سفارشی در وردپرس

هر نوع داده ای که به هر طریقی وارد وب سایت تان می کنید در دیتابیس وردپرس ذخیره می شود، وردپرس هم برای نمایش این اطلاعات بسته به اینکه در چه صفحه ای حضور دارید کوئری های مختلفی را اجرا می کند.

به عنوان مثال اگر در صفحه index.php باشید آخرین نوشته های(Latest Posts) وب سایت بر اساس مقدار posts_per_page (منوی تنظیمات/خواندن/گزینه بیشترین تعداد نوشته‌ها در هر برگهٔ وبلاگ) برگشت داده می شود.

اگر در صفحه یکی از دسته ها(Category) حضور داشته باشید آخرین پست های متعلق به آن دسته لیست می شود، اگر در صفحه single.php باشید فقط به اطلاعات آن پست خاص دسترسی خواهید داشت.

این مفهوم با عنوان سلسه مراتب قالب شناخته می شود که می توانید جزئیات کامل آن را در مطلب آموزشی آشنایی با مفهوم Template Hierarchy در وردپرس مطالعه فرمائید.

با این حال بعضی مواقع شرایطی پیش می آید که می خواهید این شیوه عملکردی و پیش فرض کوئری ها را تغییر دهید، به عنوان مثال در صفحه index.php نوشته های متعلق به یک طبقه خاص را Exclude کرده و نشان ندهید، یا در صفحه آرشیو(بایگانی) نوشته ها را به جای عنوان طبقه بر اساس تاریخ مرتب کنید.

خوشبختانه این قبیل کارها به راحتی در وردپرس قابل پیاده سازی است، ما در این مطلب آموزشی ۴ روش کوئری نویسی سفارشی را به شما آموزش می دهیم.

۱- اکشن pre_get_posts

اولین کاری که می توانید انجام دهید اجرای یک تابع اختصاصی از طریق اکشن pre_get_posts است، وردپرس این اکشن را قبل از فراخوانی(Fetch) اطلاعات از دیتابیس اجرا می کند.

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

<?php
  function modiredev_custom_query( $query ) {
      // contents of function go here
   }
  add_action( 'pre_get_posts', 'modiredev_custom_query' );
?>

حالا تابع modiredev_custom_query از طریق پارامتر query$ به کوئری هایی که وردپرس اجرا می کند دسترسی دارد، اکشن pre_get_posts قبل از اجرای کوئری به شما اجازه می دهد تغییرات مدنظر خود را بر روی آن اعمال کنید.

بسیار مهم: در اکشن pre_get_posts به تمامی کوئری ها حتی مواردی که در پنل مدیریتی وردپرس اجرا می شوند دسترسی خواهید داشت، از این رو باید مطمئن شوید که کوئری هدف را به درستی انتخاب و تغییر می دهید.

برای این منظور باید از تگ های شرطی(Conditional Tags) استفاده کنید در غیر این صورت وردپرس تغییرات شما را همیشه اجرا خواهد کرد، برای درک بهتر موضوع به بررسی چند مثال می پردازیم.

۱- عدم نمایش یک طبقه خاص در صفحه index

در این مثال می خواهیم طبقه اسلایدر را از صفحه اصلی(Home Page) وب سایت خارج کنیم، برای این منظور کدهای زیر را در فایل functions.php قرار می دهیم.

function exclude_category_from_home_page( $query ) {
    if ( $query->is_home() && $query->is_main_query() ){
        $slider_cat_id = get_cat_ID( 'slider' );
        $query->set( 'category__not_in' => array( $slider_cat_id ) );
    }
    return $query;
}
add_action( 'pre_get_posts', 'exclude_category_from_home_page' );

چند نکته در رابطه با کدهای بالا:

  • همانطور که قبلا هم گفتیم وردپرس در هر صفحه کوئری متفاوت و البته مرتبط با آن صفحه را از روی URL درخواستی شناسایی و اجرا می کند، تابع is_main_query به ما این اطمینان خاطر را می دهد که تغییرات مدنظرمان فقط بر روی کوئری اصلی یا Main Query که توسط وردپرس به صورت خودکار اجرا می شود اعمال می گردد.
  • از تابع is_home برای تشخیص صفحه اصلی وب سایت استفاده کردیم.

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

۲- تغییر تعداد پست ها در صفحات آرشیو(بایگانی)

در دومین مثال مقدار posts_per_page را مستقیما در داخل کوئری تغییر می دهیم، کد زیر فقط در صفحات بایگانی اجرا خواهد شد و در هر صفحه به ۱۰ پست دسترسی داریم.

function get_one_post_home_page( $query ) {
    if ( $query->is_archive() && $query->is_main_query() ){
        $query->set( 'posts_per_page', 10 );
    }
    return $query;
}
add_action( 'pre_get_posts', 'get_one_post_home_page' );

۳- افزودن یک پست تایپ سفارشی(Custom Post Type) به صفحه index

وردپرس به طور پیش فرض در صفحه index.php فقط پست تایپ نوشته ها(Post) را لیست می کند، فرض کنید یک پست تایپ سفارشی به نام اخبار(news) ایجاد کرده اید و می خواهید اخبار را هم در کنار نوشته های وب سایت نمایش دهید، کد زیر اینکار را انجام می دهد.

function set_news_post_type() {
    if ( $query->is_home() && $query->is_main_query() ) {
       $query->set( 'post_type', array( 'post', 'news' ) );
    }
    return $query;
}
add_action( 'pre_get_posts', 'set_news_post_type');

اگر می خواهید پست تایپ سفارشی news به صفحه دسته ها اعمال گردد به جای تابع ()is_home از ()is_category استفاده کنید.

 ۴- اکشن pre_get_posts و پنل مدیریتی وردپرس

در نهایت برای تغییر کوئری های سطح مدیریتی مثال کاربردی و عملی نوشته های وردپرس: چگونه پست های کاربران را از دید یکدیگر مخفی کنیم؟ را بررسی نمائید.

۲- کلاس WP_Query

دومین روش پیاده سازی کوئری های سفارشی استفاده از کلاس WP_Query است، این کلاس قدرتمند در فایل query.php دایرکتوری wp-includes قرار دارد.

ساختار کوئری های WP_Query از اجزاء زیر تشکیل می شود:

  1. آرگومان هایی که برای کوئری ست می کنیم، مشابه query->set$ در اکشن pre_get_posts
  2. ایجاد یک آبجکت یا نمونه(Instance) از کلاس WP_Query
  3. حلقه(Loop)
  4. پاکسازی کوئری

پس از قرار دادن این اجزاء در کنار یکدیگر اسکریپت زیر تولید می شود.

<?php
  $args = array(
      // arguments for your custom query
  );

  // the query
  $query = new WP_Query( $args );

  // The Loop
  if ( $query->found_posts ) {

     while ( $query->have_posts() ) {

         $query->the_post();

	 // contents of the Loop

   }

 }

 // Restore original Post Data
 wp_reset_postdata();
?>

حالا در قالب یک مثال کاربردی می خواهیم عنوان ۵ خبر پربازدید را نمایش دهیم، در این مثال فرض کردیم تعداد بازدیدهای هر خبر در فیلد سفارشی views ذخیره شده است.

<?php 
   $args = array(
            'post_type' => 'news',
            'posts_per_page' => '5',
            'meta_key' => 'views',
            'orderby' => 'meta_value_num',
            'order' => 'DESC'
        );

   $query = new WP_Query( $args );
   if ( $query->found_posts ) {
	 while ( $query->have_posts()){
          $query->the_post();
         ?> 
          <p class="post-title"><?php echo get_the_title(); ?></p>
        <?php
     }
  }
  wp_reset_postdata();
?>

کلاس WP_Query متدها و پارامترهای متعددی دارد که بهتر است جزئیات کامل آن را در WP_Query Codex Page مطالعه کنید.

۳- تابع get_posts

اگر تا اینجای آموزش از روش های قبلی راضی نبودید پیشنهاد می کنیم ()get_posts را هم امتحان کنید، این تابع مستقیما به کلاس WP_Query دسترسی دارد و از آن استفاده می کند.

برای درک بهتر موضوع اینبار مثال قبلی را به کمک get_posts پیاده سازی می کنیم.

<?php 
$args = array(
            'post_type' => 'news',
            'posts_per_page' => '5',
            'meta_key' => 'views',
            'orderby' => 'meta_value_num',
            'order' => 'DESC'
        );

 $posts = get_posts( $args );

 if ( $posts ) {
	 foreach( $posts as $post ){
	   setup_postdata( $post );
        ?> 
          <p class="post-title"><?php echo get_the_title(); ?></p>
        <?php
    }
 }
 wp_reset_postdata();
?>

چند نکته در رابطه با کدهای بالا:

  • در تابع get_posts اگر پارامتر post_type ست نشود به طور پیش فرض از پست تایپ post استفاده خواهد شد، اما در کلاس WP_Query حتما باید post_type مشخص شود.
  • به جای حلقه while از foreach استفاده کردیم.
  •  این امکان وجود دارد که در حلقه foreach به جای فراخوانی توابعی مثل get_the_ID، the_content و … مستقیما از نام فیلدهای جدول wp_posts استفاده کنید.
  • مثلا post->ID ، $post->post_content$
  • در حلقه foreach برخی توابع مثل the_content در دسترس نیستند، از این جهت با فراخوانی setup_postdata می توانید به اینگونه توابع دسترسی داشته باشید.

۴- کلاس wpdb$

در نهایت آخرین و قدرتمندترین روش کوئری نویسی را بررسی می کنیم، اگر هیچکدام از روش های بالا نیازتان را برآورده نمی کند مطمئنا کلاس wpdb$ راه چاره خواهد بود. در این روش کوئری ها مستقیما بر روی دیتابیس اجرا می شوند، یک اشتباه کوچک می تواند زحمات شما را به باد دهد.

بسیار مهم: اگر کوئری شما آنقدر پیچیده است که کلاس WP_Query ،اکشن pre_get_posts و تابع get_posts پاسخگوی آن نیست آنگاه از کلاس wpdb$ استفاده کنید.

نکته: از کلاس wpdb$ می توانید برای نوشتن کوئری های بهینه استفاده کنید، کوئری هایی که نسبت به روش های قبلی سربار کمتری داشته باشند.

برای اطلاعات بیشتر پیشنهاد می کنیم مقاله دیتابیس وردپرس: مقدمه ای بر کلاس wpdb  را مطالعه فرمائید.

جمع بندی

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

برچسب ها
سعید یاورنیا 117 نوشته 117 دیدگاه

توسعه دهنده وب، کارشناس ارشد نرم افزار.

دیدگاه ‌ها

  • امین ۲۶ اردیبهشت ۱۳۹۹ - ۰۱:۱۰

    سلام وقت بخیر مهندس واقعا مطالبتون بسیار عاای و مفیده در مطلب بالا امکان داره قسمت زیر را بیشتر توضیح بدید
    تابع is_main_query به ما این اطمینان خاطر را می دهد که تغییرات مدنظر بر روی کوئری شناسایی شده اعمال خواهد شد
    ممنون

    • سعید یاورنیا ۲۶ اردیبهشت ۱۳۹۹ - ۱۰:۱۱

      سلام دوست عزیز.
      ببینید کوئری ها در وردپرس به دو دسته کلی تقسیم میشن:

      ۱- کوئری های اصلی یا Main Query که توسط خود وردپرس به صورت خودکار اجرا می شن، مثلا در صفحه category.php وردپرس تشخیص میده که الان باید کوئری رو بر اساس دسته اجرا کنه، در صفحه index.php لیست پست ها رو در اختیارتون قرار میده که می تونید داخل حلقه while همه رو لیست کنید، در صفحه single.php محتوای یک پست رو از دیتابیس فراخوانی می کنه و …

      اگر دقت کنید در هیچکدوم از این سناریوها شما هیچ کوئری اختصاصی ننوشتید و خود وردپرس همه کارهارو انجام میده و شما فقط باید در حلقه while چیدمان و محتوای صفحات قالب را ایجاد کنید.

      سوال پیش میادکه وردپرس از کجا تشخیص میده برای هر صفحه چه کوئری رو فراخوانی و اجرا کنه؟ وردپرس اینکار رو بر اساس URL صفحه هندل و مدیریت می کنه، برای اطلاعات بیشتر پیشنهاد میکنم مطلب آشنایی با مفهوم Template Hierarchy در وردپرس رو مطالعه کنید.

      ۲- کوئری های ثانویه یا Secondary Query: اینها کوئری هایی هستند توسط توسعه دهنده و با کلاس wp_query ایجاد می شوند، مطمئنا در توسعه قالب و افزونه سناریوهایی پیش خواهد آمد که مجبورید دست به کد شده و کوئری سفارشی بنویسید.

      در بخش “عدم نمایش یک طبقه خاص در صفحه index” مربوط به این مقاله به کمک تابع is_main_query مطمئن شدیم که تغییرات ما فقط بر روی کوئری اصلی که وردپرس در صفحه Home Page فراخوانی می کند اعمال خواهد شد، به طور خلاصه از تابع is_main_query برای شناسایی Main Query ها استفاده می کنیم.

  • امین ۲۶ اردیبهشت ۱۳۹۹ - ۱۲:۴۰

    ممنون
    مهندس بسیار توضیح عالی و قابل درک بود
    اگر امکان داره دوره های آموزشی ویدیویی وردپرس را به صورت غیر رایگان برگزار کنید خیلی میتونه مفید باشه
    من تا کنون دوره های زیادی را شرکت کردم ولی هیچکدام به اندازه این چند خط بار مفهومی نداشته در حالی که بسیار مختصر و ساده هم بیان شده

  • مرتضی ۸ خرداد ۱۳۹۹ - ۱۲:۴۷

    سلام.وقت بخیر.
    یه سوالی دارم در رابطه با نوشتن کوئری در صفحه اصلی index.php
    میخوام چند آیتم مابین آخرین نوشته هایی که منتشر میشه بذارم.مثلا تبلیغ…حالا چجوری باید این حلقه ها را بنویسم؟
    که مثلا بعد از دو پست که رسید به حلقه رد کنه و بره پست بعدی رو نشون بده. با offست انجام میدم درست نمیشه.
    عکس رو براتون ارسال میکنم بیشتر متوجه بشید که ایندکسم چجوریه. ممنون میشم راهنمایی کنید که چجوری باید این حلقه ها را بنویسم.

    http://s13.picofile.com/file/8398450892/testt.jpg

    • سعید یاورنیا ۸ خرداد ۱۳۹۹ - ۱۳:۲۳

      سلام مرتضی جان، قبل از حلقه while که برای نمایش پست ها استفاده می کنی این کد رو اضافه کن:


      global $wp_query;

      حالا هر دفعه که حلقه اجرا میشه متغیر current_post مثل یک شمارنده از ۰ شروع می شه، مثلا برای پست شماره ۶ این متغیر مقدار ۵ رو بر می گردونه، می تونید با یه همچین کدی بحث تبلیغات رو وسط پست هات پیاده کنی.


      if( $wp_query->current_post === 3 ){
      echo do_shortcode( 'شورتکد نمایش تبلغ' )
      }

      می تونی به جای عدد ثابت ۳ یک فرمول تعریف کنی که اعداد رندوم باشند یا مثلا اگر current_post جاری تقسیم بر تعداد پست ها باقیماندش ۰ بود تبلیغ نمایش داده بشه.

  • مرتضی ۸ خرداد ۱۳۹۹ - ۱۸:۵۰

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

    کدها رو من اینجا نوشتم. ببینید درسته اصن؟ و این که این چجوری میشه…

    http://sandbox.onlinephpfunctions.com/code/37747294dd69ad747fdfa7f13b59190426cec663

  • مرتضی ۹ خرداد ۱۳۹۹ - ۰۱:۰۴

    بی نهایت ممنون..اوکی شد..
    خیلی لطف بزرگی کردین آقا سعید عزیز.ایشاله همیشه در پناه حق شاد و پیروز باشید 🙂

  • مرتضی ۹ خرداد ۱۳۹۹ - ۰۲:۰۴

    و اینکه من اینجا global $wp_query;
    offset را چجوری استفاده کنم؟

دیدگاهتان را بنویسید.

نشانی ایمیل شما منتشر نخواهد شد، بخش‌های موردنیاز با * مشخص شده‌اند.