phpwordpressperformancecachingtransient

Using PHP, can I put a foreach statement into a transient?


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";
    }
}

Solution

  • 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";
            } 
        }
    }