Class representing a submission.
Description
Source
File: src/db-objects/submissions/submission.php
class Submission extends Model { use Sitewide_Model_Trait; /** * Submission ID. * * @since 1.0.0 * @var int */ protected $id = 0; /** * ID of the form this submission applies to. * * @since 1.0.0 * @var int */ protected $form_id = 0; /** * User ID of the user who created this submission, if any. * * @since 1.0.0 * @var int */ protected $user_id = 0; /** * Timestamp of when the submission was created. * * @since 1.0.0 * @var int */ protected $timestamp = 0; /** * IP address of the user who created this submission. * * @since 1.0.0 * @var string */ protected $remote_addr = ''; /** * Submission user key. * * @since 1.0.0 * @var string */ protected $user_key = ''; /** * Submission status identifier. * * @since 1.0.0 * @var string */ protected $status = 'completed'; /** * Internal submission value storage. * * @since 1.0.0 * @var array|null */ protected $values = null; /** * Internal element value storage. * * @since 1.0.0 * @var array|null */ protected $element_values = null; /** * Magic isset-er. * * Checks whether a property is set. * * @since 1.0.0 * * @param string $property Property to check for. * @return bool True if the property is set, false otherwise. */ public function __isset( $property ) { if ( 'values' === $property ) { return true; } if ( preg_match( '/^element_([0-9]+)_([a-z_]+)_value$/U', $property, $matches ) ) { $values = $this->get_element_values_data(); if ( isset( $values[ $matches[1] ] ) && isset( $values[ $matches[1] ][ $matches[2] ] ) ) { return true; } return false; } return parent::__isset( $property ); } /** * Magic getter. * * Returns a property value. * * @since 1.0.0 * * @param string $property Property to get. * @return mixed Property value, or null if property is not set. */ public function __get( $property ) { if ( 'values' === $property ) { if ( is_array( $this->values ) ) { return $this->values; } return $this->get_submission_values_data(); } if ( preg_match( '/^element_([0-9]+)_([a-z_]+)_value$/U', $property, $matches ) ) { $values = $this->get_element_values_data(); if ( isset( $values[ $matches[1] ] ) && isset( $values[ $matches[1] ][ $matches[2] ] ) ) { return $values[ $matches[1] ][ $matches[2] ]; } return null; } return parent::__get( $property ); } /** * Magic setter. * * Sets a property value. * * @since 1.0.0 * * @param string $property Property to set. * @param mixed $value Property value. */ public function __set( $property, $value ) { if ( 'values' === $property ) { $this->set_submission_values_data( $value ); return; } if ( preg_match( '/^element_([0-9]+)_([a-z_]+)_value$/U', $property, $matches ) ) { if ( ! isset( $this->values ) ) { $this->values = $this->get_submission_values_data(); } $original_value = $value; $value = (array) $value; $indexes_to_remove = array(); foreach ( $this->values as $index => $item ) { $item['field'] = ! empty( $item['field'] ) ? $item['field'] : '_main'; if ( (int) $matches[1] !== (int) $item['element_id'] || $matches[2] !== $item['field'] ) { continue; } if ( ! empty( $value ) ) { $this->values[ $index ]['value'] = array_shift( $value ); } else { $indexes_to_remove[] = $index; } } foreach ( $indexes_to_remove as $index_to_remove ) { unset( $this->values[ $index_to_remove ] ); } foreach ( $value as $single_value ) { $this->values[] = array( 'id' => 0, 'element_id' => (int) $matches[1], 'field' => $matches[2], 'value' => $single_value, ); } $this->get_element_values_data(); $this->element_values[ $matches[1] ][ $matches[2] ] = $original_value; } parent::__set( $property, $value ); } /** * Returns the parent form for the submission. * * @since 1.0.0 * * @return Form|null Parent form, or null if none set. */ public function get_form() { if ( empty( $this->form_id ) ) { return null; } return $this->manager->get_parent_manager( 'forms' )->get( $this->form_id ); } /** * Returns all submission values that belong to the submission. * * @since 1.0.0 * * @param array $args Optional. Additional query arguments. Default empty array. * @return Submission_Value_Collection List of submission values. */ public function get_submission_values( $args = array() ) { if ( empty( $this->id ) ) { return $this->manager->get_child_manager( 'submission_values' )->get_collection( array(), 0, 'objects' ); } $args = wp_parse_args( $args, array( 'number' => -1, 'submission_id' => $this->id, ) ); return $this->manager->get_child_manager( 'submission_values' )->query( $args ); } /** * Synchronizes the model with the database by storing the currently pending values. * * If the model is new (i.e. does not have an ID yet), it will be inserted to the database. * * @since 1.0.0 * * @return true|WP_Error True on success, or an error object on failure. */ public function sync_upstream() { $result = parent::sync_upstream(); if ( is_wp_error( $result ) ) { return $result; } if ( null !== $this->values ) { $manager = $this->manager->get_child_manager( 'submission_values' ); $ids = array(); foreach ( $this->values as $item ) { $submission_value = null; if ( ! empty( $item['id'] ) ) { $submission_value = $manager->get( $item['id'] ); if ( $submission_value && $this->id !== $submission_value->submission_id ) { continue; } } if ( ! $submission_value ) { $submission_value = $manager->create(); } $submission_value->submission_id = $this->id; $submission_value->field = $item['field']; $submission_value->value = $item['value']; if ( ! empty( $item['element_id'] ) ) { $submission_value->element_id = $item['element_id']; } $submission_value->sync_upstream(); if ( empty( $submission_value->id ) ) { continue; } $ids[] = $submission_value->id; } if ( ! empty( $ids ) ) { $old_values = $this->get_submission_values( array( 'exclude' => $ids, ) ); foreach ( $old_values as $old_value ) { $old_value->delete(); } } $this->values = null; } return $result; } /** * Synchronizes the model with the database by fetching the currently stored values. * * If the model contains unsynchronized changes, these will be overridden. This method basically allows * to reset the model to the values stored in the database. * * @since 1.0.0 * * @return true|WP_Error True on success, or an error object on failure. */ public function sync_downstream() { $result = parent::sync_downstream(); if ( is_wp_error( $result ) ) { return $result; } $this->values = null; return $result; } /** * Deletes the model from the database. * * @since 1.0.0 * * @return true|WP_Error True on success, or an error object on failure. */ public function delete() { $submission_values = $this->get_submission_values(); foreach ( $submission_values as $submission_value ) { $submission_value->delete(); } return parent::delete(); } /** * Formats the submission date and time. * * @since 1.0.0 * * @param string $format Datetime format string. Will be localized. * @param bool $gmt Optional. Whether to return as GMT. Default true. * @return string Formatted date and time. */ public function format_datetime( $format, $gmt = true ) { $timestamp = $this->timestamp; if ( ! $gmt ) { $timestamp = strtotime( get_date_from_gmt( date( 'Y-m-d H:i:s', $timestamp ) ) ); } return date_i18n( $format, $timestamp ); } /** * Sets the current container for the submission. * * The form ID of the container must match the submission's form ID. * * @since 1.0.0 * * @param Container|null $container Container, or null if the current container data should be unset. */ public function set_current_container( $container ) { if ( null === $container ) { $this->__set( 'current_container_id', null ); return true; } if ( $container->form_id !== $this->form_id ) { return false; } $this->__set( 'current_container_id', $container->id ); return true; } /** * Returns the current container for the submission. * * @since 1.0.0 * * @return Container|null Current container, or null on failure. */ public function get_current_container() { $container_id = (int) $this->__get( 'current_container_id' ); if ( ! empty( $container_id ) ) { $container = $this->manager->get_parent_manager( 'forms' )->get_child_manager( 'containers' )->get( $container_id ); } else { $form = $this->get_form(); if ( ! $form ) { return null; } $container_collection = $form->get_containers( array( 'number' => 1, 'orderby' => array( 'sort' => 'ASC', ), 'no_found_rows' => true, ) ); if ( 1 > count( $container_collection ) ) { return null; } $container = $container_collection[0]; $this->set_current_container( $container ); } return $container; } /** * Returns the next container for the submission, if there is one. * * @since 1.0.0 * * @return Container|null Next container, or null if there is none. */ public function get_next_container() { $container = $this->get_current_container(); if ( ! $container ) { return null; } $form = $this->get_form(); if ( ! $form ) { return null; } $container_collection = $form->get_containers( array( 'number' => 1, 'sort' => array( 'greater_than' => $container->sort, ), 'orderby' => array( 'sort' => 'ASC', ), 'no_found_rows' => true, ) ); if ( 1 > count( $container_collection ) ) { return null; } return $container_collection[0]; } /** * Returns the previous container for the submission, if there is one. * * @since 1.0.0 * * @return Container|null Previous container, or null if there is none. */ public function get_previous_container() { $container = $this->get_current_container(); if ( ! $container ) { return null; } $form = $this->get_form(); if ( ! $form ) { return null; } $container_collection = $form->get_containers( array( 'number' => 1, 'sort' => array( 'lower_than' => $container->sort, ), 'orderby' => array( 'sort' => 'DESC', ), 'no_found_rows' => true, ) ); if ( 1 > count( $container_collection ) ) { return null; } return $container_collection[0]; } /** * Gets all element values set for the submission. * * The returned element values data array is an multi-dimensional associative array * where the keys are element IDs and their inner keys field slugs belonging to the element * with the actual value for the element and field combination as value. * * @since 1.0.0 * * @return array Element values data set for the submission. */ public function get_element_values_data() { if ( ! $this->primary_property_value() ) { return array(); } if ( ! isset( $this->element_values ) ) { $this->element_values = $this->manager->get_element_values_data_for_submission( $this ); } return $this->element_values; } /** * Adds an error to the submission. * * @since 1.0.0 * * @param int $element_id Element ID to add the error for. * @param string $code Error code. * @param string $message Error message. * @return bool True on success, false on failure. */ public function add_error( $element_id, $code, $message ) { if ( ! array_key_exists( 'errors', $this->pending_meta ) ) { if ( $this->primary_property_value() ) { $this->pending_meta['errors'] = $this->manager->get_meta( $this->primary_property_value(), 'errors', true ); } else { $this->pending_meta['errors'] = array(); } } if ( ! is_array( $this->pending_meta['errors'] ) ) { $this->pending_meta['errors'] = array(); } if ( ! isset( $this->pending_meta['errors'][ $element_id ] ) || ! is_array( $this->pending_meta['errors'][ $element_id ] ) ) { $this->pending_meta['errors'][ $element_id ] = array(); } $this->pending_meta['errors'][ $element_id ][ $code ] = $message; return true; } /** * Removes an error from the submission. * * @since 1.0.0 * * @param int $element_id Element ID to remove an error for. * @param string $code Error code to remove. * @return bool True on success, false on failure. */ public function remove_error( $element_id, $code ) { if ( ! array_key_exists( 'errors', $this->pending_meta ) ) { if ( $this->primary_property_value() ) { $this->pending_meta['errors'] = $this->manager->get_meta( $this->primary_property_value(), 'errors', true ); } else { $this->pending_meta['errors'] = array(); } } if ( ! is_array( $this->pending_meta['errors'] ) ) { return false; } if ( ! is_array( $this->pending_meta['errors'][ $element_id ] ) ) { return false; } if ( ! is_array( $this->pending_meta['errors'][ $element_id ][ $code ] ) ) { return false; } unset( $this->pending_meta['errors'][ $element_id ][ $code ] ); if ( empty( $this->pending_meta['errors'][ $element_id ] ) ) { unset( $this->pending_meta['errors'][ $element_id ] ); } if ( empty( $this->pending_meta['errors'] ) ) { $this->pending_meta['errors'] = null; } return true; } /** * Gets all errors, for the entire submission or a specific element. * * @since 1.0.0 * * @param int|null $element_id Optional. If an element ID is given, only errors for that element are returned. * @return array If $element_id is given, the array of `$code => $message` pairs is returned. Otherwise the array * of `$element_id => $errors` pairs is returned. */ public function get_errors( $element_id = null ) { if ( ! array_key_exists( 'errors', $this->pending_meta ) ) { if ( ! $this->primary_property_value() ) { return array(); } $errors = $this->manager->get_meta( $this->primary_property_value(), 'errors', true ); } else { $errors = $this->pending_meta['errors']; } if ( ! is_array( $errors ) ) { return array(); } if ( null !== $element_id ) { if ( empty( $errors[ $element_id ] ) ) { return array(); } return $errors[ $element_id ]; } return $errors; } /** * Checks whether there are errors, for the entire submission or a specific element. * * @since 1.0.0 * * @param int|null $element_id Optional. If an element ID is given, only errors for that element are returned. * @return bool True if there are errors, false otherwise. */ public function has_errors( $element_id = null ) { $errors = $this->get_errors( $element_id ); return ! empty( $errors ); } /** * Resets all errors, for the entire submission or a specific element. * * @since 1.0.0 * * @param int|null $element_id Optional. If an element ID is given, only errors for that element are reset. * @return bool True on success, false on failure. */ public function reset_errors( $element_id = null ) { if ( ! array_key_exists( 'errors', $this->pending_meta ) ) { if ( ! $this->primary_property_value() ) { return false; } $this->pending_meta['errors'] = $this->manager->get_meta( $this->primary_property_value(), 'errors', true ); } if ( ! is_array( $this->pending_meta['errors'] ) ) { if ( null !== $this->pending_meta['errors'] ) { unset( $this->pending_meta['errors'] ); } return false; } if ( null !== $element_id ) { if ( ! is_array( $this->pending_meta['errors'] ) || empty( $this->pending_meta['errors'][ $element_id ] ) ) { return false; } unset( $this->pending_meta['errors'][ $element_id ] ); return true; } $this->pending_meta['errors'] = null; return true; } /** * Gets submission values data for the submission, to be used with the field manager. * * @since 1.0.0 * * @return array Submission values data. */ protected function get_submission_values_data() { $data = array(); foreach ( $this->get_submission_values() as $submission_value ) { $data[] = array( 'id' => $submission_value->id, 'element_id' => $submission_value->element_id, 'field' => $submission_value->field, 'value' => $submission_value->value, ); } return $data; } /** * Sets submission values data for the submission, to be used with the field manager. * * @since 1.0.0 * * @param array $value Submission values data. */ protected function set_submission_values_data( $value ) { if ( ! is_array( $value ) ) { return; } $data = array(); foreach ( $value as $item ) { $data[] = array( 'id' => ! empty( $item['id'] ) ? (int) $item['id'] : 0, 'element_id' => ! empty( $item['element_id'] ) ? (int) $item['element_id'] : 0, 'field' => ! empty( $item['field'] ) ? sanitize_key( $item['field'] ) : '', 'value' => ! empty( $item['value'] ) ? $item['value'] : '', ); } $this->values = $data; } /** * Returns a list of internal properties that are not publicly accessible. * * When overriding this method, always make sure to merge with the parent result. * * @since 1.0.0 * * @return array Property blacklist. */ protected function get_blacklist() { $blacklist = parent::get_blacklist(); $blacklist[] = 'values'; $blacklist[] = 'element_values'; return $blacklist; } }
Changelog
Version | Description |
---|---|
1.0.0 | Introduced. |
Methods
- __get — Magic getter.
- __isset — Magic isset-er.
- __set — Magic setter.
- add_error — Adds an error to the submission.
- delete — Deletes the model from the database.
- format_datetime — Formats the submission date and time.
- get_blacklist — Returns a list of internal properties that are not publicly accessible.
- get_current_container — Returns the current container for the submission.
- get_element_values_data — Gets all element values set for the submission.
- get_errors — Gets all errors, for the entire submission or a specific element.
- get_form — Returns the parent form for the submission.
- get_next_container — Returns the next container for the submission, if there is one.
- get_previous_container — Returns the previous container for the submission, if there is one.
- get_submission_values — Returns all submission values that belong to the submission.
- get_submission_values_data — Gets submission values data for the submission, to be used with the field manager.
- has_errors — Checks whether there are errors, for the entire submission or a specific element.
- remove_error — Removes an error from the submission.
- reset_errors — Resets all errors, for the entire submission or a specific element.
- set_current_container — Sets the current container for the submission.
- set_submission_values_data — Sets submission values data for the submission, to be used with the field manager.
- sync_downstream — Synchronizes the model with the database by fetching the currently stored values.
- sync_upstream — Synchronizes the model with the database by storing the currently pending values.