Passed
Pull Request — master (#800)
by Stefano
03:33
created

RelationsController::save()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 8
dl 0
loc 11
rs 10
c 0
b 0
f 0
cc 1
nc 1
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 Cake\Http\Response;
16
use Cake\Utility\Hash;
17
18
/**
19
 * Relations Model Controller: list, add, edit, remove relations
20
 *
21
 * @property \App\Controller\Component\PropertiesComponent $Properties
22
 */
23
class RelationsController extends ModelBaseController
24
{
25
    /**
26
     * Resource type currently used
27
     *
28
     * @var string
29
     */
30
    protected $resourceType = 'relations';
31
32
    /**
33
     * @inheritDoc
34
     */
35
    public function index(): ?Response
36
    {
37
        parent::index();
38
        $resources = (array)$this->viewBuilder()->getVar('resources');
39
        foreach ($resources as &$resource) {
40
            $resource['left_object_types'] = $this->relatedTypes($resource['id'], 'left');
41
            $resource['right_object_types'] = $this->relatedTypes($resource['id'], 'right');
42
        }
43
        $this->set(compact('resources'));
44
45
        return null;
46
    }
47
48
    /**
49
     * @inheritDoc
50
     */
51
    public function view($id): ?Response
52
    {
53
        parent::view($id);
54
        $this->set('left_object_types', $this->relatedTypes($id, 'left'));
55
        $this->set('right_object_types', $this->relatedTypes($id, 'right'));
56
57
        return null;
58
    }
59
60
    /**
61
     * Save relation.
62
     *
63
     * @return \Cake\Http\Response|null
64
     */
65
    public function save(): ?Response
66
    {
67
        $data = (array)$this->request->getData();
68
        $this->updateRelatedTypes($data, 'left');
69
        $this->updateRelatedTypes($data, 'right');
70
        $this->request = $this->request->withoutData('change_left')
71
            ->withoutData('change_right')
72
            ->withoutData('current_left')
73
            ->withoutData('current_right');
74
75
        return parent::save();
76
    }
77
78
    /**
79
     * Update relation types on the `left` or `right` side of a relation
80
     *
81
     * @param array $data Request data
82
     * @param string $side Relation side, `left` or `right`
83
     * @return void
84
     */
85
    protected function updateRelatedTypes(array $data, string $side): void
86
    {
87
        $current = array_filter(explode(',', (string)Hash::get($data, sprintf('current_%s', $side))));
88
        $change = array_filter(explode(',', (string)Hash::get($data, sprintf('change_%s', $side))));
89
        sort($current);
90
        sort($change);
91
        if ($current == $change) {
92
            return;
93
        }
94
        $id = Hash::get($data, 'id');
95
        $endpoint = sprintf('/model/relations/%s/relationships/%s_object_types', $id, $side);
96
        $data = $this->relatedItems($change);
97
        $this->apiClient->patch($endpoint, json_encode(compact('data')));
98
    }
99
100
    /**
101
     * Retrieve body item for API call
102
     *
103
     * @param array $types Object type names array
104
     * @return array
105
     */
106
    protected function relatedItems(array $types): array
107
    {
108
        return array_map(
109
            function ($item) {
110
                $response = $this->apiClient->get(sprintf('/model/object_types/%s', trim($item)));
111
                $id = Hash::get((array)$response, 'data.id');
112
113
                return compact('id') + ['type' => 'object_types'];
114
            },
115
            $types
116
        );
117
    }
118
119
    /**
120
     * Get related types by relation ID and side
121
     *
122
     * @param string $id The relation ID
123
     * @param string $side The side, can be 'left' or 'right'
124
     * @return array
125
     */
126
    public function relatedTypes(string $id, string $side): array
127
    {
128
        if (!is_numeric($id)) {
129
            $resource = (array)$this->viewBuilder()->getVar('resource');
130
            $id = Hash::get($resource, 'id');
131
        }
132
        $endpoint = sprintf('/model/relations/%s/%s_object_types', $id, $side);
133
        $response = $this->apiClient->get($endpoint);
134
135
        return (array)Hash::extract($response, 'data.{n}.attributes.name');
136
    }
137
}
138