Laravel/queries

من موسوعة حسوب
اذهب إلى التنقل اذهب إلى البحث

قواعد البيانات: منشئ الاستعلامات

مقدمة

يوفّر منشئ استعلامات قاعدة بيانات Laravel واجهة ملائمة وسلسة لإنشاء وإدارة استعلامات قاعدة البيانات. يمكن استخدامه لتنفيذ معظم عمليات قاعدة البيانات بتطبيقك كما أنه يعمل على جميع أنظمة قواعد البيانات المدعومة.

يستخدم منشئ استعلامات Laravel ارتباط المعاملة PDO لحماية تطبيقك من هجمات حقن SQL. لا حاجة لتنظيف السلاسل النصيّة التي تُمرّر كارتباطات.

استرداد النتائج

استرداد جميع الصفوف من الجدول

تستطيع استخدام التابع table على الواجهة الساكنة DB لبدء استعلام. يرد التابع table نسخة منشئ استعلامات فصيحة (fluent) للجدول المحدد، ممّا يسمح لك بربط المزيد من القيود بالاستعلام ثم الحصول على النتائج في النهاية باستخدام التابع get:

<?php

namespace App\Http\Controllers;

use Illuminate\Support\Facades\DB;
use App\Http\Controllers\Controller;

class UserController extends Controller
{
    /**
     * عرض قائمة كل مستخدمي التطبيق.
     *
     * @return Response
     */
    public function index()
    {
        $users = DB::table('users')->get();

        return view('user.index', ['users' => $users]);
    }
}

يعيد التابع get الملف Illuminate\Support\Collection الذي يحتوي على النتائج بحيث تكون كل نتيجة نسخة من الكائن PHP StdClass. تستطيع الوصول لقيمة كل عمود عن طريق الوصول للعمود كخاصية للكائن:

foreach ($users as $user) {
    echo $user->name;
}

استرداد صف أو عمود مفرد من جدول

تستطيع استخدام التابع first إن احتجت لاسترداد صف واحد فقط  من جدول قاعدة البيانات. سيعيد هذا التابع كائن StdClass واحد:

$user = DB::table('users')->where('name', 'John')->first();

echo $user->name;

تستطيع استخراج قيمة واحدة من سجلّ ما باستخدام التابع value إن لم تحتج حتى لصفّ كامل. سيعيد هذا التابع قيمة العمود مباشرة:

$email = DB::table('users')->where('name', 'John')->value('email');

استرداد قائمة قيم الأعمدة

تستطيع استخدام التابع pluck إن رغبت في استرداد مجموعة تحتوي على قيم عمود واحد. سنسترد في هذا المثال، مجموعة من عناوين الوظائف (roles):

$titles = DB::table('roles')->pluck('title');

foreach ($titles as $title) {
    echo $title;
}

تستطيع أيضًا تحديد عمود مفاتيح مخصصّة للمجموعة المردودة:

$roles = DB::table('roles')->pluck('title', 'name');

foreach ($roles as $name => $title) {
    echo $title;
}

تقسيم النتائج (Chunking Results)

من المُقترح استخدام التابع chunk إن إحتجت للعمل مع الآلاف من سجلات قاعدة البيانات. يعيد هذا التابع جزءًا صغيرًا من النتائج كل مرّة ويعطي كل قطعة لنطاق مغلق Closure للمعالجة. هذا التابع مفيد جدًا في كتابة الأوامر Artisan التي تعالج آلاف السجلات. دعنا نعمل مثلًا مع الجدول users كاملًا لكن على أجزاء من 100 سجل في كل مرّة:

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;
});

المجاميع

يوفّر منشئ الاستعلامات أيضًا مجموعة متنوعة من التوابع التجميعية (aggregate methods) مثل count، و max، و min، و avg، و sum. تستطيع مناداة أي من هذه التوابع بعد إنشاء استعلامك:

$users = DB::table('users')->count();

$price = DB::table('orders')->max('price');

تستطيع طبعًا دمج هذه التوابع مع بنود أخرى:

$price = DB::table('orders')
                ->where('finalized', 1)
                ->avg('price');

تحديد ما إذا كانت السجلات موجودة

تستطيع استخدام الأساليب exists و doesntExist بدل استخدام التابع count لتحديد ما إذا طابقت أية سجلات قيود استعلامك:

return DB::table('orders')->where('finalized', 1)->exists();

return DB::table('orders')->where('finalized', 1)->doesntExist();

معاملات عرض السجلّات Selects

تحديد بند Select

قد لا ترغب دائمًا طبعًا في تحديد (select) كافة الأعمدة من جدول قاعدة البيانات. تستطيع تحديد بند select مُخصّص للاستعلام باستخدام التابع select:

$users = DB::table('users')->select('name', 'email as user_email')->get();

يسمح لك التابع distinct باجبار الاستعلام على ردّ النتائج الفريدة:

$users = DB::table('users')->distinct()->get();

تستطيع استخدام التابع addSelect إن كان لديك نسخة منشئ استعلامات مسبقًا ورغبت في إضافة عمود آخر لعموده Select الحالي:

$query = DB::table('users')->select('name');

$users = $query->addSelect('age')->get();

تعبيرات خام

قد تحتاج في بعض الأحيان لاستخدام تعبير خام في استعلام. تستطيع استخدام التابع DB::raw لإنشاء تعبير خام:

$users = DB::table('users')
                     ->select(DB::raw('count(*) as user_count, status'))
                     ->where('status', '<>', 1)
                     ->groupBy('status')
                     ->get();

ملاحظة: ستُضاف التعابير الخامة للاستعلام كسلاسل نصيّة لذا عليك التزام الحذر الشديد كيلا تخلق نقاط ضعف حُقن SQL.

التوابع الخامة

تستطيع أيضًا استخدام التوابع التالية بدلاً من استخدام DB::raw لإدراج تعبير خام في أجزاء مختلفة من استعلامك.

selectRaw

يمكن استخدام التابع selectRaw بدلاً من select(DB::raw(...))‎. يقبل هذا التابع مصفوفةً اختياريّةً من الارتباطات كمتغيّر وسيط ثاني لها:

$orders = DB::table('orders')
                ->selectRaw('price * ? as price_with_tax', [1.0825])
                ->get();

whereRaw / orWhereRaw

whereRaw و orWhereRaw هما تابعان يمكن استخدامها لإضافة بند where خام لاستعلامك. تقبل هذه التوابع مصفوفة اختياريّة من الارتباطات كمتغيّر وسيط ثاني لها:

$orders = DB::table('orders')
                ->whereRaw('price > IF(state = "TX", ?, 100)', [200])
                ->get();

havingRaw / orHavingRaw

havingRaw و orHavingRaw هما تابعان يمكن استخدامها لتعيين سلسلة نصيّة خام كقيمة البند having. تقبل هذه التوابع مصفوفة اختيارية من الارتباطات كمتغيّر وسيط ثاني لها:

$orders = DB::table('orders')
                ->select('department', DB::raw('SUM(price) as total_sales'))
                ->groupBy('department')
                ->havingRaw('SUM(price) > ?', [2500])
                ->get();

orderByRaw

يمكن استخدام التابع orderByRaw لتعيين سلسلة نصيّة خامة كقيمة البند order by:

$orders = DB::table('orders')
                ->orderByRaw('updated_at - created_at DESC')
                ->get();

معاملات ربط الجداول Joins

بند الربط الداخلي (Inner Join Clause)

يمكن استخدام منشئ الاستعلامات لكتابة تعابير الربط (join statements). لإجراء "ربط داخلي" بسيط، تستطيع استخدام التابع join على نسخة منشئ استعلامات. أوّل متغيّر وسيط يُمرّر إلى التابع join هو اسم الجدول الذي تحتاج للربط به في حين أن المُغيّرات الباقية تحدد قيود العمود للربط. تستطيع كما ترى الربط بعدّة جداول في استعلام واحد:

$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();

بند الربط اليساري (Left Join Clause)

إن رغبت في عمل "ربط يساري" بدلاً من "ربط داخلي"، استخدم التابع leftJoin. يملك التابع leftJoin نفس توقيع التابع join:

$users = DB::table('users')
            ->leftJoin('posts', 'users.id', '=', 'posts.user_id')
            ->get();

بند الربط المُتقاطع (Cross Join Clause)

لعمل "ربط مُتقاطع"، استخدم التابع crossJoin مع اسم الجدول الذي ترغب في الربط به. يولّد الربط المُتقاطع نتيجة ديكارتيّة بين الجدول الأول والجدول المربوط:

$users = DB::table('sizes')
            ->crossJoin('colours')
            ->get();

بنود الربط المتقدّمة

تستطيع أيضًا تحديد شروط ربط أكثر تقّدمًا. للبدء، مرّر Closure كالمتغيّر الوسيط الثاني في التابع join. سيتلقّى Closure كائن JoinClause والذي يسمح لك بتحديد القيود على البند join:

DB::table('users')
        ->join('contacts', function ($join) {
            $join->on('users.id', '=', 'contacts.user_id')->orOn(...);
        })
        ->get();

تستطيع استخدام التابعين where و orWhere في الربط إن رغبت في استخدام شرط على نمط "where" في ارتباطاتك (joins). ستُقارن هذه التوابع العمود بالقيمة بدلاً من مقارنة عمودين ببعضهما:

DB::table('users')
        ->join('contacts', function ($join) {
            $join->on('users.id', '=', 'contacts.user_id')
                 ->where('contacts.user_id', '>', 5);
        })
        ->get();

ربط الاستعلام الفرعي ( Sub-Query Joins)

تستطيع استخدام التوابع joinSub leftJoinSub و rightJoinSub و rightJoinSub لربط استعلام باستعلام فرعي. يتلقى كل تابع من هذه التوابع ثلاث مُتغيّرات وسيطة: الاستعلام الفرعي، واسم جدولها المستعار (table alias)، ونطاق مغلق يحدّد الأعمدة ذات الصلة: