<?php if ( ! defined( 'ABSPATH' ) ) exit;
* Class NF_Abstracts_Model
* The ID is assigned after being saved to the database.
* The temporary ID is used to reference unsaved objects
* before they are stored in the database.
* The type is used to pragmatically identify the type
* of an object without inspecting the class.
* The ID of the parent object.
protected $_parent_id = '';
* The type of the parent object.
protected $_parent_type = '';
* The name of the table where the model objects are stored.
protected $_table_name = '';
* The name of the table where the object settings are stored.
protected $_meta_table_name = '';
protected $_relationships_table = 'nf3_relationships';
* A list of settings that are stored in the main table as columns.
* These settings are NOT stored in the meta table.
protected $_columns = array();
* A list of settings that are stored in the meta table.
protected $_settings = array();
* The last results returned by a query.
protected $_results = array();
* A Flag for using or bypassing caching.
protected $_cache = TRUE;
* A Flag for testing whether or not we've completed stage 1 of our db.
private $db_stage_1_complete = TRUE;
//-----------------------------------------------------
//-----------------------------------------------------
* NF_Abstracts_Model constructor.
public function __construct( $db, $id = NULL, $parent_id = '' )
* Injected the Database Dependency
* Assign Database Tables using the DB prefix
$this->_table_name = $this->_db->prefix . $this->_table_name;
$this->_meta_table_name = $this->_db->prefix . $this->_meta_table_name;
$this->_relationships_table = $this->_db->prefix . $this->_relationships_table;
* Check if the ID is Permanent (int) or Temporary (string)
if( is_numeric( $id ) ) {
$this->_id = absint( $id );
* Set the Parent ID for context
$this->_parent_id = $parent_id;
return intval( $this->_id );
public function get_tmp_id()
public function get_type()
* Get a single setting with a default fallback
* @param bool $default optional
* @return string|int|bool
public function get_setting( $setting, $default = FALSE )
if( isset( $this->_settings[ $setting ] )){
$return = $this->_settings[ $setting ];
$return = $this->get_settings($setting);
if( is_array( $return ) && empty( $return ) ) $return = false;
return ( $return !== false ) ? $return : $default;
* @param string ...$only returns a subset of the object's settings
public function get_settings()
// If the ID is not set, then we cannot pull settings from the Database.
if( ! $this->_id ) return $this->_settings;
if( ! $this->_settings && 'field' == $this->_type ) {
$results = $wpdb->get_results(
SELECT Meta.key, Meta.value
FROM $this->_table_name as Object
JOIN $this->_meta_table_name as Meta
ON Object.id = Meta.parent_id
WHERE Object.id = '$this->_id'
foreach( $results as $result ) {
$this->_settings[ $key ] = $result[ 'value' ];
SELECT `label`, `key`, `type`
WHERE `id` = '$this->_id'
if( ! is_wp_error( $field ) ){
$this->_settings[ 'label' ] = $field[ 'label' ];
$this->_settings[ 'key' ] = $field[ 'key' ];
$this->_settings[ 'type' ] = $field[ 'type' ];
$this->_settings[ 'field_label' ] = $field[ 'label' ];
$this->_settings[ 'field_key' ] = $field[ 'key' ];
if( ! $this->_settings ) {
if( WPN_Helper::use_cache() ) {
$form_cache = WPN_Helper::get_nf_cache( $this->_parent_id );
if ('field' == $this->_type) {
if (isset($form_cache['fields'])) {
foreach ($form_cache['fields'] as $object) {
if ($this->_id != $object['id']) continue;
$this->update_settings($object['settings']);
// Only query if settings haven't been already queried or cache is FALSE.
if( ! $this->_settings || ! $this->_cache ) {
// Build query syntax from the columns property.
$columns = '`' . implode( '`, `', $this->_columns ) . '`';
$results = $this->_db->get_row(
FROM `$this->_table_name`
* If the query returns results then
* assign settings using the column name as the setting key.
foreach ($this->_columns as $column) {
$this->_settings[$column] = $results->$column;
$meta_select_fields = "SELECT `key`, `value`";//, `meta_key`,
// Query settings from the meta table.
$meta_results = $this->_db->get_results(
FROM `$this->_meta_table_name`
WHERE `parent_id` = $this->_id
// Assign settings to the settings property.
foreach ($meta_results as $meta) {
// If we don't already have a value from the main table...
// OR If that value was NULL...
if ( ! isset( $this->_settings[ $meta->key ] ) || NULL == $this->_settings[ $meta->key ] ) {
// TODO: Update this logic after removal of original meta columns.
// Set the value from meta.
$this->_settings[ $meta->key ] = $meta->value;
// Un-serialize queried settings results.
foreach( $this->_settings as $key => $value ){
$this->_settings[ $key ] = maybe_unserialize( $value );
// Check for passed arguments to limit the returned settings.
if ( $only && is_array($only)
// And if the array is NOT multidimensional
&& (count($only) == count($only, COUNT_RECURSIVE))) {
// If only one setting, return a single value
if( 1 == count( $only ) ){
if( isset( $this->_settings[ $only[0] ] ) ) {
return $this->_settings[$only[0]];
// Flip the array to match the settings property
$only_settings = array_flip( $only );
// Return only the requested settings
return array_intersect_key( $this->_settings, $only_settings );
public function update_setting( $key, $value )
$this->_settings[ $key ] = $value;
public function update_settings( $data )
if( is_array( $data ) ) {
foreach ($data as $key => $value) {
$this->update_setting($key, $value);
* Delete the object, its children, and its relationships.
if( ! $this->get_id() ) return;
// Delete the object from the model's table
$results[] = $this->_db->delete(
// Delete settings from the model's meta table.
$results[] = $this->_db->delete(
'parent_id' => $this->_id
// Query for child objects using the relationships table.
$children = $this->_db->get_results(
SELECT child_id, child_type
FROM $this->_relationships_table
WHERE parent_id = $this->_id
AND parent_type = '$this->_type'
// Delete each child model
foreach( $children as $child ) {
$model = Ninja_Forms()->form()->get_model( $child->child_id, $child->child_type );
// Delete all relationships
$this->_relationships_table,
'parent_id' => $this->_id,
'parent_type' => $this->_type
// return False if there are no query errors.
return in_array( FALSE, $results );
* @param string $parent_id
public function find( $parent_id = '', array $where = array() )
// Build the query using the $where argument
$query = $this->build_meta_query( $parent_id, $where );
// Get object IDs from the query
$ids = $this->_db->get_col( $query );
// Get the current class name
$class = get_class( $this );
// Instantiate a new object for each ID
$results[] = $object = new $class( $this->_db, $id, $parent_id );
if( ! WPN_Helper::use_cache() ) {
$results = $this->get_object_settings($results);
// Return an array of objects
public function get_object_settings($obj_array ) {
$generic_object_array = array();
foreach($obj_array as $obj) {
$id_array[] = $obj->get_id();
$generic_object_array[$obj->get_id()] = $obj;
if( 0 < count($id_array) ) {
$obj_query = "SELECT `id`, `" . implode( '`, `', $this->_columns) . "` FROM {$this->_table_name} WHERE id IN (" . implode(',', $id_array) . ")";