Passed
Push — master ( 693d98...8cf93f )
by Tom
02:05
created

Maphper::offsetSet()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 17
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 12
c 0
b 0
f 0
nc 3
nop 2
dl 0
loc 17
rs 9.4285
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
52
	private function getResults() {
53
		$this->wrapFilter();
54
		foreach ($this->settings['filter'] as $name => &$filter) {
55
			if (isset($this->relations[$name])) {
56
				$this->relations[$name]->overwrite($filter, $filter[$name]);
57
			}
58
		}
59
		$results = $this->dataSource->findByField($this->settings['filter'],
60
			['order' => $this->settings['sort'], 'limit' => $this->settings['limit'], 'offset' => $this->settings['offset'] ]);
61
62
		$siblings = new \ArrayObject();
63
64
		foreach ($results as &$result) $result = $this->entity->create($result, $this->relations, $siblings);
65
66
		return $results;
67
	}
68
69
	public function item($n) {
70
		$array = $this->getResults();
71
		return isset($array[$n]) ? $array[$n] : null;
72
	}
73
74
	public function getIterator() {
75
		return new Iterator($this->getResults(), $this->dataSource->getPrimaryKey());
76
	}
77
78
	private function processFilters($value) {
79
		//When saving to a mapper with filters, write the filters back into the object being stored
80
		foreach ($this->settings['filter'] as $key => $filterValue) {
81
			if (empty($value->$key) && !is_array($filterValue)) $value->$key = $filterValue;
82
		}
83
		return $value;
84
	}
85
86
	public function offsetSet($offset, $valueObj) {
87
		if ($valueObj instanceof \Maphper\Relation) throw new \Exception();
88
89
		//Extract private properties from the object
90
		$propertyReader = new \Maphper\Lib\VisibilityOverride();
91
		$value = $propertyReader->getProperties($valueObj);
92
93
		$value = $this->processFilters($value);
94
		$pk = $this->dataSource->getPrimaryKey();
95
		if ($offset !== null) $value->{$pk[0]} = $offset;
96
		$valueCopy = clone $value;
97
		$value = $this->entity->wrap($this->relations, $value);
98
		$this->dataSource->save($value);
99
		$value = $this->entity->create(array_merge((array)$value, (array)$valueCopy), $this->relations);
100
101
		$writer = new Lib\PropertyWriter($valueObj);
102
		$writer->write($value);
103
	}
104
105
	public function offsetExists($offset) {
106 View Code Duplication
		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...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
107
        if (!empty($this->settings['filter'])) {
108
            $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

108
            /** @scrutinizer ignore-call */ 
109
            $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...
109
            return isset($data[0]);
110
        }
111
		return (bool) $this->dataSource->findById($offset);
112
	}
113
114
	public function offsetUnset($id) {
115
		$this->dataSource->deleteById($id);
116
	}
117
118
	public function offsetGet($offset) {
119 View Code Duplication
		if (count($this->dataSource->getPrimaryKey()) > 1) return new MultiPk($this, $offset, $this->dataSource->getPrimaryKey());
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
120
        if (!empty($this->settings['filter'])) {
121
            $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

121
            /** @scrutinizer ignore-call */ 
122
            $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...
122
            return $this->entity->create(isset($data[0]) ? $data[0] : null, $this->relations);
123
        }
124
		return $this->entity->create($this->dataSource->findById($offset), $this->relations);
125
	}
126
127
	public function __call($method, $args) {
128
		if (array_key_exists($method, $this->settings)) {
129
			$maphper = new Maphper($this->dataSource, $this->settings, $this->relations);
130
			if (is_array($maphper->settings[$method])) $maphper->settings[$method] = $args[0] + $maphper->settings[$method];
131
			else $maphper->settings[$method] = $args[0];
132
			return $maphper;
133
		}
134
		else throw new \Exception('Method Maphper::' . $method . ' does not exist');
135
	}
136
137
	public function findAggregate($function, $field, $group = null) {
138
		return $this->dataSource->findAggregate($function, $field, $group, $this->settings['filter']);
139
	}
140
141
	public function delete() {
142
		$this->dataSource->deleteByField($this->settings['filter'], ['order' => $this->settings['sort'], 'limit' => $this->settings['limit'], 'offset' => $this->settings['offset']]);
0 ignored issues
show
Unused Code introduced by
The call to Maphper\DataSource::deleteByField() has too many arguments starting with array('order' => $this->...is->settings['offset']). ( Ignorable by Annotation )

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

142
		$this->dataSource->/** @scrutinizer ignore-call */ 
143
                     deleteByField($this->settings['filter'], ['order' => $this->settings['sort'], 'limit' => $this->settings['limit'], 'offset' => $this->settings['offset']]);

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...
143
	}
144
}