searchStatistics->api->auth->isConnected() ) { return new \WP_REST_Response( [ 'success' => false, 'message' => 'Not connected to the Search Statistics service.' // Not shown to the user. ], 400 ); } try { $keywords = (array) ( $request->get_param( 'keywords' ) ?? [] ); $groupRows = (array) ( $request->get_param( 'groups' ) ?? [] ); $keywords = array_map( 'sanitize_text_field', $keywords ); $keywords = array_filter( array_unique( $keywords ) ); if ( empty( $keywords ) ) { return new \WP_REST_Response( [ 'success' => false ], 400 ); } $favoriteGroupRow = current( SearchStatisticsModels\KeywordGroup::getByNames( [ SearchStatisticsModels\KeywordGroup::getFavoriteGroup()['slug'] ] ) ); $favorited = $favoriteGroupRow ? ( in_array( $favoriteGroupRow->id, array_column( $groupRows, 'id' ), true ) ? 1 : 0 ) : 0; $keywords = array_map( function ( $k ) use ( $favorited ) { return [ 'name' => $k, 'favorited' => $favorited, ]; }, $keywords ); $keywordRows = SearchStatisticsModels\Keyword::bulkInsert( $keywords ); if ( ! empty( $groupRows ) ) { SearchStatisticsModels\KeywordRelationship::bulkInsert( array_column( $keywordRows, 'id' ), array_column( $groupRows, 'id' ) ); } return new \WP_REST_Response( [ 'success' => true, ], 200 ); } catch ( \Exception $e ) { return new \WP_REST_Response( [ 'success' => false, 'error' => $e->getMessage() ], 400 ); } } /** * Retrieves keywords. * * @since 4.7.0 * * @param \WP_REST_Request $request The REST Request. * @return \WP_REST_Response The response. */ public static function fetchKeywords( $request ) { if ( ! aioseo()->searchStatistics->api->auth->isConnected() ) { return new \WP_REST_Response( [ 'success' => false, 'message' => 'Not connected to the Search Statistics service.' // Not shown to the user. ], 400 ); } $params = $request->get_params(); if ( empty( $params['startDate'] ) || empty( $params['endDate'] ) ) { aioseo()->searchStatistics->stats->setDefaultDateRange(); $dateRange = aioseo()->searchStatistics->stats->getDateRange(); $params['startDate'] = $dateRange['start']; $params['endDate'] = $dateRange['end']; } $params['pageExpression'] = aioseo()->searchStatistics->helpers->buildPageExpression( $params['postId'] ?? 0 ); $filteredFormattedKeywords = aioseo()->searchStatistics->keywordRankTracker->getFormattedKeywords( $params ); $allFormattedKeywords = aioseo()->searchStatistics->keywordRankTracker->getFormattedKeywords( array_merge( $params, [ 'limit' => aioseo()->searchStatistics->keywordRankTracker->getLicenseKeywordsLimit(), 'filter' => 'all', 'searchTerm' => '', 'additionalFilters' => [], 'orderBy' => 'name', 'orderDir' => 'ASC', ] ) ); return new \WP_REST_Response( [ 'success' => true, 'count' => count( $allFormattedKeywords['all']['rows'] ), 'all' => $allFormattedKeywords['all'], 'paginated' => $filteredFormattedKeywords['paginated'], ], 200 ); } /** * Updates keywords. * * @since 4.7.0 * * @param \WP_REST_Request $request The REST Request. * @return \WP_REST_Response The response. */ public static function updateKeyword( $request ) { $id = (int) ( $request['id'] ?? 0 ); if ( empty( $id ) ) { return new \WP_REST_Response( [ 'success' => false ], 400 ); } $row = new SearchStatisticsModels\Keyword( $id ); if ( ! $row->exists() ) { return new \WP_REST_Response( [ 'success' => false ], 404 ); } $row->updateFavorited( boolval( $request->get_param( 'favorited' ) ) ); return new \WP_REST_Response( [ 'success' => true, ], 200 ); } /** * Deletes keywords. * * @since 4.7.0 * * @param \WP_REST_Request $request The REST Request. * @return \WP_REST_Response The response. */ public static function deleteKeywords( $request ) { if ( ! aioseo()->searchStatistics->api->auth->isConnected() ) { return new \WP_REST_Response( [ 'success' => false, 'message' => 'Not connected to the Search Statistics service.' // Not shown to the user. ], 400 ); } $params = $request->get_params(); $ids = (array) ( $params['ids'] ?? [] ); if ( empty( $ids ) ) { return new \WP_REST_Response( [ 'success' => false ], 400 ); } $rowsAffected = SearchStatisticsModels\Keyword::bulkDelete( $ids ); return new \WP_REST_Response( [ 'success' => true, 'rowsAffected' => $rowsAffected ], 200 ); } /** * Creates groups. * * @since 4.7.0 * * @param \WP_REST_Request $request The REST Request. * @return \WP_REST_Response The response. */ public static function insertGroups( $request ) { $groups = (array) ( $request->get_param( 'groups' ) ?? [] ); $keywordRows = (array) ( $request->get_param( 'keywords' ) ?? [] ); if ( empty( $groups ) ) { return new \WP_REST_Response( [ 'success' => false ], 400 ); } $favoriteGroup = SearchStatisticsModels\KeywordGroup::getFavoriteGroup(); // For now the UI accepts only one group at a time. Which means this loop runs once. foreach ( $groups as $name ) { if ( aioseo()->helpers->toLowercase( $name ) === $favoriteGroup['slug'] ) { return new \WP_REST_Response( [ 'success' => false, // Translators: 1 - Our reserved favorite group name. 'error' => sprintf( __( 'The group name "%s" is reserved.', 'aioseo-pro' ), $name ) ], 400 ); } $existingGroup = current( SearchStatisticsModels\KeywordGroup::getByNames( [ $name ] ) ); if ( empty( $existingGroup ) ) { $groupRow = new SearchStatisticsModels\KeywordGroup(); $groupRow->name = $name; $groupRow->save(); if ( $keywordRows ) { SearchStatisticsModels\KeywordRelationship::bulkInsert( array_column( $keywordRows, 'id' ), [ $groupRow->id ] ); } } } return new \WP_REST_Response( [ 'success' => true, ], 200 ); } /** * Retrieves groups. * * @since 4.7.0 * * @param \WP_REST_Request $request The REST Request. * @return \WP_REST_Response The response. */ public static function fetchGroups( $request ) { $params = $request->get_params(); $filteredFormattedGroups = aioseo()->searchStatistics->keywordRankTracker->getFormattedGroups( $params ); $allFormattedGroups = aioseo()->searchStatistics->keywordRankTracker->getFormattedGroups( [ 'limit' => 100, 'orderBy' => 'name', 'orderDir' => 'ASC', 'startDate' => $params['startDate'] ?? '', 'endDate' => $params['endDate'] ?? '', ] ); return new \WP_REST_Response( [ 'success' => true, 'count' => count( $allFormattedGroups['all']['rows'] ), 'all' => $allFormattedGroups['all'], 'paginated' => $filteredFormattedGroups['paginated'], ], 200 ); } /** * Updates a group. * * @since 4.7.0 * * @param \WP_REST_Request $request The REST Request. * @return \WP_REST_Response The response. */ public static function updateGroup( $request ) { $id = (int) ( $request['id'] ?? 0 ); if ( empty( $id ) ) { return new \WP_REST_Response( [ 'success' => false ], 400 ); } $row = new SearchStatisticsModels\KeywordGroup( $id ); if ( ! $row->exists() ) { return new \WP_REST_Response( [ 'success' => false ], 404 ); } $name = $row->sanitize( 'name', $request->get_param( 'name' ) ); $existingRow = current( SearchStatisticsModels\KeywordGroup::getByNames( [ $name ] ) ); if ( ! empty( $existingRow ) ) { return new \WP_REST_Response( [ 'success' => false, 'error' => __( 'The group name already exists.', 'aioseo-pro' ) ], 400 ); } $row->name = $name; $row->save(); return new \WP_REST_Response( [ 'success' => true, ], 200 ); } /** * Deletes groups. * * @since 4.7.0 * * @param \WP_REST_Request $request The REST Request. * @return \WP_REST_Response The response. */ public static function deleteGroups( $request ) { $params = $request->get_params(); $ids = (array) ( $params['ids'] ?? [] ); if ( empty( $ids ) ) { return new \WP_REST_Response( [ 'success' => false ], 400 ); } $rowsAffected = SearchStatisticsModels\KeywordGroup::bulkDelete( $ids ); return new \WP_REST_Response( [ 'success' => true, 'rowsAffected' => $rowsAffected ], 200 ); } /** * Updates relationships. * * @since 4.7.0 * * @param \WP_REST_Request $request The REST Request. * @return \WP_REST_Response The response. */ public static function updateRelationships( $request ) { $groupRows = (array) ( $request->get_param( 'groups' ) ?? [] ); $groupIds = array_column( $groupRows, 'id' ); $keywordRows = (array) ( $request->get_param( 'keywords' ) ?? [] ); $keywordIds = array_column( $keywordRows, 'id' ); // The keyword IDs are required, but the group IDs are optional. if ( empty( $keywordIds ) ) { return new \WP_REST_Response( [ 'success' => false ], 400 ); } // 1. Delete all existing relationships if only one keyword is provided, and it has no groups. This makes sure we preserve the keyword current groups. if ( 1 === count( $keywordRows ) && $groupIds ) { $keywordRow = current( $keywordRows ); $keywordGroups = array_column( $keywordRow['groups'], 'id' ); if ( array_diff( $keywordGroups, $groupIds ) ) { SearchStatisticsModels\KeywordRelationship::bulkDeleteByKeyword( $keywordIds ); } } // 2. Delete all existing relationships if no group IDs are provided. if ( ! $groupIds ) { SearchStatisticsModels\KeywordRelationship::bulkDeleteByKeyword( $keywordIds ); } // 3. Insert the new relationships if group IDs are provided. if ( $groupIds ) { SearchStatisticsModels\KeywordRelationship::bulkInsert( $keywordIds, $groupIds ); } // 4. Maybe individually set keywords as favorited in case they were also added to our reserved favorite group. $favoriteGroupRow = current( SearchStatisticsModels\KeywordGroup::getByNames( [ SearchStatisticsModels\KeywordGroup::getFavoriteGroup()['slug'] ] ) ); if ( ! empty( $favoriteGroupRow ) ) { SearchStatisticsModels\Keyword::bulkUpdate( $keywordIds, [ 'favorited' => 0 ] ); if ( in_array( $favoriteGroupRow->id, $groupIds, true ) ) { SearchStatisticsModels\Keyword::bulkUpdate( $keywordIds, [ 'favorited' => 1 ] ); } } return new \WP_REST_Response( [ 'success' => true ], 200 ); } /** * Retrieves the statistics for the keywords or groups. * * @since 4.7.0 * * @param \WP_REST_Request $request The REST Request. * @return \WP_REST_Response The response. */ public static function fetchStatistics( $request ) { try { if ( ! aioseo()->searchStatistics->api->auth->isConnected() ) { throw new \Exception( 'Not connected to the Search Statistics service.' ); } $context = (string) ( $request->get_param( 'context' ) ?? '' ); if ( empty( $context ) ) { throw new \Exception( 'No context provided.' ); } // Rows coming from the front-end which already have been formatted, and some might already have cached statistics appended. $all = (array) ( $request->get_param( 'all' ) ?? [] ); $paginated = (array) ( $request->get_param( 'paginated' ) ?? [] ); $args = [ 'startDate' => $request->get_param( 'startDate' ), 'endDate' => $request->get_param( 'endDate' ), ]; if ( empty( $args['startDate'] ) || empty( $args['endDate'] ) ) { aioseo()->searchStatistics->stats->setDefaultDateRange(); $defaultDateRange = aioseo()->searchStatistics->stats->getDateRange(); $args['startDate'] = $defaultDateRange['start']; $args['endDate'] = $defaultDateRange['end']; } if ( 'keywords' === $context ) { $args['pageExpression'] = aioseo()->searchStatistics->helpers->buildPageExpression( $request->get_param( 'postId' ) ?? 0 ); $statistics = aioseo()->searchStatistics->keywordRankTracker->fetchKeywordsStatistics( $all['rows'], $args ); } if ( 'groups' === $context ) { $statistics = aioseo()->searchStatistics->keywordRankTracker->appendGroupStatistics( $all['rows'], $args ); } if ( isset( $statistics ) ) { // Append the statistics to the paginated rows. $allStatistics = array_column( $all['rows'], 'statistics', 'id' ); foreach ( ( $paginated['rows'] ?? [] ) as $k => $paginatedRow ) { $paginated['rows'][ $k ]['statistics'] = $allStatistics[ $paginatedRow['id'] ] ?? $paginatedRow['statistics']; } return new \WP_REST_Response( [ 'success' => true, 'all' => $all, 'paginated' => $paginated, 'statistics' => $statistics ], 200 ); } throw new \Exception( 'Wrong context provided.' ); } catch ( \Exception $e ) { return new \WP_REST_Response( [ 'success' => false, 'message' => $e->getMessage() ], 400 ); } } /** * Retrieves related keywords. * * @since 4.7.8 * * @param \WP_REST_Request $request The REST Request. * @return \WP_REST_Response The response. */ public static function fetchRelatedKeywords( $request ) { $params = $request->get_params(); $keyword = (string) ( $params['keyword'] ?? '' ); $relatedKeywords = aioseo()->searchStatistics->keywordRankTracker->getFormattedRelatedKeywords( $keyword, [ 'startDate' => $params['startDate'], 'endDate' => $params['endDate'], ] ); return new \WP_REST_Response( [ 'success' => true, 'paginated' => $relatedKeywords['paginated'], ], 200 ); } }