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