پایگاه داده
- محمد علایی
- منتشر شده در
- زمان خواندن 11 دقیقه
جوملا یک لایه انتزاعی پیچیده برای پایگاه داده فراهم میکند تا استفاده برای توسعهدهندگان شخص ثالث را سادهتر کند. نسخههای جدید API پلتفرم جوملا عملکردهای اضافی را ارائه میدهند که این لایه پایگاه داده را توسعه میدهد و ویژگیهایی مانند اتصال به انواع بیشتری از سرورهای پایگاه داده و زنجیرهبندی کوئریها (query chaining) را شامل میشود تا خوانایی کد اتصال را بهبود بخشد و برنامهنویسی SQL را سادهتر کند.
جوملا میتواند از انواع مختلف سیستمهای پایگاه داده SQL استفاده کند و در محیطهای گوناگون با پیشوندهای جدول متفاوت اجرا شود. علاوه بر این عملکردها، این کلاس بهصورت خودکار اتصال به پایگاه داده را برقرار میکند. تنها با ایجاد یک نمونه از این شیء و دو خط کد میتوانید نتیجهای را از پایگاه داده در قالبهای مختلف دریافت کنید. استفاده از لایه پایگاه داده جوملا حداکثر سازگاری و انعطافپذیری را برای افزونه شما تضمین میکند.
انتخاب دادهها از پایگاه داده
از آنجا که جوملا از نسخه 1.6 پشتیبانی از انواع مختلف پایگاه داده را معرفی کرد، روش توصیهشده برای ساخت کوئریهای پایگاه داده استفاده از «زنجیرهبندی کوئریها» (query chaining) است (هرچند کوئریهای متنی یا رشتهای هم همیشه پشتیبانی میشوند).
زنجیرهبندی کوئریها روشی است که در آن چندین متد یکی پس از دیگری به هم متصل میشوند، بهطوری که هر متد یک شیء بازمیگرداند که میتواند متد بعدی را پشتیبانی کند. این کار خوانایی کد را بهبود میبخشد و برنامهنویسی را سادهتر میکند.
برای ایجاد یک نمونه جدید از کلاس \Joomla\Database\DatabaseQuery، از متد getQuery در \Joomla\Database\DatabaseDriver استفاده میکنیم:
use Joomla\CMS\Factory;
//وقتی در مدل کامپوننت استفاده میشود
$db = $this->getDatabase();
// وقتی در سایر بخشها استفاده میشود
$db = Factory::getContainer()->get('DatabaseDriver');
$query = $db->getQuery(true);
توجه: دیگر از روش جوملا 3 زیر استفاده نکنید زیرا منسوخ شده است:
$db = Factory::getDbo();
متد \Joomla\Database\DatabaseDriver::getQuery یک آرگومان اختیاری به نام $new میگیرد که میتواند مقدار true یا false داشته باشد (مقدار پیشفرض false است).
برای اجرای کوئری روی منبع داده، میتوانیم چندین متد از کلاس \Joomla\Database\DatabaseQuery را فراخوانی کنیم؛ این متدها زبان کوئری منبع داده (که معمولاً SQL است) را در قالبی انتزاعی پنهان میکنند و سینتکس خاص کوئری را از دید توسعهدهنده حذف میکنند که باعث افزایش قابلیت حمل و نگهداری کد میشود.
بعضی از متدهای پرکاربرد شامل select, from, join, where و order هستند. همچنین متدهایی مانند insert, update و delete برای تغییر دادهها در منبع داده وجود دارد. با زنجیره کردن این و دیگر متدها میتوانید تقریباً هر کوئریای را بدون از دست دادن قابلیت حمل کد بسازید.
انتخاب رکوردها از یک جدول
در ادامه نمونهای از ایجاد یک کوئری پایگاه داده با استفاده از کلاس \Joomla\Database\DatabaseQuery آمده است. با استفاده از متدهای select، from، where و order میتوان کوئریهایی ساخت که انعطافپذیر، خوانا و قابل حمل باشند:
use Joomla\CMS\Factory;
// دریافت اتصال به پایگاه داده
$db = Factory::getContainer()->get('DatabaseDriver');
// ایجاد یک شیء کوئری جدید
$query = $db->getQuery(true);
// انتخاب همه رکوردها از جدول پروفایلهای کاربر که کلید آنها با "custom." شروع میشود
// مرتبسازی بر اساس فیلد ordering
$query->select($db->quoteName(['user_id', 'profile_key', 'profile_value', 'ordering']));
$query->from($db->quoteName('#__user_profiles'));
$query->where($db->quoteName('profile_key') . ' LIKE :profile_key');
$query->order($db->quoteName('ordering') . ' ASC');
// مقداردهی به پارامتر برای کوئری آماده (prepared statement)
$query->bind(':profile_key', 'custom.%');
// قرار دادن کوئری در شیء پایگاه داده
$db->setQuery($query);
// بارگذاری نتایج به صورت لیستی از اشیاء stdClass
$results = $db->loadObjectList();
این کوئری را میتوان با زنجیرهکردن سادهتر نیز نوشت:
$query
->select($db->quoteName(['user_id', 'profile_key', 'profile_value', 'ordering']))
->from($db->quoteName('#__user_profiles'))
->where($db->quoteName('profile_key') . ' LIKE :profile_key')
->order($db->quoteName('ordering') . ' ASC')
->bind(':profile_key', 'custom.%');
زنجیرهکردن مخصوصاً زمانی مفید است که کوئریها طولانیتر و پیچیدهتر میشوند.
گروهبندی
گروهبندی نیز به سادگی قابل انجام است. مثال زیر تعداد مقالات را در هر دستهبندی شمرده است:
$query
->select(['catid', 'COUNT(*)'])
->from($db->quoteName('#__content'))
->group($db->quoteName('catid'));
محدود کردن تعداد نتایج
با استفاده از متد `setLimit` میتوانید محدودیتی روی تعداد رکوردهای بازیابی شده اعمال کنید. به طور مثال، کوئری زیر حداکثر 10 رکورد باز میگرداند:
$query
->select($db->quoteName(['user_id', 'profile_key', 'profile_value', 'ordering']))
->from($db->quoteName('#__user_profiles'))
->setLimit('10');
انتخاب رکوردها از چند جدول
با استفاده از متدهای join در \Joomla\Database\DatabaseQuery، میتوانیم رکوردهایی از چند جدول مرتبط انتخاب کنیم. متد عمومی "join" دو آرگومان میگیرد: نوع join (مانند inner، outer، left، right) و شرط join. در مثال زیر مشاهده میکنید که میتوانیم از تمام کلمات کلیدی که معمولاً هنگام نوشتن کوئری SQL استفاده میکنیم بهره ببریم، از جمله کلیدواژه AS برای نام مستعار دادن به جداول و ON برای تعریف روابط بین جداول. همچنین توجه کنید که در تمام متدهایی که به ستونهای جدول اشاره دارند (مانند select، where، order) از نام مستعار جدول استفاده شده است.
use Joomla\CMS\Factory;
// دریافت اتصال به پایگاه داده
$db = Factory::getContainer()->get('DatabaseDriver');
// ایجاد شیء کوئری جدید
$query = $db->getQuery(true);
// انتخاب همه مقالات کاربران که نام کاربری آنها با 'a' شروع میشود
// مرتبسازی بر اساس تاریخ ایجاد
// توجه: قرار دادن 'a' به عنوان پارامتر دوم باعث میشود `#__content` به صورت `a` نامگذاری شود
$query
->select(['a.*', 'b.username', 'b.name'])
->from($db->quoteName('#__content', 'a'))
->join('INNER', $db->quoteName('#__users', 'b') . ' ON (' . $db->quoteName('a.created_by') . ' = ' . $db->quoteName('b.id') . ')')
->where($db->quoteName('b.username') . ' LIKE :username')
->order($db->quoteName('a.created') . ' DESC')
->bind(':username', 'a%');
// تنظیم کوئری
$db->setQuery($query);
// بارگذاری نتایج به صورت لیستی از اشیاء stdClass
$results = $db->loadObjectList();
متد join بالا به ما اجازه میدهد تا همزمان از جداول content و users کوئری بگیریم و مقالهها را به همراه اطلاعات نویسندهشان دریافت کنیم. همچنین متدهای کمکی زیر برای انجام join وجود دارند:
- innerJoin()
- leftJoin()
- rightJoin()
- outerJoin()
میتوانیم چندین join را بهکار ببریم تا بررسی در بیش از دو جدول انجام دهیم:
$query
->select(['a.*', 'b.username', 'b.name', 'c.*', 'd.*'])
->from($db->quoteName('#__content', 'a'))
->join('INNER', $db->quoteName('#__users', 'b') . ' ON (' . $db->quoteName('a.created_by') . ' = ' . $db->quoteName('b.id') . ')')
->join('LEFT', $db->quoteName('#__user_profiles', 'c') . ' ON (' . $db->quoteName('b.id') . ' = ' . $db->quoteName('c.user_id') . ')')
->join('RIGHT', $db->quoteName('#__categories', 'd') . ' ON (' . $db->quoteName('a.catid') . ' = ' . $db->quoteName('d.id') . ')')
->where($db->quoteName('b.username') . ' LIKE :username')
->order($db->quoteName('a.created') . ' DESC')
->bind(':username', 'a%');
زنجیرهکردن باعث خواناتر شدن کد در کوئریهای طولانیتر میشود.
استفاده از AS برای جلوگیری از تداخل نام ستونها
گاهی برای جلوگیری از تداخل نام ستونها هنگام انتخاب ستونها باید از عبارت AS استفاده کنیم. در این حالت، میتوان چندین select را زنجیرهای کرد و از آرگومان دوم متد $db->quoteName برای نام مستعار دادن استفاده کرد:
$query
->select('a.*')
->select($db->quoteName('b.username', 'username'))
->select($db->quoteName('b.name', 'name'))
->from($db->quoteName('#__content', 'a'))
->join('INNER', $db->quoteName('#__users', 'b'), $db->quoteName('a.created_by') . ' = ' . $db->quoteName('b.id'))
->where($db->quoteName('b.username') . ' LIKE :username')
->order($db->quoteName('a.created') . ' DESC')
->bind(':username', 'a%');
همچنین میتوان آرایهای به عنوان آرگومان دوم متد select برای مقداردهی به کلوز AS استفاده کرد. دقت کنید اگر ستونی را نمیخواهید نام مستعار بگیرد، باید در آرایه دوم مقدار null قرار دهید:
$query
->select(['a.*'])
->select($db->quoteName(['b.username', 'b.name'], ['username', 'name']))
->from($db->quoteName('#__content', 'a'))
->join('INNER', $db->quoteName('#__users', 'b') . ' ON (' . $db->quoteName('a.created_by') . ' = ' . $db->quoteName('b.id') . ')')
->where($db->quoteName('b.username') . ' LIKE :username')
->order($db->quoteName('a.created') . ' DESC')
->bind(':username', 'a%');
استفاده از دستورات آماده (Prepared Statements)
با معرفی جوملا 4.0، تمامی کوئریها به استفاده از دستورات آماده منتقل شدهاند. برای سهولت استفاده از دستورات آماده، چند تابع کمکی اضافه شده و امکان استفاده از آرایهها در چندین فراخوانی تابع فراهم شده است. مثال یک کوئری ساده با استفاده از دستورات آماده:
$query = $this->db->getQuery(true)
->select($this->db->quoteName(['id', 'password']))
->from($this->db->quoteName('#__users'))
->where($this->db->quoteName('username') . ' = :username')
->bind(':username', $credentials['username']);
در این مثال میبینید که مقدار `$credentials['username']` مستقیماً به کوئری اضافه نمیشود، بلکه به جای آن از یک نگهدارنده (placeholder) به نام `:username` استفاده و سپس مقدار آن را با متد `bind` به کوئری متصل میکنیم. هنگام استفاده از bind نیازی به Escape یا Quote کردن مقدار نیست. توجه داشته باشید که مقادیر bind شده به صورت ارجاع (reference) هستند، این امکان را میدهد که بتوانید در یک حلقه مقدار متغیر را تغییر دهید.
مثال استفاده در حلقه:
$listOfUsernames = ['admin', 'user1'];
$query = $this->db->getQuery(true)
->select($this->db->quoteName(['id', 'password']))
->from($this->db->quoteName('#__users'))
->where($this->db->quoteName('username') . ' = :username')
->bind(':username', $username);
foreach ($listOfUsernames as $name) {
$username = $name;
$this->db->setQuery($query);
$user = $this->db->loadObject();
print_r($user);
}
در این حلقه، متغیر bind شده `$username` با مقدار `$name` مقداردهی میشود، سپس کوئری دوباره تنظیم میشود (زیرا جوملا پس از اجرای کوئری، در متدهای `load` درایور پایگاه داده را ریست میکند). نتیجه، اجرای چندین کوئری با مقادیر مختلف برای username است.
استفاده از آرایهها برای bind چند متغیر
میتوان چند مقدار را همزمان bind کرد:
$query = $this->db->getQuery(true)
->select($this->db->quoteName(['id', 'password']))
->from($this->db->quoteName('#__users'))
->where($this->db->quoteName('username') . ' = :username')
->where($this->db->quoteName('id') . ' = :id')
->bind(
[':username', ':id'],
[$credentials['username'], 42],
[Joomla\Database\ParameterType::STRING, Joomla\Database\ParameterType::INTEGER]
);
در اینجا username و id به عنوان پارامتر bind شدهاند و نوع هر کدام (ParameterType) مشخص شده است. همچنین میتوان یک مقدار را به همه پارامترها اختصاص داد:
$query = $this->db->getQuery(true)
->select($this->db->quoteName(['id', 'password']))
->from($this->db->quoteName('#__users'))
->where($this->db->quoteName('username') . ' = :username')
->where($this->db->quoteName('password') . ' = :password')
->bind([':username', ':password'], $credentials['username']);
در این مثال مقادیر `:username` و `:password` هر دو برابر مقدار `username` قرار میگیرند و از نوع پیشفرض استفاده میشود.
متدهای wherein() و whereNotIn()
متدهای `whereIn()` و `whereNotIn()` همیشه از دستورات آماده (prepared statements) استفاده میکنند و در داخل خود از متد `bindArray` بهره میبرند. متد `bindArray` قادر است آرایهای از مقادیر را بدون نیاز به مشخص کردن نگهدارنده (placeholder) به کوئری متصل کند.
مثال:
$userids = [1, 2, 3, 4];
$query = $this->db->getQuery(true)
->select($this->db->quoteName(['id', 'password']))
->from($this->db->quoteName('#__users'));
$parameterNames = $query->bindArray($userids);
$query->where($db->quoteName('id') . ' IN (' . implode(',', $parameterNames) . ')');
متد `bindArray` یک آرایه از نگهدارندهها (placeholders) برمیگرداند که ایندکس آنها در سراسر کوئری یکتا (unique) است. مثلاً ممکن است مانند زیر باشند:
$placeholders = [
':preparedArray1',
':preparedArray2',
':preparedArray3',
':preparedArray4'
];
این نگهدارندهها به صورت خودکار به مقادیر آرایه متصل شده و کوئری به صورت ایمن و آماده اجرا خواهد بود.
نتایج کوئری (Query Results)
کلاس پایگاه داده شامل متدهای زیادی برای کار با نتایج کوئری است که پس از اجرای کوئریهای SELECT میتوانید استفاده کنید.
نتیجه مقدار تک (Single Value Result)
وقتی انتظار دارید فقط یک مقدار واحد از کوئری پایگاه داده برگردد، از متد `loadResult()` استفاده کنید.
|
id |
name |
|
username |
|
1 |
John Smith |
|
johnsmith |
|
2 |
Magda Hellman |
|
magdah |
|
3 |
Yvonne de Gaulle |
|
ydegaulle |
این اغلب نتیجه یک پرس و جو "شمارشی" برای به دست آوردن تعداد رکوردها است:
use Joomla\CMS\Factory;
$db = Factory::getContainer()->get('DatabaseDriver');
$query = $db->getQuery(true);
$query->select('COUNT(*)');
$query->from($db->quoteName('#__my_table'));
$query->where($db->quoteName('name')." = :value");
$query->bind('value', $value)
// پرس و جو را با استفاده از شی پرس و جو تازه پر شده ما بازنشانی کنید.
$db->setQuery($query);
$count = $db->loadResult();
یا جایی که فقط به دنبال یک فیلد از یک ردیف جدول هستید (یا احتمالاً یک فیلد واحد از سطر اول برگردانده شده است).
use Joomla\CMS\Factory;
$db = Factory::getContainer()->get('DatabaseDriver');
$query = $db->getQuery(true);
$query->select('field_name');
$query->from($db->quoteName('#__my_table'));
$query->where($db->quoteName('some_name')." = :value");
$query->bind(':value', $some_value);
$db->setQuery($query);
$result = $db->loadResult();
نتایج تک ردیف (Single Row Results)
هر یک از این توابع نتایج یک رکورد واحد را از پایگاه داده بر می گرداند، حتی اگر چندین رکورد وجود داشته باشد که معیارهای تعیین شده شما را برآورده کند. برای دریافت رکوردهای بیشتر، باید دوباره تابع را فراخوانی کنید.
|
id |
name |
|
username |
|
1 |
John Smith |
|
johnsmith |
|
2 |
Magda Hellman |
|
magdah |
|
3 |
Yvonne de Gaulle |
|
ydegaulle |
loadRow() یک آرایه ایندکس شده را از یک رکورد در جدول بر می گرداند:
. . .
$db->setQuery($query);
$row = $db->loadRow();
print_r($row);
خواهد داد:
Array ( [0] => 1, [1] => John Smith, [2] => این آدرس ایمیل توسط spambots حفاظت می شود. برای دیدن شما نیاز به جاوا اسکریپت دارید , [3] => johnsmith )
با استفاده از موارد زیر می توانید به مقادیر فردی دسترسی پیدا کنید:
$row['index'] // e.g. $row['2']
نکته: شاخص های آرایه عددی هستند که از صفر شروع می شوند. در حالی که میتوانید برای دریافت ردیفهای بیشتر فراخوانی را تکرار کنید، یکی از عملکردهایی که چندین ردیف را برمیگرداند ممکن است مفیدتر باشد.
loadAssoc() یک آرایه مرتبط را از یک رکورد در جدول بر می گرداند:
. . .
$db->setQuery($query);
$row = $db->loadAssoc();
print_r($row);
خواهد داد:
Array ( [id] => 1, [name] => John Smith, [email] => این آدرس ایمیل توسط spambots حفاظت می شود. برای دیدن شما نیاز به جاوا اسکریپت دارید , [username] => johnsmith )
با استفاده از موارد زیر می توانید به مقادیر فردی دسترسی پیدا کنید:
$row['name'] // e.g. $row['email']
loadObject() یک شی PHP را از یک رکورد در جدول بر می گرداند:
. . .
$db->setQuery($query);
$result = $db->loadObject();
print_r($result);
خواهد داد:
stdClass Object ( [id] => 1, [name] => John Smith, [email] => این آدرس ایمیل توسط spambots حفاظت می شود. برای دیدن شما نیاز به جاوا اسکریپت دارید , [username] => johnsmith )
با استفاده از موارد زیر می توانید به مقادیر فردی دسترسی پیدا کنید:
$result->index // e.g. $result->email
نتایج تک ستونی
هر یک از این توابع نتایج یک ستون واحد را از پایگاه داده بر می گرداند.
|
id |
name |
|
username |
|
1 |
John Smith |
|
johnsmith |
|
2 |
Magda Hellman |
|
magdah |
|
3 |
Yvonne de Gaulle |
|
ydegaulle |
loadColumn() یک آرایه ایندکس شده را از یک ستون در جدول بر می گرداند:
$query->select('name'));
->from . . .";
. . .
$db->setQuery($query);
$column = $db->loadColumn();
print_r($column);
خواهد داد:
Array ( [0] => John Smith, [1] => Magda Hellman, [2] => Yvonne de Gaulle )
با استفاده از موارد زیر می توانید به مقادیر فردی دسترسی پیدا کنید:
$column['index'] // e.g. $column['2']
loadColumn($index) یک آرایه نمایه شده را از یک ستون در جدول بر می گرداند:
$query->select(array('name', 'email', 'username'));
->from . . .";
. . .
$db->setQuery($query);
$column = $db->loadColumn(1);
print_r($column);
خواهد داد:
[
[0] => این آدرس ایمیل توسط spambots حفاظت می شود. برای دیدن شما نیاز به جاوا اسکریپت دارید ,
[1] => این آدرس ایمیل توسط spambots حفاظت می شود. برای دیدن شما نیاز به جاوا اسکریپت دارید ,
[2] => این آدرس ایمیل توسط spambots حفاظت می شود. برای دیدن شما نیاز به جاوا اسکریپت دارید ,
];
با استفاده از موارد زیر می توانید به مقادیر فردی دسترسی پیدا کنید:
$column['index'] // e.g. $column['2']
loadColumn($index) به شما امکان می دهد از طریق یک سری ستون نتایج را تکرار کنید:
. . .
$db->setQuery($query);
for ( $i = 0; $i <= 2; $i++ ) {
$column = $db->loadColumn($i);
print_r($column);
}
Array (
[0] => Array ( [0] => John Smith, [1] => Magda Hellman, [2] => Yvonne de Gaulle ),
[1] => Array ( [0] => این آدرس ایمیل توسط spambots حفاظت می شود. برای دیدن شما نیاز به جاوا اسکریپت دارید , [1] => این آدرس ایمیل توسط spambots حفاظت می شود. برای دیدن شما نیاز به جاوا اسکریپت دارید , [2] => این آدرس ایمیل توسط spambots حفاظت می شود. برای دیدن شما نیاز به جاوا اسکریپت دارید ),
[2] => Array ( [0] => johnsmith, [1] => magdah, [2] => ydegaulle )
)
نتایج چند ردیفی (Multi-Row Results)
هر یک از این توابع نتایج چندین رکورد را از پایگاه داده برمی گرداند.
|
id |
name |
|
username |
|
1 |
John Smith |
|
johnsmith |
|
2 |
Magda Hellman |
|
magdah |
|
3 |
Yvonne de Gaulle |
|
ydegaulle |
loadRowList() یک آرایه ایندکس شده توسط پرس و جو از رکوردهای جدول را بر می گرداند:
. . .
$db->setQuery($query);
$row = $db->loadRowList();
print_r($row);
خواهد داد (با اضافه شدن خطوط شکسته برای وضوح):
Array (
[0] => Array ( [0] => 1, [1] => John Smith, [2] => این آدرس ایمیل توسط spambots حفاظت می شود. برای دیدن شما نیاز به جاوا اسکریپت دارید , [3] => johnsmith ),
[1] => Array ( [0] => 2, [1] => Magda Hellman, [2] => این آدرس ایمیل توسط spambots حفاظت می شود. برای دیدن شما نیاز به جاوا اسکریپت دارید , [3] => magdah ),
[2] => Array ( [0] => 3, [1] => Yvonne de Gaulle, [2] => این آدرس ایمیل توسط spambots حفاظت می شود. برای دیدن شما نیاز به جاوا اسکریپت دارید , [3] => ydegaulle )
)
با استفاده از موارد زیر می توانید به مقادیر فردی دسترسی پیدا کنید:
$row['index'] // e.g. $row['2']
// or
$row['index']['index'] // e.g. $row['2']['3']
loadAssocList() یک آرایه نمایه(ایندکس) شده از رکوردهای جدول را بر می گرداند:
. . .
$db->setQuery($query);
$row = $db->loadAssocList();
print_r($row);
خواهد داد (با اضافه شدن خطوط شکسته برای وضوح):
Array (
[0] => Array ( [id] => 1, [name] => John Smith, [email] => این آدرس ایمیل توسط spambots حفاظت می شود. برای دیدن شما نیاز به جاوا اسکریپت دارید , [username] => johnsmith ),
[1] => Array ( [id] => 2, [name] => Magda Hellman, [email] => این آدرس ایمیل توسط spambots حفاظت می شود. برای دیدن شما نیاز به جاوا اسکریپت دارید , [username] => magdah ),
[2] => Array ( [id] => 3, [name] => Yvonne de Gaulle, [email] => این آدرس ایمیل توسط spambots حفاظت می شود. برای دیدن شما نیاز به جاوا اسکریپت دارید , [username] => ydegaulle ))
با استفاده از موارد زیر می توانید به مقادیر فردی دسترسی پیدا کنید:
$row['index'] // e.g. $row['2']
$row['index']['column_name'] // e.g. $row['2']['email']
متد loadAssocList($key) یک آرایهی مرتبط (associative array) از رکوردهای جدول که توسط کوئری بازگردانده شدهاند، فراهم میکند. این آرایه بر اساس مقدار ستون مشخص شده توسط پارامتر `$key` ایندکس (شاخصگذاری) میشود.
به عبارتی، خروجی این متد یک آرایهی چند بعدی است که هر عنصر آن یک آرایهی مرتبط (معادل هر ردیف از جدول) است و کلیدهای این آرایهی اصلی بر اساس مقدار `$key` هر رکورد تعیین میشوند.
. . .
$db->setQuery($query);
$row = $db->loadAssocList('username');
print_r($row);
خواهد داد (با اضافه شدن خطوط شکسته برای وضوح):
Array (
[johnsmith] => Array ( [id] => 1, [name] => John Smith, [email] => این آدرس ایمیل توسط spambots حفاظت می شود. برای دیدن شما نیاز به جاوا اسکریپت دارید , [username] => johnsmith ),
[magdah] => Array ( [id] => 2, [name] => Magda Hellman, [email] => این آدرس ایمیل توسط spambots حفاظت می شود. برای دیدن شما نیاز به جاوا اسکریپت دارید , [username] => magdah ),
[ydegaulle] => Array ( [id] => 3, [name] => Yvonne de Gaulle, [email] => این آدرس ایمیل توسط spambots حفاظت می شود. برای دیدن شما نیاز به جاوا اسکریپت دارید , [username] => ydegaulle )
)
با استفاده از موارد زیر می توانید به مقادیر فردی دسترسی پیدا کنید:
$row['key_value'] // e.g. $row['johnsmith']
$row['key_value']['column_name'] // e.g. $row['johnsmith']['email']
متد loadAssocList('key', 'column') یک آرایهی مرتبط (associative array) برمیگرداند که بر اساس مقدار ستون 'key' ایندکس شده و مقادیر آن از ستون 'column' هر رکورد انتخاب شده توسط کوئری استخراج میشود.
مثال:
$db->setQuery($query);
$row = $db->loadAssocList('id', 'username');
print_r($row);
خروجی این مثال چیزی مشابه زیر خواهد بود (برای وضوح، هر مقدار در خط جداگانه نوشته شده است):
Array (
[1] => John Smith,
[2] => Magda Hellman,
[3] => Yvonne de Gaulle,
)
در این آرایه، کلیدها مقادیر ستون `id` و مقادیر متناظر، مقادیر ستون `username` هستند.
توجه: کلید باید یک نام ستون معتبر از جدول باشد. لازم نیست که یک شاخص یا یک کلید اولیه باشد. اما اگر یک مقدار منحصر به فرد نداشته باشد، ممکن است نتوانید نتایج را به طور قابل اعتماد بازیابی کنید.
متد loadObjectList() یک آرایهای ایندکسشده از اشیاء PHP (شیءهای `stdClass`) برمیگرداند که از رکوردهای جدول حاصل از کوئری خوانده شدهاند.
مثال:
$db->setQuery($query);
$row = $db->loadObjectList();
print_r($row);
خروجی شبیه زیر خواهد بود (برای خوانایی خط شکسته شده است):
Array (
[0] => stdClass Object ( [id] => 1, [name] => John Smith,
[email] => این آدرس ایمیل توسط spambots حفاظت می شود. برای دیدن شما نیاز به جاوا اسکریپت دارید ,
[username] => johnsmith ),
[1] => stdClass Object ( [id] => 2, [name] => Magda Hellman,
[email] => این آدرس ایمیل توسط spambots حفاظت می شود. برای دیدن شما نیاز به جاوا اسکریپت دارید ,
[username] => magdah ),
[2] => stdClass Object ( [id] => 3, [name] => Yvonne de Gaulle,
[email] => این آدرس ایمیل توسط spambots حفاظت می شود. برای دیدن شما نیاز به جاوا اسکریپت دارید ,
[username] => ydegaulle )
)
میتوانید به هر ردیف به صورت زیر دسترسی داشته باشید:
$row[2] // برای دسترسی به ردیف سوم (ایندکس 2)
و برای دسترسی به مقادیر ستونهای هر ردیف:
$row[2]->email // آدرس ایمیل ردیف سوم
اگر نام ستونی را به متد `loadObjectList()` بدهید، خروجی تبدیل به یک آرایه مرتبط (associative array) از اشیاء میشود که بر اساس مقدار آن ستون ایندکس شده است:
مثال:
$db->setQuery($query);
$row = $db->loadObjectList('username');
print_r($row);
خروجی مشابه زیر خواهد بود:
Array (
[johnsmith] => stdClass Object ( [id] => 1, [name] => John Smith,
[email] => این آدرس ایمیل توسط spambots حفاظت می شود. برای دیدن شما نیاز به جاوا اسکریپت دارید ,
[username] => johnsmith ),
[magdah] => stdClass Object ( [id] => 2, [name] => Magda Hellman,
[email] => این آدرس ایمیل توسط spambots حفاظت می شود. برای دیدن شما نیاز به جاوا اسکریپت دارید ,
[username] => magdah ),
[ydegaulle] => stdClass Object ( [id] => 3, [name] => Yvonne de Gaulle,
[email] => این آدرس ایمیل توسط spambots حفاظت می شود. برای دیدن شما نیاز به جاوا اسکریپت دارید ,
[username] => ydegaulle )
)
میتوانید به هر ردیف به صورت زیر دسترسی داشته باشید:
$row['johnsmith']
و برای دسترسی به ستونهای آن ردیف:
$row['johnsmith']->email
نکته برای برنامهنویسان: مقدار `$key` باید نام یک ستون معتبر جدول باشد؛ لازم نیست حتما کلید اصلی (Primary Key) یا ایندکس باشد، اما اگر مقدارهای آن یکتا (unique) نباشند، دسترسی قابل اعتماد به نتایج تضمین نمیشود.
متدهای مرتبط با مجموعه نتایج
متد `getNumRows()` تعداد ردیفهای نتیجه بدست آمده از آخرین کوئری SELECT یا SHOW که منتظر خواندن هستند را برمیگرداند. برای استفاده صحیح از آن باید بلافاصله بعد از اجرای کوئری و قبل از اینکه دادهها خوانده شوند، فراخوانی شود.
اگر بخواهید تعداد ردیفهای تغییر یافته توسط کوئریهای INSERT، UPDATE، REPLACE یا DELETE را بدست آورید، باید از متد `getAffectedRows()` استفاده کنید.
مثال:
$db->setQuery($query);
$db->execute();
$num_rows = $db->getNumRows();
print_r($num_rows);
$result = $db->loadRowList();
خروجی:
3
تابع getNumRows() تنها برای دستورات مثل SELECT یا SHOW معتبر است که یک مجموعه نتایج واقعی برمیگردانند. اگر پس از اجرای loadRowList() یا هر روش بازیابی دیگری، تابع getNumRows() را صدا بزنید، یک هشدار PHP دریافت خواهید کرد:
Warning: mysql_num_rows(): 80 is not a valid MySQL result resource
in libraries\joomla\database\database\mysql.php on line 344
وارد کردن داده در پایگاه داده
پرسوجو (Query)
روش پرسوجو در جوملا از وقتی که فریمورک جدید جوملا معرفی شده تغییر کرده است؛ استفاده از «زنجیرهسازی پرسوجو» (query chaining)، اکنون روش پیشنهادی برای ساختن پرسوجوهای پایگاه داده است (هرچند پرسوجوهای رشتهای همچنان پشتیبانی میشوند).
زنجیرهسازی پرسوجو یعنی اتصال چندین متد به صورت پشت سر هم که هر متد یک شیء بازمیگرداند تا متد بعدی را پشتیبانی کند، که به خوانایی کد کمک کرده و کد را سادهتر میکند.
برای ایجاد یک نمونه جدید از کلاس DatabaseQuery از متد getQuery مربوط به DatabaseDriver استفاده میکنیم:
use Joomla\CMS\Factory;
// در مدل کامپوننت
$db = $this->getDatabase();
// در جاهای دیگر
$db = Factory::getContainer()->get('DatabaseDriver');
$query = $db->getQuery(true);
توجه: دیگر از روش زیر (از جوملا ۳) استفاده نکنید چون منسوخ شده است:
$db = Factory::getDbo();
متد DatabaseDriver::getQuery یک آرگومان اختیاری $new میپذیرد که مقدار آن true یا false است (پیشفرض false).
برای اجرای پرسوجو روی منبع داده، میتوان چندین متد از DatabaseQuery را استفاده کرد؛ این متدها زبان پرسوجو (اغلب SQL) را پنهان میکنند و به قابلحملتر شدن کد توسعهدهنده کمک مینمایند.
متدهای پرکاربرد شامل: select, from, join, where, order و همچنین insert, update, delete برای تغییر دادهها هستند. با زنجیره کردن این متدها میتوان تقریباً هر پرسوجویی ساخت بدون اینکه قابلیت حمل کد آسیب ببیند.
وارد کردن یک رکورد
استفاده از SQL از طریق شیء Query
کلاس DatabaseQuery چند متد برای ساختن پرسوجوهای insert ارائه میدهد که رایجترین آنها insert، columns و values است:
// اتصال به دیتابیس
$db = Factory::getContainer()->get('DatabaseDriver');
// ایجاد شیء پرسوجو جدید
$query = $db->getQuery(true);
// ستونها برای درج
$columns = array('user_id', 'profile_key', 'profile_value', 'ordering');
// آمادهسازی پرسوجوی درج
$query
->insert($db->quoteName('#__user_profiles'))
->columns($db->quoteName($columns))
->values(':user_id, :profile_key, :profile_value, :ordering');
// اتصال مقادیر به پارامترها
$query
->bind(':user_id', 1001, Joomla\Database\ParameterType::INTEGER)
->bind(':profile_key', 'custom.message')
->bind(':profile_value', 'Inserting a record using insert()')
->bind(':ordering', 1, Joomla\Database\ParameterType::INTEGER);
// ست کردن و اجرای پرسوجو
$db->setQuery($query);
$db->execute();
استفاده از شیء مستقیم
کلاس DatabaseDriver متدی راحت برای ذخیره کردن مستقیم یک شیء در پایگاه داده دارد که بدون نوشتن یک خط SQL میتوان رکورد اضافه کرد:
// ساخت و مقداردهی شیء
$profile = new stdClass();
$profile->user_id = 1001;
$profile->profile_key = 'custom.message';
$profile->profile_value = 'Inserting a record using insertObject()';
$profile->ordering = 1;
// درج شیء در جدول پروفایل کاربر
$result = Factory::getContainer()->get('DatabaseDriver')->insertObject('#__user_profiles', $profile);
- توجه کنید که نیازی به فرار دادن (escape) نام جدول نیست چون insertObject خودش این کار را انجام میدهد.
- اگر هنگام درج رکورد مشکلی پیش بیاید، متد insertObject خطا ایجاد میکند.
- اگر کلید اصلی یکتا (primary key) میدهید مانند مثال بالا، بهتر است قبل از درج، از روی آن مقدار در جدول بررسی کنید که رکورد تکراری ایجاد نشود.
- اگر میخواهید ردیف بعدی جدول را درج کنید (یعنی کلید اصلی توسط پایگاه داده ایجاد شود) میتوانید نام ستون کلید اصلی را به عنوان پارامتر سوم به insertObject بدهید تا پس از درج، متد مقدار کلید ساختهشده را در شیء بهروزرسانی کند.
مثلاً:
$result = $dbconnect->insertObject('#__my_table', $object, 'primary_key');
پس از اجرا مقدار `$object->primary_key` با مقدار کلید اصلی رکورد درجشده جدید بهروزرسانی خواهد شد.
به یاد داشته باشید قبل از درج مقدار کلید اصلی، بهتر است مقدار `$object->primary_key` را روی null یا 0 قرار دهید.
بهروزرسانی یک رکورد
استفاده از SQL
کلاس DatabaseQuery متدهایی برای ساختن پرسوجوهای update دارد، به خصوص متدهای `update` و `set`. همچنین متد `where` را که قبلاً برای ساختن select استفاده کردیم، دوباره به کار میبریم.
$db = Factory::getContainer()->get('DatabaseDriver');
$query = $db->getQuery(true);
// فیلدهایی که میخواهیم بهروزرسانی کنیم
$fields = array(
$db->quoteName('profile_value') . ' = :profile_value',
$db->quoteName('ordering') . ' = :ordering'
);
// شرایط رکوردهایی که باید بهروزرسانی شوند
$conditions = array(
$db->quoteName('user_id') . ' = :user_id',
$db->quoteName('profile_key') . ' = :profile_key'
);
$query->update($db->quoteName('#__user_profiles'))
->set($fields)
->where($conditions);
$query
->bind(':profile_value', 'Updating custom message for user 1001.')
->bind(':ordering', 2, Joomla\Database\ParameterType::INTEGER)
->bind(':user_id', 42, Joomla\Database\ParameterType::INTEGER)
->bind(':profile_key', 'custom.message');
$db->setQuery($query);
$result = $db->execute();
استفاده از شیء
مشابه متد `insertObject`، کلاس DatabaseDriver متدی به نام `updateObject` فراهم کرده که راحتی بهروزرسانی یک شیء در پایگاه داده را امکانپذیر میکند.
مثال زیر جدول سفارشی ما را با مقادیر جدید و با استفاده از کلید اصلی id بهروزرسانی میکند:
// ساخت شیء برای رکوردی که میخواهیم بهروزرسانی کنیم
$object = new stdClass();
// باید یک مقدار کلید اصلی معتبر باشد
$object->id = 1;
$object->title = 'My Custom Record';
$object->description = 'A custom record being updated in the database.';
// بهروزرسانی رکورد در جدول با استفاده از id به عنوان کلید اصلی
$result = Factory::getContainer()->get('DatabaseDriver')->updateObject('#__custom_table', $object, 'id');
مثل `insertObject`، متد `updateObject` نیز نام جدول را به صورت خودکار صیقل (escape) میدهد.
اگر مشکلی در بهروزرسانی رکورد پیش بیاید، `updateObject` خطا ایجاد خواهد کرد.
نکته مهم: قبل از اجرای `updateObject` باید مطمئن شوید که رکورد مورد نظر وجود دارد، بنابراین معمولاً باید یک بررسی برای وجود رکورد قبل از اقدام به بهروزرسانی انجام دهید.
حذف یک رکورد
برای حذف رکوردها از پایگاه داده، از متد `delete` استفاده میکنیم.
$db = Factory::getContainer()->get('DatabaseDriver');
$query = $db->getQuery(true);
// حذف همه کلیدهای سفارشی برای کاربر با شناسه 1001
$query->delete($db->quoteName('#__user_profiles'))
->where($db->quoteName('user_id') . ' = :user_id')
->where($db->quoteName('profile_key') . ' = :profile_key')
->bind(':user_id', 1001, \Joomla\Database\ParameterType::INTEGER)
->bind(':profile_key', 'custom.%');
$db->setQuery($query);
$result = $db->execute();
علامت ":" در پارامترهایی مثل `:user_id` نمایانگر پارامترهای بایند شده (bound parameters) در پرسوجوهای SQL است و کاربرد اصلی آنها جلوگیری از مشکلات امنیتی مثل **SQL Injection** و بهبود کارایی پرسوجو است.
توضیح دقیقتر:
- وقتی در پرسوجو به جای گذاشتن مقدار مستقیم (مثلاً `user_id = 1001`) از پارامترهایی مانند `:user_id` استفاده میکنیم، یعنی یک متغیر جایگزین تعریف کردهایم.
- سپس در کد، مقدار واقعی این پارامتر به صورت جداگانه توسط متد `bind` یا مشابه آن به پرسوجو متصل (bind) میشود.
- این کار باعث میشود که مقدار پارامتر به صورت ایمن و بدون تاثیرگذاری بر نحو (Syntax) پرسوجو به سرور پایگاه داده ارسال شود.
مزایا:
- امنیت: تغییرات یا مقادیر ورودی که ممکن است باعث حملات SQL Injection شوند، به طور خودکار به شکل امنی پردازش میشوند.
- قابلیت خوانایی و نگهداری کد: پرسوجو واضحتر و منظمتر میشود.
- کاهش خطاهای نحوی: چون مقدار به صورت جداگانه ارسال میشود، احتمال اشتباه در ترکیب رشته پرسوجو کمتر میشود.
مثال ساده:
$query = $db->getQuery(true);
$query->select('*')->from('users')->where('id = :user_id');
$query->bind(':user_id', 123);
در اینجا:
- `:user_id` نقش یک متغیر جایگزین را در پرسوجوی SQL دارد.
- مقدار واقعی آن یعنی `123` هنگام اجرای پرسوجو جایگزین میشود اما به صورت ایمن.