Completed
Push — milestone/2.0 ( 0dfc11...693dca )
by
unknown
03:12
created

Key_Value_Datastore   A

Complexity

Total Complexity 25

Size/Duplication

Total Lines 197
Duplicated Lines 13.2 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
dl 26
loc 197
ccs 0
cts 75
cp 0
rs 10
c 0
b 0
f 0
wmc 25
lcom 1
cbo 5

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A get_full_hierarchy_for_field() 0 4 1
A get_full_hierarchy_index_for_field() 0 5 2
C cascading_storage_array_to_raw_value_set_tree() 0 49 11
A reduce_raw_value_set_tree_to_field() 0 17 4
get_storage_array() 0 1 ?
A load() 0 7 1
save_key_value_pair() 0 1 ?
B save() 26 26 5

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
namespace Carbon_Fields\Datastore;
4
5
use Carbon_Fields\App;
6
use Carbon_Fields\Helper\Helper;
7
use Carbon_Fields\Field\Field;
8
use Carbon_Fields\Value_Set\Value_Set;
9
use Carbon_Fields\Exception\Incorrect_Syntax_Exception;
10
11
/**
12
 * Key Value Datastore
13
 * Provides basic functionality to save in a key-value storage
14
 */
15
abstract class Key_Value_Datastore extends Datastore {
16
17
	/**
18
	 * Key Toolset for key generation and comparison utilities
19
	 * 
20
	 * @var Key_Toolset
21
	 */
22
	protected $key_toolset;
23
24
	/**
25
	 * Initialize the datastore.
26
	 */
27
	public function __construct() {
28
		$this->key_toolset = App::resolve( 'key_toolset' );
29
		parent::__construct();
30
	}
31
32
	/**
33
	 * Get array of ancestors (ordered top-bottom) with the field name appended to the end
34
	 *
35
	 * @param Field $field
36
	 * @return array<string>
37
	 */
38
	protected function get_full_hierarchy_for_field( Field $field ) {
39
		$full_hierarchy = array_merge( $field->get_hierarchy(), array( $field->get_base_name() ) );
40
		return $full_hierarchy;
41
	}
42
43
	/**
44
	 * Get array of ancestor entry indexes (ordered top-bottom)
45
	 * Indexes show which entry/group this field belongs to in a Complex_Field
46
	 *
47
	 * @param Field $field
48
	 * @return array<int>
49
	 */
50
	protected function get_full_hierarchy_index_for_field( Field $field ) {
51
		$hierarchy_index = $field->get_hierarchy_index();
52
		$full_hierarchy_index = ! empty( $hierarchy_index ) ? $hierarchy_index : array( 0 );
53
		return $full_hierarchy_index;
54
	}
55
56
	/**
57
	 * Raw Value Set Tree schema:
58
	 * array(
59
	 *     [field_name] => array(
60
	 *         'value_set' => [raw_value_set],
61
	 *         'groups' => array(
62
	 *             array(
63
	 *                 [recursion]
64
	 *             ),
65
	 *             ...
66
	 *         ),
67
	 *     ),
68
	 *     ...
69
	 * )
70
	 *
71
	 * @param array<stdClass> $storage_array
72
	 * @return array
73
	 */
74
	protected function cascading_storage_array_to_raw_value_set_tree( $storage_array ) {
75
		$tree = array();
76
77
		foreach ( $storage_array as $row ) {
78
			$parsed_storage_key = $this->key_toolset->parse_storage_key( $row->key );
79
80
			if ( $parsed_storage_key['property'] === $this->key_toolset::KEEPALIVE_PROPERTY ) {
81
				continue;
82
			}
83
84
			$level = &$tree;
85
			foreach ( $parsed_storage_key['full_hierarchy'] as $i => $field_name ) {
86
				$index = isset( $parsed_storage_key['hierarchy_index'][ $i ] ) ? $parsed_storage_key['hierarchy_index'][ $i ] : -1;
87
88
				if ( ! isset( $level[ $field_name ] ) ) {
89
					$level[ $field_name ] = array();
90
				}
91
				$level = &$level[ $field_name ];
92
93
				if ( $i < count( $parsed_storage_key['full_hierarchy'] ) - 1 ) {
94
					if ( ! isset( $level['groups'] ) ) {
95
						$level['groups'] = array();
96
					}
97
					$level = &$level['groups'];
98
99
					if ( ! isset( $level[ $index ] ) ) {
100
						$level[ $index ] = array();
101
					}
102
					$level = &$level[ $index ];
103
				} else  {
104
					if ( ! isset( $level['value_set'] ) ) {
105
						$level['value_set'] = array();
106
					}
107
					$level = &$level['value_set'];
108
109
					if ( ! isset( $level[ $parsed_storage_key['value_index'] ] ) ) {
110
						$level[ $parsed_storage_key['value_index'] ] = array();
111
					}
112
					$level = &$level[ $parsed_storage_key['value_index'] ];
113
114
					$level[ $parsed_storage_key['property'] ] = $row->value;
115
				}
116
			}
117
			$level = &$tree;
118
		}
119
120
		Helper::ksort_recursive( $tree );
121
		return $tree;
122
	}
123
124
	/**
125
	 * Get a reduced raw value set tree only relevant to the specified field
126
	 * 
127
	 * @param  array $raw_value_set_tree
128
	 * @param  Field $field
129
	 * @return array
130
	 */
131
	protected function reduce_raw_value_set_tree_to_field( $raw_value_set_tree, Field $field ) {
132
		$field_raw_value_set_tree = $raw_value_set_tree;
133
		$hierarchy = $field->get_hierarchy();
134
		$hierarchy_index = $field->get_hierarchy_index();
135
136
		foreach ( $hierarchy as $index => $parent_field ) {
137
			if ( isset( $field_raw_value_set_tree[ $parent_field ]['groups'][ $hierarchy_index[ $index ] ] ) ) {
138
				$field_raw_value_set_tree = $field_raw_value_set_tree[ $parent_field ]['groups'][ $hierarchy_index[ $index ] ];
139
			}
140
		}
141
142
		if ( isset( $field_raw_value_set_tree[ $field->get_base_name() ] ) ) {
143
			$field_raw_value_set_tree = $field_raw_value_set_tree[ $field->get_base_name() ];
144
		}
145
146
		return $field_raw_value_set_tree;
147
	}
148
149
	/**
150
	 * Get a raw database query results array for a field
151
	 *
152
	 * @param Field $field The field to retrieve value for.
153
	 * @param array $storage_key_patterns
154
	 * @return array<stdClass> Array of {key, value} objects
155
	 */
156
	abstract protected function get_storage_array( Field $field, $storage_key_patterns );
157
158
	/**
159
	 * Get the field value(s)
160
	 *
161
	 * @param Field $field The field to get value(s) for
162
	 * @return array<array>
163
	 */
164
	public function load( Field $field ) {
165
		$storage_key_patterns = $this->key_toolset->get_storage_key_getter_patterns( $field->is_simple_root_field(), $this->get_full_hierarchy_for_field( $field ) );
166
		$cascading_storage_array = $this->get_storage_array( $field, $storage_key_patterns );
167
		$raw_value_set_tree = $this->cascading_storage_array_to_raw_value_set_tree( $cascading_storage_array );
168
		$raw_value_set_tree = $this->reduce_raw_value_set_tree_to_field( $raw_value_set_tree, $field );
169
		return $raw_value_set_tree;
170
	}
171
172
	/**
173
	 * Save a single key-value pair to the database
174
	 *
175
	 * @param string $key
176
	 * @param string $value
177
	 */
178
	abstract protected function save_key_value_pair( $key, $value );
179
180
	/**
181
	 * Save the field value(s)
182
	 *
183
	 * @param Field $field The field to save.
184
	 */
185 View Code Duplication
	public function save( Field $field ) {
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in 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...
186
		$value_set = $field->get_full_value();
187
188
		if ( empty( $value_set ) && $field->get_value_set()->keepalive() ) {
189
			$storage_key = $this->key_toolset->get_storage_key(
190
				$field->is_simple_root_field(),
191
				$this->get_full_hierarchy_for_field( $field ),
192
				$this->get_full_hierarchy_index_for_field( $field ),
193
				0,
194
				$this->key_toolset::KEEPALIVE_PROPERTY
195
			);
196
			$this->save_key_value_pair( $storage_key, '' );
197
		}
198
		foreach ( $value_set as $value_group_index => $values ) {
1 ignored issue
show
Bug introduced by
The expression $value_set of type null|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
199
			foreach ( $values as $property => $value ) {
200
				$storage_key = $this->key_toolset->get_storage_key(
201
					$field->is_simple_root_field(),
202
					$this->get_full_hierarchy_for_field( $field ),
203
					$this->get_full_hierarchy_index_for_field( $field ),
204
					$value_group_index,
205
					$property
206
				);
207
				$this->save_key_value_pair( $storage_key, $value );
208
			}
209
		}
210
	}
211
}
212