[], 'names' => [], 'filter' => 'all', 'searchTerm' => '', 'additionalFilters' => [], ], array_filter( $args ) ); $names = array_map( [ aioseo()->helpers, 'toLowerCase' ], array_unique( $args['names'] ) ); $filter = $args['filter']; $searchTerm = sanitize_text_field( $args['searchTerm'] ); $chosenGroup = $args['additionalFilters']['group'] ?? 'all'; $query = aioseo()->core->db->start( 'aioseo_search_statistics_keywords as k' ) ->select( 'k.id, k.name, k.favorited, k.created, GROUP_CONCAT(g.id) as groupIds, GROUP_CONCAT(g.name) as groupNames' ) ->join( 'aioseo_search_statistics_keyword_relationships as r', 'k.id = r.keyword_id', 'LEFT' ) ->join( 'aioseo_search_statistics_keyword_groups as g', 'r.keyword_group_id = g.id', 'LEFT' ) ->groupBy( 'k.id' ) ->limit( 100 ); if ( ! empty( $args['ids'] ) ) { $query->whereIn( 'k.id', $args['ids'] ); } if ( ! empty( $names ) ) { $query->whereIn( 'k.name', $names ); } if ( 'favorited' === $filter ) { $query->where( 'k.favorited', 1 ); } if ( $searchTerm ) { $query->whereLike( 'k.name', '%' . $searchTerm . '%', true ); } if ( 'all' !== $chosenGroup ) { $query->where( 'g.id', $chosenGroup ); } $rows = $query->run()->result(); foreach ( $rows as $row ) { $row->groups = []; if ( ! empty( $row->groupIds ) && ! empty( $row->groupNames ) ) { // The groupIds and groupNames are comma separated strings, because of the GROUP_CONCAT function above. $groupIds = explode( ',', $row->groupIds ); $groupNames = explode( ',', $row->groupNames ); foreach ( $groupIds as $key => $groupId ) { $data = (object) [ 'id' => $groupId, 'name' => $groupNames[ $key ], 'value' => $groupId, // Make it easier to use Vue core components in the UI. 'label' => $groupNames[ $key ], // Make it easier to use Vue core components in the UI. ]; KeywordGroup::parseGroup( $data ); $row->groups[] = json_decode( wp_json_encode( $data ), true ); } } unset( $row->groupIds, $row->groupNames ); } return [ 'rows' => array_values( $rows ), ]; } /** * Inserts multiple keywords into the database. * * @since 4.7.0 * * @param array[] $keywords The keywords to insert. * @throws \Exception * @return array|false The newly inserted keywords. False if no keywords were inserted. */ public static function bulkInsert( $keywords ) { $limit = aioseo()->searchStatistics->keywordRankTracker->getLicenseKeywordsLimit(); $currentCount = aioseo()->core->db->start( 'aioseo_search_statistics_keywords' )->count() + count( $keywords ); if ( $currentCount > $limit ) { throw new \Exception( // Translators: 1 - The limit of keywords allowed. sprintf( esc_html__( 'You have reached the maximum number of %s keywords allowed.', 'aioseo-pro' ), esc_html( $limit ) ) ); } $columns = [ 'name', 'favorited', 'created', 'updated' ]; $currentDate = gmdate( 'Y-m-d H:i:s' ); $rows = []; foreach ( $keywords as $keyword ) { $name = aioseo()->helpers->toLowerCase( sanitize_text_field( $keyword['name'] ) ); if ( ! empty( $name ) ) { $rows[] = [ $name, ! empty( $keyword['favorited'] ) ? 1 : 0, $currentDate, $currentDate ]; } } if ( empty( $rows ) ) { return false; } aioseo()->core->db->bulkInsert( 'aioseo_search_statistics_keywords', $columns, $rows, [ 'onDuplicate' => [ 'updated' ] ] ); $tableName = aioseo()->core->db->prefix . 'aioseo_search_statistics_keywords'; return aioseo()->core->db->start( $tableName, true ) ->select( 'id' ) ->where( 'created', $currentDate ) ->run() ->result(); } /** * Bulk update keywords. * * @since 4.7.1 * * @param array $ids The keyword IDs. * @param array $set The values to update. * @return void */ public static function bulkUpdate( $ids, $set ) { aioseo()->core->db ->update( 'aioseo_search_statistics_keywords' ) ->whereIn( 'id', $ids ) ->set( $set ) ->run(); } /** * Bulk delete keywords. * * @since 4.7.0 * * @param array $ids The keyword IDs. * @return int The number of rows affected. */ public static function bulkDelete( $ids ) { // Delete the keywords. aioseo()->core->db ->delete( 'aioseo_search_statistics_keywords' ) ->whereIn( 'id', $ids ) ->run(); $rowsAffected = max( absint( aioseo()->core->db->rowsAffected() ), 0 ); // Delete the keyword relationships. KeywordRelationship::bulkDeleteByKeyword( $ids ); return $rowsAffected; } /** * Updates the "favorited" field. * Abstract the process of favoriting to allow for the keyword to be added to our reserved group. * * @since 4.7.0 * * @param boolean $value The value to update the "favorited" field to. * @return void */ public function updateFavorited( $value ) { $this->favorited = $value; $this->save(); $favoriteGroup = KeywordGroup::getFavoriteGroup(); $groupRow = current( KeywordGroup::getByNames( [ $favoriteGroup['slug'] ] ) ); if ( empty( $groupRow ) ) { $groupRow = new KeywordGroup(); $groupRow->name = $favoriteGroup['slug']; $groupRow->save(); } if ( $value ) { KeywordRelationship::bulkInsert( [ $this->id ], [ $groupRow->id ] ); } else { KeywordRelationship::deleteByRelationship( $this->id, $groupRow->id ); } } /** * Parses a keyword row/object. * * @since 4.7.0 * * @param object $row The row to format. * @return void */ public static function parseKeyword( $row ) { $row->id = intval( $row->id ); $row->favorited = (bool) $row->favorited; $row->groups = array_values( $row->groups ?? [] ); $row->statistics = null; $row->value = $row->id; // Make it easier to use Vue core components in the UI. $row->label = $row->name; // Make it easier to use Vue core components in the UI. $row->tracking = true; // Used to determine if the keyword is being tracked under the "Edit Post" page. } }