- المقدمة
- تنفيذ استعلامات قاعدة البيانات
- تحديد البيانات
- التعبيرات الصريحة
- الضم
- الدمج
- جمل where الأساسية
- جمل Where المتقدمة
- الترتيب, التجميع, الحد و الإزاحة
- الجمل الشرطية
- جمل الإدخال
- جمل التحديث
- الحذف
- الإقفال المتشائم
- التشخيص (Debugging)
يوفر مُنشئ استعلام قاعدة بيانات لارافل (database Query builder) واجهة مريحة وسلسة لإنشاء استعلامات قاعدة البيانات وتنفيذها. يمكن استخدامه لأداء معظم عمليات قاعدة البيانات في تطبيقك ويعمل بشكل مثالي مع جميع أنظمة قواعد البيانات المدعومة من لارافل.
يقوم مُنشئ استعلام لارافل باستخدام ربط المعامل (PDO parameter binding) لحماية تطبيقك من هجمات حقن SQL أو مايعرف ب (SQL injection attacks). لذلك ليست هناك حاجة لتطهير أو تعقيم السلاسل قبل تمريرها إلى مُنشئ الاستعلام كإرتباطات للاستعلام.
{ملاحظة هامة} لا يدعم PDO ربط أسماء الأعمدة. لذلك ، يجب ألا تسمح أبداً للمستخدم بإدخال أسماء الأعمدة التي تشير إليها الاستعلامات ، بما في ذلك أعمدة "الترتيب حسب" (orderBy).
يمكنك استخدام الطريقة table
التي توفرها الواجهة DB
لبدء الاستعلام. يقدم التابع table
مُنشئ استعلام (Query Builder) سلس ومرن للجدول المحدد ، مما يسمح لك بربط المزيد من القيود بالاستعلام ثم استرداد نتائج الاستعلام في النهاية باستخدام الطريقة get
:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\DB;
class UserController extends Controller
{
/**
* عرض قائمة بجميع مستخدمي التطبيق.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$users = DB::table('users')->get();
return view('user.index', ['users' => $users]);
}
}
يعيد التابع get
نسخة Illuminate\Support\Collection
تحتوي على نتائج الاستعلام حيث تكون كل نتيجة عبارة عن نسخة من كائن PHP stdClass
. يمكنك الوصول إلى قيمة كل عمود من خلال الوصول إلى العمود كخاصية لهذا الكائن:
use Illuminate\Support\Facades\DB;
$users = DB::table('users')->get();
foreach ($users as $user) {
echo $user->name;
}
{معلومة} توفر مجموعات لارافل (Laravel Collections) مجموعة متنوعة من الأساليب الفعالة للغاية لإنشاء خرائط للبيانات وتقليلها. لمزيد من المعلومات حول مجموعات لارافل ، راجع ملف collection documentation.
إذا كنت تحتاج إلى استرداد صف واحد فقط من جدول في قاعدة البيانات ، فيمكنك استخدام الطريقة first
للواجهة DB
. ستعيد هذه الطريقة كائن stdClass
واحد:
$user = DB::table('users')->where('name', 'John')->first();
return $user->email;
إذا لم تكن بحاجة إلى صف كامل ، فيمكنك استخراج قيمة واحدة من السجل باستخدام الطريقة value
. ستعيد هذه الطريقة قيمة العمود مباشرة:
$email = DB::table('users')->where('name', 'John')->value('email');
لاسترداد صف واحد من خلال قيمة عمود المعرف (id
) ، استخدم الطريقة find
:
$user = DB::table('users')->find(3);
إذا كنت ترغب باسترداد نسخة Illuminate\Support\Collection
تحتوي على قيم عمود واحد ، فيمكنك استخدام التابع pluck
. في هذا المثال ، سنقوم باسترداد مجموعة من عناوين المستخدمين:
use Illuminate\Support\Facades\DB;
$titles = DB::table('users')->pluck('title');
foreach ($titles as $title) {
echo $title;
}
يمكنك تحديد العمود الذي يجب أن تستخدمه المجموعة الناتجة كمفاتيح من خلال توفير وسيط ثانٍ للتابع pluck
:
$titles = DB::table('users')->pluck('title', 'name');
foreach ($titles as $name => $title) {
echo $title;
}
إذا كنت بحاجة إلى العمل مع الآلاف من سجلات قاعدة البيانات ، ففكر في استخدام طريقة chunk
التي توفرها الواجهة DB
. تسترجع هذه الطريقة جزءاً صغيراً من النتائج في وقت واحد وتضع كل جزء في إغلاق (closure) للمعالجة. على سبيل المثال ، دعنا نسترد جدول users
بأكمله في أجزاء من 100 سجل في المرة الواحدة:
use Illuminate\Support\Facades\DB;
DB::table('users')->orderBy('id')->chunk(100, function ($users) {
foreach ($users as $user) {
//
}
});
يمكنك إيقاف معالجة المزيد من الأجزاء عن طريق إرجاع false
من الإغلاق (closure):
DB::table('users')->orderBy('id')->chunk(100, function ($users) {
// معالجة السجلات...
return false;
});
إذا كنت تقوم بتحديث سجلات قاعدة البيانات أثناء تقسيم النتائج ، فقد تتغير نتائجك الجماعية بطرق غير متوقعة. لذلك إذا كنت تخطط لتحديث السجلات المستردة أثناء التقسيم ، فمن الأفضل دائماً استخدام الطريقة chunkById
. ستعمل هذه الطريقة على ترقيم النتائج تلقائياً بناءً على المفتاح الأساسي للسجل:
DB::table('users')->where('active', false)
->chunkById(100, function ($users) {
foreach ($users as $user) {
DB::table('users')
->where('id', $user->id)
->update(['active' => true]);
}
});
{ملاحظة} عند تحديث أو حذف السجلات أثناء تقسيم النتائج، يمكن أن تؤثر أي تغييرات على المفتاح الأساسي أو المفاتيح الأجنبية على الاستعلام المتقطع (chunk query). مما قد يؤدي إلى عدم تضمين السجلات في النتائج المقسمة.
تعمل طريقة lazy
بشكل مشابه للطريقة chunk
بمعنى أنه ينفذ الاستعلام في أجزاء. ومع ذلك ، بدلاً من تمرير كل جزء إلى استرجاع (Callback)(Callback هو الكود الذي يتم تنفيذه ضمن وسيط لتابع آخر)، فإن الطريقة ()lazy
ترجع LazyCollection
, مما يتيح لك التفاعل مع النتائج دفعة واحدة:
use Illuminate\Support\Facades\DB;
DB::table('users')->orderBy('id')->lazy()->each(function ($user) {
//
});
كما هو الحال مع chunk
، إذا كنت تخطط لتحديث السجلات المستردة أثناء الدوران عليها ، فمن الأفضل استخدام التابعين lazyById
أو lazyByIdDesc
. ستقوم هذه الطرق تلقائياً بترقيم النتائج بناءً على المفتاح الأساسي للسجل:
DB::table('users')->where('active', false)
->lazyById()->each(function ($user) {
DB::table('users')
->where('id', $user->id)
->update(['active' => true]);
});
{ملاحظة} عند تحديث السجلات أو حذفها أثناء الدوران عليها ، يمكن أن تؤثر أي تغييرات على المفتاح الأساسي أو المفاتيح الأجنبية على الاستعلام المتقطع (chunk query). من المحتمل أن يؤدي هذا إلى عدم تضمين بعض السجلات في النتائج.
يوفر مُنشئ الاستعلامات (Query builder) أيضاً مجموعة متنوعة من الطرق لاسترداد القيم الإجمالية مثل count
, max
, min
, avg
, sum
. يمكنك استدعاء أي من هذه التوابع بعد إنشاء استعلامك:
use Illuminate\Support\Facades\DB;
$users = DB::table('users')->count();
$price = DB::table('orders')->max('price');
بالطبع ، يمكنك دمج هذه الطرق مع جمل أخرى لضبط كيفية حساب القيمة الإجمالية:
$price = DB::table('orders')
->where('finalized', 1)
->avg('price');
بدلاً من استخدام طريقة count
لتحديد ما إذا كانت هناك أية سجلات تطابق قيود الاستعلام ، يمكنك استخدام الطريقتين exists
و doesntExist
:
if (DB::table('orders')->where('finalized', 1)->exists()) {
// ...
}
if (DB::table('orders')->where('finalized', 1)->doesntExist()) {
// ...
}
قد لا ترغب دائماً في تحديد جميع الأعمدة من جدول قاعدة البيانات. باستخدام الطريقة select
، يمكنك تخصيص الاستعلام لارجاع أعمدة محددة:
use Illuminate\Support\Facades\DB;
$users = DB::table('users')
->select('name', 'email as user_email')
->get();
تسمح لك الطريقة distinct
بجعل الاستعلام يجلب البيانات بدون تكرار:
$users = DB::table('users')->distinct()->get();
إذا كان لديك استعلام منشىء مسبقاً وترغب في إضافة عمود إلى جملة select
الموجودة به ، فيمكنك استخدام طريقة addSelect
:
$query = DB::table('users')->select('name');
$users = $query->addSelect('age')->get();
قد تحتاج أحياناً إلى إدخال جملة ما في استعلام لإنشاء تعبير صريح ، للقيام بذلك يمكنك استخدام التابع raw
الذي توفره الواجهة DB
:
$users = DB::table('users')
->select(DB::raw('count(*) as user_count, status'))
->where('status', '<>', 1)
->groupBy('status')
->get();
{ملاحطة} سيتم إدخال الجمل الصريحة في الاستعلام كسلاسل بدون تطهير. لذلك يجب أن تكون حذراً للغاية لتجنب إنشاء ثغرات أمنية تسمح بهجمات (SQL injection).
بدلاً من استخدام طريقة DB::raw
، يمكنك أيضاً استخدام الطرق التالية لإدراج تعبير صريح في أجزاء مختلفة من استعلامك. ** تذكر أن لارافل لا يضمن أن يكون أي استعلام يستخدم التعبيرات الصريحة محمي ضد ثغرات حقن SQL. **
يمكن استخدام الطريقة selectRaw
بدلاً من addSelect(DB::raw(...))
. يقبل هذا التابع مصفوفة اختيارية من القيم كوسيط ثاني لها:
$orders = DB::table('orders')
->selectRaw('price * ? as price_with_tax', [1.0825])
->get();
يمكن استخدام الطرق whereRaw
و orWhereRaw
لإدخال جملة where
الصريحة في استعلامك. تقبل هذه التوابع مصفوفة اختيارية من القيم كوسيط ثاني لها:
$orders = DB::table('orders')
->whereRaw('price > IF(state = "TX", ?, 100)', [200])
->get();
يمكن استخدام التابعين havingRaw
و orHavingRaw
لتقديم جملة صريحة كقيمة لجملة having
. تقبل هذه التوابع مصفوفة اختيارية من القيم كوسيط ثاني لها:
$orders = DB::table('orders')
->select('department', DB::raw('SUM(price) as total_sales'))
->groupBy('department')
->havingRaw('SUM(price) > ?', [2500])
->get();
يمكن استخدام طريقة orderByRaw
لتقديم جملة صريحة كقيمة لجملة order by
:
$orders = DB::table('orders')
->orderByRaw('updated_at - created_at DESC')
->get();
يمكن استخدام طريقة groupByRaw
لتقديم جملة صريحة كقيمة لجملة group by
:
$orders = DB::table('orders')
->select('city', 'state')
->groupByRaw('city, state')
->get();
يمكن أيضاً استخدام مُنشئ الاستعلام (Query builder) لإضافة جمل الضم إلى استعلاماتك. لإجراء الضم الداخلي الأساسي، يمكنك استخدام التابع Join
في مُنشئ الاستعلام. حيث المعامل الأول االذي يتم تمريره إلى الطريقة Join
هو اسم الجدول الذي تريد الضم إليه ، بينما تحدد الوسائط المتبقية قيود العمود الخاصة بالضم. يمكنك ضم جداول متعددة في استعلام واحد:
use Illuminate\Support\Facades\DB;
$users = DB::table('users')
->join('contacts', 'users.id', '=', 'contacts.user_id')
->join('orders', 'users.id', '=', 'orders.user_id')
->select('users.*', 'contacts.phone', 'orders.price')
->get();
إذا كنت ترغب في إجراء الضم اليميني "right join" أو الضم اليساري "left join" بدلاً من الضم الداخلي "inner join" ، فاستخدم أساليب leftJoin
أو rightJoin
. هذه التوابع لها نفس وسائط الطريقة join
:
$users = DB::table('users')
->leftJoin('posts', 'users.id', '=', 'posts.user_id')
->get();
$users = DB::table('users')
->rightJoin('posts', 'users.id', '=', 'posts.user_id')
->get();
يمكنك استخدام طريقة crossJoin
لأداء الضم المتقاطع "cross join". تؤدي هذه الطريقة لإنشاء نتيجة ديكارتيّة (تضم جميع عناصر الجدولين) بين الجدول الأول والجدول المضموم:
$sizes = DB::table('sizes')
->crossJoin('colors')
->get();
يمكنك أيضاً تحديد شروط ضم أكثر تقدماً. للبدء ، مرر إغلاقاً (closure) كوسيط ثاني لطريقة join
. سيتلقى الإغلاق(closure) نسخة من Illuminate\Database\Query\JoinClause
والتي تسمح لك بتحديد قيود على جملة join
:
DB::table('users')
->join('contacts', function ($join) {
$join->on('users.id', '=', 'contacts.user_id')->orOn(...);
})
->get();
إذا كنت ترغب في استخدام جملة "where" في جمل الضم الخاصة بك ، فيمكنك استخدام التابعين where
و orWhere
المقدمين من الغرض JoinClause
. بدلاً من مقارنة عمودين ، ستقارن هذه التوابع العمود بقيمة محددة:
DB::table('users')
->join('contacts', function ($join) {
$join->on('users.id', '=', 'contacts.user_id')
->where('contacts.user_id', '>', 5);
})
->get();
يمكنك استخدام أساليب JoinSub
و leftJoinSub
و rightJoinSub
لربط استعلام باستعلام فرعي. تتلقى كل من هذه الطرق ثلاث وسائط: الاستعلام الفرعي والاسم المستعار للجدول الخاص به والإغلاق (closure) الذي يحدد الأعمدة ذات الصلة. في هذا المثال ، سنقوم باسترداد مجموعة من المستخدمين حيث يحتوي كل سجل مستخدم على الطابع الزمني created_at
لآخر مشاركة مدونة نشرها المستخدم:
$latestPosts = DB::table('posts')
->select('user_id', DB::raw('MAX(created_at) as last_post_created_at'))
->where('is_published', true)
->groupBy('user_id');
$users = DB::table('users')
->joinSub($latestPosts, 'latest_posts', function ($join) {
$join->on('users.id', '=', 'latest_posts.user_id');
})->get();
يوفر مُنشئ الاستعلام أيضاً طريقة مناسبة لدمج استعلامين أو أكثر معاً. على سبيل المثال ، يمكنك إنشاء استعلام أولي واستخدام التابع union
لربطه بمزيد من الاستعلامات:
use Illuminate\Support\Facades\DB;
$first = DB::table('users')
->whereNull('first_name');
$users = DB::table('users')
->whereNull('last_name')
->union($first)
->get();
بالإضافة إلى طريقة union
، يوفر مُنشئ الاستعلام طريقة unionAll
. في هذه الطريقة لا تتم إزالة النتائج المكررة للاستعلامات التي تم دمجها. له نفس وسائط الطريقة union
.
يمكنك استخدام الطريقة where
لإضافة جمل "where" إلى الاستعلام. يتطلب الاستدعاء الأساسي للطريقة where
ثلاث وسائط. المعامل الأول هو اسم العمود. المعامل الثاني هو المعامل الرياضي ، والذي يمكن أن يكون أياً من العوامل الرياضية المدعومة من قاعدة البيانات. المعامل الثالث هو القيمة المطلوب مقارنتها بقيمة العمود.
على سبيل المثال ، يقوم طلب البحث التالي بإرجاع المستخدمين بحيث تكون قيمة العمود votes
مساوية لـ 100
وقيمة العمود age
أكبر من 35
ضمن السجل الخاص بكل مستخدم:
$users = DB::table('users')
->where('votes', '=', 100)
->where('age', '>', 35)
->get();
للسهولة ، إذا كنت تريد التحقق من أن العمود يساوي =
قيمة معينة ، فيمكنك تمرير القيمة كمتغير ثانٍ إلى طريقة where
. سيفترض لارافل أنك تريد استخدام المعامل =
أي إن المعامل =
هو المعامل الافتراضي للطريقة "where" في حال عدم تمرير أي معامل:
$users = DB::table('users')->where('votes', 100)->get();
كما ذكرنا سابقاً ، يمكنك استخدام أي معامل يدعمه نظام قاعدة البيانات الخاص بك:
$users = DB::table('users')
->where('votes', '>=', 100)
->get();
$users = DB::table('users')
->where('votes', '<>', 100)
->get();
$users = DB::table('users')
->where('name', 'like', 'T%')
->get();
يمكنك أيضاً تمرير مصفوفة من الشروط إلى الطريقة where
. يجب أن يكون كل عنصر في المصفوفة هو مصفوفة تحتوي على الوسائط الثلاثة التي يتم تمريرها عادةً للطريقة where
:
$users = DB::table('users')->where([
['status', '=', '1'],
['subscribed', '<>', '1'],
])->get();
{ملاحظة} لا يدعم PDO ربط أسماء الأعمدة (binding column names). لذلك ، يجب ألا تسمح للمستخدم بإدخال أسماء الأعمدة التي تشير إليها طلبات البحث ، بما في ذلك الأعمدة الخاصة بترتيب النتائج
order by
.
عند ربط الاستدعاءات معاً بطريقة "where"، يتم ضم جمل "where" معاً باستخدام المعامل and
. ومع ذلك ، يمكنك استخدام التابع orWhere
لربط الاستعلامين باستخدام المعامل or
. يقبل أسلوب orWhere
نفس الوسائط مثل طريقة where
:
$users = DB::table('users')
->where('votes', '>', 100)
->orWhere('name', 'John')
->get();
إذا كنت بحاجة إلى تجميع شرط or
داخل أقواس ، فيمكنك تمرير إغلاق(closure) كوسيط أول للتابع orWhere
:
$users = DB::table('users')
->where('votes', '>', 100)
->orWhere(function($query) {
$query->where('name', 'Abigail')
->where('votes', '>', 50);
})
->get();
المثال أعلاه سيُنتج استعلام SQL التالي:
select * from users where votes > 100 or (name = 'Abigail' and votes > 50)
{ملاحظة} يجب عليك دائماً تجميع استدعاءات
orWhere
لتجنب السلوك غير المتوقع عند تطبيق النطاقات العامة (global scopes).
يدعم لارافل أيضاً الاستعلام عن الأعمدة التي تكون من نوع JSON في قواعد البيانات التي توفر دعماً لأنواع أعمدة JSON. حالياً ، يتضمن هذا MySQL 5.7+ و PostgreSQL و SQL Server 2016 و SQLite 3.9.0 (مع extension JSON1). للاستعلام عن عمود JSON ، استخدم معامل التشغيل <-
:
$users = DB::table('users')
->where('preferences->dining->meal', 'salad')
->get();
يمكنك استخدام whereJsonContains
للاستعلام عن مصفوفات JSON. لا تدعم قاعدة بيانات SQLite هذه الميزة:
$users = DB::table('users')
->whereJsonContains('options->languages', 'en')
->get();
إذا كان تطبيقك يستخدم قواعد بيانات MySQL أو PostgreSQL ، فيمكنك تمرير مجموعة من القيم للتابع whereJsonContains
:
$users = DB::table('users')
->whereJsonContains('options->languages', ['en', 'de'])
->get();
يمكنك استخدام التابع whereJsonLength
للاستعلام عن مصفوفات JSON حسب طولها:
$users = DB::table('users')
->whereJsonLength('options->languages', 0)
->get();
$users = DB::table('users')
->whereJsonLength('options->languages', '>', 1)
->get();
الطريقتين whereBetween / orWhereBetween
تتحقق الطريقة whereBetween
من أن قيمة العمود تقع بين قيمتين:
$users = DB::table('users')
->whereBetween('votes', [1, 100])
->get();
الطريقتين whereNotBetween / orWhereNotBetween
تتحقق الطريقة whereNotBetween
من أن قيمة العمود تقع خارج مجال قيمتين:
$users = DB::table('users')
->whereNotBetween('votes', [1, 100])
->get();
الطرق whereIn / whereNotIn / orWhereIn / orWhereNotIn
تتحقق الطريقة whereIn
من أن قيمة عمود محدد موجودة ضمن قيم مصفوفة محددة:
$users = DB::table('users')
->whereIn('id', [1, 2, 3])
->get();
تتحقق الطريقة whereNotIn
من أن قيمة عمود محدد غير موجودة ضمن قيم مصفوفة محددة:
$users = DB::table('users')
->whereNotIn('id', [1, 2, 3])
->get();
{ملاحظة} إذا كنت تضيف مصفوفة كبيرة من روابط الأعداد الصحيحة إلى استعلامك ، فيمكن استخدام الطرق
whereIntegerInRaw
أوwhereIntegerNotInRaw
لتقليل استخدام الذاكرة بشكل كبير.
الطرق whereNull / whereNotNull / orWhereNull / orWhereNotNull
تتحقق الطريقة whereNull
من أن قيمة العمود المحدد هي NULL
:
$users = DB::table('users')
->whereNull('updated_at')
->get();
تتحقق الطريقة whereNotNull
من أن قيمة العمود ليست NULL
:
$users = DB::table('users')
->whereNotNull('updated_at')
->get();
الطرق whereDate / whereMonth / whereDay / whereYear / whereTime
يمكن استخدام الطريقة whereDate
لمقارنة قيمة العمود بتاريخ:
$users = DB::table('users')
->whereDate('created_at', '2016-12-31')
->get();
يمكن استخدام الطريقة whereMonth
لمقارنة قيمة العمود بشهر معين:
$users = DB::table('users')
->whereMonth('created_at', '12')
->get();
يمكن استخدام الطريقة whereDay
لمقارنة قيمة العمود بيوم معين من الشهر:
$users = DB::table('users')
->whereDay('created_at', '31')
->get();
يمكن استخدام الطريقة whereYear
لمقارنة قيمة العمود بسنة معينة:
$users = DB::table('users')
->whereYear('created_at', '2016')
->get();
يمكن استخدام الطريقة whereTime
لمقارنة قيمة العمود بوقت محدد:
$users = DB::table('users')
->whereTime('created_at', '=', '11:20:45')
->get();
الطريقتين whereColumn / orWhereColumn
يمكن استخدام التابع whereColumn
للتحقق من تساوي عمودين:
$users = DB::table('users')
->whereColumn('first_name', 'last_name')
->get();
يمكنك أيضاً تمرير عامل مقارنة إلى الطريقة whereColumn
:
$users = DB::table('users')
->whereColumn('updated_at', '>', 'created_at')
->get();
يمكنك أيضاً تمرير مصفوفة من مقارنات الأعمدة إلى الطريقة whereColumn
. سيتم ضم هذه الشروط باستخدام المعامل and
:
$users = DB::table('users')
->whereColumn([
['first_name', '=', 'last_name'],
['updated_at', '>', 'created_at'],
])->get();
قد تحتاج أحياناً إلى تجميع عدة جمل "where" داخل أقواس من أجل تحقيق التجميع المنطقي المطلوب للاستعلام. في الواقع ، يجب عليك دائماً تجميع الاستدعاءات بالطريقة orWhere
بين قوسين لتجنب سلوك غير متوقع للاستعلام. لتحقيق ذلك ، يمكنك تمرير إغلاق(closure) للطريقة where
:
$users = DB::table('users')
->where('name', '=', 'John')
->where(function ($query) {
$query->where('votes', '>', 100)
->orWhere('title', '=', 'Admin');
})
->get();
كما ترى ، فإن تمرير إغلاق في طريقة where
يوجه مُنشئ الاستعلام(Query builder) لبدء مجموعة قيود. سيتلقى الإغلاق كوسيط له نسخة عن مُنشئ الاستعلام يمكنك استخدامه لتعيين القيود التي يجب تضمينها داخل مجموعة الأقواس. المثال أعلاه سينتج جملة SQL التالية:
select * from users where name = 'John' and (votes > 100 or title = 'Admin')
{ملاحظة} يجب عليك دائماً تجميع استدعاءات "orWhere" لتجنب السلوك غير المتوقع عند تطبيق النطاقات العامة (global scopes).
تسمح لك الطريقة whereExists
بكتابة جمل "where exists" الموجودة في لغة SQL. تقبل الطريقة whereExists
إغلاقاً (closure) يتلقى كوسيط له نسخة عن مُنشئ الاستعلام ، مما يسمح لك بتعريف الاستعلام الذي يجب وضعه داخل جملة"exists":
$users = DB::table('users')
->whereExists(function ($query) {
$query->select(DB::raw(1))
->from('orders')
->whereColumn('orders.user_id', 'users.id');
})
->get();
سينتج الاستعلام أعلاه جملة SQL التالية:
select * from users
where exists (
select 1
from orders
where orders.user_id = users.id
)
قد تحتاج في بعض الأحيان إلى إنشاء جملة "where" التي تقارن نتائج استعلام فرعي بقيمة معينة. يمكنك تحقيق ذلك بتمرير إغلاق(closure) وقيمة إلى الطريقة where
. على سبيل المثال، سيقوم الاستعلام التالي باسترداد جميع المستخدمين الذين لديهم "membership" حديثة من نوع (Pro):
use App\Models\User;
$users = User::where(function ($query) {
$query->select('type')
->from('membership')
->whereColumn('membership.user_id', 'users.id')
->orderByDesc('membership.start_date')
->limit(1);
}, 'Pro')->get();
أو قد تحتاج إلى إنشاء جملة "where" تقارن عموداً بنتائج استعلام فرعي. يمكنك تحقيق ذلك بتمرير اسم العمود ومعامل المقارنة وإغلاق إلى طريقة where
. على سبيل المثال ، سيقوم الاستعلام التالي باسترداد جميع سجلات الدخل حيث يكون المبلغ أقل من المتوسط ؛
use App\Models\Income;
$incomes = Income::where('amount', '<', function ($query) {
$query->selectRaw('avg(i.amount)')->from('incomes as i');
})->get();
{ملاحظة} الطرق
whereFullText
وorWhereFullText
مدعومة من MySQL و PostgreSQL حالياً.
يمكن استخدام الطريقتين whereFullText
و orWhereFullText
لإضافة جمل "Full text where" إلى طلب البحث للأعمدة التي لديها full text indexes. سيتم تحويل هذه التوابع إلى جملة SQL المناسبة لنظام قاعدة البيانات الأساسي بواسطة لارافل. على سبيل المثال ، سيتم إنشاء جملة MATCH AGAINST
للتطبيقات التي تستخدم MySQL:
$users = DB::table('users')
->whereFullText('bio', 'web developer')
->get();
تسمح لك الطريقة orderBy
بفرز نتائج الاستعلام حسب عمود معين. يجب أن يكون الوسيط الأول الذي تقبله الطريقة orderBy
هو العمود الذي تريد الفرز وفقاً له ، بينما يحدد الوسيط الثاني اتجاه الفرز وقد يكون إما تصاعدي asc
أو تنازلي desc
:
$users = DB::table('users')
->orderBy('name', 'desc')
->get();
للفرز حسب عدة أعمدة ، يمكنك ببساطة استدعاء orderBy
عدة مرات حسب الضرورة:
$users = DB::table('users')
->orderBy('name', 'desc')
->orderBy('email', 'asc')
->get();
تسمح لك الطريقتان latest
و oldest
بطلب النتائج حسب التاريخ بسهولة. بشكل افتراضي ، سيتم ترتيب النتيجة حسب عمود الجدول "created_at". أو يمكنك تمرير اسم العمود الذي ترغب بالفرز بواسطته:
$user = DB::table('users')
->latest()
->first();
يمكن استخدام الطريقة inRandomOrder
لفرز نتائج الاستعلام عشوائياً. على سبيل المثال ، يمكنك استخدام هذا التابع لجلب مستخدم عشوائي:
$randomUser = DB::table('users')
->inRandomOrder()
->first();
تزيل الطريقة reorder
جميع جمل "order by" التي تم تطبيقها مسبقاً على الاستعلام:
$query = DB::table('users')->orderBy('name');
$unorderedUsers = $query->reorder()->get();
يمكنك تمرير عمود محدد واتجاه ترتيب تصاعدي(asc) أو تنازلي (desc) عند استدعاء التابع reorder
لإزالة كل جمل "order by" الحالية وتطبيق ترتيب جديد تماماً على الاستعلام:
$query = DB::table('users')->orderBy('name');
$usersOrderedByEmail = $query->reorder('email', 'desc')->get();
كما قد تتوقع ، يمكن استخدام الطريقتين groupBy
و having
لتجميع نتائج الاستعلام. وسائط الطريقة having
مشابهة لوسائط الطريقة where
:
$users = DB::table('users')
->groupBy('account_id')
->having('account_id', '>', 100)
->get();
يمكنك استخدام الطريقة havingBetween
لتصفية النتائج ضمن مجال معين:
$report = DB::table('orders')
->selectRaw('count(id) as number_of_orders, customer_id')
->groupBy('customer_id')
->havingBetween('number_of_orders', [5, 15])
->get();
يمكنك تمرير عدة متغيرات للتابع groupBy
للتجميع حسب عدة أعمدة:
$users = DB::table('users')
->groupBy('first_name', 'status')
->having('account_id', '>', 100)
->get();
لإنشاء جمل having
أكثر تقدماً ، راجع طريقة havingRaw
.
يمكنك استخدام الطريقتين skip
و take
للحد من عدد النتائج التي يتم إرجاعها من الاستعلام أو لتخطي عدد معين من النتائج في الاستعلام:
$users = DB::table('users')->skip(10)->take(5)->get();
بدلاً من ذلك ، يمكنك استخدام الطريقتين Limit
و Offset
. هاتان الطريقتان مكافئتان وظيفياً للطريقتين take
و skip
، على التوالي:
$users = DB::table('users')
->offset(10)
->limit(5)
->get();
قد ترغب أحياناً في تطبيق جمل استعلام معينة على إحدى الاستعلامات بناءً على شرط ما. على سبيل المثال ، قد ترغب في تطبيق جملة where
فقط إذا كانت هناك قيمة إدخال معينة موجودة في طلب HTTP الوارد. يمكنك القيام بذلك باستخدام الطريقة when
:
$role = $request->input('role');
$users = DB::table('users')
->when($role, function ($query, $role) {
return $query->where('role_id', $role);
})
->get();
لا تنفذ الطريقة when
الإغلاق (closure) المحدد إلا عندما يكون تقييم المعامل الأول true
. أما إذا كان تقييمه false
، فلن يتم تنفيذ الإغلاق. لذلك ، في المثال أعلاه ، سيتم استدعاء الإغلاق المعطى للطريقة when
فقط إذا كان حقل role
موجوداً في الطلب الوارد وتقييمه true
.
يمكنك تمرير إغلاق آخر كمعامل ثالث للتابع when
. لن يتم تنفيذ هذا الإغلاق إلا إذا تم تقييم المعامل الأول على أنه false
.
أي إنه عندما يكون تقييم المعامل الأول true
سيتم تنفيذ الإغلاق الأول, وإذا كان تقييم المعامل الأول false
سيتم تنفيذ الإغلاق الثاني.
لتوضيح كيفية استخدام هذه الميزة ، سنستخدمها لتكوين الترتيب الافتراضي للاستعلام:
$sortByVotes = $request->input('sort_by_votes');
$users = DB::table('users')
->when($sortByVotes, function ($query, $sortByVotes) {
return $query->orderBy('votes');
}, function ($query) {
return $query->orderBy('name');
})
->get();
يوفر مُنشئ الاستعلام (Query builder) أيضاً الطريقة insert
يمكن استخدامها لإدراج السجلات في جدول قاعدة البيانات. تقبل الطريقة insert
مصفوفة من أسماء الأعمدة والقيم:
DB::table('users')->insert([
'email' => 'kayla@example.com',
'votes' => 0
]);
يمكنك إدراج عدة سجلات مرة واحدة عن طريق تمرير مصفوفة من المصفوفات. تمثل كل مصفوفة سجلاً تريد إدراجه في الجدول:
DB::table('users')->insert([
['email' => 'picard@example.com', 'votes' => 0],
['email' => 'janeway@example.com', 'votes' => 0],
]);
الطريقة insertOrIgnore
تتجاهل الأخطاء أثناء إدراج السجلات في قاعدة البيانات:
DB::table('users')->insertOrIgnore([
['id' => 1, 'email' => 'sisko@example.com'],
['id' => 2, 'email' => 'archer@example.com'],
]);
{ملاحظة} ستتجاهل الطريقة
insertOrIgnore
السجلات المكررة وقد تتجاهل أيضاً الأنواع الأخرى من الأخطاء اعتماداً على محرك قاعدة البيانات. على سبيل المثال ، "insertOrIgnore` سوف يتجاوز الوضع الصارم لـ MySQL.
إذا كان الجدول يحتوي على معرّف (ID) يتزايد تلقائياً ، فاستخدم الطريقة insertGetId
لإدراج سجل ثم استرداد المعرّف:
$id = DB::table('users')->insertGetId(
['email' => 'john@example.com', 'votes' => 0]
);
{ملاحظة} عند استخدام PostgreSQL ، تتوقع طريقة
insertGetId
تسمية عمود الزيادة التلقائية باسمid
. إذا كنت ترغب في استرداد المعرف من سلسلة مختلفة ، فيمكنك تمرير اسم العمود كمعامل ثاني إلى طريقةinsertGetId
.
ستقوم الطريقة upsert
بإدراج السجلات غير الموجودة وتحديث السجلات الموجودة بالفعل بقيم جديدة تحددها أنت. يتكون المعامل الأول للطريقة من القيم المراد إدراجها أو تحديثها ، بينما يحدد المعامل الثاني العمود (أو الأعمدة) التي تحدد السجلات بشكل فريد ضمن الجدول المرتبط. المعامل الثالث والأخير للطريقة upsert
عبارة عن مصفوفة من الأعمدة التي يجب تحديثها إذا كان السجل المطابق موجوداً بالفعل في قاعدة البيانات:
DB::table('flights')->upsert([
['departure' => 'Oakland', 'destination' => 'San Diego', 'price' => 99],
['departure' => 'Chicago', 'destination' => 'New York', 'price' => 150]
], ['departure', 'destination'], ['price']);
في المثال أعلاه ، سيحاول لارافل إدخال سجلين. إذا كان السجل موجوداً بالفعل بنفس قيم العمودين departure
و destination
، فسيحدّث لارافل عمود price
في ذلك السجل.
{ملاحظة} تتطلب جميع قواعد البيانات باستثناء SQL Server أن يكون للأعمدة الموجودة في المعامل الثاني للطريقة
upsert
فهرس أساسي "primary" أو فريد "unique". بالإضافة إلى ذلك ، يتجاهل مشغل قاعدة بيانات MySQL المعامل الثاني لطريقةupsert
ويستخدم دائماً الفهارس الأولية والفريدة للجدول لاكتشاف السجلات الموجودة.
بالإضافة إلى إدراج السجلات في قاعدة البيانات ، يمكن لمُنشئ الاستعلام (Query builder) أيضاً تحديث السجلات الموجودة باستخدام الطريقة update
. تقبل الطريقة update
، مثل الطريقة insert
مصفوفة من أزواج الأعمدة والقيم التي تشير إلى الأعمدة المراد تحديثها. تعرض الطريقة update
عدد الصفوف المتأثرة. يمكنك تقييد استعلام update
باستخدام جمل where
:
$affected = DB::table('users')
->where('id', 1)
->update(['votes' => 1]);
قد ترغب أحياناً في تحديث سجل موجود في قاعدة البيانات أو إنشائه في حالة عدم وجود سجل مطابق. في هذا السيناريو ، يمكن استخدام الطريقة updateOrInsert
. تقبل الطريقة updateOrInsert
وسيطين: مصفوفة من الشروط يمكن من خلالها العثور على السجل ، ومصفوفة من أزواج الأعمدة والقيم التي تشير إلى الأعمدة المراد تحديثها.
ستحاول الطريقة updateOrInsert
تحديد موقع سجل قاعدة بيانات مطابق باستخدام أزواج العمود و القيمة المحددة في المعامل الأول. إذا كان السجل موجوداً , فسيتم تحديثه بالقيم الموجودة في المعامل الثاني. إذا تعذر العثور على السجل ، فسيتم إدراج سجل جديد بالسمات المدمجة لكل من المعاملين:
DB::table('users')
->updateOrInsert(
['email' => 'john@example.com', 'name' => 'John'],
['votes' => '2']
);
عند تحديث عمود JSON ، يجب استخدام <-
لتحديث المفتاح المناسب في كائن JSON. هذه العملية مدعومة في MySQL 5.7+ و PostgreSQL 9.5+:
$affected = DB::table('users')
->where('id', 1)
->update(['options->enabled' => true]);
يوفر مُنشئ الاستعلام (Query builder) أيضاً طرقاً ملائمة لزيادة أو إنقاص قيمة عمود معين. تقبل كلتا الطريقتين وسيط واحد على الأقل وهي العمود المراد تعديله. يمكن تقديم وسيط ثاني لتحديد المقدار الذي يجب زيادة العمود به أو إنقاصه:
DB::table('users')->increment('votes');
DB::table('users')->increment('votes', 5);
DB::table('users')->decrement('votes');
DB::table('users')->decrement('votes', 5);
يمكنك أيضاً تحديد أعمدة إضافية لتحديثها أثناء العملية:
DB::table('users')->increment('votes', 1, ['name' => 'John']);
يمكن استخدام الطريقة delete
في مُنشئ الاستعلام (Query builder) لحذف السجلات من الجدول. تعرض الطريقة delete
عدد الصفوف المتأثرة. يمكنك تقييد جمل delete
بإضافة جمل where
قبل استدعاء الطريقة delete
:
$deleted = DB::table('users')->delete();
$deleted = DB::table('users')->where('votes', '>', 100)->delete();
إذا كنت ترغب في اقتطاع جدول بأكمله ، مما سيؤدي إلى إزالة جميع السجلات من الجدول وإعادة تعيين معرف التزايد التلقائي إلى الصفر ، فيمكنك استخدام طريقة truncate
:
DB::table('users')->truncate();
عند اقتطاع قاعدة بيانات PostgreSQL ، سيتم تطبيق السلوك CASCADE
. هذا يعني أنه سيتم حذف جميع السجلات ذات الصلة بالمفتاح الأجنبي(foreign key) في الجداول الأخرى أيضاً.
يتضمن مُنشئ الاستعلام أيضاً بعض الوظائف لمساعدتك على تحقيق "القفل التشاؤمي" عند تنفيذ جمل select
. لتنفيذ جملة باستخدام "قفل مشترك" (shared lock) ، يمكنك استدعاء طريقة sharedLock
. يمنع القفل المشترك تعديل الصفوف المحددة حتى يتم تنفيذ معاملتك (transaction):
DB::table('users')
->where('votes', '>', 100)
->sharedLock()
->get();
بدلاً من ذلك ، يمكنك استخدام طريقة lockForUpdate
. يمنع قفل "التحديث" تعديل السجلات المحددة أو تحديدها باستخدام قفل مشترك آخر:
DB::table('users')
->where('votes', '>', 100)
->lockForUpdate()
->get();
يمكنك استخدام التابعين dd
و dump
أثناء بناء استعلام لتفريغ روابط الاستعلام الحالية و SQL. ستعرض الطريقة dd
معلومات التشخيص ثم تتوقف عن تنفيذ الطلب. ستعرض الطريقة dump
معلومات التشخيص ولكنه يسمح للطلب بمتابعة التنفيذ:
DB::table('users')->where('votes', '>', 100)->dd();
DB::table('users')->where('votes', '>', 100)->dump();