203 lines
6.6 KiB
PHP
203 lines
6.6 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Import;
|
|
|
|
use App\Enums\CompanyType;
|
|
use App\Enums\Portal;
|
|
use App\Models\Company;
|
|
use App\Models\LegacyImportMap;
|
|
use App\Models\User;
|
|
use Illuminate\Support\Collection;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Str;
|
|
|
|
class CompanyImporter
|
|
{
|
|
private const CHUNK_SIZE = 500;
|
|
|
|
private const CTYPE_MAP = [
|
|
'agency' => CompanyType::Agency,
|
|
'company' => CompanyType::Company,
|
|
];
|
|
|
|
public function run(ImportContext $ctx): ImportResult
|
|
{
|
|
$result = new ImportResult;
|
|
$conn = $ctx->connection;
|
|
$legacyPortal = $ctx->legacyPortalValue();
|
|
$portal = $ctx->portalEnum;
|
|
|
|
// Verantwortliche User voraufladen (responsible_company_user)
|
|
$responsibles = DB::connection($conn)
|
|
->table('responsible_company_user')
|
|
->get()
|
|
->groupBy('company_id')
|
|
->map(fn ($rows) => $rows->pluck('user_id')->all());
|
|
|
|
// Einfache company_user Mitglieder
|
|
$members = DB::connection($conn)
|
|
->table('company_user')
|
|
->get()
|
|
->groupBy('company_id')
|
|
->map(fn ($rows) => $rows->pluck('user_id')->all());
|
|
|
|
DB::connection($conn)
|
|
->table('company')
|
|
->orderBy('id')
|
|
->chunk(self::CHUNK_SIZE, function ($rows) use ($ctx, $result, $legacyPortal, $portal, $responsibles, $members): void {
|
|
foreach ($rows as $row) {
|
|
try {
|
|
$this->importRow($row, $ctx, $result, $legacyPortal, $portal, $responsibles, $members);
|
|
} catch (\Throwable $e) {
|
|
$result->addError("Company legacy_id={$row->id}: {$e->getMessage()}");
|
|
}
|
|
}
|
|
});
|
|
|
|
return $result;
|
|
}
|
|
|
|
private function importRow(
|
|
object $row,
|
|
ImportContext $ctx,
|
|
ImportResult $result,
|
|
string $legacyPortal,
|
|
Portal $portal,
|
|
Collection $responsibles,
|
|
Collection $members,
|
|
): void {
|
|
$alreadyImported = LegacyImportMap::query()
|
|
->where('legacy_portal', $legacyPortal)
|
|
->where('legacy_table', 'company')
|
|
->where('legacy_id', $row->id)
|
|
->exists();
|
|
|
|
if ($alreadyImported && ! $ctx->force) {
|
|
$result->incrementSkipped();
|
|
|
|
return;
|
|
}
|
|
|
|
if ($ctx->dryRun) {
|
|
$result->incrementImported();
|
|
|
|
return;
|
|
}
|
|
|
|
// Owner-User aus Import-Map auflösen
|
|
$ownerUserId = null;
|
|
if ($row->user_id) {
|
|
$map = LegacyImportMap::query()
|
|
->where('legacy_portal', $legacyPortal)
|
|
->where('legacy_table', 'sf_guard_user')
|
|
->where('legacy_id', $row->user_id)
|
|
->first();
|
|
$ownerUserId = $map?->target_id;
|
|
}
|
|
|
|
$type = self::CTYPE_MAP[$row->ctype] ?? CompanyType::Company;
|
|
|
|
// Eindeutigen Slug sicherstellen
|
|
$slug = $this->uniqueSlug($row->slug ?: Str::slug($row->name) ?: 'firma', $portal->value);
|
|
|
|
$company = Company::withoutTimestamps(function () use ($legacyPortal, $row, $portal, $ownerUserId, $type, $slug): Company {
|
|
return Company::withoutGlobalScopes()->updateOrCreate(
|
|
['legacy_portal' => $legacyPortal, 'legacy_id' => $row->id],
|
|
[
|
|
'portal' => $portal->value,
|
|
'owner_user_id' => $ownerUserId,
|
|
'type' => $type->value,
|
|
'name' => $row->name ?: 'Unbekannte Firma',
|
|
'slug' => $slug,
|
|
'address' => $row->address ?: null,
|
|
'phone' => $this->cleanText($row->phone, 255),
|
|
'fax' => $this->cleanText($row->fax, 255),
|
|
'email' => $this->cleanText($row->email, 190),
|
|
'website' => $this->cleanText($row->website, 190),
|
|
'logo_path' => $row->logo ?: null,
|
|
'is_active' => (bool) $row->is_active,
|
|
'disable_footer_code' => (bool) ($row->disable_footer_code ?? false),
|
|
'created_at' => $row->created_at ?? now(),
|
|
'updated_at' => $row->updated_at ?? $row->created_at ?? now(),
|
|
]
|
|
);
|
|
});
|
|
|
|
// Pivot-Zuordnungen (member + responsible)
|
|
$pivotPayload = [];
|
|
|
|
foreach ($members->get($row->id, []) as $legacyUserId) {
|
|
$userMap = LegacyImportMap::query()
|
|
->where('legacy_portal', $legacyPortal)
|
|
->where('legacy_table', 'sf_guard_user')
|
|
->where('legacy_id', $legacyUserId)
|
|
->first();
|
|
|
|
if ($userMap) {
|
|
$role = in_array($legacyUserId, $responsibles->get($row->id, []))
|
|
? 'responsible'
|
|
: 'member';
|
|
$pivotPayload[$userMap->target_id] = ['role' => $role];
|
|
}
|
|
}
|
|
|
|
if ($pivotPayload !== []) {
|
|
$company->users()->syncWithoutDetaching($pivotPayload);
|
|
}
|
|
|
|
LegacyImportMap::query()->updateOrCreate(
|
|
[
|
|
'legacy_portal' => $legacyPortal,
|
|
'legacy_table' => 'company',
|
|
'legacy_id' => $row->id,
|
|
],
|
|
[
|
|
'target_table' => 'companies',
|
|
'target_id' => $company->id,
|
|
'imported_at' => now(),
|
|
]
|
|
);
|
|
|
|
if ($alreadyImported) {
|
|
$result->incrementUpdated();
|
|
} else {
|
|
$result->incrementImported();
|
|
}
|
|
}
|
|
|
|
private function uniqueSlug(string $base, string $portal): string
|
|
{
|
|
$slug = $base;
|
|
$i = 2;
|
|
|
|
while (Company::withoutGlobalScopes()
|
|
->where('portal', $portal)
|
|
->where('slug', $slug)
|
|
->exists()
|
|
) {
|
|
$slug = $base.'-'.$i++;
|
|
}
|
|
|
|
return $slug;
|
|
}
|
|
|
|
/** Bereinigt HTML-Entities und kürzt auf maximale Feldlänge. */
|
|
private function cleanText(?string $value, int $maxLength): ?string
|
|
{
|
|
if (blank($value)) {
|
|
return null;
|
|
}
|
|
|
|
// HTML-Entities dekodieren (z.B.   → Thin Space → entfernen)
|
|
$clean = html_entity_decode((string) $value, ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
|
// Steuerzeichen und unsichtbare Unicode-Zeichen entfernen
|
|
$clean = preg_replace('/[\x00-\x1F\x7F\xC2\xA0]/u', ' ', $clean) ?? $clean;
|
|
$clean = trim((string) $clean);
|
|
|
|
if (blank($clean)) {
|
|
return null;
|
|
}
|
|
|
|
return mb_substr($clean, 0, $maxLength);
|
|
}
|
|
}
|