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

mixerapi / mixerapi-dev / 7434143287

06 Jan 2024 09:54PM UTC coverage: 94.68% (-1.1%) from 95.793%
7434143287

Pull #141

github

web-flow
Merge 4d1f37519 into 39576a822
Pull Request #141: CakePHP 5 support

961 of 1015 relevant lines covered (94.68%)

4.4 hits per line

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

93.48
/plugins/json-ld-view/src/Controller/Component/JsonLdVocabComponent.php
1
<?php
2
declare(strict_types=1);
3

4
namespace MixerApi\JsonLdView\Controller\Component;
5

6
use Cake\Collection\Collection;
7
use Cake\Controller\Component;
8
use Cake\Controller\ComponentRegistry;
9
use Cake\Core\Configure;
10
use Cake\Datasource\ConnectionManager;
11
use Cake\ORM\Locator\LocatorAwareTrait;
12
use Cake\ORM\Table;
13
use MixerApi\Core\Model\Model;
14
use MixerApi\Core\Model\ModelFactory;
15
use MixerApi\Core\Model\ModelProperty;
16
use MixerApi\Core\Utility\NamespaceUtility;
17
use MixerApi\JsonLdView\JsonLdDataInterface;
18
use MixerApi\JsonLdView\JsonLdEntityContext;
19
use MixerApi\JsonLdView\JsonLdSchema;
20
use ReflectionClass;
21

22
/**
23
 * Builds JSON-LD vocab for entities in your API.
24
 *
25
 * @link https://json-ld.org/learn.html
26
 */
27
class JsonLdVocabComponent extends Component
28
{
29
    use LocatorAwareTrait;
30

31
    private ?array $data;
32

33
    private string $hydraPrefix = '';
34

35
    private ?array $config;
36

37
    private string $hydra = '';
38

39
    /**
40
     * @param \Cake\Controller\ComponentRegistry $registry ComponentRegistry
41
     * @param array $config configurations
42
     */
43
    public function __construct(ComponentRegistry $registry, array $config = [])
44
    {
45
        parent::__construct($registry, $config);
3✔
46

47
        $this->config = Configure::read('JsonLdView');
3✔
48
        if ($this->config['isHydra']) {
3✔
49
            $this->hydra = 'hydra:';
×
50
        }
51

52
        $this->data = [
3✔
53
            '@contexts' => [
3✔
54
                '@vocab' => $this->config['vocabUrl'],
3✔
55
                'rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
3✔
56
                'rdfs' => 'http://www.w3.org/2000/01/rdf-schema#',
3✔
57
                'xmls' => 'http://www.w3.org/2001/XMLSchema#',
3✔
58
                'owl' => 'http://www.w3.org/2002/07/owl#',
3✔
59
                'schema' => $this->config['schemaUrl'],
3✔
60
            ],
3✔
61
            '@id' => $this->getController()->getRequest()->getPath(),
3✔
62
            '@type' => $this->hydraPrefix . 'ApiDocumentation',
3✔
63
            $this->hydra . 'title' => $this->config['title'],
3✔
64
            $this->hydra . 'description' => $this->config['description'],
3✔
65
            $this->hydra . 'supportedClass' => [],
3✔
66
        ];
3✔
67
    }
68

69
    /**
70
     * Returns the entity in JSON-LD form as an array
71
     *
72
     * @return array
73
     * @throws \ReflectionException
74
     */
75
    public function build(): array
76
    {
77
        /** @var \Cake\Database\Connection $connection */
78
        $connection = ConnectionManager::get(Configure::read('JsonLdView.connectionName', 'default'));
1✔
79
        $tables = NamespaceUtility::findClasses(Configure::read('App.namespace') . '\Model\Table');
1✔
80

81
        foreach ($tables as $table) {
1✔
82
            if (!class_exists($table)) {
1✔
83
                continue;
×
84
            }
85
            $reflection = new \ReflectionClass($table);
1✔
86
            if (!$reflection->isInstantiable() || !$reflection->isSubclassOf(Table::class)) {
1✔
87
                continue;
×
88
            }
89
            $class = $reflection->getShortName();
1✔
90
            if (str_ends_with($class, 'Table')) {
1✔
91
                $class = substr($class, 0, strlen($class) - 5);
1✔
92
            }
93
            $tableInstance = $this->getTableLocator()->get($class);
1✔
94
            $model = (new ModelFactory($connection, $tableInstance))->create();
1✔
95
            if ($model === null) {
1✔
96
                continue;
×
97
            }
98

99
            $supportedClass = $this->buildSupportedClass($model);
1✔
100
            if ($supportedClass === null) {
1✔
101
                continue;
1✔
102
            }
103

104
            $this->data[$this->hydra . 'supportedClass'][] = $supportedClass;
1✔
105
        }
106

107
        if ($this->config['isHydra']) {
1✔
108
            $this->data['@contexts']['hydra'] = 'http://www.w3.org/ns/hydra/core#';
×
109
        }
110

111
        return $this->data;
1✔
112
    }
113

114
    /**
115
     * @param \MixerApi\Core\Model\Model $model an instance of Model
116
     * @return array|null
117
     * @throws \ReflectionException
118
     */
119
    private function buildSupportedClass(Model $model): ?array
120
    {
121
        $entity = $model->getEntity();
1✔
122

123
        if (!$entity instanceof JsonLdDataInterface) {
1✔
124
            return null;
1✔
125
        }
126

127
        $defaultMappings = (new JsonLdEntityContext($model))->build();
1✔
128

129
        $supportedClass = [
1✔
130
            '@id' => $entity->getJsonLdType(),
1✔
131
            '@type' => $this->hydraPrefix . 'Class',
1✔
132
            $this->hydra . 'title' => (new ReflectionClass(get_class($entity)))->getShortName(),
1✔
133
        ];
1✔
134

135
        foreach ($model->getProperties() as $property) {
1✔
136
            $schemaUrl = $defaultMappings[$property->getName()] ?? null;
1✔
137
            $schemaDescription = null;
1✔
138

139
            $jsonLdSchema = $this->findJsonLdSchema($entity, $property);
1✔
140
            if ($jsonLdSchema !== null) {
1✔
141
                $schemaUrl = $jsonLdSchema->getSchemaUrl();
1✔
142
                $schemaDescription = $jsonLdSchema->getDescription();
1✔
143
            }
144

145
            $supportedClass[$this->hydra . 'supportedProperty'][] = [
1✔
146
                '@type' => $this->hydra . 'supportedProperty',
1✔
147
                $this->hydra . 'property' => [
1✔
148
                    '@id' => $schemaUrl,
1✔
149
                    '@type' => 'rdf:Property',
1✔
150
                    'rdfs:label' => $property->getName(),
1✔
151
                    'domain' => $entity->getJsonLdType(),
1✔
152
                    'range' => 'xmls:' . $property->getType(),
1✔
153
                ],
1✔
154
                $this->hydra . 'title' => $property->getName(),
1✔
155
                $this->hydra . 'required' => $this->isPropertyRequired($property),
1✔
156
                $this->hydra . 'readable' => true,
1✔
157
                $this->hydra . 'writeable' => $this->isPropertyWriteable($property),
1✔
158
                $this->hydra . 'description' => $schemaDescription,
1✔
159
            ];
1✔
160
        }
161

162
        return $supportedClass;
1✔
163
    }
164

165
    /**
166
     * @param \MixerApi\JsonLdView\JsonLdDataInterface $entity JsonLdDataInterface
167
     * @param \MixerApi\Core\Model\ModelProperty $property ModelProperty
168
     * @return \MixerApi\JsonLdView\JsonLdSchema|null
169
     */
170
    private function findJsonLdSchema(JsonLdDataInterface $entity, ModelProperty $property): ?JsonLdSchema
171
    {
172
        $results = (new Collection($entity->getJsonLdSchemas()))->filter(
1✔
173
            function (JsonLdSchema $schema) use ($property) {
1✔
174
                return $schema->getProperty() == $property->getName();
1✔
175
            }
1✔
176
        );
1✔
177

178
        return $results->first();
1✔
179
    }
180

181
    /**
182
     * @param \MixerApi\Core\Model\ModelProperty $property ModelProperty
183
     * @return bool
184
     */
185
    private function isPropertyRequired(ModelProperty $property): bool
186
    {
187
        $validationSet = $property->getValidationSet();
1✔
188
        if (is_bool($validationSet->isPresenceRequired())) {
1✔
189
            return $validationSet->isPresenceRequired();
1✔
190
        }
191
        if (strtoupper($validationSet->isPresenceRequired()) === 'UPDATE') {
1✔
192
            return true;
×
193
        }
194

195
        return false;
1✔
196
    }
197

198
    /**
199
     * @param \MixerApi\Core\Model\ModelProperty $property ModelProperty
200
     * @return bool
201
     */
202
    private function isPropertyWriteable(ModelProperty $property): bool
203
    {
204
        if ($property->isPrimaryKey()) {
1✔
205
            return false;
1✔
206
        }
207

208
        $isTimeBehaviorField = in_array($property->getName(), ['created','modified']);
1✔
209
        $isDateTimeField = in_array($property->getType(), ['date','datetime','timestamp']);
1✔
210

211
        if ($isTimeBehaviorField && $isDateTimeField) {
1✔
212
            return false;
1✔
213
        }
214

215
        return true;
1✔
216
    }
217
}
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

© 2025 Coveralls, Inc