Completed
Push — master ( 95f960...704bb5 )
by Alberto
16s queued 12s
created

CloneComponent::stream()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 17
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 3
eloc 12
c 1
b 1
f 0
nc 3
nop 3
dl 0
loc 17
rs 9.8666
1
<?php
2
declare(strict_types=1);
3
/**
4
 * BEdita, API-first content management framework
5
 * Copyright 2022 Atlas Srl, Chialab Srl
6
 *
7
 * This file is part of BEdita: you can redistribute it and/or modify
8
 * it under the terms of the GNU Lesser General Public License as published
9
 * by the Free Software Foundation, either version 3 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * See LICENSE.LGPL or <http://gnu.org/licenses/lgpl-3.0.html> for more details.
13
 */
14
15
namespace App\Controller\Component;
16
17
use BEdita\WebTools\ApiClientProvider;
18
use Cake\Controller\Component;
19
use Cake\Utility\Hash;
20
21
/**
22
 * Clone component
23
 */
24
class CloneComponent extends Component
25
{
26
    /**
27
     * BEdita Api client
28
     *
29
     * @var \BEdita\SDK\BEditaClient
30
     */
31
    protected $apiClient = null;
32
33
    /**
34
     * {@inheritDoc}
35
     * {@codeCoverageIgnore}
36
     */
37
    public function startup(): void
38
    {
39
        $this->apiClient = ApiClientProvider::getApiClient();
40
    }
41
42
    /**
43
     * Get the value of query 'cloneRelations'.
44
     * Return true when cloneRelations is not false.
45
     *
46
     * @return bool
47
     */
48
    public function queryCloneRelations(): bool
49
    {
50
        $cloneRelations = $this->getController()->getRequest()->getQuery('cloneRelations');
51
52
        return filter_var($cloneRelations, FILTER_VALIDATE_BOOLEAN) !== false;
53
    }
54
55
    /**
56
     * Clone relation from source object $source to destination object ID $destination.
57
     * Exclude 'children', if present.
58
     *
59
     * @param array $source The source object
60
     * @param string $destinationId The destination ID
61
     * @return bool
62
     */
63
    public function relations(array $source, string $destinationId): bool
64
    {
65
        if (!$this->queryCloneRelations()) {
66
            return false;
67
        }
68
        $sourceId = (string)Hash::get($source, 'data.id');
69
        $type = (string)Hash::get($source, 'data.type');
70
        $relationships = array_keys((array)Hash::extract($source, 'data.relationships'));
71
        $relationships = $this->filterRelations($relationships);
72
        foreach ($relationships as $relation) {
73
            $this->relation($sourceId, $type, $relation, $destinationId);
74
        }
75
76
        return true;
77
    }
78
79
    /**
80
     * Filter relationships, remove not allowed 'children', 'parents', 'translations'
81
     *
82
     * @param array $relationships The relationships
83
     * @return array
84
     */
85
    public function filterRelations(array $relationships): array
86
    {
87
        return array_values(
88
            array_filter(
89
                $relationships,
90
                function ($relationship) {
91
                    return !in_array($relationship, ModulesComponent::FIXED_RELATIONSHIPS);
92
                }
93
            )
94
        );
95
    }
96
97
    /**
98
     * Clone single relation data.
99
     * This calls multiple times `BEditaClient::addRelated`, instead of calling it once,
100
     * to avoid time and resources consuming api operations locking tables.
101
     *
102
     * @param string $sourceId The source ID
103
     * @param string $type The object type
104
     * @param string $relation The relation name
105
     * @param string $destinationId The destination ID
106
     * @return bool
107
     */
108
    public function relation(string $sourceId, string $type, string $relation, string $destinationId): bool
109
    {
110
        $related = $this->apiClient->getRelated($sourceId, $type, $relation, ['page_size' => 100]);
111
        if (empty($related['data'])) {
112
            return false;
113
        }
114
        foreach ($related['data'] as $obj) {
115
            $this->apiClient->addRelated($destinationId, $type, $relation, [
116
                [
117
                    'id' => (string)Hash::get($obj, 'id'),
118
                    'type' => (string)Hash::get($obj, 'type'),
119
                ],
120
            ]);
121
        }
122
123
        return true;
124
    }
125
126
    /**
127
     * Clone stream if schema has Streams in associations and source object has a related stream.
128
     * Return media ID, or null.
129
     *
130
     * @param array $schema The object schema
131
     * @param array $source The object source to clone
132
     * @param array $attributes The destination attributes
133
     * @return ?string The media ID
134
     */
135
    public function stream(array $schema, array $source, array &$attributes): ?string
136
    {
137
        if (!in_array('Streams', (array)Hash::get($schema, 'associations'))) {
138
            return null;
139
        }
140
        $uuid = (string)Hash::get($source, 'data.relationships.streams.data.0.id');
141
        if (empty($uuid)) {
142
            return null;
143
        }
144
        $response = $this->apiClient->post(sprintf('/streams/clone/%s', $uuid), '');
145
        $streamId = (string)Hash::get($response, 'data.id');
146
        $type = (string)Hash::get($source, 'data.type');
147
        $data = compact('type');
148
        $response = $this->apiClient->createMediaFromStream($streamId, $type, compact('data'));
149
        $attributes['id'] = (string)Hash::get($response, 'data.id');
150
151
        return $attributes['id'];
152
    }
153
}
154