skip to Main Content

I’m working in WP with PHP, and am trying to lessen my loading time for a function that grabs a bunch of data. I was thinking a transient would work, and(as you can see below) I got the courses query to load under a transient, however it’s not working on the foreach statement, or rather I can’t figure out how to set it up correctly for it to work.

The main code that’s slowing everything down is the foreach statement that grabs the Vimeo video id of each topic, runs it through the vimeo_api() function which then grabs the duration of the video, and then attaches it to the topic id inside an array.

Technically speaking I don’t have to have the get_vimeo_duration_all_courses() function (which calls all the courses/lessons/topics), but rather just grab the Vimeo ID from each topic as it loads as that’s the way I had it to begin with and it did work, but unfortunately it increased my page load time. My thought is if I can load all the durations inside of an array and have each one attached to the topic ID, then I don’t have to call the vimeo_api() function every time I want to get a video duration. I can more so just update the $vimeo variable once a day and then search the $vimeo variable for a topic ID to get the vimeo duration.

So I guess I’m wondering if I’m heading in the right direction or if you know of a better way to go about it?

Is there a way that I can store the $vimeo variable with all the values inside of it so that the vimeo_api() function doesn’t have to run each time?

Any help would be amazing! Thank you


function get_vimeo_duration_all_courses($vimeo)
{
    $courses = get_transient('all_courses'); 
    if (false === $courses) {

        $courses = new WP_Query(array(
            'posts_per_page' => -1,
            'post_type'  => 'sfwd-courses',
            'no_found_rows' => true,
            'cache_results' => true,
            'ignore_sticky_posts'  => true,
            'fields' => 'ids',
        )); //Grabs all the courses

        set_transient('all_courses', $courses, (24 * HOUR_IN_SECONDS));
    }

    $vimeo = get_transient('all_vimeo_durations');
    if (false === $vimeo) {
        $vimeo = array();

        foreach ($courses->posts as $course_id) {
            $lessons = learndash_course_get_lessons($course_id); //Gets all the lessons under the course
            
            foreach ($lessons as $lesson) {
                $topics = learndash_course_get_topics($course_id, $lesson->ID); //Gets all the topics under the lesson
                
                foreach ($topics as $topic) {
                    $vimeoVideo = get_field('lesson_video', $topic->ID); 
                    
                    if (!empty($vimeoVideo)) { //Looks at each topic and determines whether it has a vimeo video or not
                        $vimeoDuration = vimeo_api($vimeoVideo); //If true, it runs the vimeo ID through the vimeo_api() function (THIS IS WHERE IT BECOMES SLOW)
                        $vimeo[$topic->ID] = $vimeoDuration;
                    }
                }

            }

        }

        set_transient('all_vimeo_durations', $vimeo, (24 * HOUR_IN_SECONDS));
    }

   return $vimeo;
}


function show_vimeo_duration($topicID) {
    return $vimeo[$topicID]; //Returns the value of the vimeo_api() function that matches the topicID
}

Here’s where I grab the Vimeo api and return the duration of the video

function vimeo_api($id)
{
    try {
        $authorization = 'CODELEFTOUTONPURPOSE';
        $ch = curl_init();

        curl_setopt_array($ch, array(
            CURLOPT_URL => "https://api.vimeo.com/videos/$id?fields=duration",
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_ENCODING => "",
            CURLOPT_MAXREDIRS => 10,
            CURLOPT_TIMEOUT => 30,
            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
            CURLOPT_CUSTOMREQUEST => "GET",
            CURLOPT_HTTPHEADER => array(
                "authorization: Bearer {$authorization}",
                "cache-control: no-cache",
            ),
        ));

        $res = curl_exec($ch);
        $obj = json_decode($res, true);
        return $obj['duration'];//Returns the duration of the video
    } catch (Exception $e) {
        return "0";
    }
}

2

Answers


  1. Chosen as BEST ANSWER

    Thank you for the suggestions, they definitely helped push me in the right direction. I ended up deleting the function that called all the courses and built in a caching file which hosts all the Vimeo IDs and durations. Then when I call the function, it first searches through the caching file and grabs the duration if it finds the Vimeo ID, but if it can't find the ID, then it runs the API.

    Below is the final code...

    function vimeo_api($vimeoID)
    {
        $vimeoCacheUrl = WP_CONTENT_DIR . '/cache/vimeo-duration-cache.txt';
        $newData = '';
        $vimeoData = array();
        $vimeoCache = file($vimeoCacheUrl);
        $vimeoCache = array_unique($vimeoCache);
        file_put_contents($vimeoCacheUrl, implode($vimeoCache), LOCK_EX);
        foreach ($vimeoCache as $key => $value) {
            $data = explode(',', $value, 2);
            $vimeoData[$data[0]] = $data[1];
        }
    
        if (isset($vimeoData[$vimeoID])) {
            return $vimeoData[$vimeoID];
        } else {
            try {
                $authorization = 'XXXX';
                $ch = curl_init();
    
                curl_setopt_array($ch, array(
                    CURLOPT_URL => "https://api.vimeo.com/videos/$vimeoID?fields=duration",
                    CURLOPT_RETURNTRANSFER => true,
                    CURLOPT_ENCODING => "",
                    CURLOPT_MAXREDIRS => 10,
                    CURLOPT_TIMEOUT => 30,
                    CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
                    CURLOPT_CUSTOMREQUEST => "GET",
                    CURLOPT_HTTPHEADER => array(
                        "authorization: Bearer {$authorization}",
                        "cache-control: cache",
                    ),
                ));
    
                $res = curl_exec($ch);
                $obj = json_decode($res, true);
                $newData = $vimeoID . ',' .$obj['duration'];
                file_put_contents($vimeoCacheUrl, str_replace("'", '', var_export($newData, true)) . PHP_EOL, FILE_APPEND | LOCK_EX);
                return $obj['duration'];
            } catch (Exception $e) {
                return "0";
            } 
        }
    }
    

  2. It doesn’t make sense to store the object returned from WP_Query in a transient. That object is for controlling a WordPress loop. But it does make sense, a lot of sense, to use a transient the way you do for all_video_durations. Transients will hold the data for any php array or object persistently.

    Transients are terrific for caching hard-to-get values like the ones you get from the video service API. They work in all WordPress installs, and they work extra fast in installs with a persistent object cache.

    You may want to use a separate transient for each video item rather than bundling them all together. That will make your code work a little better if you add new items.

    (Also, if you use WordPress’s built-in Requests class rather than cURL, your code will work on WordPress installs in hosting services that don’t handle cURL correctly. And if you submit cURL code to the plugin repository, a reviewer will make you replace it. Ask me how I know this sometime 🙂

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search