Completed
Push — master ( 84dee3...084275 )
by Michal
36:03
created

ExistsIn::_fieldsAreNull()   B

Complexity

Conditions 5
Paths 3

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 11
rs 8.8571
cc 5
eloc 7
nc 3
nop 2
1
<?php
2
/**
3
 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
4
 * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
5
 *
6
 * Licensed under The MIT License
7
 * For full copyright and license information, please see the LICENSE.txt
8
 * Redistributions of files must retain the above copyright notice.
9
 *
10
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
11
 * @link          http://cakephp.org CakePHP(tm) Project
12
 * @since         3.0.0
13
 * @license       http://www.opensource.org/licenses/mit-license.php MIT License
14
 */
15
namespace Cake\ORM\Rule;
16
17
use Cake\Datasource\EntityInterface;
18
use Cake\ORM\Association;
19
use RuntimeException;
20
21
/**
22
 * Checks that the value provided in a field exists as the primary key of another
23
 * table.
24
 */
25
class ExistsIn
26
{
27
28
    /**
29
     * The list of fields to check
30
     *
31
     * @var array
32
     */
33
    protected $_fields;
34
35
    /**
36
     * The repository where the field will be looked for
37
     *
38
     * @var array
39
     */
40
    protected $_repository;
41
42
    /**
43
     * Constructor.
44
     *
45
     * @param string|array $fields The field or fields to check existence as primary key.
46
     * @param object|string $repository The repository where the field will be looked for,
47
     * or the association name for the repository.
48
     */
49
    public function __construct($fields, $repository)
50
    {
51
        $this->_fields = (array)$fields;
52
        $this->_repository = $repository;
0 ignored issues
show
Documentation Bug introduced by
It seems like $repository of type object or string is incompatible with the declared type array of property $_repository.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
53
    }
54
55
    /**
56
     * Performs the existence check
57
     *
58
     * @param \Cake\Datasource\EntityInterface $entity The entity from where to extract the fields
59
     * @param array $options Options passed to the check,
60
     * where the `repository` key is required.
61
     * @throws \RuntimeException When the rule refers to an undefined association.
62
     * @return bool
63
     */
64
    public function __invoke(EntityInterface $entity, array $options)
65
    {
66
        if (is_string($this->_repository)) {
67
            $alias = $this->_repository;
68
            $this->_repository = $options['repository']->association($alias);
69
70
            if (empty($this->_repository)) {
71
                throw new RuntimeException(sprintf(
72
                    "ExistsIn rule for '%s' is invalid. The '%s' association is not defined.",
73
                    implode(', ', $this->_fields),
74
                    $alias
75
                ));
76
            }
77
        }
78
79
        $source = $target = $this->_repository;
80
        if (!empty($options['repository'])) {
81
            $source = $options['repository'];
82
        }
83
        if ($source instanceof Association) {
84
            $source = $source->source();
85
        }
86
        if ($target instanceof Association) {
87
            $bindingKey = (array)$target->bindingKey();
88
            $target = $target->target();
89
        } else {
90
            $bindingKey = (array)$target->primaryKey();
91
        }
92
93
        if (!empty($options['_sourceTable']) && $target === $options['_sourceTable']) {
94
            return true;
95
        }
96
97
        if (!$entity->extract($this->_fields, true)) {
98
            return true;
99
        }
100
101
        if ($this->_fieldsAreNull($entity, $source)) {
102
            return true;
103
        }
104
105
        $primary = array_map(
106
            [$target, 'aliasField'],
107
            $bindingKey
108
        );
109
        $conditions = array_combine(
110
            $primary,
111
            $entity->extract($this->_fields)
112
        );
113
        return $target->exists($conditions);
114
    }
115
116
    /**
117
     * Check whether or not the entity fields are nullable and null.
118
     *
119
     * @param \Cake\ORM\EntityInterface $entity The entity to check.
120
     * @param \Cake\ORM\Table $source The table to use schema from.
121
     * @return bool
122
     */
123
    protected function _fieldsAreNull($entity, $source)
124
    {
125
        $nulls = 0;
126
        $schema = $source->schema();
127
        foreach ($this->_fields as $field) {
128
            if ($schema->column($field) && $schema->isNullable($field) && $entity->get($field) === null) {
129
                $nulls++;
130
            }
131
        }
132
        return $nulls === count($this->_fields);
133
    }
134
}
135