av3nger / cf-images

Offload, Store, Resize & Optimize with Cloudflare Images - plugin for WordPress
https://wordpress.org/plugins/cf-images/
GNU General Public License v3.0
20 stars 2 forks source link

How to use this plugin for wordpress headless via own endpoints rest api? #48

Open web-vitality opened 1 month ago

web-vitality commented 1 month ago

Hi there! For my project I started to write code for uploading images to cloudflare, but I came across your plugin. Thanks for sharing it! It's a huge help. :) I use WordPress as a headless cms for backend, and on the frontend I use one of the popular frameworks. I have a custom plugin that is responsible for all requests and endpoints and returns the date in the response. I use the REST API. I use new WP_Query for queries. I also use ACF but the links are not changed in the response either. It's important to note that if I use vordpress as a normal system everything works. I don't quite understand if this is even possible how I can use the functionality of your plugin so that I can change the links of images from WordPress to Cloudflare in the response output. I hope I've been clear in my explanation. Thanks.

av3nger commented 1 month ago

Hi @web-vitality ,

It might be required to add some additional checks for REST API endpoints. Do you have any details on which endpoints are being used?

web-vitality commented 1 month ago

Yes of course, i have many endpoints. For example here are some of them.

To pull posts i register an endpoint.

add_action('rest_api_init', 'post');

function post () {
    register_rest_route('myendpoint1', '/post/(?P<slug>\S+)', [
        'methods' => WP_REST_SERVER::READABLE,
        'callback' => 'postRes',
        'publicly_queryable' => true,
        'permission_callback' => '__return_true',
        'args'  => array(
            'slug' => array(
                'required' => true
            ),
        ),

    ] );
}

function postRes($data) {

    $post_result = [];

    $post = new WP_Query([
        'post_type' => 'post',
        'name' => $data['slug'],
        'post_status' => 'publish',
        'posts_per_page' => 1,
    ]);

while($post->have_posts()){
        $post->the_post();

        $seo = [];
        $post_result = [];
        $post_result['id'] = get_the_ID();
        $post_result['title'] = get_the_title();
        $post_result['text'] = get_the_content();
        $post_result['slug'] = get_post_field('post_name', get_post());
        $post_result['img'] = get_the_post_thumbnail_url(get_the_ID(), 'medium');
        ........
        ............
}
    wp_reset_postdata();

    return $post_result;
}

For example, I pull array of teachers with ACF fields

add_action('rest_api_init', 'teachers');

function teachers (){
    register_rest_route('myendpoint2', '/teachers', [
        'methods' => WP_REST_SERVER::READABLE,
        'callback' => 'teachersRes',
        'publicly_queryable' => true,
        'permission_callback' => '__return_true',

    ] );
}

function teachersRes($data) {

    $teachers_result = [];

    $teachers = new WP_Query([
        'post_type' => 'teachers',
        'post_status' => 'publish',
        'posts_per_page' => 100,
    ]);

    while($teachers->have_posts()){
        $teachers->the_post();

        $teachers_result[$loop]['id'] = get_the_ID();
        $teachers_result[$loop]['title'] = get_the_title();
        $teachers_result[$loop]['text'] = get_the_content();
        $teachers_result[$loop]['slug'] = get_post_field('post_name', get_post());

        $teachers_result[$loop]['img'] = get_field('teacher')['img'];
        ........
        ............

    }
    wp_reset_postdata();

    return $teachers_result;
}

Thanks for the help.

av3nger commented 1 month ago

I haven't tested this, but just throwing some ideas.

For the REST API request, you need to have a look at this code. The reason this is so tricky is because of Gutenberg, which uses REST API. If we just replace all images on all API endpoints, then editing a Gutenberg-based post will save Cloudflare Images as content, which makes it really hard to revert back. But if you're not using Gutenberg, you can just return false on that method and see if that helps.

That might actually fix the ACF issues as well.

web-vitality commented 1 month ago

It doesn't help me. Thanks for trying.

web-vitality commented 1 month ago

Is it possible to use one of the hooks or functions from your plugin, into which I will pass the URL of the image or the ID of the image (the image will already be uploaded to cloudflare and there will be a post_meta in the database for this image), then this function will return me the value of the link to cloudflare for this image and I will assign the result of execution to my array?

av3nger commented 1 month ago

Yes, if you have the URL and image ID, you can do something like this. But, if you're doing this as part of a REST API endpoint, you will also need to adjust that method I linked above.

I'll be able to have a look at this in a few days. Just need to setup a headless test site.

web-vitality commented 1 month ago

I wrote my custom code. A bit ugly and not neat but working. Here is a partial example if it helps anyone. I call the get_cloudflare_image_url() function, pass the attachment_id, the function returns a ready link to the cloudflare image.

$image_src = get_cloudflare_image_url( attachment_id );

$posts_result[$loop]['post_thumb'] = ( get_cloudflare_image_url(get_the_ID(), 'post_thumb') ?? get_the_post_thumbnail_url(get_the_ID(), 'post_thumb') );

I also use a filter for get_field() of ACF.

add_filter( "acf/format_value/type=image", function ($value, $post_id, $field) {
        if ( isset($value['id'] ) ) {
            $value['url'] = get_cloudflare_image_url( $value['id'] ) ?? $value['url'];
        }
        return $value;
}, 10, 3 );
use CF_Images\App\Modules\Cloudflare_Images;

/**
 * Get cloudflare image link if exist in DB
 */
function get_cloudflare_image_url($post_id, $size = 'public') {
    $cf_image = NULL;
    $attachment_id = get_post_thumbnail_id( $post_id );

    if ( get_image_sizes( $size ) !== false ) {
        $size_tmp = get_image_sizes( $size );
        $size = 'w=' . $size_tmp['width'] . ',h=' . $size_tmp['height'] . ',fit=crop';
    } else {
        $size = 'public';
    }

    if ( empty($attachment_id) ) {
        $attachment_id = $post_id;
    }

    // if cf-images plugin not active
    if ( !class_exists('CF_Images\App\Modules\Cloudflare_Images') ) {
        $cf_image_id = get_post_meta( $attachment_id, '_cloudflare_image_id', true );
        $cf_image = "https://imagedelivery.net/{HASH}/${cf_image_id}/public";

        if ( empty($cf_image_id) ) {
            return NULL;
        }

        return $cf_image;
    }

    $cf_images = new Cloudflare_Images( 'cloudflare-images' );
    list( $hash, $cloudflare_image_id ) = $cf_images->get_hash_id_url_string( (int) $attachment_id );

    if ( $cloudflare_image_id ) {
        $cf_image = trailingslashit( "https://imagedelivery.net" . "/$hash" ) . $cloudflare_image_id . "/" . $size;
    }

    return $cf_image;
}

/**
 * Get information about available image sizes
 */
function get_image_sizes( $sizetmp = '' ) {
    $wp_additional_image_sizes = wp_get_additional_image_sizes();

    $sizes = array();
    $get_intermediate_image_sizes = get_intermediate_image_sizes();

    // Create the full array with sizes and crop info
    foreach( $get_intermediate_image_sizes as $_size ) {
        if ( in_array( $_size, array( 'thumbnail', 'medium', 'large' ) ) ) {
            $sizes[ $_size ]['width'] = get_option( $_size . '_size_w' );
            $sizes[ $_size ]['height'] = get_option( $_size . '_size_h' );
            $sizes[ $_size ]['crop'] = (bool) get_option( $_size . '_crop' );
        } elseif ( isset( $wp_additional_image_sizes[ $_size ] ) ) {
            $sizes[ $_size ] = array( 
                'width' => $wp_additional_image_sizes[ $_size ]['width'],
                'height' => $wp_additional_image_sizes[ $_size ]['height'],
                'crop' =>  $wp_additional_image_sizes[ $_size ]['crop']
            );
        }
    }

    // Get only 1 size if found
    if ( $sizetmp ) {
        if( isset( $sizes[ $sizetmp ] ) ) {
            return $sizes[ $sizetmp ];
        } else {
            return false;
        }
    }
    return $sizes;
}