Completed
Push — master ( 5e8f33...9f28e5 )
by Stefano
23s queued 13s
created

RelationsController::allTypes()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
eloc 9
c 1
b 1
f 0
dl 0
loc 15
rs 9.9666
cc 2
nc 2
nop 0
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
        $this->updateRelatedTypes($data, 'left');
88
        $this->updateRelatedTypes($data, 'right');
89
        $this->request = $this->request->withoutData('change_left')
90
            ->withoutData('change_right')
91
            ->withoutData('current_left')
92
            ->withoutData('current_right');
93
94
        return parent::save();
95
    }
96
97
    /**
98
     * Update relation types on the `left` or `right` side of a relation
99
     *
100
     * @param array $data Request data
101
     * @param string $side Relation side, `left` or `right`
102
     * @return void
103
     */
104
    protected function updateRelatedTypes(array $data, string $side): void
105
    {
106
        $current = array_filter(explode(',', (string)Hash::get($data, sprintf('current_%s', $side))));
107
        $change = array_filter(explode(',', (string)Hash::get($data, sprintf('change_%s', $side))));
108
        sort($current);
109
        sort($change);
110
        if ($current == $change) {
111
            return;
112
        }
113
        $id = Hash::get($data, 'id');
114
        $endpoint = sprintf('/model/relations/%s/relationships/%s_object_types', $id, $side);
115
        $data = $this->relatedItems($change);
116
        $this->apiClient->patch($endpoint, json_encode(compact('data')));
117
    }
118
119
    /**
120
     * Retrieve body item for API call
121
     *
122
     * @param array $types Object type names array
123
     * @return array
124
     */
125
    protected function relatedItems(array $types): array
126
    {
127
        return array_map(
128
            function ($item) {
129
                $response = $this->apiClient->get(sprintf('/model/object_types/%s', trim($item)));
130
                $id = Hash::get((array)$response, 'data.id');
131
132
                return compact('id') + ['type' => 'object_types'];
133
            },
134
            $types
135
        );
136
    }
137
138
    /**
139
     * Get related types by relation resource and side
140
     *
141
     * @param array $resource The resource data
142
     * @param string $side The side, can be 'left' or 'right'
143
     * @return array
144
     */
145
    protected function relatedTypes(array $resource, string $side): array
146
    {
147
        $path = sprintf('relationships.%s_object_types.data.{n}.attributes.name', $side);
148
149
        return (array)Hash::extract($resource, $path);
150
    }
151
152
    /**
153
     * Get all types
154
     *
155
     * @return array
156
     */
157
    protected function allTypes(): array
158
    {
159
        try {
160
            $response = $this->apiClient->get('/model/object_types', [
161
                'page_size' => 100,
162
                'filter' => ['enabled' => true],
163
            ]);
164
        } catch (BEditaClientException $e) {
165
            $this->log($e->getMessage(), 'error');
166
            $this->Flash->error($e->getMessage(), ['params' => $e]);
167
168
            return [];
169
        }
170
171
        return (array)Hash::extract($response, 'data.{n}.attributes.name');
172
    }
173
}
174