استخدام نموذج قاعدة البيانات (نمط Laravel)
نموذج ويب مان قائم على Eloquent ORM. كل جدول في قاعدة البيانات يحتوي على نموذج "مرتبط" للتفاعل مع هذا الجدول. يمكنك من خلال النموذج استعلام البيانات من جدول قاعدة البيانات، وكذلك إدراج سجلات جديدة في الجدول.
قبل البدء، تأكد من تكوين الاتصال بقاعدة البيانات في config/database.php
.
ملاحظة: يتطلب دعم Eloquent ORM لمراقبي النموذج استيرادًا إضافيًا
composer require "illuminate/events"
[مثال](#مراقبو النماذج)
مثال على نموذج قاعدة البيانات
<?php
namespace app\model;
use support\Model;
class User extends Model
{
/**
* اسم الجدول المرتبط بالنموذج
*
* @var string
*/
protected $table = 'user';
/**
* إعادة تعريف المفتاح الرئيسي، الافتراضي هو id
*
* @var string
*/
protected $primaryKey = 'uid';
/**
* يشير إلى ما إذا كان يجب الحفاظ على الطوابع الزمنية تلقائيًا
*
* @var bool
*/
public $timestamps = false;
}
اسم الجدول
يمكنك تحديد جدول البيانات المخصص عن طريق تعريف خاصية table على النموذج:
class User extends Model
{
/**
* اسم الجدول المرتبط بالنموذج
*
* @var string
*/
protected $table = 'user';
}
المفتاح الرئيسي
تفترض Eloquent أيضًا أن كل جدول بيانات يحتوي على عمود مفتاح رئيسي باسم id. يمكنك تعريف خاصية محمية $primaryKey لإعادة كتابة الافتراض.
class User extends Model
{
/**
* إعادة تعريف المفتاح الرئيسي، الافتراضي هو id
*
* @var string
*/
protected $primaryKey = 'uid';
}
تفترض Eloquent أن المفتاح الرئيسي هو قيمة عدد صحي تلقائية، مما يعني أنه سيتم تحويل المفتاح الرئيسي تلقائيًا إلى نوع int بشكل افتراضي. إذا كنت ترغب في استخدام مفتاح رئيسي غير تزايدي أو غير رقمي، فأنت بحاجة إلى تعيين الخاصية العامة $incrementing إلى false.
class User extends Model
{
/**
* يشير إلى ما إذا كان المفتاح الرئيسي للنموذج متزايدًا
*
* @var bool
*/
public $incrementing = false;
}
إذا لم يكن مفتاحك الرئيسي عددًا صحيحًا، فستحتاج إلى تعيين الخاصية المحمية $keyType على string في النموذج:
class User extends Model
{
/**
* "نوع" الطابع الزمني ID التلقائي.
*
* @var string
*/
protected $keyType = 'string';
}
الطوابع الزمنية
بشكل افتراضي، تتوقع Eloquent أن تكون هناك أعمدة created_at و updated_at في جدول البيانات الخاص بك. إذا كنت لا ترغب في السماح لـ Eloquent بإدارة هذين العمودين تلقائيًا، فيرجى تعيين خاصية $timestamps في النموذج إلى false:
class User extends Model
{
/**
* يشير إلى ما إذا كان يجب الحفاظ على الطوابع الزمنية تلقائيًا
*
* @var bool
*/
public $timestamps = false;
}
إذا كنت بحاجة إلى تخصيص تنسيق الطوابع الزمنية، يمكنك تعيين خاصية $dateFormat في نموذجك. تحدد هذه الخاصية طريقة تخزين خصائص التاريخ في قاعدة البيانات، وكذلك التنسيق عند تسلسل النموذج إلى مصفوفة أو JSON:
class User extends Model
{
/**
* تنسيق تخزين الطوابع الزمنية
*
* @var string
*/
protected $dateFormat = 'U';
}
إذا كنت بحاجة إلى تخصيص أسماء الحقول التي يتم تخزين الطوابع الزمنية فيها، يمكنك تعيين قيم الثوابت CREATED_AT و UPDATED_AT في النموذج لتحقيق ذلك:
class User extends Model
{
const CREATED_AT = 'creation_date';
const UPDATED_AT = 'last_update';
}
الاتصال بقاعدة البيانات
عادةً، ستستخدم نماذج Eloquent الاتصال الافتراضي لقاعدة البيانات الذي تم تكوينه في التطبيق الخاص بك. إذا كنت ترغب في تعيين اتصال مختلف للنموذج، يمكنك تعيين خاصية $connection:
class User extends Model
{
/**
* اسم الاتصال للنموذج
*
* @var string
*/
protected $connection = 'connection-name';
}
قيم الخصائص الافتراضية
إذا كنت ترغب في تعريف قيم افتراضية لبعض الخصائص في النموذج، يمكنك تعريف خاصية $attributes في النموذج:
class User extends Model
{
/**
* القيم الافتراضية للنموذج.
*
* @var array
*/
protected $attributes = [
'delayed' => false,
];
}
استرداد النماذج
بعد إنشاء النموذج والجدول المرتبط به، يمكنك استعلام البيانات من قاعدة البيانات. تخيل كل نموذج Eloquent كمنشئ استعلام قوي، يمكنك استخدامه لاستعلام أسرع يتعلق بجدول البيانات المرتبط به. على سبيل المثال:
$users = app\model\User::all();
foreach ($users as $user) {
echo $user->name;
}
تلميح: نظرًا لأن نماذج Eloquent تعتبر أيضًا منشئي استعلامات، يجب عليك قراءة منشئ الاستعلامات لجميع الأساليب المتاحة. يمكنك استخدام هذه الأساليب في استعلامات Eloquent.
قيود إضافية
ترجع طريقة all الخاصة بـ Eloquent جميع النتائج في النموذج. نظرًا لأن كل نموذج Eloquent يعمل كمنشئ استعلام، يمكنك أيضًا إضافة شروط استعلام، ثم استخدام طريقة get للحصول على نتائج الاستعلام:
$users = app\model\User::where('name', 'like', '%tom')
->orderBy('uid', 'desc')
->limit(10)
->get();
إعادة تحميل النموذج
يمكنك استخدام طرق fresh و refresh لإعادة تحميل النموذج. تعيد طريقة fresh استرداد النموذج من قاعدة البيانات مرة أخرى. لن تتأثر مثيلات النموذج الموجودة:
$user = app\model\User::where('name', 'tom')->first();
$fresh_user = $user->fresh();
تستخدم طريقة refresh البيانات الجديدة من قاعدة البيانات لإعادة تعيين النموذج الحالي. بالإضافة إلى ذلك، سيتم إعادة تحميل العلاقات المحملة بالفعل:
$user = app\model\User::where('name', 'tom')->first();
$user->name = 'jerry';
$user = $user->fresh();
$user->name; // "tom"
المجموعات
يمكن لطريقتي all و get في Eloquent استرداد عدة نتائج، مما يؤدي إلى إرجاع مثيل Illuminate\Database\Eloquent\Collection
. توفر فئة Collection
العديد من الدوال المساعدة لمعالجة نتائج Eloquent:
$users = $users->reject(function ($user) {
return $user->disabled;
});
استخدام المؤشرات
تتيح لك طريقة cursor استخدام مؤشرات للتنقل عبر قاعدة البيانات، وتنفيذ استعلام مرة واحدة فقط. عند معالجة كميات كبيرة من البيانات، يمكن أن تقلل طريقة cursor بشكل كبير من استخدام الذاكرة:
foreach (app\model\User::where('sex', 1)->cursor() as $user) {
//
}
يعيد cursor مثيل Illuminate\Support\LazyCollection
. تتيح لك المجموعات الكسولة استخدام معظم طرق المجموعات في Laravel، وكل مرة يتم تحميل نموذج واحد فقط في الذاكرة:
$users = app\model\User::cursor()->filter(function ($user) {
return $user->id > 500;
});
foreach ($users as $user) {
echo $user->id;
}
استعلامات فرعية selects
يقدم Eloquent دعمًا متقدمًا للاستعلامات الفرعية، يمكنك استخراج المعلومات من الجداول المرتبطة في استعلام واحد. على سبيل المثال، لنفترض أن لدينا جدول وجهات destinations وجدول رحلات flights. يحتوي جدول flights على حقل arrival_at، الذي يشير إلى متى تصل الرحلة إلى وجهته.
باستخدام طرق select و addSelect المقدمة من ميزة الاستعلامات الفرعية، يمكننا استخدام عبارة واحدة لاستعلام جميع الوجهات destinations، واسم آخر رحلة تصل إلى تلك الوجهة:
use app\model\Destination;
use app\model\Flight;
return Destination::addSelect(['last_flight' => Flight::select('name')
->whereColumn('destination_id', 'destinations.id')
->orderBy('arrived_at', 'desc')
->limit(1)
])->get();
الترتيب حسب الاستعلام الفرعي
علاوة على ذلك، تدعم وظيفة orderBy في منشئ الاستعلامات الاستعلامات الفرعية. يمكننا استخدام هذه الوظيفة لترتيب جميع الوجهات حسب وقت وصول آخر رحلة إلى الوجهة. أيضًا، يمكن تنفيذ هذا بتشغيل استعلام واحد فقط على قاعدة البيانات:
return Destination::orderByDesc(
Flight::select('arrived_at')
->whereColumn('destination_id', 'destinations.id')
->orderBy('arrived_at', 'desc')
->limit(1)
)->get();
استرداد نموذج واحد / مجموعة
بخلاف استرداد جميع السجلات من جدول معين، يمكنك استخدام find و first أو firstWhere لاسترداد سجل واحد. تُرجع هذه الطرق مثيل نموذج واحد، بدلاً من إرجاع مجموعة نماذج:
// للبحث عن نموذج بواسطة المفتاح الرئيسي...
$flight = app\model\Flight::find(1);
// البحث عن أول نموذج يتوافق مع شروط الاستعلام...
$flight = app\model\Flight::where('active', 1)->first();
// البحث عن أول نموذج يتوافق مع شروط الاستعلام بتبسيط سريع...
$flight = app\model\Flight::firstWhere('active', 1);
يمكنك أيضًا استخدام مصفوفات المفاتيح الرئيسية كمعلمات لاستدعاء طريقة find، والتي سترجع مجموعة بالسجلات المتطابقة:
$flights = app\model\Flight::find([1, 2, 3]);
في بعض الأحيان، قد ترغب في القيام بعملية أخرى عند البحث عن أول نتيجة وعدم العثور على قيمة. ستعيد طريقة firstOr أول نتيجة عند العثور عليها، وإذا لم توجد، فستنفذ الاستدعاء المعطى. ستكون القيمة المعادة من الاستدعاء هي القيمة المعادة من طريقة firstOr:
$model = app\model\Flight::where('legs', '>', 100)->firstOr(function () {
// ...
});
تقبل طريقة firstOr أيضًا مصفوفة أعمدة للاستعلام:
$model = app\model\Flight::where('legs', '>', 100)
->firstOr(['id', 'legs'], function () {
// ...
});
استثناء "لم يُعثر عليه"
في بعض الأحيان، قد ترغب في إلقاء استثناء عند عدم العثور على نموذج. يكون هذا مفيدًا جدًا في وحدات التحكم والمسارات. تقوم طريقتا findOrFail و firstOrFail باسترداد أول نتيجة للاستعلام، وإذا لم يُعثر عليها، سيقوم بإلقاء استثناء Illuminate\Database\Eloquent\ModelNotFoundException:
$model = app\model\Flight::findOrFail(1);
$model = app\model\Flight::where('legs', '>', 100)->firstOrFail();
استرداد المجموعات
يمكنك أيضًا استخدام طرق count و sum و max التي يوفرها منشئ الاستعلامات، وكذلك وظائف المجموعات الأخرى للتلاعب بالمجموعات. تُرجع هذه الطرق فقط قيمًا عددية صحيحة مناسبة بدلاً من مثيل نموذج:
$count = app\model\Flight::where('active', 1)->count();
$max = app\model\Flight::where('active', 1)->max('price');
الإدراج
لإضافة سجل جديد إلى قاعدة البيانات، قم أولاً بإنشاء مثيل نموذج جديد، ثم قم بتعيين الخصائص للمثيل، ثم استدعاء طريقة save:
<?php
namespace app\controller;
use app\model\User;
use support\Request;
use support\Response;
class FooController
{
/**
* إضافة سجل جديد في جدول المستخدمين
*
* @param Request $request
* @return Response
*/
public function store(Request $request)
{
// التحقق من الطلب
$user = new User;
$user->name = $request->get('name');
$user->save();
}
}
سيتم تعيين الطوابع الزمنية created_at و updated_at تلقائيًا (عندما تكون خاصية $timestamps في النموذج فعالة)، ولا تحتاج إلى تعيين قيم يدويًا.
التحديث
يمكن أيضًا استخدام طريقة save لتحديث نموذج موجود بالفعل في قاعدة البيانات. لتحديث النموذج، تحتاج إلى استرداده أولاً، ثم تعيين الخصائص التي تريد تحديثها، ثم استدعاء طريقة save. بنفس الطريقة، سيتم تحديث الطابع الزمني updated_at تلقائيًا، لذا لا حاجة لتعيين القيمة يدويًا:
$user = app\model\User::find(1);
$user->name = 'jerry';
$user->save();
التحديث الجماعي
app\model\User::where('uid', '>', 10)
->update(['name' => 'tom']);
التحقق من تغييرات الخصائص
تقدم Eloquent طرق isDirty و isClean و wasChanged للتحقق من الحالة الداخلية للنموذج وتحديد كيفية تغير خصائصه منذ تحميله في البداية.
تحدد طريقة isDirty ما إذا كانت أي خصائص قد تغيرت منذ تحميل النموذج. يمكنك تمرير اسم خاصية محددة لتحديد ما إذا كانت خاصية معينة قد تغيرت. تكون طريقة isClean عكس isDirty، كما أنها تقبل معلمات خصائص اختيارية:
$user = User::create([
'first_name' => 'Taylor',
'last_name' => 'Otwell',
'title' => 'Developer',
]);
$user->title = 'Painter';
$user->isDirty(); // true
$user->isDirty('title'); // true
$user->isDirty('first_name'); // false
$user->isClean(); // false
$user->isClean('title'); // false
$user->isClean('first_name'); // true
$user->save();
$user->isDirty(); // false
$user->isClean(); // true
تحدد طريقة wasChanged ما إذا كانت أي خصائص قد تغيّرت في آخر حفظ للنموذج خلال فترة الطلب الحالية. يمكنك أيضًا تمرير أسماء الخصائص لرؤية ما إذا كانت خاصية معينة قد تغيّرت:
$user = User::create([
'first_name' => 'Taylor',
'last_name' => 'Otwell',
'title' => 'Developer',
]);
$user->title = 'Painter';
$user->save();
$user->wasChanged(); // true
$user->wasChanged('title'); // true
$user->wasChanged('first_name'); // false
التعيين الجماعي
يمكنك أيضًا استخدام طريقة create لحفظ نموذج جديد. تُرجع هذه الطريقة مثيل النموذج. ومع ذلك، قبل الاستخدام، تحتاج إلى تحديد خاصية fillable أو guarded على النموذج، حيث أن جميع نماذج Eloquent تكون افتراضيًا غير قابلة للتعيين الجماعي.
تحدث ثغرات التعيين الجماعي عندما يمرر المستخدم معلمات HTTP غير متوقعة عبر الطلب، وتغير هذه المعلمة حقولًا في قاعدة البيانات لا تحتاج إلى تغيير. على سبيل المثال: قد يمرر مستخدم خبيث معلمة is_admin من خلال طلب HTTP، ثم يمروها إلى طريقة create، مما يسمح له بترقية نفسه إلى مسؤول.
لذا، قبل البدء، يجب عليك تحديد الخصائص التي يمكن أن تكون قابلة للتعيين الجماعي على النموذج. يمكنك القيام بذلك من خلال خاصية $fillable في النموذج. على سبيل المثال: السماح لخاصية name في نموذج Flight بالدخول في التعيين الجماعي:
<?php
namespace app\model;
use support\Model;
class Flight extends Model
{
/**
* الخصائص التي يمكن تعيينها جماعيًا.
*
* @var array
*/
protected $fillable = ['name'];
}
بمجرد أن نقوم بتعيين الخصائص القابلة للتعيين الجماعي، يمكننا استخدام طريقة create لإدخال بيانات جديدة في قاعدة البيانات. ستعيد طريقة create مثيل النموذج الذي تم حفظه:
$flight = app\model\Flight::create(['name' => 'Flight 10']);
إذا كان لديك بالفعل مثيل نموذج، يمكنك تمرير مصفوفة إلى طريقة fill لإجراء التعيين:
$flight->fill(['name' => 'Flight 22']);
يمكن اعتبار $fillable بمثابة "قائمة بيضاء" للتعيين الجماعي، يمكنك أيضًا استخدام خاصية $guarded لتحقيق ذلك. تحتوي خاصية $guarded على مصفوفة من الخصائص التي لا يمكن تعيينها جماعيًا. أي، تعتبر خاصية $guarded من الناحية الوظيفية "قائمة سوداء". ملاحظة: يمكنك استخدام $fillable أو $guarded، ولكن ليس كلاهما في وقت واحد. في المثال أدناه، يمكن تعيين جميع الخصائص باستثناء خاصية price:
<?php
namespace app\model;
use support\Model;
class Flight extends Model
{
/**
* الخصائص التي لا يمكن تعيينها جماعيًا.
*
* @var array
*/
protected $guarded = ['price'];
}
إذا كنت ترغب في السماح بتعيين جميع الخصائص جماعيًا، يمكنك تعيين $guarded إلى مصفوفة فارغة:
/**
* الخصائص التي لا يمكن تعيينها جماعيًا.
*
* @var array
*/
protected $guarded = [];
طرق إنشاء أخرى
firstOrCreate / firstOrNew
هناك طريقتان يمكنك استخدامهما لإنشاء قيم جماعية جديدة: firstOrCreate و firstOrNew. تقوم طريقة firstOrCreate بملاءمة البيانات في قاعدة البيانات من خلال مجموعة من المفاتيح / القيم المعطاة. إذا لم يُعثر على النموذج في قاعدة البيانات، فسيتم إدراج سجل يتضمن خاصيات المعامل الأول وكذلك الخاصيات الاختيارية للمعامل الثاني.
تعمل طريقة firstOrNew مثل طريقة firstOrCreate في محاولة البحث عن سجل في قاعدة البيانات باستخدام مجموعة الخصائص المعطاة. ومع ذلك، إذا لم تعثر طريقة firstOrNew على النموذج المطابق، فستعيد نموذجًا جديدًا. لاحظ أن مثيل النموذج الذي تعيده firstOrNew لم يتم حفظه في قاعدة البيانات، ستحتاج إلى استدعاء طريقة save يدويًا لحفظه:
// استرجاع رحلة بواسطة الاسم، وإن لم توجد، سيتم إنشاؤها...
$flight = app\model\Flight::firstOrCreate(['name' => 'Flight 10']);
// استرجاع رحلة بواسطة الاسم، أو استخدم الاسم وخصائص delayed و arrival_time لإنشاءها...
$flight = app\model\Flight::firstOrCreate(
['name' => 'Flight 10'],
['delayed' => 1, 'arrival_time' => '11:30']
);
// استرجاع رحلة بواسطة الاسم، وإن لم توجد، سيتم إنشاء مثيل...
$flight = app\model\Flight::firstOrNew(['name' => 'Flight 10']);
// استرجاع رحلة بواسطة الاسم، أو استخدم الاسم وخصائص delayed و arrival_time لإنشاء مثال النموذج...
$flight = app\model\Flight::firstOrNew(
['name' => 'Flight 10'],
['delayed' => 1, 'arrival_time' => '11:30']
);
قد تجد أيضًا حالتين تحتاج فيهما إلى تحديث نموذج موجود أو إنشاء نموذج جديد إذا لم يوجد. تعالج طريقة updateOrCreate هذا في خطوة واحدة. كطريقة firstOrCreate، يقوم updateOrCreate بتخزين النموذج، لذا لا تحتاج إلى استدعاء save():
// إذا كانت هناك رحلة من أوكلاند إلى سان دييغو، فسيكون السعر 99 دولارًا.
// وإذا لم يتم العثور على نموذج مطابق، فسيتم إنشاؤه.
$flight = app\model\Flight::updateOrCreate(
['departure' => 'Oakland', 'destination' => 'San Diego'],
['price' => 99, 'discounted' => 1]
);
حذف النموذج
يمكنك استدعاء طريقة delete على مثيل النموذج لحذف المثيل:
$flight = app\model\Flight::find(1);
$flight->delete();
حذف النموذج بواسطة المفتاح الرئيسي
app\model\Flight::destroy(1);
app\model\Flight::destroy(1, 2, 3);
app\model\Flight::destroy([1, 2, 3]);
app\model\Flight::destroy(collect([1, 2, 3]));
حذف النموذج بواسطة الاستعلام
$deletedRows = app\model\Flight::where('active', 0)->delete();
نسخ النموذج
يمكنك استخدام طريقة replicate لإنشاء نسخ لمثيل جديد غير محفوظ في قاعدة البيانات. تُعتبر هذه الطريقة مفيدة عندما تشترك مثيلات النموذج في العديد من الخصائص المماثلة.
$shipping = App\Address::create([
'type' => 'shipping',
'line_1' => '123 Example Street',
'city' => 'Victorville',
'state' => 'CA',
'postcode' => '90001',
]);
$billing = $shipping->replicate()->fill([
'type' => 'billing'
]);
$billing->save();
مقارنة النموذجات
قد تحتاج أحيانًا إلى معرفة ما إذا كان نموذجان "متساويان". يمكن استخدام طريقة is للتحقق بسرعة مما إذا كان النموذجان لهما نفس المفتاح الرئيسي، والجدول، والاتصال بقاعدة البيانات:
if ($post->is($anotherPost)) {
//
}
مراقبو النماذج
للاستخدام، راجع أحداث نماذج Laravel ومراقب النماذج
ملاحظة: يتطلب دعم Eloquent ORM لمراقبي النماذج استيرادًا إضافيًا
composer require "illuminate/events"
<?php
namespace app\model;
use support\Model;
use app\observer\UserObserver;
class User extends Model
{
public static function boot()
{
parent::boot();
static::observe(UserObserver::class);
}
}