Laravel HR系统请假余额功能开发及代码修正求助
Hey there! Let's tackle your leave balance management issues for your Laravel HR system. I'll break down each problem with practical code examples and best practices.
First, let's fix the issue where you're deducting a fixed 1 day instead of the actual days between the start and end dates. Here's how to do it properly:
Step 1: Set Up Model Relationships
Even though you don't have a foreign key constraint yet, adding model relationships will make your code cleaner and more maintainable.
Leave Model (app/Models/Leave.php):
namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Carbon\Carbon; class Leave extends Model { protected $fillable = ['start_date', 'end_date', 'user_id', /* add other leave fields here */]; // Define relationship to User public function user(): BelongsTo { return $this->belongsTo(User::class); } }
User Model (app/Models/User.php):
namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasMany; class User extends Model { // ... existing code // Define relationship to Leave public function leaves(): HasMany { return $this->hasMany(Leave::class); } }
Step 2: Fix the Controller Logic (Store Method)
You'll want to handle this in the store method of your LeaveController—this is where you process new leave requests. We'll use database transactions to ensure both the leave record is created and the balance is deducted (no partial failures!).
namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Models\Leave; use Carbon\Carbon; use Illuminate\Support\Facades\DB; class LeaveController extends Controller { public function store(Request $request) { // Validate incoming request data $validated = $request->validate([ 'start_date' => 'required|date', 'end_date' => 'required|date|after_or_equal:start_date', // Add validation for other leave fields (e.g., reason) ]); // Calculate total leave days (includes both start and end dates) $startDate = Carbon::parse($validated['start_date']); $endDate = Carbon::parse($validated['end_date']); $totalLeaveDays = $startDate->diffInDays($endDate) + 1; $user = auth()->user(); // Check if user has enough leave balance if ($user->leave_balance < $totalLeaveDays) { return back()->withErrors(['message' => 'Insufficient leave balance to submit this request.']); } // Use transaction to ensure data consistency DB::transaction(function () use ($user, $validated, $totalLeaveDays) { // Create the leave record $user->leaves()->create($validated); // Deduct the days from the user's balance $user->decrement('leave_balance', $totalLeaveDays); }); return redirect()->route('leaves.index')->with('success', 'Leave request submitted successfully! Balance updated.'); } }
Note: If you need to calculate working days only (exclude weekends/holidays), replace diffInDays with diffInWeekdays(). For holiday support, consider packages like spatie/laravel-holidays to exclude custom holidays.
For this, Laravel's Task Scheduling is the perfect tool—no need to hack around with Carbon manually. Here's how to set it up:
Step 1: Create an Artisan Command
Run this in your terminal to generate a custom command:
php artisan make:command UpdateLeaveBalance
Edit the generated file (app/Console/Commands/UpdateLeaveBalance.php):
namespace App\Console\Commands; use Illuminate\Console\Command; use App\Models\User; class UpdateLeaveBalance extends Command { protected $signature = 'leave:update-balance'; protected $description = 'Automatically add 0.05 to each user\'s leave balance every day'; public function handle() { // Increment balance for all users by 0.05 User::query()->increment('leave_balance', 0.05); $this->info('Successfully updated all user leave balances!'); } }
Step 2: Schedule the Command
Open app/Console/Kernel.php and add the schedule entry in the schedule method:
protected function schedule(Schedule $schedule) { // Run the command daily at midnight (adjust time if needed) $schedule->command('leave:update-balance')->daily(); // Add other scheduled tasks here }
Step 3: Set Up Server Cron Job
For the scheduler to work, you need to add a cron job on your server that runs Laravel's schedule command every minute. Add this line to your server's crontab (run crontab -e to edit):
* * * * * cd /path-to-your-laravel-project && php artisan schedule:run >> /dev/null 2>&1
Replace /path-to-your-laravel-project with the actual path to your project.
Add Foreign Key Constraint: To ensure data integrity, add a foreign key to the
user_idcolumn in yourleavestable. Create a migration:php artisan make:migration add_foreign_key_to_leaves_tableThen edit the migration file:
public function up() { Schema::table('leaves', function (Blueprint $table) { $table->foreignId('user_id')->constrained()->onDelete('cascade'); }); }Run
php artisan migrateto apply it.Move Logic to Model Events: For cleaner separation of concerns, you can move the balance deduction logic to the
Leavemodel'screatedevent instead of handling it in the controller. Add this to yourLeavemodel'sbootmethod:protected static function boot() { parent::boot(); static::created(function ($leave) { $startDate = Carbon::parse($leave->start_date); $endDate = Carbon::parse($leave->end_date); $totalDays = $startDate->diffInDays($endDate) + 1; $leave->user->decrement('leave_balance', $totalDays); }); }This way, any time a leave record is created (even via seeders or other parts of the app), the balance will automatically be deducted.
Prevent Negative Balance: Add validation in the controller (as shown earlier) to ensure users can't request more leave than they have. You can also add a database constraint to the
leave_balancecolumn to prevent negative values:// In your users table migration $table->decimal('leave_balance', 8, 2)->default(20.00)->min(0);
内容的提问来源于stack exchange,提问作者Vit

