string('category')->toString(); $search = $request->string('q')->toString(); $tools = Tool::approved() ->when($category, fn ($query) => $query->where('category', $category)) ->when($search, fn ($query) => $query->where('name', 'like', "%{$search}%")) ->orderByDesc('featured') ->orderByDesc('views') ->paginate(12) ->withQueryString(); return view('tools.index', [ 'tools' => $tools, 'categories' => config('tools.categories'), 'activeCategory' => $category, 'search' => $search, 'title' => 'AI Tools Directory — '.config('app.name'), 'description' => 'Discover AI tools for developers, designers, marketers, and more.', 'canonical' => route('tools.index'), ]); } public function category(string $category): View { abort_unless(array_key_exists($category, config('tools.categories')), 404); $tools = Tool::approved() ->where('category', $category) ->orderByDesc('featured') ->orderByDesc('views') ->paginate(12); $label = config("tools.categories.{$category}"); return view('tools.category', [ 'tools' => $tools, 'categories' => config('tools.categories'), 'category' => $category, 'label' => $label, 'title' => $label.' AI Tools — '.config('app.name'), 'description' => "AI tools for {$label}.", 'canonical' => route('tools.category', $category), ]); } public function show(Tool $tool): View { abort_unless($tool->approved, 404); $tool->increment('views'); $related = Tool::approved() ->where('category', $tool->category) ->whereKeyNot($tool->id) ->limit(4) ->get(); return view('tools.show', [ 'tool' => $tool, 'related' => $related, 'title' => $tool->name.' — AI Tools — '.config('app.name'), 'description' => str($tool->description)->limit(155, '')->toString(), 'canonical' => route('tools.show', $tool), 'image' => $tool->logo_url, ]); } public function visit(Tool $tool): RedirectResponse { abort_unless($tool->approved, 404); $tool->increment('clicks'); return redirect()->away($tool->url); } }