"واحدة من أكثر الأمور شيوعًا التي ستقوم بها باستخدام الدالة 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>