'%s', 'country' => '%s', 'label' => '%s', 'status' => '%s', 'credentials' => '%s', 'tokens' => '%s', 'account_name' => '%s', 'is_business' => '%d', ); /** * Full table name. */ public static function table(): string { global $wpdb; return $wpdb->prefix . self::TABLE; } /** * CREATE TABLE statement for dbDelta. credentials and tokens hold sealed (encrypted) JSON. * * @param string $charset_collate Charset/collation clause. */ public static function schema( string $charset_collate ): string { $table = self::table(); return "CREATE TABLE {$table} ( id bigint(20) unsigned NOT NULL AUTO_INCREMENT, channel varchar(20) NOT NULL DEFAULT '', country char(2) NOT NULL DEFAULT '', label varchar(100) NOT NULL DEFAULT '', status varchar(20) NOT NULL DEFAULT 'active', credentials text NULL, tokens text NULL, account_name varchar(255) NOT NULL DEFAULT '', is_business tinyint(1) NOT NULL DEFAULT 0, created_at datetime NOT NULL, updated_at datetime NOT NULL, PRIMARY KEY (id), KEY channel (channel) ) {$charset_collate};"; } /** * Validates and completes the fields of a new account (pure). * * @param array $fields Fields. * @param string $now Current time (Y-m-d H:i:s, UTC). * @throws InvalidArgumentException On missing or invalid data. */ public static function prepare_insert( array $fields, string $now ): array { $data = self::prepare_update( $fields ); if ( empty( $data['channel'] ) ) { throw new InvalidArgumentException( 'An account needs a channel.' ); } return array_merge( array( 'country' => '', 'status' => 'active', ), $data, array( 'created_at' => $now, 'updated_at' => $now, ) ); } /** * Keeps only writable columns and validates them (pure). * * @param array $fields Fields. * @throws InvalidArgumentException On an invalid country. */ public static function prepare_update( array $fields ): array { $data = array_intersect_key( $fields, self::WRITABLE ); if ( isset( $data['country'] ) ) { $data['country'] = strtoupper( trim( (string) $data['country'] ) ); if ( '' !== $data['country'] && ! preg_match( '/^[A-Z]{2}$/', $data['country'] ) ) { throw new InvalidArgumentException( 'The country must be a two-letter code.' ); } } if ( isset( $data['is_business'] ) ) { $data['is_business'] = $data['is_business'] ? 1 : 0; } return $data; } /** * One account row. * * @param int $id Account ID. */ public function find( int $id ): ?array { global $wpdb; $table = self::table(); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- own table. $row = $wpdb->get_row( $wpdb->prepare( 'SELECT * FROM %i WHERE id = %d', $table, $id ), ARRAY_A ); return is_array( $row ) ? $row : null; } /** * Account rows, oldest first. * * @param string|null $channel Only this channel. * @return array */ public function all( ?string $channel = null ): array { global $wpdb; $table = self::table(); if ( null === $channel ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- own table. $rows = (array) $wpdb->get_results( $wpdb->prepare( 'SELECT * FROM %i ORDER BY id', $table ), ARRAY_A ); } else { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- own table. $rows = (array) $wpdb->get_results( $wpdb->prepare( 'SELECT * FROM %i WHERE channel = %s ORDER BY id', $table, $channel ), ARRAY_A ); } return array_values( $rows ); } /** * Inserts an account. * * @param array $fields Fields. */ public function insert( array $fields ): int { global $wpdb; $data = self::prepare_insert( $fields, gmdate( 'Y-m-d H:i:s' ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- own table. $wpdb->insert( self::table(), $data ); return (int) $wpdb->insert_id; } /** * Updates an account. * * @param int $id Account ID. * @param array $fields Fields. */ public function update( int $id, array $fields ): void { global $wpdb; $data = self::prepare_update( $fields ); if ( ! $data ) { return; } $data['updated_at'] = gmdate( 'Y-m-d H:i:s' ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- own table. $wpdb->update( self::table(), $data, array( 'id' => $id ) ); } /** * Deletes an account. * * @param int $id Account ID. */ public function delete( int $id ): void { global $wpdb; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- own table. $wpdb->delete( self::table(), array( 'id' => $id ), array( '%d' ) ); } }