Example 2. Generating a Monthly Report for Employee Work Hours

Example 2. Generating a Monthly Report for Employee Work Hours

2024-09-08 وقت القراءه : 5 دقائق

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



إضافة تعليق
Loading...