مقدمة في المجموعات
المجموعات هي واحدة من أقوى ميزات Laravel. توفر غلافاً سلساً ومريحاً للعمل مع مصفوفات البيانات. فكر في المجموعات كمصفوفات محسّنة - تقدم العشرات من الدوال للتصفية، التعيين، التقليص، وتحويل البيانات بطرق أنيقة.
ملاحظة: كل استعلام Eloquent يُرجع نتائج متعددة يُرجع نسخة من Collection. يمكنك أيضاً إنشاء مجموعات يدوياً من أي مصفوفة بيانات.
إنشاء المجموعات
هناك طرق متعددة لإنشاء المجموعات:
<?php
use Illuminate\Support\Collection;
// من مصفوفة باستخدام دالة مساعدة collect()
$collection = collect([1, 2, 3, 4, 5]);
// استخدام صنف Collection
$collection = new Collection(['تفاح', 'موز', 'برتقال']);
// من استعلام Eloquent (تلقائي)
$users = User::all(); // يرجع Collection
$posts = Post::where('published', true)->get(); // يرجع Collection
// مجموعة فارغة
$empty = collect();
// من نطاق
$numbers = collect(range(1, 10));
دوال المجموعات الأساسية
لنستكشف دوال المجموعات الأساسية:
<?php
$collection = collect([1, 2, 3, 4, 5]);
// all() - الحصول على جميع العناصر كمصفوفة
$array = $collection->all();
// [1, 2, 3, 4, 5]
// count() - عد العناصر
$count = $collection->count();
// 5
// first() - الحصول على العنصر الأول
$first = $collection->first();
// 1
// last() - الحصول على العنصر الأخير
$last = $collection->last();
// 5
// isEmpty() - التحقق من الفراغ
$isEmpty = $collection->isEmpty();
// false
// isNotEmpty() - التحقق من عدم الفراغ
$isNotEmpty = $collection->isNotEmpty();
// true
تحويل المجموعات مع ()map
الدالة map() تحول كل عنصر في المجموعة:
<?php
// مضاعفة كل رقم
$numbers = collect([1, 2, 3, 4, 5]);
$doubled = $numbers->map(function ($number) {
return $number * 2;
});
// [2, 4, 6, 8, 10]
// استخدام دوال السهم (PHP 7.4+)
$doubled = $numbers->map(fn($n) => $n * 2);
// تعيين كائنات المستخدمين إلى الأسماء
$users = User::all();
$names = $users->map(function ($user) {
return $user->name;
});
// التعيين مع المفتاح والقيمة
$products = collect([
['name' => 'مكتب', 'price' => 200],
['name' => 'كرسي', 'price' => 100],
]);
$formatted = $products->map(function ($product) {
return $product['name'] . ': $' . $product['price'];
});
// ['مكتب: $200', 'كرسي: $100']
تصفية المجموعات مع ()filter
الدالة filter() تصفي عناصر المجموعة بناءً على callback:
<?php
// تصفية الأرقام الزوجية
$numbers = collect([1, 2, 3, 4, 5, 6]);
$even = $numbers->filter(function ($number) {
return $number % 2 === 0;
});
// [2, 4, 6]
// تصفية المستخدمين حسب الدور
$users = User::all();
$admins = $users->filter(function ($user) {
return $user->role === 'admin';
});
// تصفية القيم الصحيحة (truthy)
$values = collect([1, 2, null, 4, false, 6]);
$truthy = $values->filter();
// [1, 2, 4, 6]
// reject() - عكس filter()
$numbers = collect([1, 2, 3, 4, 5]);
$odd = $numbers->reject(function ($number) {
return $number % 2 === 0;
});
// [1, 3, 5]
استخراج القيم مع ()pluck
الدالة pluck() تسترجع القيم لمفتاح معين:
<?php
// استخراج الأسماء من مصفوفة مصفوفات
$users = collect([
['name' => 'أحمد', 'age' => 30],
['name' => 'فاطمة', 'age' => 25],
['name' => 'محمد', 'age' => 35],
]);
$names = $users->pluck('name');
// ['أحمد', 'فاطمة', 'محمد']
// الاستخراج مع مفتاح
$namesByAge = $users->pluck('name', 'age');
// [30 => 'أحمد', 25 => 'فاطمة', 35 => 'محمد']
// الاستخراج من نماذج Eloquent
$users = User::all();
$emails = $users->pluck('email');
// استخراج القيم المتداخلة باستخدام تدوين النقطة
$posts = collect([
['title' => 'منشور 1', 'author' => ['name' => 'أحمد']],
['title' => 'منشور 2', 'author' => ['name' => 'فاطمة']],
]);
$authorNames = $posts->pluck('author.name');
// ['أحمد', 'فاطمة']
تقليص المجموعات مع ()reduce
الدالة reduce() تقلص المجموعة إلى قيمة واحدة:
<?php
// مجموع الأرقام
$numbers = collect([1, 2, 3, 4, 5]);
$sum = $numbers->reduce(function ($carry, $item) {
return $carry + $item;
}, 0);
// 15
// ربط السلاسل النصية
$words = collect(['مرحباً', 'بك', 'في', 'Laravel']);
$sentence = $words->reduce(function ($carry, $word) {
return $carry . ' ' . $word;
});
// " مرحباً بك في Laravel"
// بناء مصفوفة ترابطية
$products = collect([
['id' => 1, 'name' => 'مكتب'],
['id' => 2, 'name' => 'كرسي'],
]);
$indexed = $products->reduce(function ($carry, $product) {
$carry[$product['id']] = $product['name'];
return $carry;
}, []);
// [1 => 'مكتب', 2 => 'كرسي']
نصيحة: للجمع البسيط، استخدم الدالة sum() بدلاً من reduce: $numbers->sum()
التجميع مع ()groupBy
الدالة groupBy() تجمع عناصر المجموعة حسب مفتاح معين:
<?php
// تجميع المستخدمين حسب الدور
$users = collect([
['name' => 'أحمد', 'role' => 'admin'],
['name' => 'فاطمة', 'role' => 'user'],
['name' => 'محمد', 'role' => 'admin'],
['name' => 'سارة', 'role' => 'user'],
]);
$grouped = $users->groupBy('role');
/*
[
'admin' => [
['name' => 'أحمد', 'role' => 'admin'],
['name' => 'محمد', 'role' => 'admin'],
],
'user' => [
['name' => 'فاطمة', 'role' => 'user'],
['name' => 'سارة', 'role' => 'user'],
],
]
*/
// التجميع بواسطة callback
$products = collect([
['name' => 'مكتب', 'price' => 200],
['name' => 'كرسي', 'price' => 100],
['name' => 'مصباح', 'price' => 50],
]);
$priceRanges = $products->groupBy(function ($product) {
return $product['price'] > 100 ? 'غالي' : 'معقول';
});
ترتيب المجموعات
تقدم المجموعات دوال ترتيب متعددة:
<?php
// sort() - الترتيب حسب القيمة (تصاعدي)
$numbers = collect([5, 3, 1, 4, 2]);
$sorted = $numbers->sort();
// [1, 2, 3, 4, 5]
// sortDesc() - الترتيب تنازلي
$sorted = $numbers->sortDesc();
// [5, 4, 3, 2, 1]
// sortBy() - الترتيب حسب المفتاح
$users = collect([
['name' => 'أحمد', 'age' => 30],
['name' => 'فاطمة', 'age' => 25],
['name' => 'محمد', 'age' => 35],
]);
$sortedByAge = $users->sortBy('age');
// sortByDesc() - الترتيب حسب المفتاح تنازلياً
$sortedByAgeDesc = $users->sortByDesc('age');
// الترتيب بواسطة callback
$sortedByNameLength = $users->sortBy(function ($user) {
return strlen($user['name']);
});
// ترتيبات متعددة
$sorted = $users->sortBy('role')
->sortBy('name');
دمج المجموعات
دمج، ربط، وجمع المجموعات:
<?php
// merge() - دمج المصفوفات (يستبدل المفاتيح)
$collection1 = collect(['a' => 1, 'b' => 2]);
$collection2 = collect(['b' => 3, 'c' => 4]);
$merged = $collection1->merge($collection2);
// ['a' => 1, 'b' => 3, 'c' => 4]
// concat() - إلحاق القيم (يحتفظ بالكل)
$collection1 = collect([1, 2, 3]);
$collection2 = collect([4, 5, 6]);
$concatenated = $collection1->concat($collection2);
// [1, 2, 3, 4, 5, 6]
// union() - إضافة عناصر بدون استبدال
$collection1 = collect([1 => 'a', 2 => 'b']);
$collection2 = collect([2 => 'c', 3 => 'd']);
$union = $collection1->union($collection2);
// [1 => 'a', 2 => 'b', 3 => 'd']
// flatten() - تسطيح مصفوفة متعددة الأبعاد
$collection = collect([
[1, 2, 3],
[4, 5, 6],
]);
$flattened = $collection->flatten();
// [1, 2, 3, 4, 5, 6]
القيم الفريدة والمكررة
العمل مع القيم الفريدة وإيجاد المكررات:
<?php
// unique() - إزالة المكررات
$numbers = collect([1, 2, 2, 3, 3, 3, 4]);
$unique = $numbers->unique();
// [1, 2, 3, 4]
// unique() حسب المفتاح
$users = collect([
['name' => 'أحمد', 'role' => 'admin'],
['name' => 'فاطمة', 'role' => 'user'],
['name' => 'محمد', 'role' => 'admin'],
]);
$uniqueRoles = $users->unique('role');
// duplicates() - الحصول على القيم المكررة
$numbers = collect([1, 2, 2, 3, 3, 3]);
$duplicates = $numbers->duplicates();
// [2 => 2, 4 => 3, 5 => 3]
تقسيم المجموعات
تقسيم المجموعات إلى أجزاء أصغر:
<?php
// chunk() - التقسيم إلى أجزاء
$collection = collect([1, 2, 3, 4, 5, 6, 7]);
$chunks = $collection->chunk(3);
/*
[
[1, 2, 3],
[4, 5, 6],
[7]
]
*/
// split() - التقسيم إلى مجموعات
$collection = collect([1, 2, 3, 4, 5]);
$groups = $collection->split(3);
/*
[
[1, 2],
[3, 4],
[5]
]
*/
// مثال عملي: العرض في أعمدة
$products = Product::all();
$columns = $products->chunk(ceil($products->count() / 3));
foreach ($columns as $column) {
echo '<div class="column">';
foreach ($column as $product) {
echo '<div>' . $product->name . '</div>';
}
echo '</div>';
}
عمليات المجموعات العملية
أمثلة من العالم الحقيقي تجمع بين دوال المجموعات المتعددة:
<?php
// مثال 1: حساب متوسط قيمة الطلب حسب العميل
$orders = collect([
['customer' => 'أحمد', 'amount' => 100],
['customer' => 'فاطمة', 'amount' => 150],
['customer' => 'أحمد', 'amount' => 200],
['customer' => 'فاطمة', 'amount' => 250],
]);
$averages = $orders->groupBy('customer')
->map(function ($customerOrders) {
return $customerOrders->avg('amount');
});
// ['أحمد' => 150, 'فاطمة' => 200]
// مثال 2: الحصول على أعلى 5 منشورات مشاهدة
$posts = Post::all();
$topPosts = $posts->sortByDesc('views')
->take(5)
->pluck('title', 'id');
// مثال 3: حساب الإحصائيات
$numbers = collect([10, 20, 30, 40, 50]);
$stats = [
'sum' => $numbers->sum(),
'avg' => $numbers->avg(),
'min' => $numbers->min(),
'max' => $numbers->max(),
'median' => $numbers->median(),
];
// مثال 4: تحويل وتصفية بيانات المستخدم
$users = User::all();
$activeUserEmails = $users
->filter(fn($user) => $user->is_active)
->sortBy('created_at')
->pluck('email')
->unique()
->values();
المجموعات الكسولة (Lazy)
لمجموعات البيانات الكبيرة، استخدم المجموعات الكسولة لتقليل استخدام الذاكرة:
<?php
// المجموعة العادية - تحمل الكل في الذاكرة
$users = User::all(); // يحمل 10,000 مستخدم
// المجموعة الكسولة - تعالج واحداً تلو الآخر
$users = User::cursor(); // يرجع LazyCollection
// معالجة مجموعة بيانات ضخمة بكفاءة
$users->filter(function ($user) {
return $user->is_active;
})->each(function ($user) {
// معالجة مستخدم واحد في كل مرة
Mail::to($user)->send(new Newsletter);
});
// إنشاء مجموعة كسولة من مولّد
$lazyCollection = LazyCollection::make(function () {
$file = fopen('large-file.csv', 'r');
while (($line = fgets($file)) !== false) {
yield $line;
}
fclose($file);
});
$lazyCollection->chunk(100)
->each(function ($chunk) {
// معالجة 100 سطر في كل مرة
});
نصيحة: استخدم المجموعات الكسولة عند العمل مع مجموعات بيانات كبيرة (10,000+ عنصر) أو عند قراءة ملفات كبيرة لتجنب مشاكل الذاكرة.
الرسائل عالية المستوى
تدعم المجموعات "الرسائل عالية المستوى" للعمليات الشائعة:
<?php
$users = User::all();
// بدلاً من:
$names = $users->map(function ($user) {
return $user->name;
});
// استخدم رسالة عالية المستوى:
$names = $users->map->name;
// أمثلة أخرى:
$users->each->markAsActive();
$posts->filter->isPublished();
$orders->sum->total;
$products->sortBy->price;
تمرين 1: تحليلات التجارة الإلكترونية
استخدم المجموعات لتحليل بيانات الطلبات وحساب المقاييس الرئيسية.
- أنشئ مجموعة من الطلبات مع: customer_id, product_id, quantity, price
- احسب إجمالي الإيرادات: مجموع (quantity * price)
- ابحث عن متوسط قيمة الطلب
- جمّع الطلبات حسب العميل واحسب إجمالي إنفاق كل عميل
- حدد أعلى 3 عملاء حسب إجمالي الإنفاق
- عد المنتجات الفريدة المطلوبة
- ابحث عن المنتج الأكثر شعبية (أعلى كمية إجمالية مباعة)
تمرين 2: معالجة درجات الطلاب
معالجة درجات الطلاب وإنشاء إحصائيات باستخدام المجموعات.
- أنشئ مجموعة من الطلاب مع: name, subject, grade (0-100)
- احسب متوسط الصف لكل مادة
- ابحث عن الطلاب بدرجة >= 90 (طلاب A)
- جمّع الطلاب حسب مستوى الدرجة (A: 90-100، B: 80-89، إلخ)
- رتّب الطلاب حسب الدرجة (من الأعلى إلى الأدنى)
- احصل على قائمة الطلاب الذين رسبوا (درجة < 60)
- احسب الوسيط للدرجات لكل مادة
تمرين 3: لوحة تحكم منشورات المدونة
بناء لوحة تحكم مع إحصائيات منشورات المدونة باستخدام المجموعات.
- استرجع جميع المنشورات مع: title, author_id, category, views, created_at
- جمّع المنشورات حسب الفئة وعد المنشورات لكل فئة
- ابحث عن أعلى 5 منشورات مشاهدة
- احسب متوسط المشاهدات لكل مؤلف
- احصل على المنشورات المنشورة في آخر 30 يوماً
- أنشئ قائمة "رائجة": منشورات مع >1000 مشاهدة في آخر 7 أيام
- ابحث عن المؤلفين الذين نشروا >5 منشورات هذا الشهر
الملخص
في هذا الدرس، تعلمت:
- إنشاء والعمل مع مجموعات Laravel
- تحويل البيانات باستخدام map()، filter()، و reduce()
- استخراج القيم باستخدام pluck()
- تجميع وترتيب المجموعات
- دمج المجموعات باستخدام merge()، concat()، flatten()
- إيجاد القيم الفريدة والمكررات
- تقسيم المجموعات للترقيم/المعالجة
- أنماط واستخدامات المجموعات من العالم الحقيقي
- المجموعات الكسولة لكفاءة الذاكرة
- الرسائل عالية المستوى للحصول على كود أنظف
الدرس التالي: سنستكشف معالجة الأخطاء، التسجيل، وتقنيات تصحيح الأخطاء في Laravel.