Created
February 25, 2026 22:32
-
-
Save ivanmercedes/4599be70b83efe17f933b6a6d85ef5b4 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| namespace App\Http\Controllers; | |
| use Illuminate\Http\Request; | |
| use Inertia\Inertia; | |
| use Symfony\Component\DomCrawler\Crawler; | |
| use Illuminate\Support\Facades\Http; | |
| class SeoCheckerController extends Controller | |
| { | |
| public function index(Request $request) | |
| { | |
| return Inertia::render('seo'); | |
| } | |
| /** | |
| * Analiza una URL y devuelve un puntaje SEO | |
| * | |
| * @param Request $request | |
| */ | |
| public function analyzeUrl(Request $request) | |
| { | |
| $validated = $request->validate([ | |
| 'url' => ['required', 'url'], | |
| ]); | |
| $baseUrl = parse_url($validated['url'], PHP_URL_SCHEME) . '://' . parse_url($validated['url'], PHP_URL_HOST); | |
| $visited = []; | |
| $results = []; | |
| $this->crawl($validated['url'], $baseUrl, $visited, $results); | |
| $returnData = $this->calculateSeoScore($results); | |
| return Inertia::render('seo', [ | |
| 'item' => $returnData | |
| ]); | |
| } | |
| /** | |
| * Realiza un rastreo de la página web | |
| * | |
| * @param string $url URL de la página a rastrear | |
| * @param string $baseUrl URL base de la página | |
| * @param array $visited URLs visitadas | |
| * @param array $results Resultados del rastreo | |
| * @param int $depth Profundidad actual | |
| * @param int $maxDepth Profundidad máxima | |
| * @return void | |
| */ | |
| private function crawl($url, $baseUrl, &$visited, &$results, $depth = 0, $maxDepth = 10): void | |
| { | |
| $normalizedUrl = $this->normalizeUrl($url); | |
| if (isset($visited[$normalizedUrl]) || $depth > $maxDepth) { | |
| return; | |
| } | |
| $visited[$normalizedUrl] = true; | |
| $response = Http::get($url); | |
| if (!$response->successful()) { | |
| return; | |
| } | |
| $crawler = new Crawler($response->body(), $url); | |
| $has_title = $crawler->filter('title')->count() ? true : false; | |
| $has_description = $crawler->filter('meta[name="description"]')->count() | |
| ? true | |
| : false; | |
| $title = $crawler->filter('title')->count() ? $crawler->filter('title')->text() : ''; | |
| $metaDescription = $crawler->filter('meta[name="description"]')->count() | |
| ? $crawler->filter('meta[name="description"]')->attr('content') | |
| : ''; | |
| $h1Text = $crawler->filter('h1')->count() ? $crawler->filter('h1')->text() : 'No H1 found'; | |
| $has_h1 = $crawler->filter('h1')->count() ? true : false; | |
| $results[] = [ | |
| 'url' => $normalizedUrl, | |
| 'title' => $title, | |
| 'meta_description' => $metaDescription, | |
| 'has_description' => $has_description, | |
| 'has_title' => $has_title, | |
| 'h1' => $h1Text, | |
| 'has_h1' => $has_h1 | |
| ]; | |
| $links = $crawler->filter('a')->links(); | |
| foreach ($links as $link) { | |
| $href = $link->getUri(); | |
| if ( | |
| strpos($href, $baseUrl) === 0 && | |
| strpos($href, '#') === false && | |
| !preg_match('/\.(pdf|docx?|xlsx?|pptx?|zip|rar|png|jpg|jpeg|webp)$/i', $href) | |
| ) { | |
| $this->crawl($href, $baseUrl, $visited, $results, $depth + 1, $maxDepth); | |
| } | |
| } | |
| } | |
| /** | |
| * Normaliza una URL para evitar duplicados | |
| * | |
| * @param string $url URL a normalizar | |
| * @return string URL normalizada | |
| * | |
| */ | |
| private function normalizeUrl($url): string | |
| { | |
| $parsedUrl = parse_url($url); | |
| $scheme = $parsedUrl['scheme'] ?? 'http'; | |
| $host = $parsedUrl['host'] ?? ''; | |
| $path = $parsedUrl['path'] ?? '/'; | |
| $path = ($path === '/') ? '/' : rtrim($path, '/'); | |
| $normalizedUrl = "{$scheme}://{$host}{$path}"; | |
| if (isset($parsedUrl['query'])) { | |
| $normalizedUrl .= "?{$parsedUrl['query']}"; | |
| } | |
| return $normalizedUrl; | |
| } | |
| /** | |
| * Calcula el puntaje SEO de las páginas | |
| * | |
| * @param array $data Datos de las páginas | |
| * @return array Datos de las páginas con puntaje SEO | |
| * | |
| */ | |
| private function calculateSeoScore(array $data): array | |
| { | |
| $totalScore = 0; | |
| $pageCount = count($data); | |
| $data = array_map(function ($page) use (&$totalScore) { | |
| $score = 100; | |
| if (!$page['has_title']) { | |
| $score -= 40; | |
| } | |
| if (!$page['has_description']) { | |
| $score -= 20; | |
| } | |
| if (!$page['has_h1']) { | |
| $score -= 30; | |
| } | |
| $score = max(0, $score); | |
| $totalScore += $score; | |
| return array_merge($page, ['score' => $score]); | |
| }, $data); | |
| $averageScore = $pageCount > 0 ? $totalScore / $pageCount : 0; | |
| return ['pages' => $data, 'average_score' => $averageScore]; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment