310 lines
7.7 KiB
PHP
310 lines
7.7 KiB
PHP
<?php
|
|
|
|
namespace AIOSEO\Plugin\Pro\Models\SearchStatistics;
|
|
|
|
// Exit if accessed directly.
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit;
|
|
}
|
|
|
|
use AIOSEO\Plugin\Common\Models as CommonModels;
|
|
|
|
/**
|
|
* The Keyword DB Model.
|
|
*
|
|
* @since 4.7.0
|
|
*/
|
|
class Keyword extends CommonModels\Model {
|
|
/**
|
|
* Database table name with no prefix.
|
|
*
|
|
* @since 4.7.0
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $table = 'aioseo_search_statistics_keywords';
|
|
|
|
/**
|
|
* Fields that should be numeric values.
|
|
*
|
|
* @since 4.7.0
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $integerFields = [ 'id' ];
|
|
|
|
/**
|
|
* Fields that should be json encoded on save and decoded on get.
|
|
*
|
|
* @since 4.7.0
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $jsonFields = [];
|
|
|
|
/**
|
|
* Fields that should be boolean values.
|
|
*
|
|
* @since 4.7.0
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $booleanFields = [ 'favorited' ];
|
|
|
|
/**
|
|
* Fields that should be hidden when serialized.
|
|
*
|
|
* @since 4.7.0
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $hidden = [ 'id' ];
|
|
|
|
/**
|
|
* Column: Name.
|
|
*
|
|
* @since 4.7.0
|
|
*
|
|
* @var string $name
|
|
*/
|
|
public $name;
|
|
|
|
/**
|
|
* Column: Favorited.
|
|
*
|
|
* @since 4.7.0
|
|
*
|
|
* @var int $favorited
|
|
*/
|
|
public $favorited;
|
|
|
|
/**
|
|
* Class constructor.
|
|
*
|
|
* @since 4.7.0
|
|
*
|
|
* @param mixed $var This can be the primary key of the resource, or it could be an array of data to manufacture a resource without a database query.
|
|
* @return void
|
|
*/
|
|
public function __construct( $var = null ) {
|
|
parent::__construct( $var );
|
|
}
|
|
|
|
/**
|
|
* Gets the keywords from the database.
|
|
*
|
|
* @since 4.7.0
|
|
*
|
|
* @param array $args The arguments.
|
|
* @return array The keywords.
|
|
*/
|
|
public static function getKeywords( $args = [] ) {
|
|
$args = array_merge( [
|
|
'ids' => [],
|
|
'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.
|
|
}
|
|
} |