139 lines
4.2 KiB
PHP
139 lines
4.2 KiB
PHP
<?php
|
|
|
|
namespace App\Services\JobApi\Transformers;
|
|
|
|
use App\Services\JobApi\DTO\JobData;
|
|
use App\Services\JobApi\Exceptions\TransformationException;
|
|
use DateTime;
|
|
|
|
class TheMuseTransformer implements JobTransformer
|
|
{
|
|
public function transform(array $data): JobData
|
|
{
|
|
try {
|
|
return new JobData(
|
|
externalId: (string) $data['id'],
|
|
title: $data['name'] ?? 'Untitled',
|
|
company: $data['company']['name'] ?? 'Unknown Company',
|
|
description: $this->sanitizeDescription($data['contents'] ?? ''),
|
|
location: $this->parseLocation($data),
|
|
salaryMin: null, // The Muse doesn't provide salary info
|
|
salaryMax: null,
|
|
salaryCurrency: 'USD',
|
|
jobType: $this->mapJobType($data['type'] ?? null),
|
|
sourceUrl: $data['refs']['landing_page'] ?? null,
|
|
applyUrl: $data['refs']['landing_page'] ?? null,
|
|
postedAt: $this->parseDate($data['publication_date'] ?? null),
|
|
expiresAt: null,
|
|
remoteAllowed: $this->isRemote($data),
|
|
experienceLevel: $this->mapExperienceLevel($data['levels'] ?? []),
|
|
category: $this->parseCategory($data['categories'] ?? []),
|
|
rawData: $data,
|
|
);
|
|
} catch (\Throwable $e) {
|
|
throw new TransformationException(
|
|
'The Muse',
|
|
'Failed to transform The Muse job data: ' . $e->getMessage(),
|
|
['data' => $data, 'error' => $e->getMessage()]
|
|
);
|
|
}
|
|
}
|
|
|
|
public function transformMany(array $jobs): array
|
|
{
|
|
return array_map(fn($job) => $this->transform($job), $jobs);
|
|
}
|
|
|
|
private function sanitizeDescription(string $description): string
|
|
{
|
|
// $clean = strip_tags($description);
|
|
// $clean = html_entity_decode($clean, ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
|
// $clean = preg_replace('/\s+/', ' ', $clean);
|
|
return trim($description);
|
|
}
|
|
|
|
private function parseLocation(array $data): ?string
|
|
{
|
|
if (empty($data['locations'])) {
|
|
return null;
|
|
}
|
|
|
|
$locations = array_map(fn($loc) => $loc['name'] ?? '', $data['locations']);
|
|
return implode(', ', array_filter($locations));
|
|
}
|
|
|
|
private function isRemote(array $data): bool
|
|
{
|
|
if (empty($data['locations'])) {
|
|
return false;
|
|
}
|
|
|
|
foreach ($data['locations'] as $location) {
|
|
$name = strtolower($location['name'] ?? '');
|
|
if (str_contains($name, 'remote') || str_contains($name, 'flexible')) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private function parseDate(?string $date): ?DateTime
|
|
{
|
|
if (empty($date)) {
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
return new DateTime($date);
|
|
} catch (\Exception $e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private function mapJobType(?string $type): ?string
|
|
{
|
|
if (empty($type)) {
|
|
return 'full-time';
|
|
}
|
|
|
|
return match (strtolower($type)) {
|
|
'full-time', 'fulltime' => 'full-time',
|
|
'part-time', 'parttime' => 'part-time',
|
|
'contract' => 'contract',
|
|
'temporary', 'temp' => 'temporary',
|
|
'internship', 'intern' => 'internship',
|
|
'freelance' => 'freelance',
|
|
default => 'full-time',
|
|
};
|
|
}
|
|
|
|
private function mapExperienceLevel(array $levels): ?string
|
|
{
|
|
if (empty($levels)) {
|
|
return null;
|
|
}
|
|
|
|
// The Muse uses: Entry Level, Mid Level, Senior Level
|
|
$firstLevel = strtolower($levels[0]['name'] ?? '');
|
|
|
|
return match (true) {
|
|
str_contains($firstLevel, 'entry') => 'entry',
|
|
str_contains($firstLevel, 'mid') => 'mid',
|
|
str_contains($firstLevel, 'senior') => 'senior',
|
|
str_contains($firstLevel, 'executive') => 'executive',
|
|
default => null,
|
|
};
|
|
}
|
|
|
|
private function parseCategory(array $categories): ?string
|
|
{
|
|
if (empty($categories)) {
|
|
return null;
|
|
}
|
|
|
|
// Return the first category name
|
|
return $categories[0]['name'] ?? null;
|
|
}
|
|
}
|