\n\n\n```\n\n### 9. Тесты\n\n```php\npostJson(\"/api/shorten\", [\"url\" => \"https://example.com\"]);\n $response->assertStatus(201);\n $this->assertDatabaseHas(\"short_links\", [\"original_url\" => \"https://example.com\"]);\n }\n\n public function test_redirect_increments_clicks(): void {\n $link = ShortLink::create([\n \"original_url\" => \"https://google.com\",\n \"code\" => \"abc123\",\n ]);\n $this->get(\"/abc123\")->assertRedirect(\"https://google.com\");\n $link->refresh();\n $this->assertEquals(1, $link->clicks);\n }\n\n public function test_get_stats(): void {\n ShortLink::create([\n \"original_url\" => \"https://google.com\",\n \"code\" => \"abc123\",\n \"clicks\" => 5,\n ]);\n $response = $this->getJson(\"/api/stats/abc123\");\n $response->assertStatus(200);\n $response->assertJsonPath(\"clicks\", 5);\n }\n\n public function test_invalid_url_validation(): void {\n $response = $this->postJson(\"/api/shorten\", [\"url\" => \"invalid\"]);\n $response->assertStatus(422);\n }\n}\n```\n\nЭто полное решение сервиса с API, фронтенд-частью и тестами. Все требования выполнены: перенаправление, подсчёт кликов, валидация URL, асинхронная отправка.","dateCreated":"2026-03-23T12:10:16.727586","upvoteCount":0,"author":{"@type":"Person","name":"claude-haiku-4.5"}}}}
← Назад к вопросам

Сервис сокращения ссылок

2.0 Middle🔥 201 комментариев
#API и веб-протоколы#Базы данных и SQL#Фреймворки

Условие

Реализовать сервис по сокращению ссылок на Laravel.

Требования

  • Одна страница с формой ввода длинной ссылки
  • Валидация URL
  • Асинхронная отправка формы через JavaScript
  • Генерация короткого уникального кода
  • Перенаправление по короткой ссылке на оригинальный URL
  • Подсчёт количества переходов

API эндпоинты

  • POST /api/shorten - создание короткой ссылки
  • GET /{code} - перенаправление на оригинальный URL
  • GET /api/stats/{code} - статистика переходов

Технологии

Laravel 10+, MySQL, JavaScript/Vue.js

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Решение

1. Миграция базы данных

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration {
    public function up(): void {
        Schema::create("short_links", function (Blueprint $table) {
            $table->id();
            $table->string("original_url", 2048);
            $table->string("code", 10)->unique();
            $table->unsignedInteger("clicks")->default(0);
            $table->timestamps();
            $table->index("code");
        });
    }

    public function down(): void {
        Schema::dropIfExists("short_links");
    }
};

2. Eloquent модель

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class ShortLink extends Model {
    protected $fillable = ["original_url", "code", "clicks"];
}

3. Service класс для логики

<?php

namespace App\Services;

use App\Models\ShortLink;
use Illuminate\Support\Str;

class UrlShorteningService {
    public function generateUniqueCode(): string {
        do {
            $code = Str::random(6);
        } while (ShortLink::where("code", $code)->exists());
        return $code;
    }

    public function createShortLink(string $url): ShortLink {
        return ShortLink::create([
            "original_url" => $url,
            "code" => $this->generateUniqueCode(),
        ]);
    }

    public function getOriginalUrl(string $code): ?string {
        $link = ShortLink::where("code", $code)->first();
        if ($link) {
            $link->increment("clicks");
            return $link->original_url;
        }
        return null;
    }

    public function getStats(string $code): ?array {
        $link = ShortLink::where("code", $code)->first();
        if (!$link) return null;
        return [
            "code" => $link->code,
            "original_url" => $link->original_url,
            "clicks" => $link->clicks,
            "created_at" => $link->created_at,
        ];
    }
}

4. API контроллер

<?php

namespace App\Http\Controllers\Api;

use App\Http\Requests\ShortenUrlRequest;
use App\Services\UrlShorteningService;
use Illuminate\Http\JsonResponse;

class UrlController extends Controller {
    protected $service;

    public function __construct(UrlShorteningService $service) {
        $this->service = $service;
    }

    public function shorten(ShortenUrlRequest $request): JsonResponse {
        $link = $this->service->createShortLink($request->validated()["url"]);
        return response()->json([
            "original_url" => $link->original_url,
            "short_url" => url("/{$link->code}"),
            "code" => $link->code,
        ], 201);
    }

    public function stats(string $code): JsonResponse {
        $stats = $this->service->getStats($code);
        if (!$stats) return response()->json(["message" => "Link not found"], 404);
        return response()->json($stats);
    }
}

5. Веб контроллер

<?php

namespace App\Http\Controllers;

use App\Services\UrlShorteningService;
use Illuminate\Http\RedirectResponse;

class RedirectController extends Controller {
    protected $service;

    public function __construct(UrlShorteningService $service) {
        $this->service = $service;
    }

    public function redirect(string $code): RedirectResponse {
        $originalUrl = $this->service->getOriginalUrl($code);
        if (!$originalUrl) abort(404);
        return redirect()->away($originalUrl);
    }
}

6. Form Request валидация

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class ShortenUrlRequest extends FormRequest {
    public function authorize(): bool { return true; }

    public function rules(): array {
        return ["url" => "required|url|max:2048"];
    }
}

7. Маршруты

<?php

use App\Http\Controllers\RedirectController;
use App\Http\Controllers\Api\UrlController;
use Illuminate\Support\Facades\Route;

Route::get("/", fn() => view("shorten"));
Route::get("/{code}", [RedirectController::class, "redirect"]);
Route::post("/api/shorten", [UrlController::class, "shorten"]);
Route::get("/api/stats/{code}", [UrlController::class, "stats"]);

8. Blade шаблон

<!DOCTYPE html>
<html>
<head>
    <title>URL Shortener</title>
    <meta name="csrf-token" content="{{ csrf_token() }}">
    <style>
        body { font-family: Arial; background: #f5f5f5; padding: 20px; }
        .container { max-width: 600px; margin: 50px auto; }
        input { width: 100%; padding: 10px; font-size: 14px; margin-bottom: 10px; }
        button { background: #007bff; color: white; padding: 10px 20px; border: none; border-radius: 4px; cursor: pointer; }
        .result { background: white; padding: 20px; margin-top: 20px; display: none; }
    </style>
</head>
<body>
    <div class="container">
        <h1>URL Shortener</h1>
        <input type="url" id="url" placeholder="https://example.com/long/path">
        <button onclick="shortenUrl()">Shorten</button>
        <div class="result" id="result">
            <p>Short URL: <a id="shortUrl" target="_blank"></a></p>
        </div>
    </div>
    <script>
        async function shortenUrl() {
            const url = document.getElementById("url").value;
            const response = await fetch("/api/shorten", {
                method: "POST",
                headers: {
                    "Content-Type": "application/json",
                    "X-CSRF-TOKEN": document.querySelector("meta[name=csrf-token]").content,
                },
                body: JSON.stringify({ url }),
            });
            const data = await response.json();
            document.getElementById("shortUrl").href = data.short_url;
            document.getElementById("shortUrl").textContent = data.short_url;
            document.getElementById("result").style.display = "block";
        }
    </script>
</body>
</html>

9. Тесты

<?php

namespace Tests\Feature;

use App\Models\ShortLink;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class UrlShorteningTest extends TestCase {
    use RefreshDatabase;

    public function test_shorten_url_creates_link(): void {
        $response = $this->postJson("/api/shorten", ["url" => "https://example.com"]);
        $response->assertStatus(201);
        $this->assertDatabaseHas("short_links", ["original_url" => "https://example.com"]);
    }

    public function test_redirect_increments_clicks(): void {
        $link = ShortLink::create([
            "original_url" => "https://google.com",
            "code" => "abc123",
        ]);
        $this->get("/abc123")->assertRedirect("https://google.com");
        $link->refresh();
        $this->assertEquals(1, $link->clicks);
    }

    public function test_get_stats(): void {
        ShortLink::create([
            "original_url" => "https://google.com",
            "code" => "abc123",
            "clicks" => 5,
        ]);
        $response = $this->getJson("/api/stats/abc123");
        $response->assertStatus(200);
        $response->assertJsonPath("clicks", 5);
    }

    public function test_invalid_url_validation(): void {
        $response = $this->postJson("/api/shorten", ["url" => "invalid"]);
        $response->assertStatus(422);
    }
}

Это полное решение сервиса с API, фронтенд-частью и тестами. Все требования выполнены: перенаправление, подсчёт кликов, валидация URL, асинхронная отправка.

Сервис сокращения ссылок | PrepBro