27日2050-修复宠物技能时间格式化方法调用错误.md 8.0 KB

修复宠物技能时间格式化方法调用错误

任务时间: 2025年05月27日 20:50
任务状态: ✅ 已完成
处理人员: AI Assistant

问题描述

在实现宠物技能持续时间友好显示功能后,出现了以下错误:

Call to undefined method App\Module\Pet\Models\PetSkill::formatDuration()

问题分析

🔍 错误根因

Grid回调函数上下文问题

PetSkillController 的Grid列定义中:

$grid->column('duration_time', '持续时间')->display(function ($value) {
    return $this->formatDuration($value);  // ❌ 错误:$this指向PetSkill模型实例
})->unescape()->sortable();

上下文分析

  • 预期$this 指向 PetSkillController 实例
  • 实际$this 指向 PetSkill 模型实例
  • 结果:模型实例没有 formatDuration() 方法,导致调用失败

Laravel Grid机制

在Dcat Admin的Grid列display回调中:

  • 回调函数的 $this 上下文是当前行的模型实例
  • 不是控制器实例
  • 因此无法调用控制器的私有方法

修复方案

🔧 方案选择

考虑了以下几种解决方案:

方案1:使用静态方法(✅ 采用)

将格式化方法移到模型中作为静态方法,便于复用

方案2:使用闭包变量

在回调外部定义变量,但不够优雅

方案3:使用全局函数

创建全局helper函数,但污染全局命名空间

🔧 具体实现

1. 在PetSkill模型中添加静态方法

/**
 * 格式化持续时间显示
 *
 * @param int $seconds 秒数
 * @return string 友好的时间显示
 */
public static function formatDuration(int $seconds): string
{
    if ($seconds <= 0) {
        return '<span class="text-muted">无持续时间</span>';
    }

    $days = floor($seconds / 86400);
    $hours = floor(($seconds % 86400) / 3600);
    $minutes = floor(($seconds % 3600) / 60);
    $remainingSeconds = $seconds % 60;

    $parts = [];

    if ($days > 0) {
        $parts[] = "<span class='badge badge-primary'>{$days}天</span>";
    }

    if ($hours > 0) {
        $parts[] = "<span class='badge badge-info'>{$hours}小时</span>";
    }

    if ($minutes > 0) {
        $parts[] = "<span class='badge badge-success'>{$minutes}分钟</span>";
    }

    if ($remainingSeconds > 0 && empty($parts)) {
        $parts[] = "<span class='badge badge-secondary'>{$remainingSeconds}秒</span>";
    }

    if (empty($parts)) {
        return '<span class="text-muted">瞬间</span>';
    }

    $result = implode(' ', $parts);
    $result .= "<br><small class='text-muted'>({$seconds}秒)</small>";

    return $result;
}

2. 添加模型访问器

/**
 * 获取格式化的持续时间
 *
 * @return string
 */
public function getFormattedDurationTimeAttribute(): string
{
    return self::formatDuration($this->duration_time);
}

/**
 * 获取格式化的冷却时间
 *
 * @return string
 */
public function getFormattedCoolDownAttribute(): string
{
    return self::formatDuration($this->cool_down);
}

3. 修改控制器调用方式

// 修改前(错误)
$grid->column('duration_time', '持续时间')->display(function ($value) {
    return $this->formatDuration($value);  // ❌ $this指向模型实例
})->unescape()->sortable();

// 修改后(正确)
$grid->column('duration_time', '持续时间')->display(function ($value) {
    return \App\Module\Pet\Models\PetSkill::formatDuration($value);  // ✅ 静态方法调用
})->unescape()->sortable();

4. 清理冗余代码

删除控制器中不再使用的 formatDuration() 方法。

技术优势

📈 代码组织改善

更好的职责分离

  • 模型层:负责数据格式化逻辑
  • 控制器层:负责界面展示逻辑
  • 符合MVC模式:数据相关的方法放在模型中

更强的复用性

// 可以在任何地方使用
$formattedTime = PetSkill::formatDuration(7200);

// 模型访问器
$skill = PetSkill::find(1);
echo $skill->formatted_duration_time;
echo $skill->formatted_cool_down;

更好的可测试性

// 静态方法易于单元测试
$this->assertEquals('2小时', strip_tags(PetSkill::formatDuration(7200)));

🔧 扩展性提升

其他模块复用

// 其他模块可以直接使用
use App\Module\Pet\Models\PetSkill;

$formattedTime = PetSkill::formatDuration($someSeconds);

访问器便利性

// 在模板中直接使用
{{ $skill->formatted_duration_time }}
{{ $skill->formatted_cool_down }}

测试验证

🧪 测试结果

测试宠物技能格式化方法修复
============================
测试静态方法 PetSkill::formatDuration():
----------------------------------------
0秒       -> 无持续时间
2小时    -> 2小时(7200秒)
20小时   -> 20小时(72000秒)
1天       -> 1天(86400秒)

测试模型访问器:
================
自动除草: 持续时间=20小时(72000秒), 冷却时间=无持续时间
自动种植: 持续时间=20小时(72000秒), 冷却时间=无持续时间
自动收获: 持续时间=2小时(7200秒), 冷却时间=无持续时间
自动浇水: 持续时间=2小时(7200秒), 冷却时间=无持续时间
自动杀虫: 持续时间=2小时(7200秒), 冷却时间=无持续时间

✅ 验证项目

  • 静态方法调用PetSkill::formatDuration() 正常工作
  • 访问器功能$skill->formatted_duration_time 正常工作
  • 时间格式化:各种时间值正确转换
  • 错误消除:不再出现方法未定义错误

经验总结

🎯 技术教训

Grid回调函数上下文

  • Grid的display回调中,$this 指向当前行的模型实例
  • 不能直接调用控制器的方法
  • 需要使用静态方法或全局函数

Laravel访问器模式

// 访问器命名规范
public function getFormattedDurationTimeAttribute()  // formatted_duration_time
public function getFormattedCoolDownAttribute()     // formatted_cool_down

代码组织原则

  • 数据格式化逻辑应该放在模型中
  • 控制器只负责界面逻辑
  • 静态方法便于复用和测试

🎯 最佳实践

Grid列自定义显示

// ✅ 推荐:使用模型静态方法
$grid->column('field')->display(function ($value) {
    return ModelClass::formatMethod($value);
});

// ✅ 推荐:使用模型访问器
$grid->column('formatted_field');

// ❌ 避免:调用控制器方法
$grid->column('field')->display(function ($value) {
    return $this->controllerMethod($value);  // $this不是控制器
});

时间格式化复用

// 在模型中定义静态方法
public static function formatDuration(int $seconds): string

// 在其他地方复用
PetSkill::formatDuration($seconds);

系统影响

📈 正面影响

代码质量提升

  • 更好的组织结构:格式化逻辑归属明确
  • 更强的复用性:其他地方可以直接使用
  • 更易的维护性:集中管理格式化逻辑

功能稳定性

  • 错误消除:解决了方法调用错误
  • 功能正常:时间格式化正常工作
  • 用户体验:界面显示正常

🔧 技术债务清理

代码重构

  • 删除了控制器中冗余的方法
  • 清理了未使用的import
  • 优化了代码结构

设计改进

  • 遵循了MVC模式
  • 提高了代码的可测试性
  • 增强了代码的可复用性

总结

成功修复了宠物技能时间格式化方法调用错误:

  • 问题解决:修复了Grid回调函数上下文错误
  • 代码重构:将格式化方法移到模型中
  • 功能增强:添加了模型访问器便于使用
  • 代码优化:清理了冗余代码和import
  • 测试验证:确保功能正常工作

现在宠物技能管理页面的时间显示功能完全正常,代码结构也更加合理和可维护。

🎉 修复完成!宠物技能时间格式化功能现在完全正常工作!