Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
90.32% covered (success)
90.32%
28 / 31
40.00% covered (danger)
40.00%
2 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
Scheduler
90.32% covered (success)
90.32%
28 / 31
40.00% covered (danger)
40.00%
2 / 5
15.20
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
1
 schedule_services_update
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 schedule_locations_update
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
 schedule_single_event
87.50% covered (success)
87.50%
7 / 8
0.00% covered (danger)
0.00%
0 / 1
3.02
 should_skip
87.50% covered (success)
87.50%
7 / 8
0.00% covered (danger)
0.00%
0 / 1
7.10
1<?php
2/**
3 * Class: Scheduler
4 * Manages the interaction between the model and the view.
5 *
6 * @package ChoctawNation
7 * @subpackage CNHSA_Federation
8 */
9
10namespace ChoctawNation\CNHSA_Federation\WP;
11
12use WP_Post;
13
14/**
15 * Scheduler Class
16 */
17class Scheduler {
18    /**
19     * Cron keys for scheduling updates and creations.
20     *
21     * @var array $cron_keys
22     */
23    public array $cron_keys;
24
25    /**
26     * Notifier instance for sending notifications.
27     *
28     * @var Notifier $notifier
29     */
30    public Notifier $notifier;
31
32    /**
33     * Constructor
34     *
35     * @param Notifier $notifier The notifier instance to use for sending notifications.
36     */
37    public function __construct( Notifier $notifier ) {
38        $this->cron_keys = array(
39            'services'  => array(
40                'update' => 'cnhsa_federation_update_services',
41            ),
42            'locations' => array(
43                'update' => 'cnhsa_federation_update_health_location',
44            ),
45        );
46        $this->notifier  = $notifier;
47    }
48
49    /**
50     * Schedule an update for the specified post.
51     *
52     * @param int     $post_id The ID of the post to update.
53     * @param WP_Post $post The post object.
54     * @param bool    $update Whether this is an update or a new post.
55     * @return ?bool Bool on schedule or null if the scheduling was skipped due to conditions not being met.
56     */
57    public function schedule_services_update( int $post_id, WP_Post $post, bool $update ): ?bool {
58        if ( $this->should_skip( $post, $update ) ) {
59            return null;
60        }
61        return $this->schedule_single_event( $this->cron_keys['services']['update'], array( $post ) );
62    }
63
64    /**
65     * Schedule an update for the specified health location post.
66     *
67     * @param int     $post_id The ID of the post to update.
68     * @param WP_Post $post The post object.
69     * @param bool    $update Whether this is an update or a new post.
70     */
71    public function schedule_locations_update( int $post_id, WP_Post $post, bool $update ): ?bool {
72        if ( $this->should_skip( $post, $update ) ) {
73            return null;
74        }
75        return $this->schedule_single_event( $this->cron_keys['locations']['update'], array( $post ) );
76    }
77
78    /**
79     * Schedule a single event with the given hook and arguments, avoiding duplicates.
80     * Sends an email if scheduling fails.
81     *
82     * @param string $hook The name of the hook to schedule.
83     * @param array  $args The arguments to pass to the hook when it is executed.
84     * @return bool True if the event was scheduled successfully, false if it failed or was already scheduled.
85     */
86    private function schedule_single_event( string $hook, array $args ): bool {
87        // avoid duplicate schedules
88        $next = wp_next_scheduled( $hook, $args );
89        if ( $next ) {
90            return true; // already scheduled
91        }
92
93        $timestamp = time() + 5;
94        $ok        = wp_schedule_single_event( $timestamp, $hook, $args );
95        if ( ! $ok ) {
96            $this->notifier->notify( 'Error scheduling cron', sprintf( 'Failed to schedule %s with args: %s', $hook, wp_json_encode( $args ) ) );
97        }
98        return $ok;
99    }
100
101    /**
102     * Bail out if conditions aren't met. Conditions include:
103     * - If the post is being autosaved.
104     * - If the post status is not 'publish', 'draft', or 'pending'
105     * - If the post does not have the category with ID 12 (which is the "Health" category).
106     *
107     * @param WP_Post $post The post object.
108     * @param bool    $update Whether this is an update or a new post.
109     * @return bool True if the post should be returned, false otherwise.
110     */
111    private function should_skip( WP_Post $post, bool $update ): bool {
112        $should_skip = false;
113        if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
114            // Quick early return if this is an autosave.
115            return true;
116        }
117
118        if ( $update && ! in_array( $post->post_status, array( 'publish', 'draft', 'pending' ), true ) ) {
119            $should_skip = true;
120        }
121        if ( 'services' === $post->post_type && ! has_term( 12, 'category', $post ) ) {
122            $should_skip = true;
123        }
124        return $should_skip;
125    }
126}