<?php
/**
* SocialEngine
*
* @category Application_Core
* @package Fields
* @copyright Copyright 2006-2020 Webligo Developments
* @license http://www.socialengine.com/license/
* @version $Id: Core.php 9910 2013-02-14 19:22:15Z john $
* @author John
*/
/**
* @category Application_Core
* @package Fields
* @copyright Copyright 2006-2020 Webligo Developments
* @license http://www.socialengine.com/license/
* @author John
*/
class Fields_Api_Core extends Core_Api_Abstract
{
// Properties
/**
* @var array An array of table objects
*/
protected $_tables = array();
/**
* @var array Contains information about the various field types
*/
protected $_fieldTypeInfo;
// Tables
/**
* Gets a typed table class
*
* @param string $type The item type, i.e. user
* @param string $name The name of the table, i.e. fields, map, options, values
* @return Engine_Db_Table
*/
public function getTable($type, $name)
{
$type = $this->getFieldType($type);
if( !isset($this->_tables[$type][$name]) )
{
$this->_tables[$type][$name] = Fields_Model_DbTable_Abstract::factory($type, $name);
}
return $this->_tables[$type][$name];
}
// Data
/**
* Gets the rowset of field-option mapping for the specified field system class
*
* @param Core_Model_Item_Abstract|string $spec The field system class
* @return Engine_Db_Table_Rowset
*/
public function getFieldsMaps($type)
{
return $this->getTable($this->getFieldType($type), 'maps')->getMaps();
}
/**
* Gets the rowset of field metadata for the specified field system class
*
* @param Core_Model_Item_Abstract|string $spec The field system class
* @return Engine_Db_Table_Rowset
*/
public function getFieldsMeta($type)
{
return $this->getTable($this->getFieldType($type), 'meta')->getMeta();
}
/**
* Gets the rowset of option metadata for the specified field system class
*
* @param Core_Model_Item_Abstract|string $spec The field system class
* @return Engine_Db_Table_Rowset
*/
public function getFieldsOptions($type, $params = array())
{
return $this->getTable($this->getFieldType($type), 'options')->getOptions($params);
}
/**
* Gets the search index table row for the specified item
*
* @param Core_Model_Item_Abstract $spec
* @return Engine_Db_Table_Row|null
*/
public function getFieldsSearch($spec)
{
return $this->getTable($this->getFieldType($spec), 'search')->getSearch($spec);
}
/**
* Gets the rowset of value data for the specified item
*
* @param Core_Model_Item_Abstract $spec The field system class
* @return Engine_Db_Table_Rowset
*/
public function getFieldsValues($spec)
{
return $this->getTable($this->getFieldType($spec), 'values')->getValues($spec);
}
public function removeItemValues($spec)
{
$this->getTable($this->getFieldType($spec), 'values')->removeItemValues($spec);
$this->getTable($this->getFieldType($spec), 'search')->removeItemValues($spec);
return $this;
}
public function getMatchingItems($type, $field, $pattern, $match = null)
{
return Engine_Api::_()->getItemMulti($type, $this->getMatchingItemIds($type, $field, $pattern, $match));
}
public function getMatchingItemIds($type, $field, $pattern, $match = null)
{
$type = $this->getFieldType($type);
$field = $this->getField($field, $type);
// Mmm hacking
if( $match == 'date' ) {
$match = 'range';
}
// If match not given, try to infer pattern type
if( null === $match ) {
// range
if( is_array($pattern) && (isset($match['min']) || isset($match['max'])) ) {
$match = 'range';
}
// list
else if( is_array($pattern) ) {
$match = 'list';
}
// exact
else if( is_scalar($pattern) ) {
if( is_string($pattern) && trim($pattern, '%') != $pattern ) {
$match = 'like';
} else {
$match = 'exact';
}
}
// unknown
else {
throw new Fields_Model_Exception('Unknown pattern type: ' . gettype($pattern));
}
} else if( !engine_in_array($match, array('range', 'list', 'exact')) ) {
throw new Fields_Model_Exception('Unknown match type: ' . $match);
}
// Prepare query
$table = $this->getTable($type, 'values');
$select = new Zend_Db_Select($table->getAdapter());
$select
->from($table->info('name'), 'item_id')
->where('field_id = ?', $field->field_id);
// Try to do preliminary matching
if( $match == 'exact' ) {
$select->where('value = ?', $pattern);
} else if( $match == 'like' ) {
$select->where('value LIKE ?', $pattern);
} else if( $match == 'list' ) {
$select->where('value IN(?)', array_values($pattern));
} else if( $match == 'range' ) {
if( !empty($pattern['min']) ) {
$select->where('value >= ?', $pattern['min']);
}
if( !empty($pattern['max']) ) {
$select->where('value >= ?', $pattern['max']);
}
} else {
throw new Fields_Model_Exception('Unknown match type: ' . $match);
}
// Get data
$ids = array();
$ids_raw = $select->query()->fetchAll();
foreach( $ids_raw as $id_raw ) {
$ids[] = $id_raw['item_id'];
}
return $ids;
}
// Structures
/**
* Generates a flattened array structure of all fields (generally for signup)
*
* @param Core_Model_Item_Abstract|string $spec The field system class
* @param int $parent The field id to generate the structure from
* @return array
*/
public function getFieldsStructureFull($spec, $parent_field_id = null, $parent_option_id = null)
{
$type = $this->getFieldType($spec);
$structure = array();
foreach( $this->getFieldsMaps($type)->getRowsMatching('field_id', (int) $parent_field_id) as $map ) {
// Skip maps that don't match parent_option_id (if provided)
if( null !== $parent_option_id && $map->option_id != $parent_option_id ) {
continue;
}
// Get child field
$field = $this->getFieldsMeta($type)->getRowMatching('field_id', $map->child_id);
if( empty($field) ) {
continue;
}
// Add to structure
$structure[$map->getKey()] = $map;
// Get children
if( $field->canHaveDependents() ) {
$structure += $this->getFieldsStructureFull($spec, $map->child_id);
}
}
return $structure;
}
/**
* Generates a flattened array structure of only the fields that apply to the
* specified item based on it's current values
*
* @param Core_Model_Item_Abstract $spec The item to use for generation
* @param int $parent The field id to start with
* @return array
*/
public function getFieldsStructurePartial($spec, $parent_field_id = null)
{
// Spec must be a item for this one
if( !($spec instanceof Core_Model_Item_Abstract) )
{
throw new Fields_Model_Exception("First argument of getFieldsValues must be an instance of Core_Model_Item_Abstract");
}
$type = $this->getFieldType($spec);
$parentMeta = null;
$parentValue = null;
// Get current field values
if( $parent_field_id ) {
$parentMeta = $this->getFieldsMeta($type)->getRowMatching('field_id', $parent_field_id);
$parentValueObject = $parentMeta->getValue($spec);
if( is_array($parentValueObject) ) {
$parentValue = array();
foreach( $parentValueObject as $parentValueObjectSingle ) {
$parentValue[] = $parentValueObjectSingle->value;
}
} else if( is_object($parentValueObject) ) {
$parentValue = $parentValueObject->value;
}
}
// Build structure
$structure = array();
foreach( $this->getFieldsMaps($spec)->getRowsMatching('field_id', (int) $parent_field_id) as $map ) {
// Parent value does not match id
if( $parent_field_id ) {
if( !is_object($parentMeta) ) {
continue;
} else if( is_array($parentValue) && !engine_in_array($map->option_id, $parentValue) ) {
continue;
} else if( null !== $parentValue && is_scalar($parentValue) && $parentValue != $map->option_id ) {
continue;
}
}
// Get child field
$field = $this->getFieldsMeta($type)->getRowMatching('field_id', $map->child_id);
if( empty($field) ) {
continue;
}
// Add to structure
$structure[$map->getKey()] = $map;
// Get dependents
if( $field->canHaveDependents() )
{
$structure += $this->getFieldsStructurePartial($spec, $field->field_id);
}
}
return $structure;
}
/**
* Returns an array of top-level fields (usually just the type)
*
* @param Core_Model_Item_Abstract|string $spec The field system class
* @return array
*/
public function getFieldsStructureParent($spec)
{
return $this->getFieldsMaps($spec)->getRowsMatching('field_id', 0);
}
public function getFieldsStructureSearch($spec, $parent_field_id = null, $parent_option_id = null, $showGlobal = true)
{
$type = $this->getFieldType($spec);
$structure = array();
foreach( $this->getFieldsMaps($type)->getRowsMatching('field_id', (int) $parent_field_id) as $map ) {
// Skip maps that don't match parent_option_id (if provided)
if( null !== $parent_option_id && $map->option_id != $parent_option_id ) {
continue;
}
// Get child field
$field = $this->getFieldsMeta($type)->getRowMatching('field_id', $map->child_id);
if( empty($field) ) {
continue;
}
// Add to structure
if( $field->search ) {
//if( $field->search == 2 && $showGlobal ) {
// $structure[$map->getKey()] = $map;
//} else if( !$showGlobal ) {
$structure[$map->getKey()] = $map;
//}
}
// Get children
if( $field->canHaveDependents() ) {
$structure += $this->getFieldsStructureSearch($spec, $map->child_id, null, $showGlobal);
}
}
return $structure;
}
public function getFieldStructureTop($spec)
{
$type = $this->getFieldType($spec);
$structure = array();
foreach( $this->getFieldsMaps($type)->getRowsMatching('field_id', 0) as $map ) {
$structure[] = $map;
}
return $structure;
}
// Aliasing
/**
* Gets all of the current field values by alias / value
* @param Core_Model_Item_Abstract $spec
* @return array
*/
public function getFieldsValuesByAlias(Core_Model_Item_Abstract $spec)
{
$values = array();
$structure = $this->getFieldsStructurePartial($spec);
foreach( $structure as $key => $map )
{
$meta = $this->getFieldsMeta($spec)->getRowMatching('field_id', $map->child_id);
if( is_object($meta) && !empty($meta->alias) )
{
$value = $meta->getValue($spec);
if( is_object($value) )
{
$values[$meta->alias] = $value->value;
}
else if( is_array($value) )
{
$vals = array();
foreach( $value as $sval ) {
if( is_object($sval) ) $vals[] = $sval->value;
}
$values[$meta->alias] = $vals;
}
else
{
$values[$meta->alias] = null;
}
}
}
return $values;
}
/**
* Gets field objects by alias
*
* @param Core_Model_Item_Abstract $spec
* @return array
*/
public function getFieldsObjectsByAlias($spec, $alias = null)
{
$fields = array();
if( $spec instanceof Core_Model_Item_Abstract ) {
$structure = $this->getFieldsStructurePartial($spec);
} else {
$structure = $this->getFieldsStructureFull($spec);
}
foreach( $structure as $key => $map )
{
$meta = $this->getFieldsMeta($spec)->getRowMatching('field_id', $map->child_id);
if( is_object($meta) && !empty($meta->alias) ) {
if( null === $alias || $meta->alias == $alias ) {
$fields[$meta->alias] = $meta;
}
}
}
return $fields;
}
// Edit
public function createMap($field, $option)
{
$field = $this->getField($field);
$option = $this->getOption($option, $field->getFieldType());
return $this->getTable($field->getFieldType(), 'maps')->createMap($field, $option);
}
public function deleteMap($map)
{
if( !($map instanceof Fields_Model_Map) ) {
throw new Fields_Model_Exception('Not a map');
}
$this->getTable($map->getFieldType(), 'maps')->deleteMap($map);
return $this;
}
public function createField($type, $options)
{
return $this->getTable($this->getFieldType($type), 'meta')->createMeta($options);
}
/**
* Edit a field
*
* @param string $type The field system class
* @param Fields_Model_DbRow_Meta|int $field The row or id of the field
* @param array $options The field options
* @return Fields_Model_Api
*/
public function editField($type, $field, $options)
{
$type = $this->getFieldType($type);
$field = $this->getField($field, $type);
return $this->getTable($type, 'meta')->editMeta($field, $options);
}
public function deleteField($type, $field)
{
$type = $this->getFieldType($type);
$field = $this->getField($field, $type);
$this->getTable($type, 'meta')->deleteMeta($field);
return $this;
}
/**
* Adds an option to a select-type field
*
* @param string $type The field system class
* @param Fields_Model_DbRow_Meta|int $field The row or id of the field
* @param array $options The options for the option
* @return int The id of the new option
* @throws Fields_Model_Exception If the field cannot have dependents
*/
public function createOption($type, $field, $options)
{
$type = $this->getFieldType($type);
$field = $this->getField($field, $type);
return $this->getTable($type, 'options')->createOption($field, $options);
}
/**
* Edit an option
*
* @param string $type The field system class
* @param Fields_Model_DbRow_Meta|int $option The row or id of the option
* @param array $options The option options
* @return Fields_Model_Api
*/
public function editOption($type, $option, $options)
{
$type = $this->getFieldType($type);
$option = $this->getOption($option, $type);
$this->getTable($type, 'options')->editOption($option, $options);
return $this;
}
public function deleteOption($type, $option)
{
$type = $this->getFieldType($type);
$option = $this->getOption($option, $type);
$this->getTable($type, 'options')->deleteOption($option);
return $this;
}
// Type stuff
public function getFieldInfo($type = null, $value = null)
{
if( null === $this->_fieldTypeInfo ) {
$this->_fieldTypeInfo = include APPLICATION_PATH . '/application/modules/Fields/settings/fields.php';
}
switch( $type ) {
case null:
return $this->_fieldTypeInfo;
break;
case 'categories':
return $this->_fieldTypeInfo['categories'];
break;
case 'fields':
return $this->_fieldTypeInfo['fields'];
break;
case 'dependents':
return $this->_fieldTypeInfo['dependents'];
break;
}
// Get base field info
if( isset($this->_fieldTypeInfo['fields'][$type]) ) {
$info = $this->_fieldTypeInfo['fields'][$type];
if( !empty($info['base']) && !empty($this->_fieldTypeInfo['fields'][$info['base']]) ) {
$info = array_merge($this->_fieldTypeInfo['fields'][$info['base']], $info);
}
if( null !== $value ) {
if( isset($info[$value]) ) {
return $info[$value];
}
} else {
return $info;
}
}
return null;
}
public function inflectFieldType($string)
{
return str_replace(' ', '', ucwords(str_replace('_', ' ', $string)));
}
// Search
public function checkSearchIndex($field)
{
$this->getTable($field->getFieldType(), 'search')->checkSearchIndex($field);
return $this;
}
public function updateSearch($spec, $values)
{
$type = $this->getFieldType($spec);
return $this->getTable($type, 'search')->updateSearch($spec, $values);
}
public function getSearchQuery($spec, $params)
{
$type = $this->getFieldType($spec);
return $this->getTable($type, 'search')->getSearchQuery($params);
}
public function getSearchSelect($spec, $params)
{
$type = $this->getFieldType($spec);
return $this->getTable($type, 'search')->getSearchSelect($params);
}
// Utility
/**
* Get a field row
*
* @param Core_Model_Item_Abstract|string $type
* @param Fields_Model_DbRow_Meta|int The row or id of the field
* @return Fields_Model_DbRow_Meta
* @throws Fields_Model_Exception If the row could not be found
*/
public function getField($field, $type = null, $throw = true)
{
$type = $this->getFieldType($type, false);
if( $field instanceof Fields_Model_Meta ) {
if( null !== $type && $field->getFieldType() != $type ) {
if( $throw ) {
throw new Fields_Model_Exception("Field type did not match passed type");
} else {
return null;
}
}
return $field;
}
if( null === $type ) {
if( $throw ) {
throw new Fields_Model_Exception("No field type");
} else {
return null;
}
}
if( is_numeric($field) ) {
$field = $this->getFieldsMeta($type)->getRowMatching('field_id', $field);
if( null === $field || !($field instanceof Fields_Model_Meta) ) {
if( $throw ) {
throw new Fields_Model_Exception("Missing field");
} else {
return null;
}
}
return $field;
}
if( $throw ) {
throw new Fields_Model_Exception("Invalid field identifier");
} else {
return null;
}
}
/**
* Get a field row
*
* @param Core_Model_Item_Abstract|string $type
* @param Fields_Model_DbRow_Option|int The row or id of the option
* @return Fields_Model_DbRow_Option
* @throws Fields_Model_Exception If the row could not be found
*/
public function getOption($option, $type = null, $throw = true)
{
$type = $this->getFieldType($type, false);
if( $option instanceof Fields_Model_Option ) {
if( null !== $type && $option->getFieldType() != $type ) {
if( $throw ) {
throw new Fields_Model_Exception("Option type did not match passed type");
} else {
return null;
}
}
return $option;
}
if( null === $type ) {
if( $throw ) {
throw new Fields_Model_Exception("No option type");
} else {
return null;
}
}
if( is_numeric($option) ) {
$option = $this->getFieldsOptions($type)->getRowMatching('option_id', $option);
if( null === $option || !($option instanceof Fields_Model_Option) ) {
if( $throw ) {
throw new Fields_Model_Exception("Missing option");
} else {
return null;
}
}
return $option;
}
if( $throw ) {
throw new Fields_Model_Exception("Invalid option identifier");
} else {
return null;
}
}
public function getMap($field, $option, $type = null, $throw = true)
{
$type = $this->getFieldType($type, false);
if( $field instanceof Fields_Model_Meta ) {
if( $type && $type != $field->getFieldType() ) {
if( $throw ) {
throw new Fields_Model_Exception("Map type did not match passed type");
} else {
return null;
}
} else {
$type = $field->getFieldType();
}
// Convert to int
$field = $field->field_id;
}
if( $option instanceof Fields_Model_Option ) {
if( $type && $type != $option->getFieldType() ) {
if( $throw ) {
throw new Fields_Model_Exception("Map type did not match passed type");
} else {
return null;
}
} else {
$type = $option->getFieldType();
}
$option = $option->field_id;
}
if( !is_numeric($field) || !is_numeric($option) ) {
if( $throw ) {
throw new Fields_Model_Exception("missing option or field");
} else {
return null;
}
}
$map = $this->getFieldsMaps($type)->getRowMatching(array('child_id' => $field, 'option_id' => $option));
if( !($map instanceof Fields_Model_Map) ) {
if( $throw ) {
throw new Fields_Model_Exception("missing map");
} else {
return null;
}
}
return $map;
}
/**
* Simply returns the passed type, or the type of the item if an item
*
* @param Core_Model_Item_Abstract|string $type
* @return string
* @throws Fields_Model_Exception If the first argument is neither a string
* nor an instance of Core_Model_Item_Abstract
*/
public function getFieldType($dat, $throw = true)
{
if( $dat instanceof Fields_Model_Abstract ) {
return $dat->getFieldType();
} else if( $dat instanceof Core_Model_Item_Abstract ) {
return $dat->getType();
} else if( is_string($dat) ) {
return $dat;
} else {
if( $throw ) {
throw new Fields_Model_Exception("Unable to get field type");
} else {
return null;
}
}
}
static public function getFieldPrivacyOptions()
{
return array(
'everyone' => 'Everyone',
'registered' => 'Members',
'friends' => 'Friends',
'self' => 'Only Me',
);
}
}