presseportale/app/Services/Import/CompanyImporter.php
Kevin Adametz 5b8bdf4182
Some checks are pending
linter / quality (push) Waiting to run
tests / ci (push) Waiting to run
12-05-2026 Frontend dev
2026-05-12 18:32:33 +02:00

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. &#8201; → 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);
}
}