update deploy
This commit is contained in:
169
database/seeders/CourseContentSeeder.php
Normal file
169
database/seeders/CourseContentSeeder.php
Normal file
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\Category;
|
||||
use App\Models\Course;
|
||||
use App\Models\CourseBlock;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class CourseContentSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Seed course blocks (rich content) from parsed docx articles.
|
||||
* Updates long_desc/requirements/scope on existing courses if empty.
|
||||
* Creates new courses from articles that don't match existing ones.
|
||||
*
|
||||
* Source: storage/app/course_blocks_data.json
|
||||
* Run AFTER CourseSeeder. Existing CourseSeeder is untouched.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
$jsonPath = storage_path('app/course_blocks_data.json');
|
||||
|
||||
if (! file_exists($jsonPath)) {
|
||||
$this->command->error('course_blocks_data.json bulunamadı. Önce Python parse script çalıştırın.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var array<int, array<string, mixed>> $data */
|
||||
$data = json_decode(file_get_contents($jsonPath), true);
|
||||
$cats = Category::pluck('id', 'slug');
|
||||
$allCourses = Course::all();
|
||||
$matched = 0;
|
||||
$created = 0;
|
||||
$newCourses = 0;
|
||||
$blockCount = 0;
|
||||
|
||||
foreach ($data as $item) {
|
||||
$course = $this->findCourse($allCourses, $item);
|
||||
|
||||
if (! $course) {
|
||||
// Create new course from article data
|
||||
$categoryId = $cats[$item['category_slug']] ?? null;
|
||||
if (! $categoryId) {
|
||||
$this->command->warn("Kategori bulunamadı: {$item['category_slug']} -> {$item['title_short']}");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$course = Course::create([
|
||||
'category_id' => $categoryId,
|
||||
'slug' => Str::slug($item['title_short']),
|
||||
'title' => $item['title_short'],
|
||||
'desc' => Str::limit($item['long_desc'], 200),
|
||||
'long_desc' => $item['long_desc'],
|
||||
'duration' => '',
|
||||
'requirements' => $item['requirements'],
|
||||
'scope' => $item['scope'],
|
||||
'standard' => 'STCW / IMO Uyumlu',
|
||||
'language' => 'Türkçe',
|
||||
'location' => 'Kadıköy, İstanbul',
|
||||
'meta_title' => $item['title_short'].' | Boğaziçi Denizcilik',
|
||||
'meta_description' => Str::limit($item['long_desc'], 155),
|
||||
]);
|
||||
$newCourses++;
|
||||
$allCourses->push($course);
|
||||
} else {
|
||||
$matched++;
|
||||
$this->updateCourseFields($course, $item);
|
||||
}
|
||||
|
||||
// Sync blocks
|
||||
$count = $this->syncBlocks($course, $item['blocks']);
|
||||
$blockCount += $count;
|
||||
}
|
||||
|
||||
$total = count($data);
|
||||
$this->command->info("Toplam: {$total} makale, {$matched} eşleşme, {$newCourses} yeni kurs, {$blockCount} blok.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection<int, Course> $allCourses
|
||||
* @param array<string, mixed> $item
|
||||
*/
|
||||
private function findCourse($allCourses, array $item): ?Course
|
||||
{
|
||||
$articleSlug = Str::slug($item['title_short']);
|
||||
$titleNorm = $this->normalize($item['title_short']);
|
||||
|
||||
// 1. Exact slug match
|
||||
$found = $allCourses->first(fn (Course $c) => $c->slug === $articleSlug);
|
||||
if ($found) {
|
||||
return $found;
|
||||
}
|
||||
|
||||
// 2. Normalized title contains match
|
||||
$found = $allCourses->first(fn (Course $c) => $this->normalize($c->title) === $titleNorm);
|
||||
if ($found) {
|
||||
return $found;
|
||||
}
|
||||
|
||||
// 3. Fuzzy — first 25 chars of normalized title
|
||||
$prefix = mb_substr($titleNorm, 0, 25);
|
||||
|
||||
return $allCourses->first(fn (Course $c) => str_starts_with($this->normalize($c->title), $prefix));
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize text: lowercase, strip accents, collapse whitespace.
|
||||
*/
|
||||
private function normalize(string $text): string
|
||||
{
|
||||
$text = mb_strtolower($text);
|
||||
// Turkish specific replacements
|
||||
$text = str_replace(
|
||||
['ç', 'ğ', 'ı', 'ö', 'ş', 'ü', 'â', 'î', 'û'],
|
||||
['c', 'g', 'i', 'o', 's', 'u', 'a', 'i', 'u'],
|
||||
$text,
|
||||
);
|
||||
$text = preg_replace('/[^a-z0-9\s]/', '', $text);
|
||||
|
||||
return preg_replace('/\s+/', ' ', trim($text));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $item
|
||||
*/
|
||||
private function updateCourseFields(Course $course, array $item): void
|
||||
{
|
||||
$updates = [];
|
||||
|
||||
if (empty($course->long_desc) && ! empty($item['long_desc'])) {
|
||||
$updates['long_desc'] = $item['long_desc'];
|
||||
}
|
||||
|
||||
if ((empty($course->requirements) || $course->requirements === []) && ! empty($item['requirements'])) {
|
||||
$updates['requirements'] = $item['requirements'];
|
||||
}
|
||||
|
||||
if ((empty($course->scope) || $course->scope === []) && ! empty($item['scope'])) {
|
||||
$updates['scope'] = $item['scope'];
|
||||
}
|
||||
|
||||
if (! empty($updates)) {
|
||||
$course->update($updates);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, array<string, mixed>> $blocks
|
||||
*/
|
||||
private function syncBlocks(Course $course, array $blocks): int
|
||||
{
|
||||
$count = 0;
|
||||
|
||||
foreach ($blocks as $block) {
|
||||
CourseBlock::updateOrCreate(
|
||||
['course_id' => $course->id, 'order_index' => $block['order_index']],
|
||||
['type' => $block['type'], 'content' => $block['content']],
|
||||
);
|
||||
$count++;
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user