Completed
Push — master ( 6cab57...287bd7 )
by Sam
02:34
created

src/DB/Record.php (1 issue)

Check that method contracts are obeyed on return types with regard to error conditions

Best Practice Comprehensibility Minor

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * This file contains only a single class.
4
 *
5
 * @file
6
 * @package Tabulate
7
 */
8
9
namespace WordPress\Tabulate\DB;
10
11
/**
12
 * A record is a single row from a database table.
13
 */
14
class Record {
15
16
	/**
17
	 * The table that this record belongs to.
18
	 *
19
	 * @var Table
20
	 */
21
	protected $table;
22
23
	/**
24
	 * The raw data of this database row.
25
	 *
26
	 * @var \stdClass
27
	 */
28
	protected $data;
29
30
	/**
31
	 * The suffix that is added to foreign keys when we want to get their
32
	 * 'title' value instead of their raw integer (or whatever) form.
33
	 */
34
	const FKTITLE = 'FKTITLE';
35
36
	/**
37
	 * Create a new Record object.
38
	 *
39
	 * @param Table $table The table object.
40
	 * @param array $data The data of this record.
41
	 */
42
	public function __construct( $table, $data = array() ) {
43
		$this->table = $table;
44
		$this->data = (object) $data;
45
	}
46
47
	/**
48
	 * Magic method to set one item in the data object.
49
	 *
50
	 * @param string $name The name of the column.
51
	 * @param mixed  $value The value to set.
52
	 */
53
	public function __set( $name, $value ) {
54
		$this->data->{$name} = $value;
55
	}
56
57
	/**
58
	 * Set multiple columns' values.
59
	 *
60
	 * @param mixed[] $data An array of column names to data values.
61
	 */
62
	public function set_multiple( $data ) {
63
		if ( ! is_array( $data ) ) {
64
			return;
65
		}
66
		foreach ( $data as $col => $datum ) {
67
			$this->$col = $datum;
68
		}
69
	}
70
71
	/**
72
	 * Get a column's value. If suffixed with 'FKTITLE', then get the title of
73
	 * the foreign record (where applicable).
74
	 *
75
	 * @param string   $name The column name.
76
	 * @param mixed [] $args Parameter not used.
77
	 * @return string|boolean
78
	 * @throws Exception If any arguments are passed (as there should never be any).
79
	 */
80
	public function __call( $name, $args ) {
81
		if ( ! empty( $args ) ) {
82
			throw new Exception( 'Record::colname() functions take no arguments.' );
83
		}
84
85
		// Foreign key 'title' values.
86
		$use_title = substr( $name, -strlen( self::FKTITLE ) ) === self::FKTITLE;
87
		if ( $use_title ) {
88
			$name = substr( $name, 0, -strlen( self::FKTITLE ) );
89
			$col = $this->get_col( $name );
90
			if ( $col->is_foreign_key() && ! empty( $this->data->$name ) ) {
91
				$referenced_table = $col->get_referenced_table();
92
				$fk_record = $referenced_table->get_record( $this->data->$name );
93
				$fk_title_col = $referenced_table->get_title_column();
94
				$fk_title_col_name = $fk_title_col->get_name();
95
				if ( $fk_title_col->is_foreign_key() ) {
96
					// Use title if the FK's title column is also an FK.
97
					$fk_title_col_name .= self::FKTITLE;
98
				}
99
				return $fk_record->$fk_title_col_name();
100
			}
101
		}
102
		$col = $this->get_col( $name );
103
104
		// Booleans.
105
		if ( $col->is_boolean() ) {
106
			// Numbers are fetched from the DB as strings.
107
			if ( '1' === $this->data->$name ) {
108
				return true;
109
			} elseif ( '0' === $this->data->$name ) {
110
				return false;
111
			} else {
112
				return null;
113
			}
114
		}
115
116
		// Standard column values.
117
		if ( isset( $this->data->$name ) ) {
118
			return $this->data->$name;
119
		}
120
	}
121
122
	/**
123
	 * Get a column of this record's table, optionally throwing an Exception if
124
	 * it doesn't exist.
125
	 *
126
	 * @param string  $name The name of the column.
127
	 * @param boolean $required True if this should throw an Exception.
128
	 * @return \WordPress\Tabulate\DB\Column The column.
129
	 * @throws \Exception If the column named doesn't exist.
130
	 */
131
	protected function get_col( $name, $required = true ) {
132
		$col = $this->table->get_column( $name );
0 ignored issues
show
Comprehensibility Best Practice introduced by
The expression $this->table->get_column($name); of type WordPress\Tabulate\DB\Column|false adds false to the return on line 136 which is incompatible with the return type documented by WordPress\Tabulate\DB\Record::get_col of type WordPress\Tabulate\DB\Column. It seems like you forgot to handle an error condition.
Loading history...
133
		if ( $required && false === $col ) {
134
			throw new \Exception( "Unable to get column $name on table " . $this->table->get_name() );
135
		}
136
		return $col;
137
	}
138
139
	/**
140
	 * Get a string representation of this record.
141
	 *
142
	 * @return string
143
	 */
144
	public function __toString() {
145
		return join( ', ', $this->data );
146
	}
147
148
	/**
149
	 * Get the value of this record's primary key, or false if it doesn't have
150
	 * one.
151
	 *
152
	 * @return string|false
153
	 */
154
	public function get_primary_key() {
155
		if ( $this->table->get_pk_column() ) {
156
			$pk_col_name = $this->table->get_pk_column()->get_name();
157
			if ( isset( $this->data->$pk_col_name ) ) {
158
				return $this->data->$pk_col_name;
159
			}
160
		}
161
		return false;
162
	}
163
164
	/**
165
	 * Get the value of this Record's title column.
166
	 *
167
	 * @return string
168
	 */
169
	public function get_title() {
170
		$title_col = $this->table->get_title_column();
171
		if ( $title_col !== $this->table->get_pk_column() ) {
172
			$title_col_name = $title_col->get_name();
173
			return $this->data->$title_col_name;
174
		} else {
175
			$title_parts = array();
176
			foreach ( $this->table->get_columns() as $col ) {
177
				$col_name = $col->get_name() . self::FKTITLE;
178
				$title_parts[] = $this->$col_name();
179
			}
180
			return '[ ' . join( ' | ', $title_parts ) . ' ]';
181
		}
182
	}
183
184
	/**
185
	 * Get the record that is referenced by this one from the column given.
186
	 *
187
	 * @param string $column_name The name of the column.
188
	 * @return boolean|\WordPress\Tabulate\DB\Record
189
	 */
190
	public function get_referenced_record( $column_name ) {
191
		if ( ! isset( $this->data->$column_name ) ) {
192
			return false;
193
		}
194
		return $this->table
195
			->get_column( $column_name )
196
			->get_referenced_table()
197
			->get_record( $this->data->$column_name );
198
	}
199
200
	/**
201
	 * Get a list of records that reference this record in one of their columns.
202
	 *
203
	 * @param string|\WordPress\Tabulate\DB\Table  $foreign_table The foreign table.
204
	 * @param string|\WordPress\Tabulate\DB\Column $foreign_column The column in the foreign table that references this record's table.
205
	 * @param boolean                              $with_pagination Whether to only return the top N records.
206
	 * @return \WordPress\Tabulate\DB\Record[]
207
	 */
208
	public function get_referencing_records( $foreign_table, $foreign_column, $with_pagination = true ) {
209
		$foreign_table->reset_filters();
210
		$foreign_table->add_filter( $foreign_column, '=', $this->get_primary_key(), true );
211
		return $foreign_table->get_records( $with_pagination );
212
	}
213
214
	/**
215
	 * Get an Admin Area URL.
216
	 *
217
	 * @param string   $action The action.
218
	 * @param boolean  $include_ident Whether to include the record Primary Key.
219
	 * @param string[] $extra_params Other parameters to append to the URL.
220
	 * @return string The URL.
221
	 */
222
	public function get_url( $action = 'index', $include_ident = true, $extra_params = false ) {
223
		$params = array(
224
			'page' => 'tabulate',
225
			'controller' => 'record',
226
			'action' => $action,
227
			'table' => $this->table->get_name(),
228
		);
229
		if ( $include_ident && false !== $this->get_primary_key() ) {
230
			$params['ident'] = $this->get_primary_key();
231
		}
232
		if ( is_array( $extra_params ) ) {
233
			$params = array_merge( $params, $extra_params );
234
		}
235
		return admin_url( 'admin.php?' . http_build_query( $params ) );
236
	}
237
238
	/**
239
	 * Get most recent changes.
240
	 *
241
	 * @return string[]
242
	 */
243
	public function get_changes() {
244
		$wpdb = $this->table->get_database()->get_wpdb();
245
		$sql = "SELECT cs.id AS changeset_id, c.id AS change_id, date_and_time, "
246
			. "user_nicename, table_name, record_ident, column_name, old_value, "
247
			. "new_value, comment "
248
			. "FROM " . ChangeTracker::changes_name() . " c "
249
			. "  JOIN " . ChangeTracker::changesets_name() . " cs ON (c.changeset_id=cs.id) "
250
			. "  JOIN {$wpdb->prefix}users u ON (u.ID=cs.user_id) "
251
			. "WHERE table_name = %s AND record_ident = %s"
252
			. "ORDER BY date_and_time DESC, cs.id DESC "
253
			. "LIMIT 15 ";
254
		$params = array( $this->table->get_name(), $this->get_primary_key() );
255
		return $wpdb->get_results( $wpdb->prepare( $sql, $params ) );
256
	}
257
}
258