timestamps) { if (! in_array('created_at', $fillable, true)) { $fillable[] = 'created_at'; } if (! in_array('updated_at', $fillable, true)) { $fillable[] = 'updated_at'; } } return $fillable; } private function tableColumns(): array { if (self::$tableColumnsCache === null) { self::$tableColumnsCache = $this->loadTableColumns(); } return self::$tableColumnsCache[$this->getTable()] ?? []; } private function loadTableColumns(): array { $map = []; $pattern = null; if (function_exists('database_path')) { try { $pattern = database_path('migrations/*.php'); } catch (\Throwable $e) { $pattern = null; } } if ($pattern === null) { $pattern = __DIR__.'/../../database/migrations/*.php'; } $files = glob($pattern) ?: []; foreach ($files as $file) { $contents = file_get_contents($file); if ($contents === false) { continue; } foreach ($this->parseSqlCreateTables($contents) as $table => $columns) { $map[$table] = $columns; } foreach ($this->parseSchemaCreateTables($contents) as $table => $columns) { if (! isset($map[$table]) || empty($map[$table])) { $map[$table] = $columns; } } } return $map; } private function parseSqlCreateTables(string $contents): array { $map = []; if (preg_match_all('/CREATE TABLE `([^`]+)`\\s*\\((.*?)\\)\\s*(?:ENGINE|;)/is', $contents, $matches, PREG_SET_ORDER)) { foreach ($matches as $match) { $table = $match[1]; $colsBlock = $match[2]; preg_match_all('/`([^`]+)`\\s+[^,]+/m', $colsBlock, $colsMatch); $columns = $colsMatch[1] ?? []; if (! empty($columns)) { $map[$table] = $columns; } } } return $map; } private function parseSchemaCreateTables(string $contents): array { $map = []; if (! preg_match_all('/Schema::create\\(\\s*[\'"]([^\'"]+)[\'"]\\s*,\\s*function\\s*\\([^)]*\\)\\s*\\{(.*?)\\n\\s*\\}\\);/is', $contents, $matches, PREG_SET_ORDER)) { return $map; } foreach ($matches as $match) { $table = $match[1]; $body = $match[2]; $columns = []; if (preg_match_all('/\\$table->\\w+\\(\\s*[\'"]([^\'"]+)[\'"]/', $body, $colMatches)) { $columns = array_merge($columns, $colMatches[1]); } if (str_contains($body, 'timestamps(') || str_contains($body, 'timestamps()')) { $columns[] = 'created_at'; $columns[] = 'updated_at'; } if (str_contains($body, 'softDeletes(') || str_contains($body, 'softDeletes()')) { $columns[] = 'deleted_at'; } if (! empty($columns)) { $map[$table] = $columns; } } return $map; } }