إطار Laravel

مجموعات Laravel - معالجة البيانات القوية

18 دقيقة الدرس 14 من 45

مقدمة في المجموعات

المجموعات هي واحدة من أقوى ميزات 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: تحليلات التجارة الإلكترونية

استخدم المجموعات لتحليل بيانات الطلبات وحساب المقاييس الرئيسية.

  1. أنشئ مجموعة من الطلبات مع: customer_id, product_id, quantity, price
  2. احسب إجمالي الإيرادات: مجموع (quantity * price)
  3. ابحث عن متوسط قيمة الطلب
  4. جمّع الطلبات حسب العميل واحسب إجمالي إنفاق كل عميل
  5. حدد أعلى 3 عملاء حسب إجمالي الإنفاق
  6. عد المنتجات الفريدة المطلوبة
  7. ابحث عن المنتج الأكثر شعبية (أعلى كمية إجمالية مباعة)

تمرين 2: معالجة درجات الطلاب

معالجة درجات الطلاب وإنشاء إحصائيات باستخدام المجموعات.

  1. أنشئ مجموعة من الطلاب مع: name, subject, grade (0-100)
  2. احسب متوسط الصف لكل مادة
  3. ابحث عن الطلاب بدرجة >= 90 (طلاب A)
  4. جمّع الطلاب حسب مستوى الدرجة (A: 90-100، B: 80-89، إلخ)
  5. رتّب الطلاب حسب الدرجة (من الأعلى إلى الأدنى)
  6. احصل على قائمة الطلاب الذين رسبوا (درجة < 60)
  7. احسب الوسيط للدرجات لكل مادة

تمرين 3: لوحة تحكم منشورات المدونة

بناء لوحة تحكم مع إحصائيات منشورات المدونة باستخدام المجموعات.

  1. استرجع جميع المنشورات مع: title, author_id, category, views, created_at
  2. جمّع المنشورات حسب الفئة وعد المنشورات لكل فئة
  3. ابحث عن أعلى 5 منشورات مشاهدة
  4. احسب متوسط المشاهدات لكل مؤلف
  5. احصل على المنشورات المنشورة في آخر 30 يوماً
  6. أنشئ قائمة "رائجة": منشورات مع >1000 مشاهدة في آخر 7 أيام
  7. ابحث عن المؤلفين الذين نشروا >5 منشورات هذا الشهر

الملخص

في هذا الدرس، تعلمت:

  • إنشاء والعمل مع مجموعات Laravel
  • تحويل البيانات باستخدام map()، filter()، و reduce()
  • استخراج القيم باستخدام pluck()
  • تجميع وترتيب المجموعات
  • دمج المجموعات باستخدام merge()، concat()، flatten()
  • إيجاد القيم الفريدة والمكررات
  • تقسيم المجموعات للترقيم/المعالجة
  • أنماط واستخدامات المجموعات من العالم الحقيقي
  • المجموعات الكسولة لكفاءة الذاكرة
  • الرسائل عالية المستوى للحصول على كود أنظف
الدرس التالي: سنستكشف معالجة الأخطاء، التسجيل، وتقنيات تصحيح الأخطاء في Laravel.