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

mixerapi / mixerapi-dev / 7944560910

17 Feb 2024 10:08PM UTC coverage: 87.512% (-8.3%) from 95.793%
7944560910

Pull #141

github

web-flow
Merge 948144447 into 39576a822
Pull Request #141: MixerAPI v2.0.0 - CakePHP 5 support

36 of 54 new or added lines in 15 files covered. (66.67%)

71 existing lines in 5 files now uncovered.

890 of 1017 relevant lines covered (87.51%)

4.22 hits per line

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

19.35
/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);
2✔
46

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

52
        $this->data = [
2✔
53
            '@contexts' => [
2✔
54
                '@vocab' => $this->config['vocabUrl'],
2✔
55
                'rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
2✔
56
                'rdfs' => 'http://www.w3.org/2000/01/rdf-schema#',
2✔
57
                'xmls' => 'http://www.w3.org/2001/XMLSchema#',
2✔
58
                'owl' => 'http://www.w3.org/2002/07/owl#',
2✔
59
                'schema' => $this->config['schemaUrl'],
2✔
60
            ],
2✔
61
            '@id' => $this->getController()->getRequest()->getPath(),
2✔
62
            '@type' => $this->hydraPrefix . 'ApiDocumentation',
2✔
63
            $this->hydra . 'title' => $this->config['title'],
2✔
64
            $this->hydra . 'description' => $this->config['description'],
2✔
65
            $this->hydra . 'supportedClass' => [],
2✔
66
        ];
2✔
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 */
NEW
78
        $connection = ConnectionManager::get(Configure::read('JsonLdView.connectionName', 'default'));
×
NEW
79
        $fqn = Configure::read('App.namespace') . '\Model\Table';
×
NEW
80
        $tables = NamespaceUtility::findClasses($fqn);
×
81

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

UNCOV
100
            $supportedClass = $this->buildSupportedClass($model);
×
UNCOV
101
            if ($supportedClass === null) {
×
UNCOV
102
                continue;
×
103
            }
104

UNCOV
105
            $this->data[$this->hydra . 'supportedClass'][] = $supportedClass;
×
106
        }
107

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

UNCOV
112
        return $this->data;
×
113
    }
114

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

UNCOV
124
        if (!$entity instanceof JsonLdDataInterface) {
×
UNCOV
125
            return null;
×
126
        }
127

UNCOV
128
        $defaultMappings = (new JsonLdEntityContext($model))->build();
×
129

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

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

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

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

UNCOV
163
        return $supportedClass;
×
164
    }
165

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

UNCOV
179
        return $results->first();
×
180
    }
181

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

UNCOV
196
        return false;
×
197
    }
198

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

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

UNCOV
212
        if ($isTimeBehaviorField && $isDateTimeField) {
×
UNCOV
213
            return false;
×
214
        }
215

UNCOV
216
        return true;
×
217
    }
218
}
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