src/Manager/GoogleForJobsManager.php line 139

Open in your IDE?
  1. <?php
  2. namespace App\Manager;
  3. use App\Annotations\JobPositionMappingProperty\Date;
  4. use App\Annotations\JobPositionMappingProperty\Image;
  5. use App\Annotations\JobPositionMappingProperty\Text;
  6. use App\Component\Configuration\Util\Config;
  7. use App\Entity\Asset;
  8. use App\Entity\OptionValue;
  9. use App\Entity\Vacancy;
  10. use App\Transformer\VacancyMappingToJobPostingStructuredDataTransformer;
  11. use App\Twig\DescriptionExtension;
  12. use App\Util\AssetUtil;
  13. use Doctrine\Common\Annotations\AnnotationReader;
  14. use Doctrine\ORM\EntityManagerInterface;
  15. use Liip\ImagineBundle\Imagine\Cache\CacheManager;
  16. use Money\Money;
  17. use ReflectionClass;
  18. use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
  19. use Symfony\Component\PropertyAccess\PropertyAccess;
  20. use Vich\UploaderBundle\Templating\Helper\UploaderHelper;
  21. class GoogleForJobsManager
  22. {
  23.     public const CONFIG_NAME 'job_position_mapping';
  24.     private ParameterBagInterface $parameterBag;
  25.     private DescriptionExtension $descriptionExtension;
  26.     private VacancyMappingToJobPostingStructuredDataTransformer $dataTransformer;
  27.     private OptionManager $optionManager;
  28.     private UploaderHelper $uploaderHelper;
  29.     private CacheManager $cacheManager;
  30.     private Config $config;
  31.     private EntityManagerInterface $entityManager;
  32.     private AssetUtil $assetUtil;
  33.     /**
  34.      * GoogleForJobsManager constructor.
  35.      */
  36.     public function __construct(
  37.         ParameterBagInterface $parameterBag,
  38.         DescriptionExtension $descriptionExtension,
  39.         VacancyMappingToJobPostingStructuredDataTransformer $dataTransformer,
  40.         OptionManager $optionManager,
  41.         UploaderHelper $uploaderHelper,
  42.         CacheManager $cacheManager,
  43.         Config $config,
  44.         EntityManagerInterface $entityManager,
  45.         AssetUtil $assetUtil
  46.     ) {
  47.         $this->parameterBag $parameterBag;
  48.         $this->descriptionExtension $descriptionExtension;
  49.         $this->dataTransformer $dataTransformer;
  50.         $this->optionManager $optionManager;
  51.         $this->uploaderHelper $uploaderHelper;
  52.         $this->cacheManager $cacheManager;
  53.         $this->config $config;
  54.         $this->entityManager $entityManager;
  55.         $this->assetUtil $assetUtil;
  56.     }
  57.     public function getVacancyMappingProperties(): array
  58.     {
  59.         $propertyMappings = [];
  60.         $jobPositionMappingProperties = [Text::class => 'text'Date::class => 'date'Image::class => 'image'];
  61.         $reader = new AnnotationReader();
  62.         $reflectionClass = new ReflectionClass(Vacancy::class);
  63.         foreach ($reflectionClass->getProperties() as $property) {
  64.             $propertyAnnotations array_map(
  65.                 static fn ($object): string => \get_class($object),
  66.                 $reader->getPropertyAnnotations($property)
  67.             );
  68.             foreach (array_intersect($propertyAnnotationsarray_keys($jobPositionMappingProperties)) as $mapping) {
  69.                 $propertyName $property->name;
  70.                 if (Text::class === $mapping) {
  71.                     $propertyName 'vacancy.'.$property->name;
  72.                 }
  73.                 $propertyMappings[$jobPositionMappingProperties[$mapping]][] = $propertyName;
  74.             }
  75.         }
  76.         // add custom types
  77.         $propertyMappings['text'][] = 'companyName';
  78.         $propertyMappings['text'][] = 'salary';
  79.         $propertyMappings['image'][] = 'siteLogo';
  80.         // add options as placeholders
  81.         $options array_map(
  82.             static fn ($option): array => ['id' => $option['id'], 'value' => sprintf('option.%s'$option['title'])],
  83.             $this->optionManager->getOptionsWithTitle()
  84.         );
  85.         $propertyMappings['text'] = array_merge($propertyMappings['text'], $options);
  86.         return $propertyMappings;
  87.     }
  88.     private function getMapping(): array
  89.     {
  90.         if (!$this->parameterBag->has(self::CONFIG_NAME)) {
  91.             return [];
  92.         }
  93.         return json_decode($this->parameterBag->get(self::CONFIG_NAME), true);
  94.     }
  95.     public function generateStructureData(Vacancy $vacancy): string
  96.     {
  97.         $propertyMappings = [];
  98.         $propertyAccessor PropertyAccess::createPropertyAccessor();
  99.         // transform propertyMappings to single dimension with mapping as index
  100.         foreach ($this->getVacancyMappingProperties() as $type => $values) {
  101.             foreach ($values as $value) {
  102.                 if (\is_array($value) && \array_key_exists('value'$value)) {
  103.                     $value $value['value'];
  104.                 }
  105.                 $propertyMappings[$value] = $type;
  106.             }
  107.         }
  108.         $mapping $this->getMapping();
  109.         if ($vacancy->getGoogleForJobMapping()) {
  110.             $mapping array_merge($mappingjson_decode($vacancy->getGoogleForJobMapping(), true));
  111.         }
  112.         foreach ($mapping as $placeholder => &$value) {
  113.             // replace nbsp to space
  114.             $value str_replace("\xc2\xa0"' '$value);
  115.             $value preg_replace_callback('/(\[\[(\S*)]])+/x', function ($matches) use (
  116.                 $vacancy,
  117.                 $propertyAccessor,
  118.                 $placeholder
  119.             ) {
  120.                 $jsonData json_decode($matches[2], true);
  121.                 if (!\is_array($jsonData) || !\array_key_exists('value'$jsonData)) {
  122.                     return '';
  123.                 }
  124.                 $matchValue $jsonData['value'];
  125.                 // description needs to be transformed
  126.                 if ('vacancy.description' === $matchValue) {
  127.                     return $this->descriptionExtension->getDescription($vacancy);
  128.                 }
  129.                 // description needs to be transformed
  130.                 if ('companyName' === $matchValue) {
  131.                     return $this->config->get('site_vacancy_job_posting_show_company_name') && $vacancy->getCompany() ?
  132.                     $vacancy->getCompany()->getName() : $this->parameterBag->get('site_company_name');
  133.                 }
  134.                 // if baseSalary is set to special salary value (default), then return a json array when
  135.                 // min salary is not zero
  136.                 if ('baseSalary' === $placeholder && 'salary' === $matchValue) {
  137.                     if ($vacancy->getMinSalary()->isZero()) {
  138.                         return $vacancy->getSalaryAmount() > ? ($vacancy->getSalaryAmount() / 100) : 0;
  139.                     }
  140.                     return json_encode([
  141.                         'minValue' => ($vacancy->getMinSalary()->getAmount() / 100),
  142.                         'maxValue' => ($vacancy->getMaxSalary()->greaterThan(
  143.                             new Money(0$vacancy->getMaxSalary()->getCurrency()))
  144.                             ? ($vacancy->getMaxSalary()->getAmount() / 100)
  145.                             : 0
  146.                         ),
  147.                     ]);
  148.                 }
  149.                 if (=== mb_strpos($matchValue'vacancy.')) {
  150.                     $matchValue str_replace('vacancy.'''$matchValue);
  151.                     if ($propertyAccessor->isReadable($vacancy$matchValue)) {
  152.                         return $propertyAccessor->getValue($vacancy$matchValue);
  153.                     }
  154.                     return '';
  155.                 }
  156.                 if (=== mb_strpos($matchValue'option.')) {
  157.                     if (!\array_key_exists('id'$jsonData) || !$vacancy->getOptionValues()) {
  158.                         return '';
  159.                     }
  160.                     $optionValues $vacancy->getOptionValues()
  161.                         ->filter(fn (OptionValue $optionValue): bool => $optionValue->getOption() && $optionValue->getOption()->getId() === $jsonData['id']
  162.                         )
  163.                         ->map(fn (OptionValue $optionValue): ?string => $optionValue->getValue())
  164.                         ->toArray()
  165.                     ;
  166.                     return implode(','$optionValues);
  167.                 }
  168.                 return '';
  169.             }, $value);
  170.             if ('directApply' === $placeholder) {
  171.                 $value = !($vacancy->getExternalUrl() || $vacancy->getExternalDetailUrl());
  172.                 continue;
  173.             }
  174.             /* @var string $value */
  175.             if (!\array_key_exists($value$propertyMappings)) {
  176.                 continue;
  177.             }
  178.             if ('date' === $propertyMappings[$value] && $propertyAccessor->isReadable($vacancy$value)) {
  179.                 $dateTime $propertyAccessor->getValue($vacancy$value);
  180.                 if ($dateTime instanceof \DateTime) {
  181.                     $value $dateTime->format(\DATE_ATOM);
  182.                 } else {
  183.                     $value '';
  184.                 }
  185.             } elseif ('image' === $propertyMappings[$value] && $propertyAccessor->isReadable($vacancy$value)) {
  186.                 $image $propertyAccessor->getValue($vacancy$value);
  187.                 if (!$image || Asset::class !== $this->entityManager->getClassMetadata(\get_class($image))->getName()) {
  188.                     continue;
  189.                 }
  190.                 if (!$relativePath $this->uploaderHelper->asset($image'assetFile')) {
  191.                     continue;
  192.                 }
  193.                 $value $this->cacheManager->getBrowserPath($relativePath'square_100x100');
  194.             } elseif ('siteLogo' === $value) {
  195.                 /**
  196.                  * @var asset $image
  197.                  */
  198.                 $image $this->config->get('site_logo');
  199.                 $value $this->assetUtil->getAssetUrlForFilter($image'square_100x100');
  200.             }
  201.         }
  202.         unset($value);
  203.         $structuredData $this->dataTransformer->transform($mapping);
  204.         return $structuredData ?? '';
  205.     }
  206. }