RelationsController   A
last analyzed

Complexity

Total Complexity 13

Size/Duplication

Total Lines 155
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 56
dl 0
loc 155
rs 10
c 0
b 0
f 0
wmc 13

9 Methods

Rating   Name   Duplication   Size   Complexity  
A index() 0 11 2
A view() 0 9 1
A relatedTypes() 0 5 1
A updateRelatedTypes() 0 13 2
A relatedItems() 0 10 1
A indexQuery() 0 3 1
A viewQuery() 0 3 1
A allTypes() 0 15 2
A save() 0 18 2
1
<?php
2
/**
3
 * BEdita, API-first content management framework
4
 * Copyright 2021 ChannelWeb Srl, Chialab Srl
5
 *
6
 * This file is part of BEdita: you can redistribute it and/or modify
7
 * it under the terms of the GNU Lesser General Public License as published
8
 * by the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * See LICENSE.LGPL or <http://gnu.org/licenses/lgpl-3.0.html> for more details.
12
 */
13
namespace App\Controller\Model;
14
15
use BEdita\SDK\BEditaClientException;
16
use Cake\Http\Response;
17
use Cake\Utility\Hash;
18
19
/**
20
 * Relations Model Controller: list, add, edit, remove relations
21
 *
22
 * @property \App\Controller\Component\PropertiesComponent $Properties
23
 */
24
class RelationsController extends ModelBaseController
25
{
26
    /**
27
     * Resource type currently used
28
     *
29
     * @var string
30
     */
31
    protected $resourceType = 'relations';
32
33
    /**
34
     * @inheritDoc
35
     */
36
    public function index(): ?Response
37
    {
38
        parent::index();
39
        $resources = (array)$this->viewBuilder()->getVar('resources');
40
        foreach ($resources as &$resource) {
41
            $resource['left_object_types'] = $this->relatedTypes($resource, 'left');
42
            $resource['right_object_types'] = $this->relatedTypes($resource, 'right');
43
        }
44
        $this->set(compact('resources'));
45
46
        return null;
47
    }
48
49
    /**
50
     * @inheritDoc
51
     */
52
    protected function indexQuery(): array
53
    {
54
        return parent::indexQuery() + ['include' => 'left_object_types,right_object_types'];
55
    }
56
57
    /**
58
     * @inheritDoc
59
     */
60
    protected function viewQuery(): array
61
    {
62
        return parent::viewQuery() + ['include' => 'left_object_types,right_object_types'];
63
    }
64
65
    /**
66
     * @inheritDoc
67
     */
68
    public function view($id): ?Response
69
    {
70
        parent::view($id);
71
        $resource = (array)$this->viewBuilder()->getVar('resource');
72
        $this->set('left_object_types', $this->relatedTypes($resource, 'left'));
73
        $this->set('right_object_types', $this->relatedTypes($resource, 'right'));
74
        $this->set('all_types', $this->allTypes());
75
76
        return null;
77
    }
78
79
    /**
80
     * Save relation.
81
     *
82
     * @return \Cake\Http\Response|null
83
     */
84
    public function save(): ?Response
85
    {
86
        $data = (array)$this->request->getData();
87
88
        // remove 'definitions' from JSON-SCHEMA relation 'params' attribute to avoid validation errors
89
        $params = (array)json_decode((string)Hash::get($data, 'params'), true);
90
        unset($params['definitions']);
91
        $params = empty($params) ? null : json_encode($params);
92
93
        $this->updateRelatedTypes($data, 'left');
94
        $this->updateRelatedTypes($data, 'right');
95
        $this->request = $this->request->withoutData('change_left')
96
            ->withoutData('change_right')
97
            ->withoutData('current_left')
98
            ->withoutData('current_right')
99
            ->withData('params', $params);
100
101
        return parent::save();
102
    }
103
104
    /**
105
     * Update relation types on the `left` or `right` side of a relation
106
     *
107
     * @param array $data Request data
108
     * @param string $side Relation side, `left` or `right`
109
     * @return void
110
     */
111
    protected function updateRelatedTypes(array $data, string $side): void
112
    {
113
        $current = array_filter(explode(',', (string)Hash::get($data, sprintf('current_%s', $side))));
114
        $change = array_filter(explode(',', (string)Hash::get($data, sprintf('change_%s', $side))));
115
        sort($current);
116
        sort($change);
117
        if ($current == $change) {
118
            return;
119
        }
120
        $id = Hash::get($data, 'id');
121
        $endpoint = sprintf('/model/relations/%s/relationships/%s_object_types', $id, $side);
122
        $data = $this->relatedItems($change);
123
        $this->apiClient->patch($endpoint, json_encode(compact('data')));
124
    }
125
126
    /**
127
     * Retrieve body item for API call
128
     *
129
     * @param array $types Object type names array
130
     * @return array
131
     */
132
    protected function relatedItems(array $types): array
133
    {
134
        return array_map(
135
            function ($item) {
136
                $response = $this->apiClient->get(sprintf('/model/object_types/%s', trim($item)));
137
                $id = Hash::get((array)$response, 'data.id');
138
139
                return compact('id') + ['type' => 'object_types'];
140
            },
141
            $types
142
        );
143
    }
144
145
    /**
146
     * Get related types by relation resource and side
147
     *
148
     * @param array $resource The resource data
149
     * @param string $side The side, can be 'left' or 'right'
150
     * @return array
151
     */
152
    protected function relatedTypes(array $resource, string $side): array
153
    {
154
        $path = sprintf('relationships.%s_object_types.data.{n}.attributes.name', $side);
155
156
        return (array)Hash::extract($resource, $path);
157
    }
158
159
    /**
160
     * Get all types
161
     *
162
     * @return array
163
     */
164
    protected function allTypes(): array
165
    {
166
        try {
167
            $response = $this->apiClient->get('/model/object_types', [
168
                'page_size' => 100,
169
                'filter' => ['enabled' => true],
170
            ]);
171
        } catch (BEditaClientException $e) {
172
            $this->log($e->getMessage(), 'error');
173
            $this->Flash->error($e->getMessage(), ['params' => $e]);
174
175
            return [];
176
        }
177
178
        return (array)Hash::extract($response, 'data.{n}.attributes.name');
179
    }
180
}
181