KTV/app/Imports/SongDataImport.php

177 lines
6.1 KiB
PHP
Raw Normal View History

2025-05-05 16:54:21 +08:00
<?php
namespace App\Imports;
use App\Models\Song;
use App\Models\Artist;
use App\Models\SongCategory;
use App\Enums\ArtistCategory;
use App\Enums\SongLanguageType;
use App\Enums\SongSituation;
use App\Helpers\ChineseNameConverter;
use App\Helpers\ChineseStrokesConverter;
2025-05-05 16:54:21 +08:00
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Log;
use Maatwebsite\Excel\Concerns\ToCollection;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
use Maatwebsite\Excel\Concerns\WithChunkReading;
use Maatwebsite\Excel\Imports\HeadingRowFormatter;
2025-05-05 16:54:21 +08:00
class SongDataImport implements ToCollection, WithHeadingRow, WithChunkReading
{
protected array $artistCache = [];
protected array $categoryMap = [];
public function __construct()
{
// 關閉 heading row 格式化
HeadingRowFormatter::default('none');
2025-05-05 16:54:21 +08:00
// 快取分類代碼對應 ID
$this->categoryMap = SongCategory::pluck('id', 'code')->toArray();
}
public function collection(Collection $rows)
{
// 建立現有歌手名稱的查找表,避免重複建立
static $existingIDs = null;
if ($existingIDs === null) {
$existingIDs = array_flip(array_map('trim', Song::pluck('id')->all()));
}
$ToInsert = [];
2025-05-05 16:54:21 +08:00
$artistMap = []; // [song_id => [artist_id]]
$categoryMap = []; // [song_id => [category_id]]
foreach ($rows as $row) {
$songId = trim($row['編號'] ?? '');
if (!$songId) {
continue;
}
// 若資料庫已有該編號,跳過
if (isset($existingIDs[$songId])) continue;
// 字元處理
$songName=trim($row['歌名'] ?? '');
$simplified=ChineseNameConverter::convertToSimplified($songName);// 繁體轉簡體
if (!$row->has('注音')) {
$phoneticAbbr = ChineseNameConverter::getKTVZhuyinAbbr($simplified);// 注音符號
} else {
$phoneticAbbr = trim($row['注音']);
}
if (!$row->has('拼音')) {
$pinyinAbbr = ChineseNameConverter::getKTVPinyinAbbr($simplified);// 拼音首字母
} else {
$pinyinAbbr = trim($row['拼音']);
}
if (!$row->has('kk3')) {//歌名第一個字筆畫
$chars = preg_split('//u', $songName, -1, PREG_SPLIT_NO_EMPTY);
$firstChar = $chars[0] ?? null;
$strokesAbbr=$firstChar ? ChineseStrokesConverter::getStrokes($firstChar) : null;
} else {
$strokesAbbr=trim($row['kk3'] ?? 0);
}
if (!$row->has('kk4')) {//歌名字數
$songNumber = mb_strlen($songName, 'UTF-8');
} else {
$songNumber=trim($row['kk4'] ?? 0);
}
2025-05-05 16:54:21 +08:00
// 準備 song 資料
$ToInsert[] = [
2025-05-05 16:54:21 +08:00
'id' => $songId,
'name' => $songName,
2025-05-05 16:54:21 +08:00
'adddate' => trim($row['日期'] ?? null),
'filename' => trim($row['檔名'] ?? ''),
'language_type' => SongLanguageType::tryFrom(trim($row['語別'] ?? '')) ?? SongLanguageType::Unset,
'db_change' => trim($row['kk2'] ?? 0),//分貝增減
'vocal' => trim($row['kk6'] ?? 0),//人聲
'situation' => SongSituation::tryFrom(trim($row['kk7'] ?? '')) ?? SongSituation::Unset,//情境
2025-05-05 16:54:21 +08:00
'copyright01' => trim($row['版權01'] ?? ''),
'copyright02' => trim($row['版權02'] ?? ''),
'note01' => trim($row['版權03'] ?? ''),
'note02' => trim($row['版權04'] ?? ''),
'note03' => trim($row['版權05'] ?? ''),
'note04' => trim($row['版權06'] ?? ''),
2025-05-05 16:54:21 +08:00
'enable' => trim($row['狀態'] ?? 1),
'simplified' => $simplified,
'phonetic_abbr' => $phoneticAbbr,
'pinyin_abbr' => $pinyinAbbr,
'strokes_abbr' => $strokesAbbr,
'song_number' => $songNumber,
2025-05-05 16:54:21 +08:00
'song_counts' => trim($row['點播次數'] ?? 0),
];
foreach (['歌星A', '歌星B'] as $key) {
$artistName = trim($row[$key] ?? '');
if ($artistName === '') continue;
// 若是歌星B且與歌星A相同則跳過
if ($key === '歌星B' && $artistName === trim($row['歌星A'] ?? '')) continue;
2025-05-05 16:54:21 +08:00
$artistId = $this->getOrCreateArtistId($artistName);
$artistMap[$songId][] = $artistId;
}
// 分類處理(多個用 , 分隔)
if (!empty($row['分類'])) {
$codes = explode(',', $row['分類']);
foreach ($codes as $code) {
$code = trim($code);
if (isset($this->categoryMap[$code])) {
$categoryMap[$songId][] = $this->categoryMap[$code];
}
}
}
// 新增到快取,避免後面重複匯入
$existingIDs[$songId] = true;
2025-05-05 16:54:21 +08:00
}
// 寫入資料庫
Song::insert($ToInsert);
2025-05-05 16:54:21 +08:00
// 同步關聯(建議可用事件或批次處理)
foreach ($artistMap as $songId => $artistIds) {
$song = Song::find($songId);
if ($song) {
$song->artists()->sync($artistIds);
}
}
foreach ($categoryMap as $songId => $categoryIds) {
$song = Song::find($songId);
if ($song) {
$song->categories()->sync($categoryIds);
}
}
}
protected function getOrCreateArtistId(string $name): int
{
if (isset($this->artistCache[$name])) {
return $this->artistCache[$name];
}
$artist = Artist::firstOrCreate(
['name' => $name],
['category' => ArtistCategory::Unset]
);
return $this->artistCache[$name] = $artist->id;
}
public function chunkSize(): int
{
return 100;
2025-05-05 16:54:21 +08:00
}
public function headingRow(): int
{
return 1;
}
}