• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

CPS-IT / personio-jobs / 15388590830

02 Jun 2025 09:19AM UTC coverage: 3.649% (-0.005%) from 3.654%
15388590830

push

github

web-flow
Merge pull request #229 from CPS-IT/fix/type-factory

[BUGFIX] Avoid deprecated type factory access in EXT:schema

0 of 8 new or added lines in 1 file covered. (0.0%)

1 existing line in 1 file now uncovered.

27 of 740 relevant lines covered (3.65%)

0.16 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/Classes/Domain/Factory/SchemaFactory.php
1
<?php
2

3
declare(strict_types=1);
4

5
/*
6
 * This file is part of the TYPO3 CMS extension "personio_jobs".
7
 *
8
 * Copyright (C) 2023 Elias Häußler <e.haeussler@familie-redlich.de>
9
 *
10
 * This program is free software: you can redistribute it and/or modify
11
 * it under the terms of the GNU General Public License as published by
12
 * the Free Software Foundation, either version 2 of the License, or
13
 * (at your option) any later version.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 * GNU General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU General Public License
21
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
22
 */
23

24
namespace CPSIT\Typo3PersonioJobs\Domain\Factory;
25

26
use Brotkrueml\Schema\Model\Type\JobPosting;
27
use Brotkrueml\Schema\Model\Type\Organization;
28
use Brotkrueml\Schema\Model\Type\Place;
29
use Brotkrueml\Schema\Type\TypeFactory;
30
use CPSIT\Typo3PersonioJobs\Domain\Model\Job;
31
use CPSIT\Typo3PersonioJobs\Enums\Job\EmploymentType;
32
use CPSIT\Typo3PersonioJobs\Enums\Job\Schedule;
33
use CPSIT\Typo3PersonioJobs\Enums\Schema\EmploymentType as EmploymentTypeSchema;
34
use CPSIT\Typo3PersonioJobs\Event\EnrichJobPostingSchemaEvent;
35
use CPSIT\Typo3PersonioJobs\Exception\ExtensionNotLoadedException;
36
use CPSIT\Typo3PersonioJobs\Service\PersonioApiService;
37
use CPSIT\Typo3PersonioJobs\Utility\FrontendUtility;
38
use Psr\EventDispatcher\EventDispatcherInterface;
39
use TYPO3\CMS\Core\Information\Typo3Version;
40
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
41
use TYPO3\CMS\Core\Utility\GeneralUtility;
42
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
43

44
/**
45
 * SchemaFactory
46
 *
47
 * @author Elias Häußler <e.haeussler@familie-redlich.de>
48
 * @license GPL-2.0-or-later
49
 */
50
final readonly class SchemaFactory
51
{
52
    private ?TypeFactory $typeFactory;
53

UNCOV
54
    public function __construct(
×
55
        private PersonioApiService $personioApiService,
56
        private ContentObjectRenderer $contentObjectRenderer,
57
        private EventDispatcherInterface $eventDispatcher,
58
    ) {
NEW
59
        if (\class_exists(TypeFactory::class)) {
×
NEW
60
            $this->typeFactory = GeneralUtility::makeInstance(TypeFactory::class);
×
61
        } else {
NEW
62
            $this->typeFactory = null;
×
63
        }
64
    }
65

66
    /**
67
     * @throws ExtensionNotLoadedException
68
     */
69
    public function createJobPosting(Job $job): JobPosting
×
70
    {
71
        // Throw exception if schema extension is not installed
72
        if (!ExtensionManagementUtility::isLoaded('schema')) {
×
73
            throw ExtensionNotLoadedException::create('schema');
×
74
        }
75

76
        $serverRequest = FrontendUtility::getServerRequest();
×
77
        $organizationType = $this->createOrganization($job);
×
78
        $placeType = $this->createPlace($job);
×
79

80
        /** @var JobPosting $jobPosting */
NEW
81
        $jobPosting = $this->typeFactory?->create('JobPosting');
×
82
        $jobPosting
×
83
            ->setProperty('datePosted', ($job->getCreateDate() ?? new \DateTime())->format('Y-m-d'))
×
84
            ->setProperty('employmentType', $this->decorateEmploymentType($job))
×
85
            ->setProperty('hiringOrganization', $organizationType)
×
86
            ->setProperty('jobLocation', $placeType)
×
87
            ->setProperty('occupationalCategory', $job->getOccupationCategory())
×
88
            ->setProperty('title', $job->getName())
×
89
            ->setProperty('description', $this->decorateDescription($job))
×
90
            ->setProperty('url', (string)$serverRequest->getUri())
×
91
            ->setProperty('sameAs', (string)$this->personioApiService->getJobUrl($job))
×
92
        ;
×
93

94
        $this->eventDispatcher->dispatch(new EnrichJobPostingSchemaEvent($job, $jobPosting));
×
95

96
        return $jobPosting;
×
97
    }
98

99
    private function createOrganization(Job $job): Organization
×
100
    {
101
        /** @var Organization $organization */
NEW
102
        $organization = $this->typeFactory?->create('Organization');
×
NEW
103
        $organization
×
104
            ->setProperty('name', $job->getSubcompany())
×
105
            ->setProperty('address', $job->getOffice())
×
106
        ;
×
107

108
        return $organization;
×
109
    }
110

111
    private function createPlace(Job $job): Place
×
112
    {
113
        /** @var Place $place */
NEW
114
        $place = $this->typeFactory?->create('Place');
×
NEW
115
        $place->setProperty('address', $job->getOffice());
×
116

117
        return $place;
×
118
    }
119

120
    /**
121
     * @return value-of<EmploymentTypeSchema>|list<value-of<EmploymentTypeSchema>>
122
     * @see https://developers.google.com/search/docs/appearance/structured-data/job-posting#job-posting-definition
123
     */
124
    private function decorateEmploymentType(Job $job): string|array
×
125
    {
126
        $employmentType = EmploymentType::tryFrom($job->getEmploymentType());
×
127
        $schedule = Schedule::tryFrom($job->getSchedule());
×
128

129
        if ($employmentType === null && $schedule === null) {
×
130
            return EmploymentTypeSchema::Other->value;
×
131
        }
132

133
        if ($employmentType === EmploymentType::Intern) {
×
134
            return EmploymentTypeSchema::Intern->value;
×
135
        }
136

137
        return match ($schedule) {
×
138
            Schedule::FullTime => EmploymentTypeSchema::FullTime->value,
×
139
            Schedule::PartTime => EmploymentTypeSchema::PartTime->value,
×
140
            Schedule::FullOrPartTime => [EmploymentTypeSchema::FullTime->value, EmploymentTypeSchema::PartTime->value],
×
141
            null => EmploymentTypeSchema::Other->value,
×
142
        };
×
143
    }
144

145
    private function decorateDescription(Job $job): string
×
146
    {
147
        $description = '';
×
148

149
        foreach ($job->getJobDescriptions() as $jobDescription) {
×
150
            $rawJobDescription = $jobDescription->getBodytext();
×
151
            $description .= $rawJobDescription . ' ';
×
152
        }
153

154
        if ((new Typo3Version())->getMajorVersion() >= 12) {
×
155
            // https://docs.typo3.org/c/typo3/cms-core/main/en-us/Changelog/12.0/Breaking-96520-EnforceNon-emptyConfigurationInCObjparseFunc.html
156
            $parsedDescription = $this->contentObjectRenderer->parseFunc($description, null, '< lib.parseFunc_RTE');
×
157
        } else {
158
            /* @phpstan-ignore-next-line argument.type (Only relevant for legacy TYPO3 versions) */
159
            $parsedDescription = $this->contentObjectRenderer->parseFunc($description, [], '< lib.parseFunc_RTE');
×
160
        }
161

162
        return $parsedDescription;
×
163
    }
164
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc