Browse Source

ссылки для парсера

Stas 1 day ago
parent
commit
7839a9f2fc

+ 50 - 0
app/Http/Controllers/Admin/ParserLinkController.php

@@ -0,0 +1,50 @@
+<?php
+
+namespace App\Http\Controllers\Admin;
+
+/*
+ * ParserLinkController — управление ссылками для парсера в административной панели.
+ *
+ * Маршруты: GET /admin/parser-links (список/форма), POST /admin/parser-links (сохранение)
+ * Логика: при сохранении удаляет все старые записи и вставляет новые (полная замена списка).
+ * Создан: 2026-06-06
+ */
+
+use App\Http\Controllers\Controller;
+use App\Models\ParserLink;
+use Illuminate\Http\RedirectResponse;
+use Illuminate\Http\Request;
+use Illuminate\View\View;
+
+class ParserLinkController extends Controller
+{
+    // Показывает страницу со списком ссылок для парсера
+    public function index(): View
+    {
+        $links = ParserLink::orderBy('sort_order')->orderBy('id')->get();
+
+        return view('admin.parser_links.index', compact('links'));
+    }
+
+    // Сохраняет список ссылок: удаляет старые, вставляет новые
+    public function update(Request $request): RedirectResponse
+    {
+        $request->validate([
+            'urls' => 'nullable|array',
+            'urls.*' => 'nullable|url|max:500',
+        ]);
+
+        $urls = collect($request->input('urls', []))
+            ->filter(fn ($u) => filled($u))
+            ->values();
+
+        ParserLink::truncate();
+
+        $urls->each(function ($url, $i) {
+            ParserLink::create(['url' => $url, 'sort_order' => $i]);
+        });
+
+        return redirect()->route('admin.parser-links.index')
+            ->with('success', 'Ссылки для парсера сохранены.');
+    }
+}

+ 18 - 0
app/Models/ParserLink.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace App\Models;
+
+/*
+ * ParserLink — URL-адрес для парсера.
+ *
+ * Таблица: parser_links
+ * Поля: url (строка), sort_order (порядок сортировки)
+ * Создана: 2026-06-06
+ */
+
+use Illuminate\Database\Eloquent\Model;
+
+class ParserLink extends Model
+{
+    protected $fillable = ['url', 'sort_order'];
+}

+ 7 - 0
config/adminlte.php

@@ -338,12 +338,19 @@ return [
                 ],
             ],
         ],
+        ['header' => 'ДОП ИНФОРМАЦИЯ'],
         [
             'text' => 'Справочники',
             'url'  => 'admin/manuals',
             'icon' => 'fas fa-fw fa-book',
             'can'  => 'manuals.view',
         ],
+        [
+            'text' => 'Ссылки для парсера',
+            'url'  => 'admin/parser-links',
+            'icon' => 'fas fa-fw fa-link',
+            'can'  => 'settings.view',
+        ],
 
         ['header' => 'КОНТЕНТ САЙТА'],
         [

+ 29 - 0
database/migrations/2026_06_06_113837_create_parser_links_table.php

@@ -0,0 +1,29 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+/*
+ * Таблица ссылок для парсера.
+ * Каждая строка — один URL, который парсер обходит.
+ * Создана: 2026-06-06
+ * Индексы: sort_order (сортировка списка)
+ */
+return new class extends Migration
+{
+    public function up(): void
+    {
+        Schema::create('parser_links', function (Blueprint $table) {
+            $table->id();
+            $table->string('url');
+            $table->integer('sort_order')->default(0)->index();
+            $table->timestamps();
+        });
+    }
+
+    public function down(): void
+    {
+        Schema::dropIfExists('parser_links');
+    }
+};

File diff suppressed because it is too large
+ 3 - 0
public/fonts-manifest.dev.json


+ 137 - 0
resources/views/admin/parser_links/index.blade.php

@@ -0,0 +1,137 @@
+{{--
+    Вьюха: Ссылки для парсера — динамический список URL-адресов
+    Создана: 2026-06-06
+    Контроллер: Admin\ParserLinkController::index() / update()
+    Переменные: $links (коллекция ParserLink, отсортированная по sort_order)
+    Логика: POST сохраняет весь список целиком (truncate + insert).
+            JS позволяет добавлять/удалять строки без перезагрузки страницы.
+--}}
+@extends('admin.layout')
+
+@section('title', 'Ссылки для парсера')
+
+@section('content_header')
+    <div class="d-flex justify-content-between align-items-center">
+        <h1 class="m-0">Ссылки для парсера</h1>
+    </div>
+@stop
+
+@section('breadcrumb')
+    <li class="breadcrumb-item"><a href="{{ route('admin.dashboard') }}">Главная</a></li>
+    <li class="breadcrumb-item active">Ссылки для парсера</li>
+@stop
+
+@section('content')
+    @if(session('success'))
+        <div class="alert alert-success alert-dismissible">
+            <button type="button" class="close" data-dismiss="alert">&times;</button>
+            {{ session('success') }}
+        </div>
+    @endif
+    @if($errors->any())
+        <div class="alert alert-danger alert-dismissible">
+            <button type="button" class="close" data-dismiss="alert">&times;</button>
+            <ul class="mb-0">
+                @foreach($errors->all() as $e)<li>{{ $e }}</li>@endforeach
+            </ul>
+        </div>
+    @endif
+
+    <div class="row justify-content-center">
+        <div class="col-md-8">
+            <div class="card card-primary card-outline">
+                <div class="card-header">
+                    <h3 class="card-title">URL-адреса для обхода парсером</h3>
+                </div>
+                <div class="card-body">
+                    <form action="{{ route('admin.parser-links.update') }}" method="POST" id="parser-links-form">
+                        @csrf
+
+                        <div id="links-container">
+                            @forelse($links as $link)
+                                <div class="link-row input-group mb-2">
+                                    <input type="text" name="urls[]"
+                                           class="form-control"
+                                           value="{{ $link->url }}"
+                                           placeholder="https://example.com/catalog">
+                                    <div class="input-group-append">
+                                        <button type="button" class="btn btn-danger btn-remove-link" title="Удалить">
+                                            <i class="fas fa-times"></i>
+                                        </button>
+                                    </div>
+                                </div>
+                            @empty
+                                <div class="link-row input-group mb-2">
+                                    <input type="text" name="urls[]"
+                                           class="form-control"
+                                           placeholder="https://example.com/catalog">
+                                    <div class="input-group-append">
+                                        <button type="button" class="btn btn-danger btn-remove-link" title="Удалить">
+                                            <i class="fas fa-times"></i>
+                                        </button>
+                                    </div>
+                                </div>
+                            @endforelse
+                        </div>
+
+                        <div class="mb-3">
+                            <button type="button" id="btn-add-link" class="btn btn-outline-primary btn-sm">
+                                <i class="fas fa-plus"></i> Добавить ссылку
+                            </button>
+                        </div>
+
+                        <div class="d-flex">
+                            <button type="submit" class="btn btn-primary">
+                                <i class="fas fa-save"></i> Сохранить
+                            </button>
+                        </div>
+                    </form>
+                </div>
+                <div class="card-footer text-muted small">
+                    Каждая строка — отдельный URL. Пустые строки игнорируются при сохранении.
+                </div>
+            </div>
+        </div>
+    </div>
+@stop
+
+@push('js')
+<script>
+(function () {
+    var container = document.getElementById('links-container');
+
+    function makeRow(value) {
+        var row = document.createElement('div');
+        row.className = 'link-row input-group mb-2';
+        row.innerHTML =
+            '<input type="text" name="urls[]" class="form-control" value="' + (value || '') + '" placeholder="https://example.com/catalog">' +
+            '<div class="input-group-append">' +
+                '<button type="button" class="btn btn-danger btn-remove-link" title="Удалить">' +
+                    '<i class="fas fa-times"></i>' +
+                '</button>' +
+            '</div>';
+        return row;
+    }
+
+    // Добавить строку
+    document.getElementById('btn-add-link').addEventListener('click', function () {
+        var row = makeRow('');
+        container.appendChild(row);
+        row.querySelector('input').focus();
+    });
+
+    // Удалить строку (делегирование на контейнер)
+    container.addEventListener('click', function (e) {
+        var btn = e.target.closest('.btn-remove-link');
+        if (!btn) return;
+        var rows = container.querySelectorAll('.link-row');
+        if (rows.length === 1) {
+            // Если последняя строка — просто очищаем её
+            rows[0].querySelector('input').value = '';
+        } else {
+            btn.closest('.link-row').remove();
+        }
+    });
+})();
+</script>
+@endpush

+ 7 - 2
routes/web.php

@@ -10,6 +10,7 @@ use App\Http\Controllers\Admin\LoginController;
 use App\Http\Controllers\Admin\ManualController;
 use App\Http\Controllers\Admin\PageAdminController;
 use App\Http\Controllers\Admin\PageSectionController;
+use App\Http\Controllers\Admin\ParserLinkController;
 use App\Http\Controllers\Admin\ReviewAdminController;
 use App\Http\Controllers\Admin\ServiceAdminController;
 use App\Http\Controllers\Admin\SettingAdminController;
@@ -72,9 +73,9 @@ Route::prefix('admin')->name('admin.')->group(function () {
 
         // Блоки контента
         // Маршруты визарда должны быть ДО resource чтобы 'blocks/wizard' не трактовался как {block}
-        Route::get('blocks/wizard',          [BlockLayoutWizardController::class, 'index'])->name('blocks.wizard');
+        Route::get('blocks/wizard', [BlockLayoutWizardController::class, 'index'])->name('blocks.wizard');
         Route::post('blocks/wizard/analyze', [BlockLayoutWizardController::class, 'analyze'])->name('blocks.wizard.analyze');
-        Route::post('blocks/wizard/generate',[BlockLayoutWizardController::class, 'generate'])->name('blocks.wizard.generate');
+        Route::post('blocks/wizard/generate', [BlockLayoutWizardController::class, 'generate'])->name('blocks.wizard.generate');
         Route::resource('blocks', BlockAdminController::class)->except(['show']);
 
         // Page-builder: управление секциями страницы (встроен в edit)
@@ -117,6 +118,10 @@ Route::prefix('admin')->name('admin.')->group(function () {
         // Cars
         Route::resource('cars', CarController::class);
 
+        // Ссылки для парсера
+        Route::get('parser-links', [ParserLinkController::class, 'index'])->name('parser-links.index');
+        Route::post('parser-links', [ParserLinkController::class, 'update'])->name('parser-links.update');
+
         // Manuals — sections
         Route::prefix('manuals')->name('manuals.')->group(function () {
             Route::get('/', [ManualController::class, 'index'])->name('index');

Some files were not shown because too many files changed in this diff