تابع get_users() در وردپرس ابزاری است که برای دریافت فهرست کاربران بر اساس پارامترهای مختلف به‌کار می‌رود و تقریباً در هر افزونه یا قالب حرفه‌ای که با داده کاربران سروکار دارد، حضور دارد. این تابع در واقع یک wrapper روی کلاس WP_User_Query است که فرآیند پیچیده ساخت کوئری‌های کاربران را ساده می‌کند.

تابع get_users یکی از پرکاربردترین توابع وردپرس برای دریافت لیست کاربران بر اساس پارامترهای متنوع است. این تابع امکان فیلتر بر اساس نقش، متادیتا، جستجو و ترتیب‌دهی را فراهم می‌کند و پایه ساخت هر نوع لیست کاربری در افزونه‌های حرفه‌ای محسوب می‌شود. در این راهنما ساختار کامل، پارامترها، نمونه کدهای واقعی، اشتباهات رایج و نکات امنیتی و کارایی این تابع بررسی می‌شود. همچنین تفاوت آن با WP_User_Query و روش‌های بهینه استفاده از آن در پروژه‌های بزرگ توضیح داده می‌شود. در پایان پرسش‌های پرتکرار و منظر مهندسی سطح بالای این تابع مرور خواهد شد.

در بسیاری از پروژه‌های واقعی که به فهرست کاربران نیاز داشتند، از فیلتر کردن ساده یک نقش تا ساخت گزارش‌های پیچیده بر اساس متادیتا، همین تابع به ابزار اصلی تبدیل شده است. آنچه در نگاه اول ساده به‌نظر می‌رسد، در واقع یک لایه Query Builder نسبتاً پیچیده در پشت خود دارد که اگر درست استفاده نشود، می‌تواند به یک مشکل جدی در کارایی و امنیت تبدیل شود.

چرا get_users در توسعه وردپرس اهمیت دارد

وردپرس به عنوان یک CMS (Content Management System) نه فقط برای مدیریت محتوا، بلکه برای مدیریت کاربران با نقش‌های متفاوت طراحی شده است. از یک سایت کوچک شخصی تا یک فروشگاه اینترنتی با هزاران مشتری، لایه کاربران همیشه یکی از پرترافیک‌ترین بخش‌های دیتابیس است. به همین دلیل، پیدا کردن روشی مطمئن و بهینه برای دسترسی به این داده‌ها اهمیت حیاتی پیدا می‌کند.

تابع get_users() این کار را از طریق یک رابط آرایه‌ای ساده انجام می‌دهد. به جای اینکه شما مستقیماً با جدول wp_users و wp_usermeta کار کنید، این تابع تمام پیچیدگی‌های JOIN و شرط‌های متادیتا را پنهان می‌کند و به شما یک آرایه از اشیای WP_User برمی‌گرداند.

برای آشنایی با نقش‌های کاربری و نحوه تعامل با آن‌ها پیشنهاد می‌کنم مطلب توابع وردپرس برای مدیریت نقش‌ها و دسترسی‌ها را مطالعه کنید تا تصویر کامل‌تری از لایه دسترسی داشته باشید.

یکی از دلایلی که این تابع در پروژه‌های حرفه‌ای جایگاه ویژه‌ای دارد، انعطاف آن در ترکیب پارامترهاست. می‌توانید همزمان بر اساس نقش، متادیتا، تاریخ ثبت‌نام و حتی حروف ابتدایی نام کاربری فیلتر کنید. همین ترکیب‌پذیری است که آن را از یک query ساده SQL متمایز می‌کند.

ساختار و امضای تابع get_users

امضای این تابع به شکل زیر است:

get_users( array|string $args = array() ): array

پارامتر ورودی می‌تواند یک آرایه انجمنی از پارامترها یا یک رشته در فرمت query string باشد، اما در عمل تقریباً همیشه از آرایه استفاده می‌شود. خروجی تابع یک آرایه از اشیای WP_User است، مگر اینکه پارامتر fields را تغییر دهید تا خروجی به شکل دیگری برگردد.

هر شیء WP_User شامل propertyهایی مانند ID، user_login، user_email، display_name، roles و user_registered است. برای دسترسی به متادیتای هر کاربر، از متد get() روی شیء یا توابع مستقل مانند get_user_meta() استفاده می‌شود.

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

پارامترهای کلیدی و نحوه استفاده از آن‌ها

پارامترهای این تابع را می‌توان به چند گروه تقسیم کرد: پارامترهای مربوط به نقش، متادیتا، جستجو، مرتب‌سازی و صفحه‌بندی. در ادامه مهم‌ترین آن‌ها را مرور می‌کنیم.

پارامتر role

پارامتر role اجازه می‌دهد کاربران را بر اساس یک یا چند نقش مشخص فیلتر کنید. مقدار می‌تواند یک رشته ساده یا یک آرایه از نقش‌ها باشد:

$editors = get_users( array(
    'role' => 'editor',
) );

اگر بخواهید همزمان چند نقش را دریافت کنید، از آرایه استفاده کنید. در این حالت کوئری نهایی از عملگر IN در SQL استفاده می‌کند که کارایی مناسبی دارد.

پارامتر meta_query

برای فیلتر بر اساس متادیتای کاربر، از meta_query استفاده می‌شود. این پارامتر دقیقاً همان ساختاری را می‌پذیرد که در WP_Query برای پست‌ها استفاده می‌کنید:

$vip_users = get_users( array(
    'meta_query' => array(
        array(
            'key'   => 'vip_level',
            'value' => 'gold',
            'compare' => '=',
        ),
    ),
) );

هر شرط می‌تواند شامل key، value، compare و type باشد. برای اعداد، حتماً type را برابر NUMERIC قرار دهید تا کوئری از نظر ترتیب و مقایسه درست عمل کند.

پارامتر number و offset

برای محدود کردن تعداد نتایج از number و برای صفحه‌بندی از offset استفاده کنید:

$page_users = get_users( array(
    'number' => 20,
    'offset' => 40,
) );

نکته مهم: اگر عدد number را -1 قرار دهید، تمام کاربران برگردانده می‌شوند. این کار در سایت‌های بزرگ به شدت خطرناک است و می‌تواند حافظه سرور را پر کند.

پارامتر orderby و order

مرتب‌سازی نتایج از طریق orderby و order کنترل می‌شود. مقادیر مجاز برای orderby شامل ID، display_name، email، login، registered، post_count، meta_value و include است:

$sorted = get_users( array(
    'orderby' => 'registered',
    'order'   => 'DESC',
) );

پارامتر search

با پارامتر search می‌توانید جستجوی متنی روی فیلدهای user_login، user_email، user_url، user_nicename و display_name انجام دهید:

$found = get_users( array(
    'search' => 'ali',
) );

این جستجو از الگوی LIKE در SQL استفاده می‌کند و به همین دلیل روی دیتابیس‌های بزرگ می‌تواند کند باشد. برای جستجوهای پیشرفته‌تر بهتر است از افزونه‌های تخصصی یا ایندکس‌گذاری مناسب استفاده کنید.

پارامتر fields

به صورت پیش‌فرض، خروجی آرایه‌ای از اشیای کامل WP_User است. اما گاهی فقط به شناسه یا ایمیل کاربران نیاز دارید. در این حالت می‌توانید از پارامتر fields استفاده کنید:

$ids = get_users( array(
    'fields' => 'ID',
) );

مقادیر مجاز شامل ID، display_name، email، login، user_nicename و all_with_meta است. استفاده از fields => 'ID' در پروژه‌های پرحجم یک تصمیم بهینه‌سازی جدی محسوب می‌شود.

برای آشنایی با الگوهای مدیریت کاربران به‌صورت برنامه‌نویسی، مطلب چگونه کاربر جدید به وردپرس اضافه کنیم منبع خوبی است.

تفاوت get_users با WP_User_Query

در نگاه اول، get_users() و WP_User_Query تقریباً یک کار انجام می‌دهند، اما تفاوت‌های مهمی دارند:

ویژگیget_users()WP_User_Query
نوع خروجیآرایه اشیای کاربرشیء Query
دسترسی به متدهامحدودکامل (total_users و ...)
کاربرد اصلیدریافت سریع لیستکوئری‌های پیشرفته

اگر به تعداد کل کاربران یا شماره صفحه‌ها نیاز دارید، WP_User_Query انتخاب بهتری است چون property total_users را در اختیار شما قرار می‌دهد. اما برای بخش عمده کاربردهای روزمره، get_users() کافی و ساده‌تر است.

نمونه‌های عملی در پروژه واقعی

در ادامه چند نمونه که در پروژه‌های واقعی بارها به آن‌ها برخورده‌ام را مرور می‌کنیم:

دریافت تمام مدیران سایت

$admins = get_users( array(
    'role' => 'administrator',
    'fields' => array( 'ID', 'user_email' ),
) );

استفاده از آرایه در fields از نسخه‌های جدید وردپرس پشتیبانی می‌شود و در کوئری‌های سنگین باعث کاهش چشمگیر مصرف حافظه می‌شود.

گزارش کاربران ثبت‌نام‌شده در ماه جاری

$args = array(
    'date_query' => array(
        array(
            'after' => 'first day of this month',
        ),
    ),
);
$recent = get_users( $args );

پارامتر date_query در نسخه‌های جدید وردپرس به این تابع اضافه شده و کار با تاریخ‌ها را بسیار ساده‌تر می‌کند.

ساخت لیست کشویی از کاربران یک نقش خاص

در فرم‌های مدیریتی معمولاً به یک dropdown از کاربران نیاز دارید. اگر تعداد کاربران زیاد است، بهتر است با fields => array('ID', 'display_name') کار کنید تا سبک‌تر باشد.

برای کار با اطلاعات ورود کاربران و بررسی وضعیت نشست‌ها، مطلب توابع وردپرس برای بررسی وضعیت ورود کاربر را ببینید.

اشتباهات رایج در استفاده از get_users

در بررسی کدهای افزونه‌های مختلف، الگوهای اشتباه تکراری زیادی دیده‌ام. مهم‌ترین آن‌ها عبارتند از:

نبود بررسی capability قبل از فراخوانی

اگر تابع را در یک endpoint عمومی فراخوانی کنید و لیست کاربران را برگردانید، عملاً اطلاعات کاربران سایت را افشا کرده‌اید. همیشه قبل از فراخوانی، با current_user_can() سطح دسترسی را بررسی کنید:

if ( ! current_user_can( 'list_users' ) ) {
    wp_die( esc_html__( 'Access denied', 'my-plugin' ) );
}

برای آشنایی کامل با نقش‌های سفارشی و capability، مطلب Capability و نقش‌های کاربری سفارشی را از دست ندهید.

نبود escape در خروجی

وقتی display_name کاربران را در HTML چاپ می‌کنید، حتماً از esc_html() یا esc_attr() استفاده کنید. فیلد display_name توسط خود کاربر قابل تغییر است و می‌تواند حاوی کد مخرب باشد.

عدم استفاده از پارامتر number در سایت‌های بزرگ

یکی از بدترین اشتباهات، فراخوانی بدون محدودیت است. در سایتی با ده‌هزار کاربر، این کار حافظه PHP را پر می‌کند و می‌تواند باعث خطای 500 شود.

نبود تست خودکار روی سناریوهای مرزی

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

امنیت و عملکرد در get_users

تابع get_users() به‌صورت داخلی از prepared statement استفاده می‌کند و به همین دلیل در برابر SQL Injection نسبتاً ایمن است. اما این ایمنی مطلق نیست و به شرایط زیر بستگی دارد:

  • مقادیر پارامترها نباید دست‌کاری مستقیم روی SQL بزنند
  • در meta_query، مقدارها به‌صورت خودکار escape می‌شوند
  • در جستجوهای پیچیده که خودتان $wpdb->prepare می‌نویسید، باید دقت کنید

در مورد امنیت کوئری‌های سفارشی، مراجعه به مطلب SQL Injection Prevention در وردپرس ضروری است، چون در پروژه‌های واقعی به‌ندرت فقط با get_users کار می‌کنید.

از نظر عملکرد، سه نکته را در نظر بگیرید:

  1. هرچه meta_query پیچیده‌تر باشد، JOINهای بیشتری ساخته می‌شود و کوئری کندتر می‌شود
  2. استفاده از fields => 'ID' به جای خروجی کامل، هزینه حافظه را چند برابر کاهش می‌دهد
  3. در سایت‌های بزرگ، بهتر است نتیجه را در cache ذخیره کنید

در مورد روش‌های بهینه‌سازی کوئری در وردپرس، مطلب بهینه‌سازی کوئری‌های وردپرس با کدنویسی توضیحات عمیقی دارد.

پرسش‌های پرتکرار درباره تابع get_users

تابع get_users چه تفاوتی با get_user_by دارد؟

get_user_by() فقط یک کاربر را بر اساس یک فیلد مشخص (مثل ایمیل یا login) برمی‌گرداند، درحالی‌که get_users() یک لیست فیلترشده برمی‌گرداند. اگر فقط یک کاربر می‌خواهید، همیشه از get_user_by() استفاده کنید چون سریع‌تر و کم‌هزینه‌تر است.

آیا می‌توان با get_users کاربران بر اساس آخرین ورود فیلتر کرد؟

بله، از طریق meta_query روی کلید last_login (اگر افزونه‌ای این را ذخیره کرده باشد) یا از طریق date_query روی user_registered برای ثبت‌نام. توجه داشته باشید که خود وردپرس تاریخ آخرین ورود را به‌صورت پیش‌فرض ذخیره نمی‌کند.

آیا get_users روی Multisite کار می‌کند؟

بله، اما در حالت Multisite، این تابع به‌صورت پیش‌فرض فقط کاربران همان سایت را برمی‌گرداند. برای گرفتن کاربران تمام شبکه باید از WP_User_Query با پارامتر blog_id یا از توابع شبکه استفاده کنید.

آیا می‌توان ترتیب نتایج را بر اساس متادیتای عددی تنظیم کرد؟

بله، با meta_key و orderby => 'meta_value_num' امکان‌پذیر است، اما اگر مقادیر متادیتا در برخی کاربران وجود نداشته باشد، می‌تواند به رفتار پیش‌بینی‌نشدنی منجر شود. در این حالت از meta_query با شرط EXISTS استفاده کنید.

آیا استفاده از get_users برای لیست‌های بزرگ مقیاس‌پذیر است؟

برای لیست‌های معمول بله، اما وقتی تعداد کاربران از چند هزار عبور می‌کند، بهتر است به سراغ queryهای مستقیم با $wpdb بروید و نتایج را در cache یا در یک جدول اختصاصی نگهداری کنید.

منظر یک مهندس ارشد

در سطح معماری، get_users() را باید به‌عنوان یک abstraction در نظر گرفت که هزینه‌اش پنهان است. در پروژه‌های بزرگ، همان‌طور که برای مدیریت کاربران در مقیاس از مدیریت کاربران وردپرس در مقیاس با WP-CLI استفاده می‌کنید، باید برای خواندن داده‌ها نیز یک لایه داده جدا بسازید.

نکته ظریفی که در کدبیس وردپرس وجود دارد این است که WP_User_Query برخلاف WP_Query از SQL_CALC_FOUND_ROWS استفاده نمی‌کند و به همین دلیل در شمارش کل نتایج همیشه یک کوئری جداگانه اجرا می‌کند. این یعنی هر بار که total_users را می‌خوانید، در واقع یک SELECT COUNT(*) مستقل روی دیتابیس اجرا شده است.

همچنین، در meta_queryهای پیچیده با عملگرهای OR بین چند کلید متفاوت، ساختار SQL تولیدشده شامل JOINهای متعدد روی wp_usermeta است. این الگو در دیتابیس‌های بزرگ به سرعت به یک bottleneck تبدیل می‌شود و راه‌حل استاندارد، استفاده از جدول‌های گزارش اختصاصی است.

در نهایت، برای پروژه‌های Enterprise توصیه می‌کنم به جای اتکای کامل به این تابع، یک Data Access Layer بسازید که نتایج را cache کند و در صورت نیاز از موتورهای جستجوی خارجی مثل Elasticsearch برای جستجوی کاربران استفاده کند.

اگر در پروژه‌های خودتان با نقش‌های کاربری سفارشی سروکار دارید، مطلب مدیریت نقش‌ها با User Role Editor و همچنین افزونه Members برای مدیریت دسترسی را بررسی کنید.

در پایان، اگر تجربه‌ای از کار با این تابع در سناریوهای حساس دارید — مثلاً فیلترهای پیچیده روی سایت‌های با بیش از ده هزار کاربر — برای من جالب است بدانید کدام بخش بیشترین هزینه را داشته و چه راهکاری انتخاب کرده‌اید. تجربه خود را در دیدگاه‌ها بنویسید تا سایر توسعه‌دهندگان هم از آن استفاده کنند.