Passed
Push — master ( 8c8288...1ec2ab )
by Richard
01:43
created

Maphper::removeRelations()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
nc 3
nop 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
namespace Maphper;
3
class Maphper implements \Countable, \ArrayAccess, \IteratorAggregate {
4
	const FIND_EXACT 		= 	0x1;
5
	const FIND_LIKE 		= 	0x2;
6
	const FIND_STARTS 		= 	0x4;
7
	const FIND_ENDS 		= 	0x8;
8
	const FIND_BIT 			= 	0x10;
9
	const FIND_GREATER 		= 	0x20;
10
	const FIND_LESS 		=	0x40;
11
	const FIND_EXPRESSION 	= 	0x80;
12
	const FIND_AND 			= 	0x100;
13
	const FIND_OR 			= 	0x200;
14
	const FIND_NOT 			= 	0x400;
15
	const FIND_BETWEEN		= 	0x800;
16
	const FIND_NOCASE		= 	0x1000;
17
18
	private $dataSource;
19
	private $relations = [];
20
	private $settings = ['filter' => [], 'sort' => null, 'limit' => null, 'offset' => null, 'resultClass' => '\\stdClass'];
21
	private $iterator = 0;
22
	private $entity;
23
24
	public function __construct(DataSource $dataSource, array $settings = [], array $relations = []) {
25
		$this->dataSource = $dataSource;
26
		$this->settings = array_replace($this->settings, $settings);
27
		$this->relations = $relations;
28
		$this->entity = new Lib\Entity($this, $this->settings['resultClass'] ?? null);
29
	}
30
31
	public function addRelation($name, Relation $relation) {
32
		$this->relations[$name] = $relation;
33
	}
34
35
	public function count($group = null) {
36
		return $this->dataSource->findAggregate('count', $group == null ? $this->dataSource->getPrimaryKey() : $group, $group, $this->settings['filter']);
37
	}
38
39
	//Allow filter(['user' => $user]) where $user is an object instead of
40
	//filter(['userId' => $user->id])
41
	private function wrapFilter() {
42
		foreach ($this->settings['filter'] as $name => $value) {
43
			if (isset($this->relations[$name])) {
44
				$filter = $this->relations[$name]->getFilter($value);
45
				$this->settings['filter'] = array_merge($this->settings['filter'], $filter);
46
				unset($this->settings['filter'][$name]);
47
			}
48
		}
49
	}
50
51
	private function getResults() {
52
		$this->wrapFilter();
53
		foreach ($this->settings['filter'] as $name => &$filter) {
54
			if (isset($this->relations[$name])) {
55
				$this->relations[$name]->overwrite($filter, $filter[$name]);
56
			}
57
		}
58
		$results = $this->dataSource->findByField($this->settings['filter'],
59
			['order' => $this->settings['sort'], 'limit' => $this->settings['limit'], 'offset' => $this->settings['offset'] ]);
60
61
		$siblings = new \ArrayObject();
62
63
		foreach ($results as &$result) $result = $this->entity->create($result, $this->relations, $siblings);
64
65
		return $results;
66
	}
67
68
	public function item($n) {
69
		$array = $this->getResults();
70
		return isset($array[$n]) ? $array[$n] : null;
71
	}
72
73
	public function getIterator() {
74
		return new Iterator($this->getResults(), $this->dataSource->getPrimaryKey());
75
	}
76
77
	private function processFilters($value) {
78
		//When saving to a mapper with filters, write the filters back into the object being stored
79
		foreach ($this->settings['filter'] as $key => $filterValue) {
80
			if (empty($value->$key) && !is_array($filterValue)) $value->$key = $filterValue;
81
		}
82
		return $value;
83
	}
84
85
	public function offsetSet($offset, $valueObj) {
86
		if ($valueObj instanceof \Maphper\Relation) throw new \Exception();
87
88
		//Extract private properties from the object
89
		$visibilityOverride = new \Maphper\Lib\VisibilityOverride($valueObj);
90
		$value = $visibilityOverride->getProperties($valueObj);
0 ignored issues
show
Unused Code introduced by
The call to Maphper\Lib\VisibilityOverride::getProperties() has too many arguments starting with $valueObj. ( Ignorable by Annotation )

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

90
		/** @scrutinizer ignore-call */ 
91
  $value = $visibilityOverride->getProperties($valueObj);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
91
92
		$value = $this->processFilters($value);
93
		$pk = $this->dataSource->getPrimaryKey();
94
		if ($offset !== null) $value->{$pk[0]} = $offset;
95
		$valueCopy = $this->removeRelations(clone $value);
96
		$value = $this->entity->wrap($this->relations, $value);
97
		$this->dataSource->save($value);
98
		$visibilityOverride->write($value);
99
        $this->entity->create((array_merge((array)$value, (array)$valueCopy)), $this->relations);
100
	}
101
102
	private function removeRelations($obj) { // Prevent saving ManyMany twice
103
		foreach ($this->relations as $name => $relation)
104
			if ($relation instanceOf \Maphper\Relation\ManyMany && isset($obj->$name)) unset($obj->$name);
105
		return $obj;
106
	}
107
108
	public function offsetExists($offset) {
109
		if (count($this->dataSource->getPrimaryKey()) > 1) return new MultiPk($this, $offset, $this->dataSource->getPrimaryKey());
0 ignored issues
show
Bug Best Practice introduced by
The expression return new Maphper\Multi...ource->getPrimaryKey()) returns the type Maphper\MultiPk which is incompatible with the return type mandated by ArrayAccess::offsetExists() of boolean.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
110
        if (!empty($this->settings['filter'])) {
111
            $data = $this->dataSource->findByField(array_merge($this->settings['filter'], [$this->dataSource->getPrimaryKey()[0] => $offset]));
0 ignored issues
show
Bug introduced by
The call to Maphper\DataSource::findByField() has too few arguments starting with options. ( Ignorable by Annotation )

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

111
            /** @scrutinizer ignore-call */ 
112
            $data = $this->dataSource->findByField(array_merge($this->settings['filter'], [$this->dataSource->getPrimaryKey()[0] => $offset]));

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
112
            return isset($data[0]);
113
        }
114
		return (bool) $this->dataSource->findById($offset);
115
	}
116
117
	public function offsetUnset($id) {
118
		$this->dataSource->deleteById($id);
119
	}
120
121
	public function offsetGet($offset) {
122
		if (count($this->dataSource->getPrimaryKey()) > 1) return new MultiPk($this, $offset, $this->dataSource->getPrimaryKey());
123
        if (!empty($this->settings['filter'])) {
124
            $data = $this->dataSource->findByField(array_merge($this->settings['filter'], [$this->dataSource->getPrimaryKey()[0] => $offset]));
0 ignored issues
show
Bug introduced by
The call to Maphper\DataSource::findByField() has too few arguments starting with options. ( Ignorable by Annotation )

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

124
            /** @scrutinizer ignore-call */ 
125
            $data = $this->dataSource->findByField(array_merge($this->settings['filter'], [$this->dataSource->getPrimaryKey()[0] => $offset]));

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
125
            return $this->entity->create(isset($data[0]) ? $data[0] : null, $this->relations);
126
        }
127
		return $this->entity->create($this->dataSource->findById($offset), $this->relations);
128
	}
129
130
	public function __call($method, $args) {
131
		if (array_key_exists($method, $this->settings)) {
132
			$maphper = new Maphper($this->dataSource, $this->settings, $this->relations);
133
			if (is_array($maphper->settings[$method])) $maphper->settings[$method] = $args[0] + $maphper->settings[$method];
134
			else $maphper->settings[$method] = $args[0];
135
			return $maphper;
136
		}
137
		else throw new \Exception('Method Maphper::' . $method . ' does not exist');
138
	}
139
140
	public function findAggregate($function, $field, $group = null) {
141
		return $this->dataSource->findAggregate($function, $field, $group, $this->settings['filter']);
142
	}
143
144
	public function delete() {
145
		$this->dataSource->deleteByField($this->settings['filter'], ['order' => $this->settings['sort'], 'limit' => $this->settings['limit'], 'offset' => $this->settings['offset']]);
146
	}
147
}
148