Completed
Push — 4-cactus ( 5614e8...d450f9 )
by Stefano
20s queued 10s
created

SetAssociatedAction::belongsTo()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 9
nc 6
nop 2
dl 0
loc 17
rs 8.8333
c 0
b 0
f 0
1
<?php
2
/**
3
 * BEdita, API-first content management framework
4
 * Copyright 2016 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
14
namespace BEdita\Core\Model\Action;
15
16
use Cake\Datasource\EntityInterface;
17
use Cake\Network\Exception\BadRequestException;
18
use Cake\ORM\Association\BelongsTo;
19
use Cake\ORM\Association\BelongsToMany;
20
use Cake\ORM\Association\HasMany;
21
use Cake\ORM\Association\HasOne;
22
23
/**
24
 * Command to replace all entities associated to another entity.
25
 *
26
 * @since 4.0.0
27
 */
28
class SetAssociatedAction extends UpdateAssociatedAction
29
{
30
31
    use AssociatedTrait;
32
33
    /**
34
     * Replace existing relations.
35
     *
36
     * @param \Cake\Datasource\EntityInterface $entity Source entity.
37
     * @param \Cake\Datasource\EntityInterface|\Cake\Datasource\EntityInterface[]|null $relatedEntities Related entity(-ies).
38
     * @return int|false Number of updated relationships, or `false` on failure.
39
     * @throws \RuntimeException Throws an exception if an unsupported association is passed.
40
     */
41
    protected function update(EntityInterface $entity, $relatedEntities)
42
    {
43
        if ($this->Association instanceof BelongsToMany || $this->Association instanceof HasMany) {
0 ignored issues
show
introduced by
$this->Association is always a sub-type of Cake\ORM\Association\HasMany.
Loading history...
44
            if ($relatedEntities === null) {
45
                $relatedEntities = [];
46
            } elseif (!is_array($relatedEntities)) {
47
                $relatedEntities = [$relatedEntities];
48
            }
49
50
            $res = $this->toMany($entity, $relatedEntities);
51
            foreach ($relatedEntities as $relatedEntity) {
52
                if ($relatedEntity->has('_joinData') && $relatedEntity->get('_joinData')->getErrors()) {
53
                    throw new BadRequestException([
0 ignored issues
show
Bug introduced by
array('title' => __d('be...oinData')->getErrors()) of type array<string,mixed|null|string> is incompatible with the type null|string expected by parameter $message of Cake\Network\Exception\B...xception::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

53
                    throw new BadRequestException(/** @scrutinizer ignore-type */ [
Loading history...
54
                        'title' => __d('bedita', 'Error linking entities'),
55
                        'detail' => $relatedEntity->get('_joinData')->getErrors(),
56
                    ]);
57
                }
58
            }
59
60
            return $res;
61
        }
62
63
        if ($relatedEntities === []) {
64
            $relatedEntities = null;
65
        }
66
67
        if ($relatedEntities !== null && !($relatedEntities instanceof EntityInterface)) {
68
            throw new \InvalidArgumentException(__d('bedita', 'Unable to link multiple entities'));
69
        }
70
71
        if ($this->Association instanceof BelongsTo) {
72
            return $this->Association->getConnection()->transactional(function () use ($entity, $relatedEntities) {
73
                return $this->belongsTo($entity, $relatedEntities);
74
            });
75
        }
76
77
        if ($this->Association instanceof HasOne) {
78
            return $this->Association->getConnection()->transactional(function () use ($entity, $relatedEntities) {
79
                return $this->hasOne($entity, $relatedEntities);
80
            });
81
        }
82
83
        throw new \RuntimeException(__d('bedita', 'Unknown association of type "{0}"', get_class($this->Association)));
84
    }
85
86
    /**
87
     * Process action for to-many relationships.
88
     *
89
     * @param \Cake\Datasource\EntityInterface $entity Source entity.
90
     * @param \Cake\Datasource\EntityInterface[] $relatedEntities Related entities.
91
     * @return int|false
92
     */
93
    protected function toMany(EntityInterface $entity, array $relatedEntities)
94
    {
95
        // This doesn't need to be in a transaction.
96
        $relatedEntities = $this->diff($entity, $relatedEntities, true, $affectedEntities);
97
        $count = count($affectedEntities);
98
99
        if ($this->Association instanceof HasMany) {
100
            return $this->Association->replace($entity, $relatedEntities, ['atomic' => false]) ? $count : false;
101
        }
102
103
        if ($this->Association instanceof BelongsToMany) {
0 ignored issues
show
introduced by
$this->Association is always a sub-type of Cake\ORM\Association\BelongsToMany.
Loading history...
104
            return $this->Association->replaceLinks($entity, $relatedEntities, ['atomic' => false]) ? $count : false;
105
        }
106
107
        return false;
108
    }
109
110
    /**
111
     * Process action for "belongs to" relationships.
112
     *
113
     * @param \Cake\Datasource\EntityInterface $entity Source entity.
114
     * @param \Cake\Datasource\EntityInterface|null $relatedEntity Related entity.
115
     * @return int|false
116
     */
117
    protected function belongsTo(EntityInterface $entity, EntityInterface $relatedEntity = null)
118
    {
119
        $existing = $this->existing($entity);
120
121
        if ($existing === null && $relatedEntity === null) {
122
            return 0;
123
        } elseif ($relatedEntity !== null) {
124
            $bindingKey = (array)$this->Association->getBindingKey();
125
126
            if ($existing !== null && $relatedEntity->extract($bindingKey) == $existing->extract($bindingKey)) {
127
                return 0;
128
            }
129
        }
130
131
        $entity->set($this->Association->getProperty(), $relatedEntity);
132
133
        return $this->Association->getSource()->save($entity) ? 1 : false;
134
    }
135
136
    /**
137
     * Process action for "has one" relationships.
138
     *
139
     * @param \Cake\Datasource\EntityInterface $entity Source entity.
140
     * @param \Cake\Datasource\EntityInterface|null $relatedEntity Related entity.
141
     * @return int|false
142
     */
143
    protected function hasOne(EntityInterface $entity, EntityInterface $relatedEntity = null)
144
    {
145
        $foreignKey = (array)$this->Association->getForeignKey();
146
        $bindingKeyValue = $entity->extract((array)$this->Association->getBindingKey());
147
        $existing = $this->existing($entity);
148
149
        if ($existing === null && $relatedEntity === null) {
150
            return 0;
151
        }
152
153
        if ($relatedEntity !== null) {
154
            $primaryKey = (array)$this->Association->getPrimaryKey();
155
156
            if ($relatedEntity->extract($primaryKey) == $existing->extract($primaryKey)) {
157
                return 0;
158
            }
159
        }
160
161
        $this->Association->getTarget()->updateAll(
162
            array_combine(
163
                $foreignKey,
164
                array_fill(0, count($foreignKey), null)
165
            ),
166
            array_combine(
167
                $foreignKey,
168
                $bindingKeyValue
169
            )
170
        );
171
172
        if ($relatedEntity === null) {
173
            return 0;
174
        }
175
176
        $relatedEntity->set(array_combine(
177
            $foreignKey,
178
            $bindingKeyValue
179
        ));
180
181
        return $this->Association->getTarget()->save($relatedEntity) ? 1 : false;
182
    }
183
}
184