PluginProbe ʕ •ᴥ•ʔ
Search Regex / trunk
Search Regex vtrunk
trunk 1.4.12 1.4.13 1.4.14 1.4.15 1.4.16 2.0 2.0.1 2.1 2.2 2.2.1 2.3 2.3.1 2.3.2 2.3.3 2.4 2.4.1 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.8 3.1 3.1.1 3.1.2 3.2 3.3 3.3.0 3.3.1 3.4 3.4.1 3.4.2
search-regex / includes / modifier / value / class-member-value.php
search-regex / includes / modifier / value Last commit date
class-date-value.php 5 months ago class-integer-value.php 5 months ago class-key-value.php 5 months ago class-member-value.php 5 months ago class-string-value.php 5 months ago
class-member-value.php
172 lines
1 <?php
2
3 namespace SearchRegex\Modifier\Value;
4
5 use SearchRegex\Schema;
6 use SearchRegex\Search;
7 use SearchRegex\Source;
8 use SearchRegex\Modifier;
9 use SearchRegex\Context;
10
11 /**
12 * Modifier for member columns
13 *
14 * @phpstan-type MemberModifierOption array{
15 * column?: string,
16 * operation?: 'replace'|'include'|'exclude',
17 * values?: array<int, mixed>
18 * }
19 */
20 class Member_Value extends Modifier\Modifier {
21 /**
22 * Array of values
23 *
24 * @var array<string|integer>
25 */
26 private array $values = [];
27
28 /**
29 * Constructor
30 *
31 * @param MemberModifierOption $option Member modification options.
32 * @param Schema\Column $schema Schema.
33 */
34 public function __construct( $option, Schema\Column $schema ) {
35 parent::__construct( $option, $schema );
36
37 $this->operation = 'replace';
38 if ( isset( $option['operation'] ) && in_array( $option['operation'], [ 'replace', 'include', 'exclude' ], true ) ) {
39 $this->operation = $option['operation'];
40 }
41
42 if ( $schema->get_options() ) {
43 $this->values = [ $schema->get_options()[0]['value'] ];
44 }
45
46 // @phpstan-ignore booleanAnd.rightAlwaysTrue
47 if ( isset( $option['values'] ) && is_array( $option['values'] ) ) {
48 $this->values = array_map( [ $this, 'get_value' ], $option['values'] );
49 }
50 }
51
52 public function to_json() {
53 $parent_json = parent::to_json();
54
55 return [
56 'column' => $parent_json['column'],
57 'source' => $parent_json['source'],
58 'operation' => $this->operation,
59 'values' => $this->values,
60 ];
61 }
62
63 /**
64 * Determine if it's a numeric of string value and get the appropriate value.
65 *
66 * @param string|integer $value Value.
67 * @return string|integer
68 */
69 private function get_value( $value ) {
70 if ( is_numeric( $value ) ) {
71 return intval( $value, 10 );
72 }
73
74 return $value;
75 }
76
77 /**
78 * Get the Match_Contexs for the values
79 *
80 * @param Source\Source $source Source.
81 * @param array<int, string|int> $values Values.
82 * @param class-string<Context\Context> $cb Function callback.
83 * @return list<Context\Context>
84 */
85 private function get_contexts( Source\Source $source, array $values, $cb ) {
86 $contexts = [];
87
88 foreach ( $values as $row_value ) {
89 $contexts[] = new $cb( $row_value, $source->convert_result_value( $this->schema, (string) $row_value ) );
90 }
91
92 return $contexts;
93 }
94
95 /**
96 * Get all values that we are not matching
97 *
98 * @param array<string|int> $action_values Values to check for.
99 * @param array<string|int> $column_values Values to check against.
100 * @return array{array<string|int>, array<string|int>}
101 */
102 private function get_exclude( $action_values, $column_values ) {
103 // Remove any that we're excluding
104 $same = array_intersect( $action_values, $column_values );
105 $delete = array_diff( $column_values, $action_values );
106
107 return [ $same, $delete ];
108 }
109
110 /**
111 * Get all values that we are matching
112 *
113 * @param array<string|integer> $action_values Values to check for.
114 * @param array<string|integer> $column_values Values to check against.
115 * @return array<string|integer>
116 */
117 private function get_include( $action_values, $column_values ) {
118 return array_diff( $action_values, $column_values );
119 }
120
121 public function perform( $row_id, $row_value, Source\Source $source, Search\Column $column, array $raw, $save_mode ) {
122 $action_values = $this->values;
123 $column_values = array_map(
124 fn( $context ) => $this->get_value( $context instanceof Context\Type\Value ? $context->get_value() : '' ),
125 $column->get_contexts()
126 );
127 $column_values = array_values( array_filter( $column_values, fn( $v ) => $v !== '' && $v !== '0' ) );
128
129 // Now create a set of arrays for members that are the same, added, deleted, and updated
130 $same = $column_values;
131 $add = [];
132 $delete = [];
133 $updated = [];
134
135 if ( $this->operation === 'replace' ) {
136 $same = array_intersect( $action_values, $column_values );
137 $delete = array_diff( $column_values, $action_values );
138 $add = array_diff( $action_values, $column_values );
139
140 // Which one of the 'deletes' can 'replace' an 'add'
141 foreach ( $add as $pos => $value ) {
142 if ( count( $delete ) > 0 ) {
143 $context = new Context\Type\Replace( $delete[0], $source->convert_result_value( $this->schema, (string) $delete[0] ) );
144 $context->set_replacement( (string) $value, $source->convert_result_value( $this->schema, (string) $value ) );
145 $updated[] = $context;
146
147 unset( $add[ $pos ] );
148 unset( $delete[0] );
149 $delete = array_values( $delete );
150 }
151 }
152
153 $add = array_values( $add );
154 } elseif ( $this->operation === 'exclude' ) {
155 [$same, $delete] = $this->get_exclude( $action_values, $column_values );
156 } elseif ( $this->operation === 'include' ) {
157 $add = $this->get_include( $action_values, $column_values );
158 }
159
160 // Replace existing contexts with the new ones
161 $contexts = [
162 ...$this->get_contexts( $source, $same, \SearchRegex\Context\Type\Value::class ),
163 ...$this->get_contexts( $source, $add, \SearchRegex\Context\Type\Add::class ),
164 ...$this->get_contexts( $source, $delete, \SearchRegex\Context\Type\Delete::class ),
165 ...$updated,
166 ];
167
168 $column->set_contexts( $contexts );
169 return $column;
170 }
171 }
172