296 lines
10 KiB
PHP
296 lines
10 KiB
PHP
<?php
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use App\Helpers\MigrationHelpers;
|
|
use Illuminate\Console\Attributes\Description;
|
|
use Illuminate\Console\Attributes\Signature;
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
#[Signature('migrate:entries:execute {--limit=}')]
|
|
#[Description('Migrate WP entries')]
|
|
class MigrateEntriesExecute extends Command
|
|
{
|
|
private const array WP_CPTS = ['translations', 'romhacks', 'homebrew', 'utilities', 'documents' ];
|
|
|
|
private const array STATE_MAP = [
|
|
'draft' => 'draft',
|
|
'pending' => 'pending',
|
|
'publish' => 'published',
|
|
'private' => 'hidden',
|
|
'locked' => 'locked',
|
|
];
|
|
|
|
private const array ACF_FIELDS = [
|
|
'entry_title', 'version_number', 'release_date', 'release_site',
|
|
'youtube_video', 'staff', 'hashes',
|
|
];
|
|
|
|
private const array MULTI_TAXONOMIES = [
|
|
'language' => 'languages',
|
|
'modifications' => 'modifications',
|
|
'author-name' => 'authors',
|
|
'document-category' => 'categories',
|
|
'utility-category' => 'categories',
|
|
'utility-os' => 'systems',
|
|
];
|
|
|
|
private array $stats = [];
|
|
|
|
private function getSingle( ?int $term_id, string $targetTable ): ?int
|
|
{
|
|
if (!$term_id) {
|
|
return null;
|
|
}
|
|
|
|
return DB::table('migrations_logs')
|
|
->where('source_system', 'wp')
|
|
->where('target_table', $targetTable )
|
|
->where('source_id', $term_id)
|
|
->value('target_id');
|
|
}
|
|
|
|
private function uniqueSlug(string $baseSlug, string $table, ?int $ignoreId = null): string
|
|
{
|
|
$slug = $baseSlug;
|
|
$i = 1;
|
|
while (
|
|
DB::table($table)->where('slug', $slug)
|
|
->when($ignoreId, fn ($q) => $q->where('id', '!=', $ignoreId))
|
|
->exists() && $i < 100
|
|
) {
|
|
$slug = $baseSlug . '-' . $i++;
|
|
}
|
|
if ($i >= 100) {
|
|
$slug = (string) \Str::uuid();
|
|
}
|
|
return $slug;
|
|
}
|
|
|
|
private function parseStaffCredits(?string $raw): array
|
|
{
|
|
if (!$raw || trim($raw) === '') {
|
|
return [];
|
|
}
|
|
|
|
$credits = [];
|
|
foreach (preg_split('/\r\n|\r|\n/', $raw) as $line) {
|
|
$line = trim($line);
|
|
if ($line === '') continue;
|
|
|
|
if (preg_match('/^(.+?):\s*(.*)$/', $line, $m)) {
|
|
$credits[] = ['name' => trim($m[1]), 'description' => trim($m[2])];
|
|
} elseif (!empty($credits)) {
|
|
$last = array_key_last($credits);
|
|
$credits[$last]['description'] = trim($credits[$last]['description'] . ' ' . $line);
|
|
} else {
|
|
$credits[] = ['name' => $line, 'description' => ''];
|
|
}
|
|
}
|
|
|
|
return $credits;
|
|
}
|
|
|
|
private function parseHashes(?string $raw): array
|
|
{
|
|
if (!$raw || trim($raw) === '') return [];
|
|
|
|
$results = [];
|
|
foreach (preg_split('/\n\s*\n/', trim($raw)) as $block) {
|
|
$fields = [];
|
|
foreach (preg_split('/\r\n|\r|\n/', trim($block)) as $line) {
|
|
$line = trim($line);
|
|
if ($line === '' || !str_contains($line, ':')) continue;
|
|
[$key, $value] = array_map('trim', explode(':', $line, 2));
|
|
$fields[strtolower(preg_replace('/[^a-z0-9]/i', '', $key))] = $value;
|
|
}
|
|
if (empty($fields)) continue;
|
|
|
|
$results[] = [
|
|
'filename' => $fields['filename'] ?? '',
|
|
'hash_crc32' => $fields['crc32'] ?? '',
|
|
'hash_sha1' => $fields['sha1'] ?? '',
|
|
];
|
|
}
|
|
return $results;
|
|
}
|
|
|
|
private function attachMany(int $entryId, string $pivotTable, string $foreignKey, array $ttids, string $targetTable): void
|
|
{
|
|
if (empty($ttids)) return;
|
|
|
|
$ids = DB::table('migrations_logs')
|
|
->where('source_system', 'wp')->where('target_table', $targetTable)
|
|
->whereIn('source_id', $ttids)->pluck('target_id');
|
|
|
|
foreach ($ids as $id) {
|
|
DB::table($pivotTable)->insertOrIgnore(['entry_id' => $entryId, $foreignKey => $id]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param \StdClass $post
|
|
* @param string $cpt
|
|
*
|
|
* @return void
|
|
*/
|
|
private function migratePost( $post, string $cpt ): void
|
|
{
|
|
$exists = DB::table('migrations_logs')
|
|
->where('source_system', 'wp')
|
|
->where('source_table', 'wp_posts')
|
|
->where('source_id', $post->ID)
|
|
->exists();
|
|
|
|
if ($exists)
|
|
return;
|
|
|
|
$meta = DB::connection('old_wp')
|
|
->table('postmeta')
|
|
->where('post_id', $post->ID)
|
|
->whereIn('meta_key', self::ACF_FIELDS)
|
|
->pluck('meta_value', 'meta_key');
|
|
|
|
$terms = DB::connection('old_wp')
|
|
->table('term_relationships as tr')
|
|
->join('term_taxonomy as tt', 'tr.term_taxonomy_id', '=', 'tt.term_taxonomy_id')
|
|
->where('tr.object_id', $post->ID)
|
|
->whereIn('tt.taxonomy', array_merge(['game', 'platform', 'hack-status', 'experience-level'], array_keys(self::MULTI_TAXONOMIES)) )
|
|
->select('tt.taxonomy', 'tt.term_taxonomy_id')
|
|
->get();
|
|
|
|
$byTax = [];
|
|
foreach ($terms as $term) {
|
|
$byTax[$term->taxonomy][] = $term->term_taxonomy_id;
|
|
}
|
|
|
|
$gameId = null;
|
|
if( !empty( $byTax['game'][0] ) && !empty( $byTax['platform'][0] ) )
|
|
{
|
|
$gameId = DB::table('migration_game_plan')
|
|
->where('wp_game_id', $byTax['game'][0])
|
|
->where('wp_platform_id', $byTax['platform'][0])
|
|
->value('game_id');
|
|
|
|
if( !$gameId )
|
|
$this->stats['missing_game_plan']++;
|
|
}
|
|
|
|
$statusId = $this->getSingle( $byTax['hack-status'][0] ?? null, 'statuses' );
|
|
$levelId = $this->getSingle( $byTax['experience-level'][0] ?? null, 'levels' );
|
|
|
|
$userId = DB::table('migration_user_plan')
|
|
->where('wp_user_id', $post->post_author )
|
|
->value('user_id');
|
|
|
|
if( !$userId ) {
|
|
$this->stats['missing_author']++;
|
|
return;
|
|
}
|
|
|
|
$title = $meta['entry_title'] ?? null;
|
|
if( $cpt === 'translations' && !$title && $gameId )
|
|
{
|
|
$title = DB::table('games')->where('id', $gameId)->value('name');
|
|
}
|
|
|
|
$slug = $this->uniqueSlug( rawurldecode($post->post_name), 'entries' );
|
|
|
|
$entryId = DB::table('entries')->insertGetId([
|
|
'type' => $cpt,
|
|
'title' => $title,
|
|
'complete_title' => $post->post_title ?? null,
|
|
'slug' => $slug,
|
|
'description' => MigrationHelpers::htmlToMarkdown($post->post_content),
|
|
'state' => self::STATE_MAP[$post->post_status],
|
|
'game_id' => $gameId,
|
|
'platform_id' => null,
|
|
'status_id' => $statusId,
|
|
'level_id' => $levelId,
|
|
'version' => $meta['version_number'] ?? null,
|
|
'release_date' => $meta['release_date'] ?? null,
|
|
'staff_credits' => json_encode( $this->parseStaffCredits($meta['staff'] ?? null)),
|
|
'relevant_link' => $meta['release_site'] ?? null,
|
|
'youtube_link' => $meta['youtube_video'] ?? null,
|
|
'user_id' => $userId,
|
|
'created_at' => $post->post_date,
|
|
'updated_at' => $post->post_modified,
|
|
]);
|
|
|
|
$this->attachMany($entryId, 'entry_authors', 'author_id', $byTax['author-name'] ?? [], 'authors');
|
|
$this->attachMany($entryId, 'entry_languages', 'language_id', $byTax['language'] ?? [], 'languages');
|
|
|
|
if( $cpt === 'romhacks')
|
|
$this->attachMany($entryId, 'entry_modifications', 'modification_id', $byTax['modifications'] ?? [], 'modifications');
|
|
|
|
if ($cpt === 'utilities') {
|
|
$this->attachMany($entryId, 'entry_categories', 'category_id', $byTax['utility-category'] ?? [], 'categories');
|
|
$this->attachMany($entryId, 'entry_systems', 'system_id', $byTax['utility-os'] ?? [], 'systems');
|
|
}
|
|
if ($cpt === 'documents') {
|
|
$this->attachMany($entryId, 'entry_categories', 'category_id', $byTax['document-category'] ?? [], 'categories');
|
|
}
|
|
|
|
if( $cpt === 'translations' || $cpt === 'romhacks' ){
|
|
foreach ( $this->parseHashes( $meta['hashes'] ?? null ) as $hash ) {
|
|
DB::table('entry_hashes')->insert([
|
|
'entry_id' => $entryId,
|
|
'filename' => $hash['filename'],
|
|
'hash_crc32' => $hash['hash_crc32'],
|
|
'hash_sha1' => $hash['hash_sha1'],
|
|
'verified' => 'TBD',
|
|
'created_at' => now(),
|
|
'updated_at' => now(),
|
|
]);
|
|
}
|
|
}
|
|
|
|
DB::table('migrations_logs')->insert([
|
|
'source_system' => 'wp',
|
|
'source_table' => 'wp_posts',
|
|
'source_id' => $post->ID,
|
|
'target_table' => 'entries',
|
|
'target_id' => $entryId,
|
|
'status' => 'done',
|
|
'migrated_at' => now(),
|
|
'updated_at' => now(),
|
|
'created_at' => now(),
|
|
]);
|
|
|
|
$this->stats['created']++;
|
|
|
|
}
|
|
|
|
private function migrateCpt( string $cpt ): void
|
|
{
|
|
$this->info( "Current migration of : $cpt ");
|
|
$this->stats = [ 'missing_author' => 0, 'missing_game_plan' => 0, 'created' => 0 ];
|
|
|
|
$query = DB::connection('old_wp')
|
|
->table('posts')
|
|
->where('post_type', $cpt)
|
|
->whereIn('post_status', array_keys(self::STATE_MAP))
|
|
;
|
|
|
|
if( $limit = $this->option('limit') ) {
|
|
$query->limit((int) $limit);
|
|
}
|
|
|
|
$posts = $query->select('ID', 'post_title', 'post_name', 'post_content', 'post_status', 'post_author', 'post_date', 'post_modified')
|
|
->get();
|
|
|
|
$this->withProgressBar($posts, fn( $post ) => $this->migratePost( $post, $cpt ) );
|
|
|
|
$this->newLine();
|
|
$this->info( "Created {$this->stats['created']} entries'. No authors {$this->stats['missing_author']}. Missing game {$this->stats['missing_game_plan']} entries" );
|
|
}
|
|
|
|
public function handle()
|
|
{
|
|
foreach ( self::WP_CPTS as $cpt ) {
|
|
$this->migrateCpt( $cpt );
|
|
}
|
|
}
|
|
}
|