Developer Documentation

The information provided here is intended for user with adequate PHP knowledge, Please do not just copy and paste the example code since it will not work and probably will break your site.

If you need customization doesn’t know PHP well enough to perform it, kindly Contact us to receive custom job quotation.

What is Property Entity Factory?
Property entity factory is the PHP class wrapper for building a property entity object based on data from a property post data. The class will build all relevant type entity, field entities and attribute entities related to a single post entry.

It is recommended to use Property Factory object when trying to Edit, Delete, Save and Display a single property post.

The object will also provide a factory method for building display markup for front end display, metabox field display and search field display. Use the VTCore_Property_Entity_Property::BuildFieldDisplayObject Method when building object.

Why use property factory?
The factory provides easy grouping for all different kind of fields and attributes based on a single property post and property type, without this object, you will need to invoke different kind of field classes and attributes when trying to perform updating, deleting and displaying value from each of fields and attributes.

Relation to Post object
The plugin will automatically create the property factory object when a post is loaded, this is done via hook action the_post. The global object $post if invoked correctly should have $post->property injected into it and will have the full property factory object in it.

Example on using the factory


/**
 * Assuming that we are in the property post type loop
 */
global $post;

if (isset($post->property) && is_a($post->property, 'VTCore_Property_Entity_Property')) {

  // Try to render all available fields, display mode
  $post->property->render('fields');

  // Try to render all available attributes, display mode
  $post->property->render('attributes');

  // Grab property_location field search object
  $object = $post->property->buildFieldDisplayObject('property_location', array(), 'search');
}

/**
 * try to manually initiated the factory for post id 1
 */
$factory = new VTCore_Property_Entity_Property(array('id' => 1));

// Initialize Fields, attributes and types
$factory
  ->initializeType()
  ->initializeAttributes()
  ->initializeFields();

// Load the stored _property_metadata for the current post
$factory
  ->loadMetaData();

// Alter the metadata value, 2 is a dummy tax term
$factory
  ->mutate('meta.fields.property_status.status', 2); 

// Save the fields, attributes, type back to database
$factory
  ->saveMetaFields()
  ->saveMetaAttributes()
  ->saveMetaType();

// Save the post _property_metadata back to database
$factory
  ->saveMetaData();

What is Type Entity?
Type entity is a PHP class for managing property type entity Create Revise Update and Delete process. The purpose of this class is to act as the general gateway object for other to use.

What is property type?
Property type is actually a Taxonomy with Terms that is mapped in a single options table entry as serialized array. The maps will act as the key for storing types additional options such as bundled Fields and Attributes. Which allows user to have different settings and bundles for each different kind of types.
Available Hook actions
Property plugin Type Entity is alterable via hooks :

Hook Action – vtcore_property_type_init
This hook is invoked as the last action when the type entity is constructed.

Hook Action – vtcore_property_type_after_entity_load
This action is invoked after the type entity object finished loading type entity data from database into the object.

Hook Action – vtcore_property_type_before_entity_save
This action is triggered before the type entity object attempt to update the stored entity entry in the database.

Hook Action – vtcore_property_type_after_entity_save
This action is triggered after the type entity object performing saving type array into the database regardless whether the saving process is success or failed.

Hook Action – vtcore_property_type_before_entity_delete
This action is triggered before the actual data removal from the database.

Hook Action – vtcore_property_type_after_entity_delete
This action is triggered after the data is removed from the database.

Example on altering types entity


/**
 * Hooking into type entity init hook 
 */
add_action('vtcore_property_type_init', 'alter_my_type'); 

function alter_my_type($object) { 

  /**
   * Use dotted notation and object getter
   * Just a simple demonstration to change all the 
   * type form label
   */
  $object->mutate('label', 'Newly altered label');
 
}

What is Field Entity?
Field entity is a PHP class for wrapping all operations such as creating, updating and disabling the property field system. It is recommended to use the class object when trying to manipulate the field system as many logic and hooks is will be invoked inside the class object.

What is property field?
Property field is a programatically coded features for a property type, The fields is mapped by property field system array which is alterable via hooks. Only use Field system when you have a complex features that a property attributes system cannot handle. For example you need a field of price for your property types which can handle currencies, can be searched with maximum and minimum price entry etc.

Where does a property field stores its data?
Property field system data entry can be stored in taxonomy as taxonomy terms either hierarchical or not, custom post meta and a serialized _property_data post meta. It all depends on how you register the field and what is defined in the field configuration array.
Available Hook actions
Property plugin Type Entity is alterable via hooks :

Hook Action – vtcore_property_field_init
This hook is invoked as the last action when the field entity object constructed.

Hook Action – vtcore_property_field_after_entity_load
This action is invoked after the field entity object loads the field system configuration array from field system configuration objects (VTCore_Property_Fields())

Hook Action – vtcore_property_field_after_entity_save
This action is triggered after the field entity object saving the field system configuration array to WordPress options vtcore_property_fields database table

Hook Action – vtcore_property_field_after_entity_delete
This action is triggered after the field entity object removes current field custom configuration from database. Note that the only way to truly remove a hard coded field completely from the field system is using hook action vtcore_property_field_register

Example on altering field entity


/**
 * Hooking into type entity init hook 
 */
add_action('vtcore_property_field_init', 'alter_my_field'); 

function alter_my_field($object) { 

  /**
   * Use dotted notation and object getter
   * Just a simple demonstration to change all the 
   * type form label
   */
  $object->mutate('label', 'Newly altered label');
 

  /**
   * Changing visibility globally
   * Turn off the icon visibility
   */
  $object->mutate('visibility.icon', false);
}

What is Attributes Entity?
Attributes entity object is a PHP class for managing the Property Attributes entry to a property type. It is recommended to use this class when need to alter a Property Attributes programatically only.

What is property attributes?
Property Attributes is a simple version of Property Fields, where user can create custom attributes via front end editor without the need to know PHP. As it is intended to be as simple as possible, the features is also limited comparing to a true field.

Where does a property attributes stores its data?
All attributes will store its data in form or a taxonomy terms, it can be a terms id or free tagging terms depending on the selection of the attributes metabox form settings format.
Available Hook actions
Property plugin Attribute Entity is alterable via hooks :

Hook Action – vtcore_property_attribute_init
This hook is invoked as the last action when the attribute entity is constructed.

Hook Action – vtcore_property_attribute_after_entity_load
This action is invoked after the type entity object finished loading attribute entity data from WordPress options table with row key vtcore_property_attributes into the object.

Hook Action – vtcore_property_type_before_entity_save
This action is triggered after the attribute entity object performing saving into the database regardless whether the saving process is success or failed.

Hook Action – vtcore_property_attribute_after_entity_delete
This action is triggered after the attribute data is removed from the database and also after the deleted attributes removed from all property type entity entry and all WordPress post entry.

Example on altering attributes entity


/**
 * Hooking into type entity init hook 
 */
add_action('vtcore_property_attribute_init', 'alter_my_attribute'); 

function alter_my_type($object) { 

  /**
   * Use dotted notation and object getter
   * Just a simple demonstration to change all the 
   * type form label
   */
  $object->mutate('label', 'Newly altered label');

  /**
   * Don't allow all attributes to be searched
   */
  $object->mutate('search', false);
 
}
  

Property plugin Fields System is alterable via hooks :

Hook Filter – vtcore_property_alter_fields
This filter allows you to modify, remove or add fields into the field registration, It will be fired when VTCore_Property_Fields object is created

Hook Action – vtcore_property_field_registered
This action is triggered after fields object is created and it is the last action in the register method, the field object is passed by reference

Hook Action – vtcore_property_field_load
This action is triggered after fields array loaded from database, the field object is passed by reference

Hook Action – vtcore_property_field_save
This action is triggered after fields array saved to database, the field object is passed by reference

Hook Action – vtcore_property_field_delete
This action is triggered after fields array deleted from database, the field object is passed by reference

Example on altering fields array


/** Adding the new Field to Property Field system **/
add_filter('vtcore_property_alter_fields', 'register_my_field'); 

function register_my_field($registry) { 

  /**
   * Modify the field registry via $registry 
   * This will disable the property status
   */
  $registry['property_status']['enabled'] = false;

  return $registry; 
}
  

/**
 * We use object instead of direct array via _register action 
 * Remember that this action is called after everything else (eg. WPML integration) 
 */
add_action('vtcore_property_field_register', 'modify_field_object'); 

function modify_field_object($fieldObject) { 

  /**
   * $fieldObject is the field object and it inherits the VTCore_Wordpress_Models_Config 
   * Thus it can use dotted notation for the array key drilling and has the getter and setter 
   * Method inherited as well. 
   */

  /** this will disable the property status **/
  $fieldObject->mutate('property_status.enabled', false);
} 

The Field System Array is designed to store field related information, the information will be used in various objects under different circumstances. Like for example the translation key will be used as the array key map when integrating field dynamic strings to WPML plugin for translation.


    /**
     * Example of valid property fields registration
     * array, you can inject this array via field related hooks
     */
    $registry['property_duration'] = array(
      'field' => 'property_duration',
      'name' => __('Duration', 'victheme_property'),
      'label' => __('Availability', 'victheme_property'),
      'enabled' => true,
      'icons' => array(
        'icon' => 'real8',
        'family' => 'proicon',
      ),
      'settings' => array(
        'separator' => array(
          'mode' => 'data',
          'type' => 'VTCore_Bootstrap_Form_BsText',
          'context' => array(
            'text' => __('Separator', 'victheme_property'),
            'description' => __('Configure the text for the separator element', 'victheme_property'),
            'value' => __('To', 'victheme_property'),
          ),
        ),
        'format' => array(
          'mode' => 'data',
          'type' => 'VTCore_Bootstrap_Form_BsText',
          'context' => array(
            'text' => __('Date Format', 'victheme_property'),
            'description' => __('Configure the text for the separator element', 'victheme_property'),
            'value' => 'd/m/Y',
          ),
        ),
      ),
      'storage' => array(
        'from' => array(
          'type' => 'meta',
          'mode' => 'timestamp',
          'target' => '_property_duration_from',
        ),
        'end' => array(
          'type' => 'meta',
          'mode' => 'timestamp',
          'target' => '_property_duration_end',
        ),
      ),
      'translation' => array(
        'search.label',
        'search.settings.from.context.value',
        'search.settings.separator.context.value',
        'search.settings.end.context.value',
        'settings.separator.context.value',
        'metabox.settings.from.context.value',
        'metabox.settings.end.context.value',
      ),
      'search' => array(
        'label' => __('Availability', 'victheme_property'),
        'settings' => array(
          'from' => array(
            'type' => 'VTCore_Bootstrap_Form_BsText',
            'context' => array(
              'text' => __('From Date Form Label', 'victheme_property'),
              'description' => __('Configure the text for the from date search form label text', 'victheme_property'),
              'value' => __('Availability Start Date', 'victheme_property'),
            ),
          ),
          'separator' => array(
            'type' => 'VTCore_Bootstrap_Form_BsText',
            'context' => array(
              'text' => __('Separator Date Form Label', 'victheme_property'),
              'description' => __('Configure the text for the separator search form label text', 'victheme_property'),
              'value' => __('to', 'victheme_property'),
            ),
          ),
          'end' => array(
            'type' => 'VTCore_Bootstrap_Form_BsText',
            'context' => array(
              'text' => __('End Date Form Label', 'victheme_property'),
              'description' => __('Configure the text for the end date search form label text', 'victheme_property'),
              'value' => __('Availability End Date', 'victheme_property'),
            ),
          ),
        ),
      ),
      'metabox' => array(
        'label' => __('Duration', 'victheme_property'),
        'description' => __('Set the availability duration for this property.', 'victheme_property'),
        'settings' => array(
          'from' => array(
            'type' => 'VTCore_Bootstrap_Form_BsText',
            'context' => array(
              'text' => __('From Form Label', 'victheme_property'),
              'description' => __('Configure the text for the from form label text', 'victheme_property'),
              'value' => __('From Date', 'victheme_property'),
            ),
          ),
          'end' => array(
            'type' => 'VTCore_Bootstrap_Form_BsText',
            'context' => array(
              'text' => __('End Form Label', 'victheme_property'),
              'description' => __('Configure the text for the end form label text', 'victheme_property'),
              'value' => __('End Date', 'victheme_property'),
            ),
          ),
        ),
      ),
      'objects' => array(
        'metabox' => 'VTCore_Property_Form_Metabox_Duration',
        'search' => 'VTCore_Property_Form_Search_Duration',
        'display'=> 'VTCore_Property_Element_Duration',
      ),
      'visibility' => array(
        'from' => true,
        'separator' => true,
        'end' => true,
        'label' => true,
        'icon' => true,
        'search' => true,
      ),
      'group' => 'property_info',
      'weight' => 1,
    );

field

The field key is the field machine name, this field string entry must be unique

name

This field key is for storing the field human readable name, this is most likely to be used for the front end field label or as a fallback if the field doesn’t have any special labeling for search, metabox and front end display.

enabled

This field is for turning on / off a field instantly.

label

The string text for the field form label element, this will be served as the default fallback value if the field hasn’t defined string for metabox and search form label field.

icons

The icon array context for VTCore_Wordpress_Element_WpIcon object. By using the VTCore icon objects you can access all the icons defined via VTCore Icon Factory and if any theme or plugin register additional icon set to the library, it will also available for field to use.

settings

It is possible to define custom settings context that will be used when creating the field management form, this way you can always create a field that can be customized via WordPress backend easily.

storage

Define the field data storage behavior, you can have multiple storage point in a single field. Valid storage types are taxonomy terms, metadata and data (serialized data in a single _property_data post meta.

translation

Define the array key in dotted notation format referencing the field array context for marking that the array value defined in the defined array key will be passed through WPML hooks for registering it as a custom strings ready for translation.

translation

Define the array key in dotted notation format referencing the field array context for marking that the array value defined in the defined array key will be passed through WPML hooks for registering it as a custom strings ready for translation.

search

The search branch in the array context is for mapping context that will be passed to the field configuration GUI and the field form when in search mode.

visibility

Define what should be displayed globally, this will be used when building field front end display, search form and metabox form.

objects

Define the PHP class name that will be used when building the front end display, search form field and metabox form field. The property entity responsible for building the object will assume that the class name is either already loaded or eligible for autoloading. if it isn’t available due to any reasons the field will not build the output for field mode.

group

This is used mainly in metabox mode only, the purpose is to determine on which accordion group that the related plugin should be inserted to.

weight

The weight for ordering the field when rendered

When a registered field is called via Property Factory to build its markup for front end display purposes, the field system will search for the entry value of objects.display in the field configuration array and when found the entry value will be treated as the class name when building the display object.

The display object should extend VTCore_Property_Models_Element and follow the rules of VTCore_Html_Base / VTCore_Bootstrap_Element_Base when building the markup.

Example of a simple display object


 /**
 * Class for building the field status
 *
 * @author jason.xie@victheme.com
 *
 */
class VTCore_Property_Element_Status
extends VTCore_Property_Models_Element {

  /**
   * Define the default context array
   * The context will be merged with user supplied context
   */
  protected $context = array(
    'type' => 'div',
    'attributes' => array(
      'class' => array(
        'property-element-status',
      ),
    ),

    // The field values
    'status' => false,

    // Icons
    'icon' => false,
    'icon_type' => false,
    'label' => false,

    'value_attributes' => array(
      'type' => 'div',
      'search' => 'value',
      'attributes' => array(
        'class' => array(
          'property-value',
        ),
      ),
    ),

    'label_attributes' => array(
      'type' => 'div',
      'attributes' => array(
        'class' => array(
          'property-label',
        ),
      ),
    ),
  );



  /**
   * Overriding parent method
   * @see VTCore_Html_Base::buildElement()
   */
  public function buildElement() {

    // Removing context with empty value, including '', false, 0
    $this->cleanEmptyContext();

    // Build size
    if ($this->getContext('status')) {

      // Call the original method, this is invoked to ensure
      // if in the future parent class has implemented new
      // logic it will get called.
      parent::buildElement();

      // Let the property element models handle the icon and label
      $this->buildLabel();

      // Grab status terms
      $term_ids = $this->getContext('status');
      if (is_array($term_ids)) {
        $term_ids = array_shift($term_ids);
      }

      // Build status
      $term = get_term($term_ids, 'property_status');

      if (!empty($term) && !is_wp_error($term)) {
        $this
          ->addChildren(new VTCore_Html_Element($this->getContext('value_attributes')))
          ->lastChild()
          ->setText($term->name);
      }
    }
    
    // Seems that the field is trully empty, dont build any markup!
    else {
      $this->setType(false);
    }
  }


  /**
   * Extending base method for checking if element is trully empty or not
   */
  public function checkVisibility() {
    return (bool) $this->getContext('status');
  }
}

When a registered field is called via Property Factory to build its markup for search field display purposes, the field system will search for the entry value of objects.search in the field configuration array and when found, the entry value will be treated as the class name when building the search field object.

The search object can utilize VTCore_Property_Models_Search if the search field is simple and only going to be a textfield ($context.element.bstext) or taxonomy terms select box ($context.element.wpterms).

For a more customized and complex search form field, it is suggested to extend VTCore_Bootstrap_Form_Base directly.

The search element follow the rules of VTCore_Html_Form / VTCore_Bootstrap_Form_Base when building the markup via object.

Example of a simple search object using taxonomy select field


/**
 * Class for building the property status
 * search field
 *
 * @author jason.xie@victheme.com
 *
 */
class VTCore_Property_Form_Search_Status
extends VTCore_Property_Models_Search {

  protected $context = array(
    'type' => 'div',
    'attributes' => array(
      'class' => array(
        'property-search-group',
        'property-search-status',
      ),
    ),
    'queryid' => '',

    // This is related to pre_get_posts and serve as the query key.
    'searchkey' => '_property_status',

    // Inform the parent class that we are building
    // A taxonomy select form, use bstext if you want
    // a text field instead.
    'element' => 'wpterms',

    // Inform the parent class that the select field
    // need terms from taxonomy property_status
    'taxonomies' => array('property_status'),
  );
}

Example on building a complex search form field


/**
 * Class for building the property landsize
 * search field
 *
 * @author jason.xie@victheme.com
 *
 */
class VTCore_Property_Form_Search_LandSize
extends VTCore_Bootstrap_Form_Base {

  protected $context = array(
    'type' => 'div',
    'attributes' => array(
      'class' => array(
        'property-search-group',
        'property-search-landsize',
      ),
    ),
    'queryid' => '',
  );


  /**
   * Overriding Parent Method
   * @see VTCore_Bootstrap_Form_Base::assignContext()
   */
  protected function assignContext() {

    // We need larger column for this kind of search form
    // This is configured via backend
    if ($this->getContext('alignment') == 'horizontal') {
      $this->addContext('grids', array(
        'columns' => array(
          'mobile' => 12,
          'tablet' => 8,
          'small' => 8,
          'large' => 8,
        )
      ));
    }

    parent::assignContext();
  }

  /**
   * Overriding Parent Method
   * @see VTCore_Html_Base::buildElement()
   */
  public function buildElement() {

    // Call parent method
    parent::buildElement();

    // the dimension field default grid size
    $dimensionGrids = array(
      'columns' => array(
        'mobile' => 12,
        'tablet' => 4,
        'small' => 4,
        'large' => 4,
      ),
    );

    // The default grid size
    $grids = array(
      'columns' => array(
        'mobile' => 12,
        'tablet' => 6,
        'small' => 6,
        'large' => 6,
      ),
    );

    // Mutate the field element grid size when 2 field is visible
    if ($this->getContext('visibility.dimension')) {
      $grids = array(
        'columns' => array(
          'mobile' => 12,
          'tablet' => 4,
          'small' => 4,
          'large' => 4,
        ),
      );
    }

    // Build a grid wrapper if both landsize and dimension 
    // is visible, this is for inline form markup
    if ($this->getContext('visibility.landsize') || $this->getContext('visibility.dimension')) {

      $this->addChildren(new VTCore_Bootstrap_Grid_BsRow());

      if ($this->getContext('visibility.label')) {
        $this
          ->lastChild()
          ->addChildren(new VTCore_Bootstrap_Grid_BsColumn(array(
            'type' => 'div',
            'grids' => array(
              'columns' => array(
                'mobile' => 12,
                'tablet' => 12,
                'small' => 12,
                'large' => 12,
              ),
            ),
          )))
          ->lastChild()
          ->addChildren(new VTCore_Form_Label(array(
            'text' => $this->getContext('search.label'),
            'attributes' => array(
              'class' => array('form-group-label'),
            ),
          )));
      }
    }

    // User configure to display the landsize text field
    if ($this->getContext('visibility.landsize')) {
      $this
        ->lastChild()
        ->addChildren(new VTCore_Bootstrap_Form_BsText(array(
          'text' => $this->getContext('search.settings.minsize.context.value'),
          'name' => $this->getContext('queryid') . '_property_landsize_minimum',
          'grids' => $grids
        )))
        ->addChildren(new VTCore_Bootstrap_Form_BsText(array(
          'text' => $this->getContext('search.settings.maxsize.context.value'),
          'name' => $this->getContext('queryid') . '_property_landsize_maximum',
          'grids' => $grids
        )));
    }

    // User configured to build the dimension settings
    if ($this->getContext('visibility.dimension')) {
      $this
        ->lastChild()
        ->addChildren(new VTCore_Wordpress_Form_WpTerms(array(
          'text' => $this->getContext('search.settings.dimension.context.value'),
          'name' => $this->getContext('queryid') . '_property_dimension[]',
          'taxonomies' => array('property_dimension'),
          'grids' => $dimensionGrids,
        )));
    }
  }

}

When a custom property field is marked as allowed for searching there are couple of extra steps needed to register the query variables so it will be eligible when WP_Query is processing the actual query.

Step One – Register the searched variable key
When Building the Property Field – search object , you have to define the form field name attribute . That attribute will be passed as either POST or GET entry key when user is submitting the form back to the server.

WordPress will ignore the passed key if it hasn’t been registered yet. Invoke hook filter query_vars to register the key to WordPress

Example of adding new query vars


/**
 * Invoking the query_vars filter hook
 */
add_filter('query_vars', 'my_new_variables');
function my_new_variables($variables) {
  $variables[] = 'property_awesome';
  return $variables;
}

Step Two – Registering the search logic

After succcessfully registering the query vars key into wordpress, the next step is to register the logic to process the value passed with the query vars key

You will need to register the logic via hook action pre_get_posts and hook action vtcore_wordpress_loop_process_query

Example to add new logic to pre_get_posts


/**
 * Invoking the query_vars filter hook
 */
add_filter('pre_get_posts', 'alter_query');
function alter_query($query) {

  // Only concern about main query and on front page
  if ($query->is_main_query() != false && !is_admin()) {
    
    // add the logic here
    if (isset($_REQUEST['mykey']) {
      $query->set('tax_query', ....);
    }

  }
}

Example to add new logic to vtcore_wordpress_loop_process_query


/**
 * Invoking the vtcore_wordpress_loop_process_query action hook
 */
add_filter('vtcore_wordpress_loop_process_query', 'alter_query');
function alter_query($object) {
  
  // We are on ajax mode
  if (isset($object->request['data']) {
    $request = VTCore_Wordpress_Utility::wpParseLargeArgs($object->request['data'];
    
    // VTCore_Wordpress_Element_WpLoop object will always have custom loop id
    // and need that custom unique loop id to differentiate one loop object to another
    // The property search form will always append that unique id in front of every
    // form field name attributes to determine the right target to search / filter.
    $id = $object->getContext('id');

    // Check if we got the right request for our custom field
    if (isset($request[$id . '_my_custom_field'])) {
       
      // Perform the custom logic
      // @see wordpress taxQuery and metaQuery for valid array format
      $object->taxQuery[] = ......
      $object->metaQuery[] = .......
    }
  }
}