"واحدة من أكثر الأمور شيوعًا التي ستقوم بها باستخدام الدالة groupBy هي إنتاج التقارير. على سبيل المثال، قد ترغب في إنشاء تقرير شهري لساعات عمل الموظفين (من جدول بطاقات الدوام):"

إعدادات المشروع
ملفات Migration
Schema::create('employees', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->timestamps();
});
 
Schema::create('employee_timesheets', function (Blueprint $table) {
    $table->id();
    $table->foreignIdFor(Employee::class)->constrained();
    $table->dateTime('start');
    $table->dateTime('end')->nullable();
    $table->timestamps();
});إنشاء Employee Model
app/Models/Employee.php
class Employee extends Model
{
    use HasFactory;
 
    protected $fillable = [
        'name'
    ];
 
    public function employeeTimesheets(): HasMany
    {
        return $this->hasMany(EmployeeTimesheet::class);
    }
}
بناء EmployeeTimesheet Model 
app/Models/EmployeeTimesheet.php
class EmployeeTimesheet extends Model
{
    use HasFactory;
 
    protected $fillable = [
        'employee_id',
        'start',
        'end',
    ];
 
    public function employee(): BelongsTo
    {
        return $this->belongsTo(Employee::class);
    }
}بناء Factory
database/factories/EmployeeFactory.php
class EmployeeFactory extends Factory
{
    protected $model = Employee::class;
 
    public function definition(): array
    {
        return [
            'name' => $this->faker->name(),
        ];
    }
}database/factories/EmployeeTimesheetFactory.php
class EmployeeTimesheetFactory extends Factory
{
    protected $model = EmployeeTimesheet::class;
 
    public function definition(): array
    {
        $startTime = fake()->dateTimeBetween('-2 weeks');
        return [
            'employee_id' => Employee::factory(),
            'start' => $startTime,
            // Note that here, we take our start time and add 8 hours to it to keep our maximum time per entry to 8 hours
            'end' => fake()->dateTimeBetween($startTime, $startTime->format('Y-m-d H:i:s').' + 8 hours'),
        ];
    }
}تحضير Database Seeder
database/seeders/DatabaseSeeder.php // ... Employee::factory() ->count(10) ->has(EmployeeTimesheet::factory()->count(14)) ->create(); الأن بذلك سيكون لدينا في قاعده البيانات، 10 موظفين، و 14 تاريخ لكل موظف

إنشاء التقارير
$timesheet = EmployeeTimesheet::query()
    ->select('employee_id')
    // We will use this to calculate the total hours
    // TIMEDIFF will give us the difference between two times - end and start
    // TIME_TO_SEC will convert the time to seconds
    // SUM will sum all the seconds
    // SEC_TO_TIME will convert the seconds to time (00:00:00 format)
    ->addSelect(
        DB::raw('SEC_TO_TIME(SUM(TIME_TO_SEC(TIMEDIFF(end, start)))) as `total_hours`'),
    )
    // We will use this to get the earliest start time
    ->addSelect(
        DB::raw('min(start) as `min_start`'),
    )
    // We will use this to get the latest end time
    ->addSelect(
        DB::raw('max(end) as `max_end`'),
    )
    // We will use this to get the total days
    ->addSelect(
        DB::raw('count(distinct(start)) as `total_days`')
    )
    // Here, we apply the filters only to take the current month
    ->where('start', '>=', now()->startOfMonth())
    ->where('end', '<=', now()->endOfMonth())
    // Preloading employee details in a relationship
    ->with(['employee'])
    // Grouping by employee id
    ->groupBy('employee_id')
    // Ordering by total hours
    ->orderBy('total_hours', 'desc')
    ->get();شرح الجمله أعلاه
$timesheet = EmployeeTimesheet::query()
يبدأ هذا السطر استعلام Eloquent، مستهدفاً نموذج EmployeeTimesheet.
2. حساب إجمالي الساعات:
php
Copy code
->select('employee_id')
->addSelect(
DB::raw('SEC_TO_TIME(SUM(TIME_TO_SEC(TIMEDIFF(end, start)))) as `total_hours`'),
)
يستخدم select('employee_id') لتحديد employee_id كجزء من النتيجة.
يستخدم addSelect مع DB::raw لإضافة استعلام خام يحسب الفرق الزمني بين end و start، يحول هذا الفرق إلى ثواني (TIME_TO_SEC), يجمع هذه الثواني (SUM), وأخيراً يحول النتيجة الكلية إلى تنسيق الوقت (SEC_TO_TIME). هذا ينتج عموداً يُسمى total_hours يمثل إجمالي الساعات التي عملها الموظف.
3. أقل وقت بداية وأحدث وقت انتهاء:
php
Copy code
->addSelect(
DB::raw('min(start) as `min_start`'),
)
->addSelect(
DB::raw('max(end) as `max_end`'),
)
يحسب min(start) أقل وقت بداية لكل موظف خلال الفترة المحددة ويُخزن في min_start.
يحسب max(end) أحدث وقت انتهاء لكل موظف خلال الفترة المحددة ويُخزن في max_end.
4. حساب إجمالي الأيام:
php
Copy code
->addSelect(
DB::raw('count(distinct(start)) as `total_days`')
)
يستخدم count(distinct(start)) لحساب عدد الأيام المتميزة التي عمل فيها كل موظف، مفترضاً أن تواريخ البداية المختلفة تمثل أياماً مختلفة.
5. تصفية البيانات للشهر الحالي:
php
Copy code
->where('start', '>=', now()->startOfMonth())
->where('end', '<=', now()->endOfMonth())
يقوم هذان الشرطان بتصفية السجلات لتشمل فقط تلك التي تقع بين بداية ونهاية الشهر الحالي.
6. تحميل تفاصيل الموظف:
php
Copy code
->with(['employee'])
يستخدم with لتحميل العلاقة employee، وهي عملية تحميل مسبق للبيانات المرتبطة، مما يقلل من الحاجة لاستعلامات قاعدة البيانات المتعددة.
7. تجميع البيانات حسب معرف الموظف وترتيب النتائج:
php
Copy code
->groupBy('employee_id')
->orderBy('total_hours', 'desc')
يجمع البيانات بناءً على employee_id لضمان حصول كل موظف على ملخص بياناته.
يُرتب النتائج بناءً على total_hours بترتيب تنازلي، مما يضع الموظفين ذوي الساعات الأكثر على رأس القائمة.
لعرض البيانات
<table class="table table-striped table-bordered">
    <thead>
    <tr>
        <th>Employee</th>
        <th>Entries</th>
        <th>Total Time</th>
        <th>Earliest Date</th>
        <th>Latest Date</th>
    </tr>
    </thead>
    <tbody>
    @foreach($timesheet as $entry)
        <tr>
            <td>{{ $entry->employee->name }}</td>
            <td>{{ $entry->total_days }}</td>
            <td>{{ $entry->total_hours }}</td>
            <td>{{ $entry->min_start }}</td>
            <td>{{ $entry->max_end }}</td>
        </tr>
    @endforeach
    </tbody>
</table>