اكتشف إطار PHP Laravel


الدرس: منشئ الاستعلام Query Builder


الصفحة السابقة
لقد استخدمنا حتى الآن Eloquent لإنشاء طلباتنا. على الرغم من أهميته ، فمن الضروري في بعض الأحيان توليد طلبات تتجاوز مهاراته. هذا هو المكان الذي يأتي فيه Query Builder، وهو أداة عملية لإنشاء الطلبات و ذلك مع بناء جملة صريح و واضح وهو الرفيق المثالي لـ Eloquent .

المعطيات


الترحيل
لإجراء الاختبارات ، سنحتاج إلى بيانات. إنشاء ترحيل جديد:

php artisan make:migration create_livres_table
وأدخل هذا الكود:

<?php

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateLivresTable extends Migration {

    /**
     * Run the migrations.
     *
     * @return  void
     */
    public function up()
    {
        Schema::create('auteurs', function(Blueprint $table) {
            $table->increments('id');
            $table->timestamps();
            $table->string('nom', 100)->unique();
        });
        Schema::create('editeurs', function(Blueprint $table) {
            $table->increments('id');
            $table->timestamps();
            $table->string('nom', 100)->unique();
        });
        Schema::create('livres', function(Blueprint $table) {
            $table->increments('id');
            $table->timestamps();
            $table->string('titre', 100);
            $table->integer('editeur_id')->unsigned();
            $table->text('description');
        });
        Schema::create('auteur_livre', function(Blueprint $table) {
            $table->increments('id');
            $table->integer('auteur_id')->unsigned();
            $table->integer('livre_id')->unsigned();
        });
        Schema::table('livres', function(Blueprint $table) {
            $table->foreign('editeur_id')->references('id')->on('editeurs')
                        ->onDelete('restrict')
                        ->onUpdate('restrict');
        });
        Schema::table('auteur_livre', function(Blueprint $table) {
            $table->foreign('auteur_id')->references('id')->on('auteurs')
                        ->onDelete('restrict')
                        ->onUpdate('restrict');
        });
        Schema::table('auteur_livre', function(Blueprint $table) {
            $table->foreign('livre_id')->references('id')->on('livres')
                        ->onDelete('restrict')
                        ->onUpdate('restrict');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return  void
     */
    public function down()
    {
        Schema::table('livres', function(Blueprint $table) {
            $table->dropForeign('livres_editeur_id_foreign');
        });
        Schema::table('auteur_livre', function(Blueprint $table) {
            $table->dropForeign('auteur_livre_auteur_id_foreign');
        });
        Schema::table('auteur_livre', function(Blueprint $table) {
            $table->dropForeign('auteur_livre_livre_id_foreign');
        });
        Schema::drop('auteur_livre');
        Schema::drop('livres');
        Schema::drop('auteurs');
        Schema::drop('editeurs');
    }

}
وابدأ الترحيل:

php artisan migrate
والنتيجة هي إنشاء هذه الجداول 4 مع هذه العلاقات:
framework Laravel MVC
الجداول 4 وعلاقاتهم
لدينا علاقات:
  • 1:n بين الناشرين والكتب ،
  • n: n  بين المؤلفين والكتب.
population
بالنسبة للـ population ، سوف نستخدم مكتبة  fzaninotto / Faker  التي يتم تحميلها تلقائيا بواسطة Laravel . تسمح لك هذه المكتبة بتوليد أسماء وعناوين ونصوص وأرقام ... بكل سهولة. هذا عملي للغاية ، على سبيل المثال عند إجراء الاختبارات.
يدمج Laravel هذه المكتبة ويسمح بإنشاء "مصانع نموذجية ( Model Factories) " . انظر إلى هذا الملف:
framework Laravel MVC
Model Factories
مع هذا الكود:

<?php

/*
|--------------------------------------------------------------------------
| Model Factories
|--------------------------------------------------------------------------
|
| Here you may define all of your model factories. Model factories give
| you a convenient way to create models for testing and seeding your
| database. Just tell the factory how a default model should look.
|
*/

$factory->define(App\User::class, function (Faker\Generator $faker) {
    return [
        'name' => $faker->name,
        'email' => $faker->email,
        'password' => bcrypt(str_random(10)),
        'remember_token' => str_random(10),
    ];
});
لديك مثال مع مصنع لمُجسم User . لذلك سنقوم بإنشاء مصانعنا لنماذجنا:

<?php
$factory->define(App\Editeur::class, function (Faker\Generator $faker) {
    return [
        'nom' => $faker->name,
    ];
});

$factory->define(App\Auteur::class, function (Faker\Generator $faker) {
    return [
        'nom' => $faker->name,
    ];
});

$factory->define(App\Livre::class, function (Faker\Generator $faker) {
    return [
        'titre' => $faker->sentence(3),
        'description' => $faker->text,
        'editeur_id' => $faker->numberBetween(1, 40),
    ];
});
ثم نوفر هذا الكود في DatabaseSeeder :

<?php

use Illuminate\Database\Seeder;
use Faker\Factory;

class DatabaseSeeder extends Seeder {

    /**
     * Run the database seeds.
     *
     * @return  void
     */
    public function run()
    {
        factory(App\Editeur::class, 40)->create();
        factory(App\Auteur::class, 40)->create();  
        factory(App\Livre::class, 80)->create(); 
        
        for ($i = 1; $i < 41; $i++) {
            $number = rand(2, 8);
            for ($j = 1; $j <= $number; $j++) {
                DB::table('auteur_livre')->insert([
                    'livre_id' => rand(1, 40),
                    'auteur_id' => $i
                ]);
            }
        }

    }

}
ثم إطلاق population :

php artisan db:seed
سيكون لدينا 40 ناشرًا و 40 مؤلفًا بأسماء عشوائية. سيتم أيضًا تخصيص 80 كتابًا للناشرين وأيضًا العلاقات بين الكتب والمؤلفين. ما يكفي لتقديم طلبات بهدوء.
نحتاج أيضًا إلى المُجسمات لجعلها تعمل إذا أردنا استخدام Eloquent . للناشرين:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Editeur extends Model
{

    public function livres()
    {
        return $this->hasMany('App\Livre');
    }

}
للمؤلفين:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Auteur extends Model
{

    public function livres()
    {
        return $this->belongsToMany('App\Livre');
    }

}
للكتب:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Livre extends Model
{

    public function auteurs()
    {
        return $this->belongsToMany('App\Auteur');
    }

    public function editeur()
    {
        return $this->belongsTo('App\Editeur');
    }

}

الاختيارات


قائمة الناشرين
لنبدأ بأشياء بسيطة ، نريد جميع الناشرين . مع Eloquent فمن السهل:

<?php
$editeurs = App\Editeur::all();
foreach ($editeurs as $editeur) {
    echo $editeur->nom, '
'; }
يمنحك هذا القائمة الكاملة للناشرين. باستخدام Query Builder ، يكون بناء الجملة كما يلي:

<?php
$editeurs = DB::table('editeurs')->get();
foreach ($editeurs as $editeur) {
    echo $editeur->nom, '
'; }
يجب عليك تعيين الجدول ثم استخدام الطريقة get . والنتيجة هي نفسها.
جدول قيم العمود
للحصول على مجموعة من قيم الأعمدة ، استخدم الطريقة lists  :

<?php
$editeurs = DB::table('editeurs')->lists('nom');
foreach ($editeurs as $editeur) {
    echo $editeur, '
'; }
ما الذي يعمل أيضًا مع Eloquent :

<?php
$editeurs = App\Editeur::lists('nom');
foreach ($editeurs as $editeur) {
    echo $editeur, '
'; }
الذي يتوافق مع استعلام SQL هذا:

select nom from editeurs
سطر معين
إذا كنا نريد العثور على سطر معين ، مع Eloquent :

<?php
$editeur = App\Editeur::find(10);
echo $editeur->nom;
سيكون لدينا اسم المحرر الذي يحمل الرقم 10. مع Query Builder ، يجب عليك كتابة:

<?php
$editeur = DB::table('editeurs')->whereId(10)->first();
echo $editeur->nom;
نحدد الجدول ونحدد المعرف بالطريقة where  ونأخذ السجل الأول ( first) .
في SQL الخالص نكتب:

SELECT * FROM editeurs WHERE id = 10
عمود معزول
يمكنك فقط تحديد عمود باستخدام الطريقة pluck  :

<?php
$editeur = DB::table('editeurs')->whereId(10)->pluck('nom');
dd($editeur);
ما في SQL هو مكتوب:

select nom from editeurs where id = 10
يمكنك أيضًا استعمال select عمود واحد لتحديد الأعمدة:

<?php
$editeurs = App\Editeur::select('nom')->get();
foreach ($editeurs as $editeur) {
  echo $editeur->nom, '
'; }
أو ببساطة باستخدام Query Builder :

<?php
$editeurs = DB::table('editeurs')->select('nom')->get();
foreach ($editeurs as $editeur) {
  echo $editeur->nom, '
'; }
الطلب مرة أخرى هذا:

select nom from editeurs
أسطر مختلفة
يمكننا أيضًا استخدام الطريقة distinct  للحصول على أسطر مختلفة:

<?php
$livres = App\Livre::select('editeur_id')->distinct()->get();
foreach ($livres as $livre) {
    echo $livre->editeur_id, '
'; }
وفي إصدار Query Builder :

<?php
$livres = DB::table('livres')->select('editeur_id')->distinct()->get();
foreach ($livres as $livre) {
    echo $livre->editeur_id, '
'; }
باستخدام استعلام SQL هذا:

select distinct editeur_id from livres
عدة شروط
لقد رأينا أنه يمكننا استخدام الطريقة where، يمكننا أن نظيف إليها الطريقة orWhere  :

<?php
$livres = App\Livre::where('titre', '<', 'c')
->orWhere('titre', '>', 'v')
->get();
foreach ($livres as $livre) {
    echo $livre->titre, '
'; }
في نسخة Quider Buider :

<?php
$livres = DB::table('livres')
->where('titre', '<', 'c')
->orWhere('titre', '>', 'v')
->get();
foreach ($livres as $livre) {
    echo $livre->titre, '
'; }
باستخدام استعلام SQL هذا:

select * from livres where titre < 'c' or titre > 'v'
تأطير القيم
يمكننا صياغة القيم باستخدام whereBetween  :

<?php
$livres = App\Livre::whereBetween('titre', array('g', 'k'))->get();
foreach ($livres as $livre) {
    echo $livre->titre, '
'; }
في إصدار Query Builder :

<?php
$livres = DB::table('livres')->whereBetween('titre', array('g', 'k'))->get();
foreach ($livres as $livre) {
    echo $livre->titre, '
'; }
باستخدام استعلام SQL هذا:

select * from livres where titre between 'g' and 'k'
يمكننا أن نقوم بالعكس مع whereNotBetween :

<?php
$livres = App\Livre::whereNotBetween ('titre', array('g', 'k'))->get();
foreach ($livres as $livre) {
    echo $livre->titre, '
'; }
في إصدار Query Builder :

<?php
$livres = DB::table('livres')->whereNotBetween ('titre', array('g', 'k'))->get();
foreach ($livres as $livre) {
    echo $livre->titre, '
'; }
باستخدام استعلام SQL هذا:

select * from livres where titre not between 'g' and 'k'
أخذ القيم في جدول
يمكننا أيضًا أخذ القيم في صفيف باستخدام whereIn  :

<?php
$livres = App\Livre::whereIn('editeur_id', [10, 11, 12])->get();
foreach ($livres as $livre) {
    echo $livre->titre, '
'; }
في إصدار Query Builder :

<?php
$livres = DB::table('livres')->whereIn('editeur_id', [10, 11, 12])->get();
foreach ($livres as $livre) {
    echo $livre->titre, '
'; }
باستخدام استعلام SQL هذا:

select * from livres where editeur_id in ('10', '11', '12')
التنظيم والجمع
يمكننا أيضا تنظيم وجمع:

<?php
$livres = App\Livre::
select('editeur_id', DB::raw('count(id) as livre_count'))
->groupBy('editeur_id')
->get();
foreach ($livres as $livre) {
    echo $livre->editeur_id . ' => ' . $livre->livre_count, '
'; }
نحن هنا نقوم بتجميع الاسطر حسب الناشر ونقوم بحساب الكتب . البند select  يستحق تعليق صغير. عندما لا تعرف كيفية القيام بشيء ما باستخدام أداة Query Builder ، يكون لديك دائمًا مورد لاستخدام تعبير أولي معه DB::raw  كما فعلت هنا لاستخدام دالة count  SQL .
إذا كنت تستخدم تعبيرًا خامًا في استعلام ، فهو ليس محصنًا من حقن SQL ، لذلك يجب عليك اتخاذ تدابير الأمان بنفسك.
في إصدار Query Builder :

<?php
$livres = DB::table('livres')
->select('editeur_id', DB::raw('count(id) as livre_count'))
->groupBy('editeur_id')
->get();
foreach ($livres as $livre) {
    echo $livre->editeur_id . ' => ' . $livre->livre_count, '
'; }
باستخدام استعلام SQL هذا:

select editeur_id, count(id) as livre_count from livres group by editeur_id

ربط الصلة join


ابحث عن عناوين الكتب للناشر الذي لدينا معرفه
في الوقت الحالي ، رأينا طلبات تتعلق بجدول واحد فقط ، وهو ليس الأكثر انتشارًا. عندما يتعلق الأمر بجدولين ، يجب أن ننضم. فكر في المثال الأول: أريد عناوين الكتب لناشر لدي معرفه:

<?php
$livres = App\Editeur::find(11)->livres;
foreach ($livres as $livre) {
    echo $livre->titre, '
'; }
Eloquent مرتاح لهذا النوع من الأبحاث. من خلال Query Builder ، يتعين علينا القيام بما يلي:

<?php
$livres = DB::table('editeurs')
->where('editeurs.id', 11)
->join('livres', 'livres.editeur_id', '=', 'editeurs.id')
->get();
foreach ($livres as $livre) {
    echo $livre->titre, '
'; }
يتم الانضمام مع الطريقة join . الطلبات التي تم إنشاؤها في الحالتين ليست هي نفسها ، مع Eloquent لدينا اثنين:

select * from editeurs where id = '11' limit 1
select * from livres where livres.editeur_id = '11'
ومع Query Builder واحد فقط:

select * from editeurs inner join livres on livres.editeur_id = editeurs.id where editeurs.id = '11'
في الواقع Eloquent في هذه الحالة لا يربط الصلة.
ابحث عن كتب المؤلف الذي تعرف اسمه
الآن دعنا نبحث عن كتب المؤلف الذي نعرف اسمه:

<?php
$livres = App\Livre::whereHas('auteurs', function($q)
{
    $q->whereNom('Osbaldo White');
})->get();
foreach ($livres as $livre) {
    echo $livre->titre, '
'; }
مرة أخرى ، Eloquent بأناقة يفلت من العقاب. يقوم بإنشاء الطلب التالي:

select * from livres where (select count(*) from auteurs inner join auteur_livre on auteurs.id = auteur_livre.auteur_id where auteur_livre.livre_id = livres.id and nom = 'Osbaldo White') >= '1'
الطلب بهلواني نوعا ما لكنه يعمل. باستخدام Query Builder ، يمكنك كتابة شيء مثل هذا:

<?php
$livres = DB::table('livres')
->join('auteur_livre', 'livres.id', '=', 'auteur_livre.livre_id')
->join('auteurs', 'auteurs.id', '=', 'auteur_livre.auteur_id')
->where('auteurs.nom', '=', 'Osbaldo White')
->get();
foreach ($livres as $livre) {
    echo $livre->titre, '
'; }
صلة مزدوجة تنشئ الطلب:

select * from livre inner join auteur_livre on livres.id = auteur_livre.livre_id inner join auteurs on auteurs.id = auteur_livre.auteur_id where auteurs.nom = 'Osbaldo White'
ابحث عن مؤلفي ناشر معروف معرفه
دعنا نذهب أبعد من ذلك ونجد المؤلفين لمحرر نعرف هويته:

<?php
$livres = App\Editeur::find(10)->livres;
foreach ($livres as $livre) {
    foreach($livre->auteurs as $auteur) {
        echo $auteur->nom, '
'; } }
يمكنك بسهولة العثور على كتب الناشر ، ولكن للانتقال إلى المؤلفين ، عليك أن تدور حول النتائج. فيما يلي الطلبات الثلاثة التي تم إنشاؤها:

select * from editeurs where id = '10' limit 1
select * from livres where livres.editeur_id = '10'
select auteurs.*, auteur_livre.livre_id as pivot_livre_id, auteur_livre.auteur_id as pivot_auteur_id from auteurs inner join auteur_livre on auteurs.id = auteur_livre.auteur_id where auteur_livre.livre_id = '28'
هو الأمثل جيدا. إليك ما يمكننا تحقيقه باستخدام Query Builder :

<?php
$auteurs = DB::table('auteurs')
->select('auteurs.nom')
->join('auteur_livre', 'auteurs.id', '=', 'auteur_livre.auteur_id')
->join('livres', 'livres.id', '=', 'auteur_livre.livre_id')
->join('editeurs', function($join)
    {
        $join->on('editeurs.id', '=', 'livres.editeur_id')
        ->where('editeurs.id', '=', 10);
    })
->get();
foreach($auteurs as $auteur) {
    echo $auteur->nom, '
'; }
من الواضح أنه أكثر إحكاما من وجهة نظر الطلب مع واحد فقط:

select auteurs.nom from auteurs inner join auteur_livre on auteurs.id = auteur_livre.auteur_id inner join livres on livres.id = auteur_livre.livre_id inner join editeurs on editeurs.id = livres.editeur_id and editeurs.id = '10'
احترس من الطلبات المتداخلة
عليك أن تكون حذرا في بعض الحالات. على سبيل المثال ، لنفترض أنك تريد الحصول على قائمة المؤلفين باسم كل مؤلف قائمة أعماله. يمكنك كتابة هذا النوع من الكود:

<?php
$auteurs = App\Auteur::all();
foreach ($auteurs as $auteur) {
    echo '<h1>' . $auteur->nom . '</h1>';
    foreach($auteur->livres as $livre) {
        echo $livre->titre, '
'; } }
إنه يعمل جيدًا ولكن ... إذا نظرت إلى طلباتك ، فسوف تكون خائفًا! في حالتي أجد 41! بكل بساطة لأنك تطلق طلبًا للعثور على كتبه لكل مؤلف. في هذا النوع من المواقف ، من الضروري للغاية استخدام التحميل المرتبط ، أي أن تطلب من Eloquent تحميل جدول الكتب بالطريقة with :

<?php
$auteurs = App\Auteur::with('livres')->get();
foreach ($auteurs as $auteur) {
    echo '<h1>' . $auteur->nom . '</h1>';
    foreach($auteur->livres as $livre) {
        echo $livre->titre, '
'; } }
قد يبدو التغيير ضئيلًا ولكن لدينا فقط طلبين الآن:

select * from auteurs
select livres.*, auteur_livre.auteur_id as pivot_auteur_id, auteur_livre.livre_id as pivot_livre_id from livres inner join auteur_livre on livres.id = auteur_livre.livre_id where auteur_livre.auteur_id in ('1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '40')
كيف تفعل الشيء نفسه مع Query Builder؟ الأمر ليس بهذه البساطة ، إليك حل يستخدم تعبيرًا خامًا:

<?php
$results = DB::table('auteurs')
->select('nom', DB::raw('group_concat(titre) as titres'))
->groupBy('nom')
->join('auteur_livre', 'auteurs.id', '=', 'auteur_livre.auteur_id')
->join('livres', 'livres.id', '=', 'auteur_livre.livre_id')
->get();
foreach ($results as $result) {
    echo '<h1>' . $result->nom . '</h1>';
    $titres = explode(',', $result->titres);
    foreach($titres as $titre) {
        echo $titre, '
'; } }
الآن لدينا فقط طلب واحد:

select nom, group_concat(titre) as titres 
from auteurs 
inner join auteur_livre on auteurs.id = auteur_livre.auteur_id 
inner join livres on livres.id = auteur_livre.livre_id 
group by nom
لن تكون قادرًا دائمًا على تحقيق ما تريد مع Eloquent فقط ، بل يجب عليك استخدام Query Builder . لقد رأينا في هذه الأمثلة القليلة كيفية استخدامها.
هذا الفصل أبعد ما يكون عن استنفاد موضوع Query Builder . يمكنك العثور على جميع الملاحق المفيدة في هذه الصفحة من الوثائق .

في الخلاصة


  • يسمح Eloquent بالكثير من المعالجات على الجداول وهو مرتاح للعلاقات.
  • Query Builder هو الرفيق المثالي لـ Eloquent .
  • في بعض الأحيان يكون استخدام Query Builder أكثر فعالية من Eloquent .
  • في بعض الأحيان ، يجب عليك استخدام تعبيرات خام في الاستعلامات ولكن عليك التفكير في حماية نفسك من حقن SQL .