PageRenderTime 52ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/includes/user/classes/class-cp-user-relationships.php

https://gitlab.com/dev73/clusterpress
PHP | 543 lines | 264 code | 85 blank | 194 comment | 60 complexity | 0d8b7a4acdbf4dcfe26d035b9df51152 MD5 | raw file
  1. <?php
  2. /**
  3. * ClusterPress User Relationships.
  4. *
  5. * @package ClusterPress\user\classes
  6. * @subpackage user-relationships
  7. *
  8. * @since 1.0.0
  9. */
  10. // Exit if accessed directly.
  11. defined( 'ABSPATH' ) || exit;
  12. /**
  13. * User content relationships class.
  14. *
  15. * @since 1.0.0
  16. */
  17. class CP_User_Relationships {
  18. /**
  19. * Name of the relationships table.
  20. *
  21. * @var string
  22. */
  23. public $table = '';
  24. /**
  25. * Available relationship types.
  26. *
  27. * @var array
  28. */
  29. public $types = array();
  30. /**
  31. * Constructor
  32. *
  33. * @since 1.0.0
  34. *
  35. * @param array $args {
  36. * An array of arguments.
  37. * @type string $cluster The Cluster ID.
  38. * @type array $relationship The relationship arguments.
  39. * }
  40. */
  41. public function __construct( $args = array() ) {
  42. global $wpdb;
  43. $this->table = $wpdb->base_prefix . 'cp_relationships';
  44. // Let's make sure we'll be the first to register the last_active type :)
  45. if ( ! empty( $args['cluster'] ) && ! empty( $args['relationship'] ) ) {
  46. $this->register_type( $args['cluster'], $args['relationship'] );
  47. }
  48. }
  49. /**
  50. * Register multiple relationship types, making sure they are unique
  51. *
  52. * @since 1.0.0
  53. *
  54. * @param string $cluster The cluster ID
  55. * @param array $types The list of relationships type to add
  56. * @return bool|array True if the relationship was registered. An array
  57. * containing the unregistered types otherwise.
  58. */
  59. public function register_types( $cluster, $types = array() ) {
  60. $errors = array();
  61. foreach ( (array) $types as $type ) {
  62. if ( ! $this->register_type( $cluster, $type ) ) {
  63. $errors[] = $type;
  64. }
  65. }
  66. if ( empty( $errors ) ) {
  67. return true;
  68. }
  69. return $errors;
  70. }
  71. /**
  72. * Register a single relationship type, making sure it is unique
  73. *
  74. * @since 1.0.0
  75. *
  76. * @param string $cluster The cluster ID
  77. * @param array $type {
  78. * An array of arguments.
  79. * @type string $name The name of the relationship.
  80. * @type string $cluster The Cluster ID.
  81. * @type string $singular The singular label for the relationship.
  82. * @type string $plural The plural label for the relationship.
  83. * @type string $description The description of the relationship.
  84. * @type array $format_callback An associative array containing strings to format the output.
  85. * }
  86. * @return bool True if the relationship was registered. False otherwise.
  87. */
  88. public function register_type( $cluster, $type = array() ) {
  89. if ( empty( $type['name'] ) ) {
  90. return false;
  91. }
  92. $type['name'] = sanitize_key( $type['name'] );
  93. if ( isset( $this->types[ $type['name'] ] ) ) {
  94. return false;
  95. }
  96. $this->types[ $type['name'] ] = (object) array_intersect_key( $type, array(
  97. 'name' => '',
  98. 'cluster' => $cluster,
  99. 'singular' => '',
  100. 'plural' => '',
  101. 'description' => '',
  102. 'format_callback' => '',
  103. ) );
  104. return true;
  105. }
  106. /**
  107. * Unregister a Relationship Type.
  108. *
  109. * @since 1.0.0
  110. *
  111. * @param string $name The name of the relationship type.
  112. * @return bool True if the relationship is unregistered. False Otherwise.
  113. */
  114. public function unregister_type( $name = '' ) {
  115. if ( empty( $name ) || ! $this->get_relationship_object( $name ) ) {
  116. return false;
  117. }
  118. unset( $this->types[ $name ] );
  119. return true;
  120. }
  121. /**
  122. * Get a Relationship Type object.
  123. *
  124. * @since 1.0.0
  125. *
  126. * @param string $name The name of the relationship type.
  127. * @return false|object The relationship object if set. False Otherwise.
  128. */
  129. public function get_relationship_object( $name = '' ) {
  130. if ( empty( $name ) ) {
  131. return false;
  132. }
  133. $name = sanitize_key( $name );
  134. if ( ! isset( $this->types[ $name ] ) ) {
  135. return false;
  136. }
  137. return $this->types[ $name ];
  138. }
  139. /**
  140. * Add a relationship entry for the user.
  141. *
  142. * @since 1.0.0
  143. *
  144. * @param array $args The query arguments {
  145. * An array of arguments
  146. * @type int $user_id The user ID.
  147. * @type int $primary_id The primary content ID (eg: Post).
  148. * @type int $secondary_id The secondary content ID (eg: Site).
  149. * @type string $name The name of the relationship.
  150. * @type string $date The date the relationship was saved.
  151. * }
  152. * @return bool True on success, false otherwise
  153. */
  154. public function add( $args = array() ) {
  155. global $wpdb;
  156. $r = array_intersect_key( $args, array(
  157. 'user_id' => 0,
  158. 'primary_id' => 0,
  159. 'secondary_id' => 0,
  160. 'name' => '',
  161. 'date' => '',
  162. ) );
  163. if ( empty( $r['user_id'] ) || empty( $r['primary_id'] ) || empty( $r['name'] ) ) {
  164. return false;
  165. }
  166. if ( ! isset( $this->types[ $r['name'] ] ) ) {
  167. return false;
  168. }
  169. $added = $wpdb->insert(
  170. $this->table,
  171. $r
  172. );
  173. if ( ! empty( $args['last_insert_id'] ) ) {
  174. return (int) $wpdb->insert_id;
  175. }
  176. return (bool) $added;
  177. }
  178. /**
  179. * Update a relationship entry for the user.
  180. *
  181. * @since 1.0.0
  182. *
  183. * @param array $args The fields to update {
  184. * An array of arguments
  185. * @type int $secondary_id The secondary content ID (eg: Site).
  186. * @type string $date The date the relationship was saved.
  187. * }
  188. * @param array $where The query to match {
  189. * An array of arguments
  190. * @type int $id The relationship ID.
  191. * @type int $user_id The user ID.
  192. * @type int $primary_id The primary content ID (eg: Post).
  193. * @type string $name The name of the relationship.
  194. * @type string $date The date the relationship was saved.
  195. * }
  196. * @return bool True on success, false otherwise
  197. */
  198. public function update( $args = array(), $where = array() ) {
  199. global $wpdb;
  200. $r = array_intersect_key( $args, array(
  201. 'secondary_id' => null,
  202. 'date' => null,
  203. ) );
  204. $w = array_intersect_key( $where, array(
  205. 'id' => 0,
  206. 'user_id' => 0,
  207. 'primary_id' => 0,
  208. 'name' => '',
  209. 'date' => '',
  210. ) );
  211. if ( empty( $w['id'] ) && ( empty( $w['user_id'] ) || empty( $w['name'] ) ) && ( empty( $w['primary_id'] ) || empty( $w['name'] ) ) ) {
  212. return false;
  213. }
  214. if ( empty( $w['name'] ) || ! isset( $this->types[ $w['name'] ] ) ) {
  215. return false;
  216. }
  217. $updated = $wpdb->update(
  218. $this->table,
  219. $r,
  220. $w
  221. );
  222. return (bool) $updated;
  223. }
  224. /**
  225. * Get the last active date of a user.
  226. *
  227. * @since 1.0.0
  228. *
  229. * @param int $user_id The user ID.
  230. * @return string $value The last active date of the user.
  231. */
  232. public function get_last_active( $user_id = 0 ) {
  233. global $wpdb;
  234. return $wpdb->get_var( $wpdb->prepare( "SELECT date FROM {$this->table} WHERE name = 'last_active' AND user_id = %d", $user_id ) );
  235. }
  236. /**
  237. * Get the last active date for a specified list of user ids.
  238. *
  239. * @since 1.0.0
  240. *
  241. * @param array $user_ids The list of user IDs.
  242. * @return array The list of last active date by user_id.
  243. */
  244. public function get_last_actives( $user_ids = array() ) {
  245. global $wpdb;
  246. $in = $this->set_clause( 'user_id', $user_ids );
  247. return $wpdb->get_results( "SELECT user_id, date FROM {$this->table} WHERE {$in} AND name = 'last_active'" );
  248. }
  249. /**
  250. * Set Where clauses of the query
  251. *
  252. * @since 1.0.0
  253. *
  254. * @param string $in_field Name of the field
  255. * @param array $in List of object/item IDs
  256. * @return string The where clause.
  257. */
  258. public function set_clause( $field_name = '', $in = array() ) {
  259. global $wpdb;
  260. if ( empty( $field_name ) || empty( $in ) ) {
  261. return;
  262. }
  263. if ( ! is_array( $in ) ) {
  264. $in = (array) $in;
  265. }
  266. $count = count( $in );
  267. if ( $field_name === 'date' || $field_name === 'name' ) {
  268. if ( 1 === $count ) {
  269. $s = reset( $in );
  270. if ( is_null( $s ) || 'null' === $s ) {
  271. return "{$field_name} IS NULL";
  272. } else {
  273. return $wpdb->prepare( "{$field_name} = %s", $s );
  274. }
  275. } else {
  276. $in = array_map( 'esc_sql', $in );
  277. $in = '"' . join( '","', $in ) . '"';
  278. return "{$field_name} IN ({$in})";
  279. }
  280. } else {
  281. if ( 1 === $count ) {
  282. $d = reset( $in );
  283. if ( is_null( $d ) || 'null' === $d ) {
  284. return "{$field_name} IS NULL";
  285. } elseif ( 'exclude' === $field_name ) {
  286. return $wpdb->prepare( "id != %d", $d );
  287. } else {
  288. return $wpdb->prepare( "{$field_name} = %d", $d );
  289. }
  290. } else {
  291. $in = wp_parse_id_list( $in );
  292. $in = join( ',', $in );
  293. $operator = 'IN';
  294. $field = $field_name;
  295. if ( 'exclude' === $field_name ) {
  296. $operator = 'NOT IN';
  297. $field = 'id';
  298. }
  299. return "{$field} {$operator} ({$in})";
  300. }
  301. }
  302. }
  303. /**
  304. * Get a column or columns values matching the clauses
  305. *
  306. * @since 1.0.0
  307. *
  308. * @param string|array $column The name of the column(s) to get in the results.
  309. * @param array $args The query arguments {
  310. * An array of arguments
  311. * @type int $user_id The user ID.
  312. * @type int $primary_id The primary content ID (eg: Post).
  313. * @type int $secondary_id The secondary content ID (eg: Site).
  314. * @type string $name The name of the relationship.
  315. * @type string $date The date the relationship was saved.
  316. * @type array $exclude A list of relationship IDs to exclude from results.
  317. * }
  318. * @param int|false $offset Specify the offset or false to get any.
  319. * @param int|false $number Specify the number to limit results or false to get any.
  320. * @param bool $desc Order ASC by ID if false. Order DESC by ID otherwise.
  321. * @return array List of values
  322. */
  323. public function get( $column = '', $args = array(), $offset = false, $number = false, $desc = false ) {
  324. global $wpdb;
  325. if ( empty( $column ) || empty( $args ) ) {
  326. return false;
  327. }
  328. $type = 'col';
  329. if ( is_array( $column ) ) {
  330. $type = 'results';
  331. $column = join( ', ', $column );
  332. }
  333. $r = array_intersect_key( (array) $args, array(
  334. 'id' => 0,
  335. 'user_id' => 0,
  336. 'primary_id' => 0,
  337. 'secondary_id' => 0,
  338. 'name' => '',
  339. 'date' => '',
  340. 'exclude' => array(),
  341. ) );
  342. $sql = array(
  343. 'select' => sprintf( 'SELECT %s FROM %s', esc_sql( $column ), $this->table ),
  344. 'where' => array(),
  345. );
  346. if ( $desc ) {
  347. $sql['order'] = 'ORDER BY id DESC';
  348. }
  349. if ( false !== $offset && false !== $number ) {
  350. $sql['limit'] = $wpdb->prepare( 'LIMIT %d, %d', $offset, $number );
  351. }
  352. foreach ( $r as $field_name => $in ) {
  353. $sql['where'][] = $this->set_clause( $field_name, $in );
  354. }
  355. $sql['where'] = 'WHERE ' . join( ' AND ', $sql['where'] );
  356. if ( 'col' === $type ) {
  357. return $wpdb->get_col( join( ' ', $sql ) );
  358. }
  359. return $wpdb->get_results( join( ' ', $sql ) );
  360. }
  361. /**
  362. * Get the count of a specific relationship query.
  363. *
  364. * @since 1.0.0
  365. *
  366. * @param string|array $column The name of the column to count.
  367. * @param array $args The query arguments {
  368. * An array of arguments
  369. * @type int $user_id The user ID.
  370. * @type int $primary_id The primary content ID (eg: Post).
  371. * @type int $secondary_id The secondary content ID (eg: Site).
  372. * @type string $name The name of the relationship.
  373. * @type string $date The date the relationship was saved.
  374. * @type array $exclude A list of relationship IDs to exclude from results.
  375. * }
  376. * @param false|string $group_by False to not group the count, one of these field names to
  377. * group the count: 'user_id', 'primary_id', 'secondary_id'.
  378. * @return array|int List of counted values grouped by a specific field or the query count.
  379. */
  380. public function count( $column = '', $args = array(), $group_by = false ) {
  381. global $wpdb;
  382. if ( empty( $column ) || empty( $args ) ) {
  383. return false;
  384. }
  385. if ( is_array( $column ) ) {
  386. $column = reset( $column );
  387. }
  388. $r = array_intersect_key( (array) $args, array(
  389. 'user_id' => 0,
  390. 'primary_id' => 0,
  391. 'secondary_id' => 0,
  392. 'name' => '',
  393. 'date' => '',
  394. 'exclude' => array(),
  395. ) );
  396. $sql = array(
  397. 'select' => sprintf( 'SELECT count( %s ) FROM %s', esc_sql( $column ), $this->table ),
  398. 'where' => array(),
  399. );
  400. foreach ( $r as $field_name => $in ) {
  401. $sql['where'][] = $this->set_clause( $field_name, $in );
  402. }
  403. $sql['where'] = 'WHERE ' . join( ' AND ', $sql['where'] );
  404. if ( $group_by && in_array( $group_by, array( 'user_id', 'primary_id', 'secondary_id' ) ) ) {
  405. $sql['select'] = sprintf( 'SELECT DISTINCT %s, count( %s ) as count FROM %s', esc_sql( $group_by ), esc_sql( $column ), $this->table );
  406. $sql['groupby'] = sprintf( 'GROUP BY %s', esc_sql( $group_by ) );
  407. $sql['orderby'] = 'ORDER BY count DESC';
  408. return $wpdb->get_results( join( ' ', $sql ), OBJECT_K );
  409. }
  410. return $wpdb->get_var( join( ' ', $sql ) );
  411. }
  412. /**
  413. * Remove item ids for a given object id or vice versa
  414. *
  415. * @since 1.0.0
  416. *
  417. * @param array $args The query arguments {
  418. * An array of arguments
  419. * @type int $id The relationship ID.
  420. * @type int $user_id The user ID.
  421. * @type int $primary_id The primary content ID (eg: Post).
  422. * @type int $secondary_id The secondary content ID (eg: Site).
  423. * @type string $name The name of the relationship.
  424. * @type string $date The date the relationship was saved.
  425. * }
  426. * @return array List of item ids or object ids
  427. */
  428. public function remove( $args = array() ) {
  429. global $wpdb;
  430. if ( empty( $args ) ) {
  431. return false;
  432. }
  433. $r = array_intersect_key( (array) $args, array(
  434. 'id' => 0,
  435. 'user_id' => 0,
  436. 'primary_id' => 0,
  437. 'secondary_id' => 0,
  438. 'name' => '',
  439. 'date' => '',
  440. ) );
  441. if ( empty( $r['id'] ) ) {
  442. if ( empty( $r['name'] ) || ! isset( $this->types[ $r['name'] ] ) ) {
  443. return false;
  444. }
  445. if ( empty( $r['user_id'] ) && empty( $r['primary_id'] ) && empty( $r['secondary_id'] ) ) {
  446. return false;
  447. }
  448. }
  449. $sql = array(
  450. 'select' => sprintf( 'DELETE FROM %s', $this->table ),
  451. );
  452. foreach ( $r as $field_name => $in ) {
  453. $sql['where'][] = $this->set_clause( $field_name, $in );
  454. }
  455. $sql['where'] = 'WHERE ' . join( ' AND ', $sql['where'] );
  456. return (bool) $wpdb->query( join( ' ', $sql ) );
  457. }
  458. }