Issues (219)

Branch: 4-cactus

API/src/Model/Action/UpdateAssociatedAction.php (4 issues)

Labels
Severity
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\API\Model\Action;
15
16
use BEdita\Core\Model\Action\BaseAction;
17
use Cake\Database\Expression\QueryExpression;
18
use Cake\Datasource\Exception\RecordNotFoundException;
19
use Cake\ORM\Association;
20
use Cake\ORM\Association\BelongsTo;
21
use Cake\ORM\Association\BelongsToMany;
22
use Cake\ORM\Association\HasOne;
23
use Cake\Utility\Hash;
24
25
/**
26
 * Command to update links between entities.
27
 *
28
 * @since 4.0.0
29
 */
30
class UpdateAssociatedAction extends BaseAction
31
{
32
    /**
33
     * Add associated action.
34
     *
35
     * @var \BEdita\Core\Model\Action\UpdateAssociatedAction
36
     */
37
    protected $Action;
38
39
    /**
40
     * Request instance.
41
     *
42
     * @var \Cake\Http\ServerRequest
43
     */
44
    protected $request;
45
46
    /**
47
     * @inheritDoc
48
     */
49
    protected function initialize(array $data)
50
    {
51
        $this->Action = $this->getConfig('action');
52
        $this->request = $this->getConfig('request');
53
    }
54
55
    /**
56
     * @inheritDoc
57
     */
58
    public function execute(array $data = [])
59
    {
60
        $association = $this->Action->getConfig('association');
61
        if (!($association instanceof Association)) {
62
            throw new \LogicException(__d('bedita', 'Unknown association type'));
63
        }
64
65
        $entity = $association->getSource()->get($data['primaryKey']);
66
67
        $requestData = $this->request->getData();
68
        if (!Hash::numeric(array_keys($requestData))) {
0 ignored issues
show
It seems like $requestData can also be of type null; however, parameter $array of array_keys() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

68
        if (!Hash::numeric(array_keys(/** @scrutinizer ignore-type */ $requestData))) {
Loading history...
69
            $requestData = [$requestData];
70
        }
71
72
        $relatedEntities = $this->getTargetEntities($requestData, $association);
0 ignored issues
show
It seems like $requestData can also be of type null; however, parameter $data of BEdita\API\Model\Action\...on::getTargetEntities() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

72
        $relatedEntities = $this->getTargetEntities(/** @scrutinizer ignore-type */ $requestData, $association);
Loading history...
73
        $count = count($relatedEntities);
74
        if ($count === 0) {
75
            $relatedEntities = [];
76
        } elseif ($count === 1 && ($association instanceof BelongsTo || $association instanceof HasOne)) {
77
            $relatedEntities = reset($relatedEntities);
78
        }
79
80
        return $this->Action->execute(compact('entity', 'relatedEntities'));
81
    }
82
83
    /**
84
     * Get target entities.
85
     *
86
     * @param array $data Request data.
87
     * @param \Cake\ORM\Association $association Association.
88
     * @return \Cake\Datasource\EntityInterface[]
89
     */
90
    protected function getTargetEntities(array $data, Association $association)
91
    {
92
        $target = $association->getTarget();
93
        $primaryKeyField = $target->getPrimaryKey();
94
        $targetPKField = $target->aliasField($primaryKeyField);
0 ignored issues
show
It seems like $primaryKeyField can also be of type string[]; however, parameter $field of Cake\ORM\Table::aliasField() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

94
        $targetPKField = $target->aliasField(/** @scrutinizer ignore-type */ $primaryKeyField);
Loading history...
95
96
        $targetPrimaryKeys = array_unique(Hash::extract($data, '{*}.id'));
0 ignored issues
show
It seems like Cake\Utility\Hash::extract($data, '{*}.id') can also be of type ArrayAccess; however, parameter $array of array_unique() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

96
        $targetPrimaryKeys = array_unique(/** @scrutinizer ignore-type */ Hash::extract($data, '{*}.id'));
Loading history...
97
        if (empty($targetPrimaryKeys)) {
98
            return [];
99
        }
100
101
        $targetEntities = $target->find()
102
            ->where(function (QueryExpression $exp) use ($targetPKField, $targetPrimaryKeys) {
103
                return $exp->in($targetPKField, $targetPrimaryKeys);
104
            });
105
        $targetEntities = $targetEntities->all()->indexBy($primaryKeyField)->toArray();
106
        /** @var \Cake\Datasource\EntityInterface[] $targetEntities */
107
108
        // sort following the original order
109
        uksort(
110
            $targetEntities,
111
            function ($a, $b) use ($targetPrimaryKeys) {
112
                return array_search($a, $targetPrimaryKeys) - array_search($b, $targetPrimaryKeys);
113
            }
114
        );
115
116
        foreach ($data as $datum) {
117
            $id = Hash::get($datum, 'id');
118
            $type = Hash::get($datum, 'type');
119
            if (!isset($targetEntities[$id]) || ($targetEntities[$id]->has('type') && $targetEntities[$id]->get('type') !== $type)) {
120
                throw new RecordNotFoundException(__('Record not found in table "{0}"', $type ?: $target->getTable()));
121
            }
122
123
            $meta = Hash::get($datum, '_meta.relation');
124
            if (!$this->request->is('delete') && $association instanceof BelongsToMany && $meta !== null) {
125
                $targetEntities[$id]->_joinData = $meta;
126
            }
127
        }
128
129
        return $targetEntities;
130
    }
131
}
132