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
File renamed without changes.
27 changes: 27 additions & 0 deletions apiki-favorites/apiki-favorites.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php
/**
* Plugin Name: Apiki Favorites
* Description: Post favorites system via WP REST API for logged-in users.
* Version: 1.0.0
* Author: ANDRE AND COPILOT
*/

if ( ! defined( 'ABSPATH' ) ) {
exit;
}

define( 'APIKI_FAV_PLUGIN_PATH', plugin_dir_path( __FILE__ ) );
define( 'APIKI_FAV_TABLE_NAME', 'apiki_favorites' );
define( 'APIKI_FAV_REST_NAMESPACE', 'apiki/v1' );

require_once APIKI_FAV_PLUGIN_PATH . 'includes/class-activator.php';
require_once APIKI_FAV_PLUGIN_PATH . 'includes/class-favorites-service.php';
require_once APIKI_FAV_PLUGIN_PATH . 'includes/class-rest-controller.php';

register_activation_hook( __FILE__, [ 'Apiki_Favorites_Activator', 'activate' ] );

add_action( 'rest_api_init', function () {
$service = new Apiki_Favorites_Service();
$controller = new Apiki_Favorites_REST_Controller( $service );
$controller->register_routes();
} );
28 changes: 28 additions & 0 deletions apiki-favorites/includes/class-activator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

if ( ! defined( 'ABSPATH' ) ) {
exit;
}

class Apiki_Favorites_Activator {

public static function activate() {
global $wpdb;

$table_name = $wpdb->prefix . APIKI_FAV_TABLE_NAME;
$charset_collate = $wpdb->get_charset_collate();

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

require_once ABSPATH . 'wp-admin/includes/upgrade.php';
dbDelta( $sql );
}
}
122 changes: 122 additions & 0 deletions apiki-favorites/includes/class-favorites-service.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
<?php

if ( ! defined( 'ABSPATH' ) ) {
exit;
}

class Apiki_Favorites_Service {

/**
* Returns the full table name (including prefix).
*
* @return string
*/
protected function get_table_name() {
global $wpdb;
return $wpdb->prefix . APIKI_FAV_TABLE_NAME;
}

/**
* Adds a post to the user's favorites.
*
* @param int $user_id
* @param int $post_id
*
* @return string status: 'favorited' or 'already_favorited'
*/
public function add_favorite( $user_id, $post_id ) {
global $wpdb;

$table_name = $this->get_table_name();

$inserted = $wpdb->insert(
$table_name,
[
'user_id' => $user_id,
'post_id' => $post_id,
],
[ '%d', '%d' ]
);

if ( false === $inserted ) {
return 'already_favorited';
}

return 'favorited';
}

/**
* Remove a post from the user's favorites.
*
* @param int $user_id
* @param int $post_id
*
* @return string status: 'unfavorited' or 'not_found'
*/
public function remove_favorite( $user_id, $post_id ) {
global $wpdb;

$table_name = $this->get_table_name();

$deleted = $wpdb->delete(
$table_name,
[
'user_id' => $user_id,
'post_id' => $post_id,
],
[ '%d', '%d' ]
);

if ( $deleted ) {
return 'unfavorited';
}

return 'not_found';
}

/**
* Returns IDs of posts favorited by the user.
*
* @param int $user_id
*
* @return int[]
*/
public function get_favorites_by_user( $user_id ) {
global $wpdb;

$table_name = $this->get_table_name();

$post_ids = $wpdb->get_col(
$wpdb->prepare(
"SELECT post_id FROM {$table_name} WHERE user_id = %d",
$user_id
)
);

return array_map( 'intval', $post_ids );
}

/**
* Check if a post has been favored by the user.
*
* @param int $user_id
* @param int $post_id
*
* @return bool
*/
public function is_favorited( $user_id, $post_id ) {
global $wpdb;

$table_name = $this->get_table_name();

$exists = $wpdb->get_var(
$wpdb->prepare(
"SELECT COUNT(*) FROM {$table_name} WHERE user_id = %d AND post_id = %d",
$user_id,
$post_id
)
);

return ( $exists > 0 );
}
}
193 changes: 193 additions & 0 deletions apiki-favorites/includes/class-rest-controller.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
<?php

if ( ! defined( 'ABSPATH' ) ) {
exit;
}

class Apiki_Favorites_REST_Controller {

/**
* @var Apiki_Favorites_Service
*/
protected $service;

public function __construct( Apiki_Favorites_Service $service ) {
$this->service = $service;
}

/**
* Registers the REST API routes.
*/
public function register_routes() {
register_rest_route(
APIKI_FAV_REST_NAMESPACE,
'/favorites',
[
[
'methods' => WP_REST_Server::READABLE,
'callback' => [ $this, 'get_favorites' ],
'permission_callback' => [ $this, 'permissions_check' ],
],
[
'methods' => WP_REST_Server::CREATABLE,
'callback' => [ $this, 'add_favorite' ],
'permission_callback' => [ $this, 'permissions_check' ],
'args' => [
'post_id' => [
'required' => true,
'validate_callback' => [ $this, 'validate_post_id' ],
'sanitize_callback' => 'absint',
],
],
],
[
'methods' => WP_REST_Server::DELETABLE,
'callback' => [ $this, 'remove_favorite' ],
'permission_callback' => [ $this, 'permissions_check' ],
'args' => [
'post_id' => [
'required' => true,
'validate_callback' => [ $this, 'validate_post_id' ],
'sanitize_callback' => 'absint',
],
],
],
]
);

// Optional: route to check if a specific post is favorited.
register_rest_route(
APIKI_FAV_REST_NAMESPACE,
'/favorites/check',
[
'methods' => WP_REST_Server::READABLE,
'callback' => [ $this, 'check_favorite' ],
'permission_callback' => [ $this, 'permissions_check' ],
'args' => [
'post_id' => [
'required' => true,
'validate_callback' => [ $this, 'validate_post_id' ],
'sanitize_callback' => 'absint',
],
],
]
);
}

/**
* Ensures the user is logged in.
*/
public function permissions_check( WP_REST_Request $request ) {
if ( ! is_user_logged_in() ) {
return new WP_Error(
'rest_forbidden',
__( 'Você precisa estar logado para usar favoritos.', 'apiki-favorites' ),
[ 'status' => 401 ]
);
}

return true;
}

/**
* Validates if the post exists.
*/
public function validate_post_id( $param, WP_REST_Request $request, $key ) {
$post = get_post( (int) $param );
return ( $post instanceof WP_Post );
}

/**
* GET /favorites
* Lists the posts favorited by the logged-in user.
*/
public function get_favorites( WP_REST_Request $request ) {
$user_id = get_current_user_id();
$post_ids = $this->service->get_favorites_by_user( $user_id );

$favorites = [];

foreach ( $post_ids as $post_id ) {
$post = get_post( $post_id );
if ( ! $post ) {
continue;
}

$favorites[] = [
'id' => $post->ID,
'title' => get_the_title( $post ),
'link' => get_permalink( $post ),
];
}

return rest_ensure_response(
[
'user_id' => $user_id,
'favorites' => $favorites,
]
);
}

/**
* POST /favorites
* Add a post to your favorites.
*/
public function add_favorite( WP_REST_Request $request ) {
$user_id = get_current_user_id();
$post_id = (int) $request->get_param( 'post_id' );

$status = $this->service->add_favorite( $user_id, $post_id );

$http_status = ( 'favorited' === $status ) ? 201 : 200;

return new WP_REST_Response(
[
'status' => $status,
'user_id' => $user_id,
'post_id' => $post_id,
],
$http_status
);
}

/**
* DELETE /favorites
* Remove a post from your favorites.
*/
public function remove_favorite( WP_REST_Request $request ) {
$user_id = get_current_user_id();
$post_id = (int) $request->get_param( 'post_id' );

$status = $this->service->remove_favorite( $user_id, $post_id );

$http_status = ( 'unfavorited' === $status ) ? 200 : 404;

return new WP_REST_Response(
[
'status' => $status,
'user_id' => $user_id,
'post_id' => $post_id,
],
$http_status
);
}

/**
* GET /favorites/check?post_id=123
* Check if a post has been favorited.
*/
public function check_favorite( WP_REST_Request $request ) {
$user_id = get_current_user_id();
$post_id = (int) $request->get_param( 'post_id' );

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

return rest_ensure_response(
[
'user_id' => $user_id,
'post_id' => $post_id,
'is_favorited' => $is_favorited,
]
);
}
}
12 changes: 12 additions & 0 deletions apiki-favorites/uninstall.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
exit;
}

global $wpdb;

$table_name = $wpdb->prefix . 'apiki_favorites';

// Don't do this in production.
$wpdb->query( "DROP TABLE IF EXISTS {$table_name}" );