Lỗi Gặp Phải
PHP Fatal error: Uncaught Error: Call to undefined method App\Models\User::findByEmail() in /var/www/app/controllers/AuthController.php:45
Stack trace:
#0 /var/www/app/controllers/AuthController.php(45): App\Models\User::findByEmail('admin@example.com')
#1 /var/www/app/index.php(12): AuthController->login()
#2 {main}
thrown in /var/www/app/controllers/AuthController.php on line 45
2 giờ sáng. Deployment vừa xong. Người dùng không đăng nhập được. Log hệ thống đang la hét điều này. Đây là cách xử lý nhanh nhất.
Nguyên Nhân Gốc Rễ
PHP tìm thấy class (App\Models\User) nhưng không tìm thấy findByEmail() trong đó. Có sáu nguyên nhân gây ra điều này — mỗi nguyên nhân có cách sửa khác nhau, vì vậy xác định đúng nguyên nhân trước sẽ giúp bạn tiết kiệm 30 phút mò mẫm.
- Method chưa bao giờ được định nghĩa — bạn đang gọi thứ gì đó đơn giản là chưa tồn tại.
- Gõ nhầm tên method —
findByEmailvsfindByMailvsgetByEmail. - Sai class được resolve — autoloader đang kéo vào một class
Userkhác từ vendor package hoặc từ DI binding đã được cache. - Method nằm trong trait hoặc class cha chưa được include hoặc extend.
- Nhầm lẫn static với instance — gọi
User::findByEmail()theo kiểu static trong khi method được định nghĩa làpublic function(không static), hoặc ngược lại. - Cache đã compile bị cũ — Laravel và Symfony lưu cache định nghĩa class trong
bootstrap/cache/. Cache cũ sẽ phục vụ class cũ từ trước khi method mới của bạn tồn tại.
Bước 1 — Xác Nhận Class Bạn Đang Thực Sự Dùng
Chưa cần sửa dòng code nào. Trước tiên, dump class được resolve ngay tại điểm gọi:
// Trong AuthController.php, ngay trước dòng 45:
var_dump(get_class($user)); // nếu là instance
// hoặc với static:
var_dump(User::class);
exit;
Bạn có thể sẽ ngạc nhiên. Xung đột namespace hoặc DI container cấu hình sai có thể âm thầm đưa vào sai class — class từ vendor package chưa bao giờ có findByEmail ngay từ đầu.
Sau đó xác nhận chính xác file PHP đã load:
$ref = new ReflectionClass(App\Models\User::class);
var_dump($ref->getFileName());
exit;
Bước 2 — Kiểm Tra Định Nghĩa Class
Mở file được báo cáo bởi getFileName() và tìm kiếm method:
grep -n 'findByEmail' /var/www/app/Models/User.php
Không có kết quả? Chọn một trong các cách sửa bên dưới dựa trên lý do tại sao nó bị thiếu.
Cách Sửa A — Thêm Method Còn Thiếu
Trường hợp đơn giản nhất: method chưa bao giờ được viết. Thêm nó vào:
// app/Models/User.php
namespace App\Models;
class User
{
public static function findByEmail(string $email): ?self
{
global $pdo;
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = ? LIMIT 1');
$stmt->execute([$email]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$row) return null;
$user = new self();
foreach ($row as $key => $value) {
$user->$key = $value;
}
return $user;
}
}
Lưu ý với Laravel: findByEmail không phải method có sẵn của Eloquent. Cách viết tương đương theo chuẩn là:
// Thay vì User::findByEmail($email)
$user = User::where('email', $email)->first();
Hoặc thêm nó như một static method thực sự trên model:
// app/Models/User.php (Laravel Eloquent)
public static function findByEmail(string $email): ?static
{
return static::where('email', $email)->first();
}
Cách Sửa B — Sửa Lỗi Gõ Nhầm
Method tồn tại — chỉ là tên hơi khác một chút. Kiểm tra trước:
php -r "print_r(get_class_methods('App\\Models\\User'));"
Lệnh này dump toàn bộ method public của class. Quét danh sách để tìm cái gần nhất với findByEmail. Sau đó cập nhật điểm gọi:
// Sai
$user = User::findByEmail($email);
// Đúng (khớp với tên trong định nghĩa thực tế)
$user = User::findByMail($email);
// hoặc
$user = User::getByEmail($email);
Tính năng autocomplete của IDE sẽ phát hiện điều này trước khi bạn chạy code. Cả PhpStorm lẫn VS Code với Intelephense đều đánh dấu lỗi gọi method không tồn tại ngay trên dòng code — nên bật tính năng này nếu bạn chưa dùng.
Cách Sửa C — Include Trait Hoặc Extend Đúng Class Cha
Method được định nghĩa — chỉ là ở chỗ mà class không nhìn thấy được. Hai trường hợp phổ biến:
Thiếu trait:
// app/Traits/FindableByEmail.php
trait FindableByEmail
{
public static function findByEmail(string $email): ?static
{
return static::where('email', $email)->first();
}
}
// app/Models/User.php
class User extends Model
{
use FindableByEmail; // create(['email' => 'test@example.com']);
$found = User::findByEmail('test@example.com');
$this->assertNotNull($found);
$this->assertEquals($user->id, $found->id);
}

