gocodebox/lifterlms

View on GitHub
includes/models/model.llms.question.choice.php

Summary

Maintainability
A
3 hrs
Test Coverage
B
81%
<?php
/**
 * LifterLMS Quiz Question Model
 *
 * @package LifterLMS/Models/Classes
 *
 * @since 3.16.0
 * @version 7.4.1
 */

defined( 'ABSPATH' ) || exit;

/**
 * LLMS_Question_Choice model class
 *
 * @since 3.16.0
 */
class LLMS_Question_Choice {

    protected $prefix = '_llms_choice_';

    private $id = null;

    private $data = array();

    private $question    = null;
    private $question_id = null;

    /**
     * Constructor
     *
     * @param    int          $question_id  WP Post ID of the choice's parent LLMS_Question
     * @param    array|string $data_or_id   array of choice data or the choice ID string
     * @since    3.16.0
     * @version  3.16.0
     */
    public function __construct( $question_id, $data_or_id = array() ) {

        // Ensure the question is valid.
        if ( $this->set_question( $question_id ) ) {

            // If an ID is passed in, load the question data from post meta.
            if ( ! is_array( $data_or_id ) ) {
                $data_or_id = str_replace( $this->prefix, '', $data_or_id );
                $data_or_id = get_post_meta( $this->question_id, $this->prefix . $data_or_id, true );
            }

            // Hydrate with postmeta data or array of data passed in.
            if ( is_array( $data_or_id ) && isset( $data_or_id['id'] ) ) {
                $this->hydrate( $data_or_id );
            }
        }

    }

    /**
     * Creates a new question
     *
     * @param    array $data  question data array
     * @return   self
     * @since    3.16.0
     * @version  3.16.0
     */
    public function create( $data ) {

        $this->id = uniqid();
        return $this->update( $data )->save();

    }

    /**
     * Delete a choice
     *
     * @return   boolean
     * @since    3.16.0
     * @version  3.16.0
     */
    public function delete() {
        return delete_post_meta( $this->question_id, $this->prefix . $this->id );
    }

    /**
     * Determine if the choice that's been requested actually exists
     *
     * @return   boolean
     * @since    3.16.0
     * @version  3.16.0
     */
    public function exists() {
        return ( $this->id );
    }

    /**
     * Retrieve a piece of choice data by key
     *
     * @param    string $key      name of the data to be retrieved
     * @param    mixed  $default  default value if key isn't set
     * @return   mixed
     * @since    3.16.0
     * @version  3.16.0
     */
    public function get( $key, $default = '' ) {

        if ( isset( $this->data[ $key ] ) ) {
            return $this->data[ $key ];
        }

        return $default;

    }

    /**
     * Generic choice getter which automatically uses correct functions based on choice type
     *
     * @return   string
     * @since    3.16.0
     * @version  3.16.0
     */
    public function get_choice() {
        if ( 'image' === $this->get( 'choice_type' ) ) {
            return $this->get_image();
        }
        return $this->get( 'choice' );
    }

    /**
     * Retrieve an image for picture choices
     *
     * @return   [type]
     * @since    3.16.0
     * @version  3.16.0
     */
    public function get_image() {
        if ( 'image' !== $this->get( 'choice_type' ) ) {
            return '';
        }
        $img = $this->get( 'choice' );
        if ( is_array( $img ) && isset( $img['id'] ) ) {
            return wp_get_attachment_image( $img['id'], 'full' );
        }
        return '';
    }

    /**
     * Retrieve all of the choice data as an array
     *
     * @return   array
     * @since    3.16.0
     * @version  3.16.0
     */
    public function get_data() {
        return $this->data;
    }

    /**
     * Retrieve an instance of an LLMS_Question for questions parent
     *
     * @return   obj
     * @since    3.16.0
     * @version  3.16.0
     */
    public function get_question() {
        return $this->question;
    }

    /**
     * Retrieve the question ID for the given choice
     *
     * @return   int
     * @since    3.16.0
     * @version  3.16.0
     */
    public function get_question_id() {
        return $this->question_id;
    }

    /**
     * Setup the id and data variables
     *
     * @param    array $data  array of question data
     * @return   void
     * @since    3.16.0
     * @version  3.16.0
     */
    private function hydrate( $data ) {
        $this->id         = $data['id'];
        $this->data['id'] = $this->id;
        $this->update( $data );
    }

    /**
     * Determine if the choice is correct
     *
     * @return   bool
     * @since    3.16.0
     * @version  3.16.0
     */
    public function is_correct() {
        return filter_var( $this->get( 'correct' ), FILTER_VALIDATE_BOOLEAN );
    }

    /**
     * Save $this->data to the postmeta table
     *
     * @return   void
     * @since    3.16.0
     * @version  3.16.0
     */
    public function save() {

        $this->data['id'] = $this->id; // Always ensure the ID is set when saving data.
        $update           = update_post_meta( $this->question_id, $this->prefix . $this->id, $this->data );

        return ( $update );

    }

    /**
     * Set a piece of data by key.
     *
     * @since 3.16.0
     * @since 7.4.1 Check `$type['choices']` is an array before trying to access it as such.
     *
     * @param string $key Name of the key to set.
     * @param mixed  $val Value to set.
     * @return self
     */
    public function set( $key, $val ) {

        // Don't set the ID.
        if ( 'id' === $key ) {
            return $this;
        }

        switch ( $key ) {

            case 'choice_type':
                if ( ! in_array( $val, array( 'text', 'image' ) ) ) {
                    $val = 'text';
                }
                break;

            case 'correct':
                $val = filter_var( $val, FILTER_VALIDATE_BOOLEAN );
                break;

            case 'marker':
                $type = $this->get_question()->get_question_type();
                if ( is_array( $type['choices'] ?? false ) ) {
                    $markers = $type['choices']['markers'];
                    if ( ! in_array( $val, $markers ) ) {
                        $val = $markers[0];
                    }
                }
                break;

            case 'choice':
            default:
                if ( is_array( $val ) ) {
                    $val = array_map( 'sanitize_text_field', $val );
                } else {
                    $val = wp_kses_post( $val );
                }
                break;

        }

        $this->data[ $key ] = $val;
        return $this;

    }

    /**
     * Sets question-related data from constructor
     *
     * @param    int $id  WP Post ID of the question's parent question
     * @return   boolean
     * @since    3.16.0
     * @version  3.16.0
     */
    public function set_question( $id ) {
        $question = llms_get_post( $id );
        if ( $question && is_a( $question, 'LLMS_Question' ) ) {
            $this->question    = $question;
            $this->question_id = $id;
            return true;
        }

        return false;

    }

    /**
     * Update multiple data by key=>val pairs
     *
     * @param    array $data  array of data to set
     * @return   self
     * @since    3.16.0
     * @version  3.16.0
     */
    public function update( $data = array() ) {

        foreach ( $data as $key => $val ) {
            $this->set( $key, $val );
        }
        return $this;

    }

}