Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
178 changes: 178 additions & 0 deletions includes/class-api.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
<?php
/**
* Classe responsável pelo registro e manipulação das rotas REST.
*
* Registra os endpoints da API de favoritos e processa
* as requisições de toggle e listagem.
*
* @package WordPressBackEndChallenge
*/

// Impede acesso direto ao arquivo.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}

/**
* Classe WPBEC_Api.
*
* Responsável por registrar as rotas REST e manipular
* as requisições e respostas da API de favoritos.
*/
class WPBEC_Api {

/**
* Namespace da REST API.
*
* @var string
*/
private $namespace = 'favorites/v1';

/**
* Instância da classe de banco de dados.
*
* @var WPBEC_Database
*/
private $database;

/**
* Construtor – recebe a instância do banco de dados.
*
* @param WPBEC_Database $database Instância da classe de banco de dados.
*/
public function __construct( WPBEC_Database $database ) {
$this->database = $database;
}

/**
* Registra as rotas da REST API.
*
* @return void
*/
public function register_routes() {
// Endpoint para alternar favorito (favorite/unfavorite).
register_rest_route(
$this->namespace,
'/toggle',
array(
'methods' => 'POST',
'callback' => array( $this, 'toggle_favorite' ),
'permission_callback' => array( $this, 'check_permission' ),
'args' => array(
'post_id' => array(
'required' => true,
'validate_callback' => array( $this, 'validate_post_id' ),
'sanitize_callback' => 'absint',
),
),
)
);

// Endpoint para listar os posts favoritados.
register_rest_route(
$this->namespace,
'/list',
array(
'methods' => 'GET',
'callback' => array( $this, 'list_favorites' ),
'permission_callback' => array( $this, 'check_permission' ),
)
);
}

/**
* Verifica se o usuário está autenticado.
*
* @return bool True se o usuário estiver logado; false caso contrário.
*/
public function check_permission() {
return is_user_logged_in();
}

/**
* Valida o parâmetro post_id.
*
* Verifica se é um número inteiro positivo e se o post existe.
*
* @param mixed $value Valor do parâmetro.
* @param WP_REST_Request $request Requisição REST.
* @param string $param Nome do parâmetro.
* @return bool|WP_Error True se válido; WP_Error caso contrário.
*/
public function validate_post_id( $value, $request, $param ) {
$post_id = absint( $value );

if ( $post_id <= 0 ) {
return new WP_Error(
'rest_invalid_param',
'O parâmetro post_id deve ser um número inteiro positivo.',
array( 'status' => 400 )
);
}

// Verifica se o post existe.
$post = get_post( $post_id );
if ( null === $post ) {
return new WP_Error(
'rest_post_not_found',
'O post informado não foi encontrado.',
array( 'status' => 404 )
);
}

return true;
}

/**
* Alterna o status de favorito de um post (toggle).
*
* Se o post já estiver favoritado, remove. Caso contrário, adiciona.
*
* @param WP_REST_Request $request Requisição REST.
* @return WP_REST_Response Resposta com o status atualizado.
*/
public function toggle_favorite( WP_REST_Request $request ) {
$user_id = get_current_user_id();
$post_id = $request->get_param( 'post_id' );

$is_favorited = $this->database->is_favorited( $user_id, $post_id );

if ( $is_favorited ) {
// Remove dos favoritos.
$this->database->remove_favorite( $user_id, $post_id );
$favorited = false;
} else {
// Adiciona aos favoritos.
$this->database->add_favorite( $user_id, $post_id );
$favorited = true;
}

// Invalida o cache após a alteração.
$this->database->clear_cache( $user_id );

return new WP_REST_Response(
array(
'favorited' => $favorited,
),
200
);
}

/**
* Retorna a lista de posts favoritados pelo usuário logado.
*
* @param WP_REST_Request $request Requisição REST.
* @return WP_REST_Response Resposta com a lista de IDs favoritados.
*/
public function list_favorites( WP_REST_Request $request ) {
$user_id = get_current_user_id();
$favorites = $this->database->get_favorites( $user_id );

return new WP_REST_Response(
array(
'favorites' => $favorites,
),
200
);
}
}
188 changes: 188 additions & 0 deletions includes/class-database.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
<?php
/**
* Classe responsável pelas operações com banco de dados.
*
* Gerencia a criação da tabela de favoritos e todas as
* operações de leitura, inserção e remoção de registros.
*
* @package WordPressBackEndChallenge
*/

// Impede acesso direto ao arquivo.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}

/**
* Classe WPBEC_Database.
*
* Responsável pela criação da tabela e operações CRUD
* envolvendo favoritos de posts.
*/
class WPBEC_Database {

/**
* Nome da tabela de favoritos (com prefixo do WordPress).
*
* @var string
*/
private $table_name;

/**
* Construtor – define o nome da tabela usando o prefixo do WordPress.
*/
public function __construct() {
global $wpdb;
$this->table_name = $wpdb->prefix . 'favorites';
}

/**
* Cria a tabela de favoritos utilizando dbDelta.
*
* Deve ser chamada na ativação do plugin.
*
* @return void
*/
public function create_table() {
global $wpdb;

$charset_collate = $wpdb->get_charset_collate();

$sql = "CREATE TABLE {$this->table_name} (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
user_id BIGINT UNSIGNED NOT NULL,
post_id BIGINT UNSIGNED NOT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY user_post (user_id, post_id)
) {$charset_collate};";

require_once ABSPATH . 'wp-admin/includes/upgrade.php';
dbDelta( $sql );
}

/**
* Remove a tabela de favoritos do banco de dados.
*
* Utilizada na desinstalação do plugin.
*
* @return void
*/
public function drop_table() {
global $wpdb;
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$wpdb->query( "DROP TABLE IF EXISTS {$this->table_name}" );
}

/**
* Verifica se um post já está favoritado pelo usuário.
*
* @param int $user_id ID do usuário.
* @param int $post_id ID do post.
* @return bool True se o registro existir; false caso contrário.
*/
public function is_favorited( $user_id, $post_id ) {
global $wpdb;

// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$result = $wpdb->get_var(
$wpdb->prepare(
"SELECT COUNT(*) FROM {$this->table_name} WHERE user_id = %d AND post_id = %d",
$user_id,
$post_id
)
);

return (int) $result > 0;
}

/**
* Adiciona um post aos favoritos do usuário.
*
* @param int $user_id ID do usuário.
* @param int $post_id ID do post.
* @return bool True em caso de sucesso; false em caso de falha.
*/
public function add_favorite( $user_id, $post_id ) {
global $wpdb;

$result = $wpdb->insert(
$this->table_name,
array(
'user_id' => $user_id,
'post_id' => $post_id,
),
array( '%d', '%d' )
);

return false !== $result;
}

/**
* Remove um post dos favoritos do usuário.
*
* @param int $user_id ID do usuário.
* @param int $post_id ID do post.
* @return bool True em caso de sucesso; false em caso de falha.
*/
public function remove_favorite( $user_id, $post_id ) {
global $wpdb;

$result = $wpdb->delete(
$this->table_name,
array(
'user_id' => $user_id,
'post_id' => $post_id,
),
array( '%d', '%d' )
);

return false !== $result;
}

/**
* Retorna a lista de IDs de posts favoritados pelo usuário.
*
* Utiliza a Transient API para cache, melhorando a performance.
*
* @param int $user_id ID do usuário.
* @return array Lista de IDs de posts favoritados.
*/
public function get_favorites( $user_id ) {
$transient_key = 'wpbec_favorites_user_' . $user_id;
$cached = get_transient( $transient_key );

if ( false !== $cached ) {
return $cached;
}

global $wpdb;

// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$results = $wpdb->get_col(
$wpdb->prepare(
"SELECT post_id FROM {$this->table_name} WHERE user_id = %d ORDER BY created_at DESC",
$user_id
)
);

$favorites = array_map( 'intval', $results );

// Armazena no cache por 1 hora.
set_transient( $transient_key, $favorites, HOUR_IN_SECONDS );

return $favorites;
}

/**
* Invalida o cache de favoritos de um usuário.
*
* Deve ser chamada sempre que os favoritos forem alterados.
*
* @param int $user_id ID do usuário.
* @return void
*/
public function clear_cache( $user_id ) {
delete_transient( 'wpbec_favorites_user_' . $user_id );
}
}
Loading