<?php
/**
* File containing the class Tables_Based_Grade_Repository.
*
* @package sensei
*/
namespace Sensei\Internal\Quiz_Submission\Grade\Repositories;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
use Sensei\Internal\Quiz_Submission\Answer\Models\Answer_Interface;
use Sensei\Internal\Quiz_Submission\Grade\Models\Tables_Based_Grade;
use Sensei\Internal\Quiz_Submission\Grade\Models\Grade_Interface;
use Sensei\Internal\Quiz_Submission\Submission\Models\Submission_Interface;
use wpdb;
/**
* Class Tables_Based_Grade_Repository
*
* @internal
*
* @since 4.16.1
*/
class Tables_Based_Grade_Repository implements Grade_Repository_Interface {
/**
* WordPress database object.
*
* @var wpdb
*/
private $wpdb;
/**
* Constructor.
*
* @internal
*
* @param wpdb $wpdb WordPress database object.
*/
public function __construct( wpdb $wpdb ) {
$this->wpdb = $wpdb;
}
/**
* Creates a new grade.
*
* @internal
*
* @param Submission_Interface $submission The submission.
* @param Answer_Interface $answer The answer.
* @param int $question_id The question ID.
* @param int $points The points.
* @param string|null $feedback The feedback.
*
* @return Grade_Interface The grade.
*/
public function create( Submission_Interface $submission, Answer_Interface $answer, int $question_id, int $points, ?string $feedback = null ): Grade_Interface {
/**
* Filters the question ID when quiz grade is created.
*
* @hook sensei_quiz_grade_create_question_id
*
* @since 4.23.1
*
* @param {int} $question_id The question ID.
* @return {int} The question ID.
*/
$question_id = apply_filters( 'sensei_quiz_grade_create_question_id', $question_id );
/**
* Filters the answer ID when quiz grade is created.
*
* @hook sensei_quiz_grade_create_answer_id
*
* @since 4.23.1
*
* @param {int} $answer_id The answer ID.
* @param {string} $context The context.
* @return {int} The answer ID.
*/
$answer_id = apply_filters( 'sensei_quiz_grade_create_answer_id', $answer->get_id(), 'tables' );
$current_date = new \DateTimeImmutable( 'now', new \DateTimeZone( 'UTC' ) );
$date_format = 'Y-m-d H:i:s';
$this->wpdb->insert(
$this->get_table_name(),
[
'answer_id' => $answer_id,
'question_id' => $question_id,
'points' => $points,
'feedback' => $feedback,
'created_at' => $current_date->format( $date_format ),
'updated_at' => $current_date->format( $date_format ),
],
[
'%d',
'%d',
'%d',
is_null( $feedback ) ? null : '%s',
'%s',
'%s',
]
);
return new Tables_Based_Grade(
$this->wpdb->insert_id,
$answer_id,
$question_id,
$points,
$feedback,
$current_date,
$current_date
);
}
/**
* Get all grades for a quiz submission.
*
* @internal
*
* @param int $submission_id The submission ID.
*
* @return Grade_Interface[] An array of grades.
*/
public function get_all( int $submission_id ): array {
/**
* Filter the submission ID when getting all quiz grades.
*
* @hook sensei_quiz_grade_get_all_submission_id
*
* @since 4.23.1
*
* @param {int} $submission_id The submission ID.
* @param {string} $context The context.
* @return {int} The submission ID.
*/
$submission_id = (int) apply_filters( 'sensei_quiz_grade_get_all_submission_id', $submission_id, 'tables' );
$answer_ids = $this->get_answer_ids_by_submission_id( $submission_id );
if ( empty( $answer_ids ) ) {
return [];
}
$placeholders = implode( ', ', array_fill( 0, count( $answer_ids ), '%d' ) );
$grades_query = 'SELECT * FROM ' . $this->get_table_name() . ' WHERE answer_id IN (' . $placeholders . ')';
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$grade_rows = $this->wpdb->get_results( $this->wpdb->prepare( $grades_query, ...$answer_ids ) );
$grades = [];
foreach ( $grade_rows as $grade_row ) {
$grades[] = new Tables_Based_Grade(
$grade_row->id,
$grade_row->answer_id,
$grade_row->question_id,
$grade_row->points,
$grade_row->feedback,
new \DateTimeImmutable( $grade_row->created_at, new \DateTimeZone( 'UTC' ) ),
new \DateTimeImmutable( $grade_row->updated_at, new \DateTimeZone( 'UTC' ) )
);
}
return $grades;
}
/**
* Save multiple grades.
*
* @internal
*
* @param Submission_Interface $submission The submission.
* @param Grade_Interface[] $grades An array of grades.
*/
public function save_many( Submission_Interface $submission, array $grades ): void {
foreach ( $grades as $grade ) {
$this->save( $grade );
}
}
/**
* Delete all grades for a submission.
*
* @internal
*
* @param Submission_Interface $submission The submission.
*/
public function delete_all( Submission_Interface $submission ): void {
/**
* Filters the submission ID when deleting all quiz grades.
*
* @hook sensei_quiz_grade_delete_all_submission_id
*
* @since 4.23.1
*
* @param {int} $submission_id The submission ID.
* @param {string} $context The context.
* @return {int} The submission ID.
*/
$submission_id = (int) apply_filters( 'sensei_quiz_grade_delete_all_submission_id', $submission->get_id(), 'tables' );
$answer_ids = $this->get_answer_ids_by_submission_id( $submission_id );
if ( empty( $answer_ids ) ) {
return;
}
$placeholders = implode( ', ', array_fill( 0, count( $answer_ids ), '%d' ) );
$delete_query = 'DELETE FROM ' . $this->get_table_name() . ' WHERE answer_id IN (' . $placeholders . ')';
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$this->wpdb->query( $this->wpdb->prepare( $delete_query, ...$answer_ids ) );
}
/**
* Save single grade.
*
* @param Grade_Interface $grade The grade.
*/
private function save( Grade_Interface $grade ): void {
$updated_at = new \DateTimeImmutable( 'now', new \DateTimeZone( 'UTC' ) );
$this->wpdb->update(
$this->get_table_name(),
[
'points' => $grade->get_points(),
'feedback' => $grade->get_feedback(),
'updated_at' => $updated_at->format( 'Y-m-d H:i:s' ),
],
[
'id' => $grade->get_id(),
],
[
'%d',
is_null( $grade->get_feedback() ) ? null : '%s',
'%s',
],
[
'%d',
]
);
}
/**
* Get all answer IDs for a submission.
*
* @param int $submission_id The submission ID.
*/
private function get_answer_ids_by_submission_id( int $submission_id ): array {
$answers_query = $this->wpdb->prepare(
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
'SELECT id FROM ' . $this->get_answers_table_name() . ' WHERE submission_id = %d',
$submission_id
);
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
return $this->wpdb->get_col( $answers_query );
}
/**
* Get the quiz grades table name.
*
* @return string
*/
private function get_table_name(): string {
return $this->wpdb->prefix . 'sensei_lms_quiz_grades';
}
/**
* Get the quiz answers table name.
*
* @return string
*/
private function get_answers_table_name(): string {
return $this->wpdb->prefix . 'sensei_lms_quiz_answers';
}
}