<?php
abstract class Install_Import_Version3_AbstractFields extends Install_Import_Version3_Abstract
{
protected $_toTableTruncate = false;
protected $_fromResourceType;
protected $_fromAlternateResourceType;
protected $_toResourceType;
protected $_useProfileType = false;
protected $_profileTypeFieldIdentity;
protected $_profileTypeOptionMap;
protected $_profileTypeSubcatMap;
protected $_profileTypeHeadingMap;
protected $_profileFieldsMap;
protected $_profileFieldsOptionsMap;
protected $_profileDependentFieldsMap;
protected $_profileOrderIndex;
protected $_profileOptionOrderIndex;
protected $_profileSearchColumns;
protected $_profileSearchIndex;
protected $_profileFieldCount = 0;
protected $_profileOptionCount = 0;
protected $_profileSearchCount = 0;
public function __sleep()
{
return array_merge(parent::__sleep(), array(
'_fromResourceType', '_fromAlternateResourceType', '_toResourceType',
'_useProfileType',
'_profileTypeFieldIdentity', '_profileTypeOptionMap',
'_profileTypeSubcatMap', '_profileTypeHeadingMap', '_profileFieldsMap',
'_profileFieldsOptionsMap', '_profileDependentFieldsMap',
'_profileOrderIndex', '_profileOptionOrderIndex', '_profileSearchColumns',
'_profileSearchIndex',
'_profileFieldCount', '_profileOptionCount', '_profileSearchCount',
));
}
protected function _initPost()
{
$this->_fromTable = 'se_' . $this->_fromAlternateResourceType .'s';
$this->_fromColumns = array(
$this->_fromAlternateResourceType . '_id',
$this->_fromAlternateResourceType . '_' . $this->_fromResourceType .'cat_id'
);
$this->_toTable = 'engine4_' . $this->_toResourceType . '_fields_values';
}
protected function _runPre()
{
// Step 1: Prepare data ----------------------------------------------------
$fromDb = $this->getFromDb();
$toDb = $this->getToDb();
// Check all from tables exist
$required_from_tables = array(
'se_' . $this->_fromResourceType . 'cats',
'se_' . $this->_fromResourceType . 'fields',
'se_' . $this->_fromResourceType . 'values',
);
if( !$this->_tableExists($fromDb, $required_from_tables) ) {
throw new Engine_Exception(sprintf('One of the source tables does not exist: %s', join(', ', $required_from_tables)));
}
// Check all to tables exist
$required_to_tables = array(
'engine4_' . $this->_toResourceType . '_fields_meta',
'engine4_' . $this->_toResourceType . '_fields_maps',
'engine4_' . $this->_toResourceType . '_fields_options',
'engine4_' . $this->_toResourceType . '_fields_search',
'engine4_' . $this->_toResourceType . '_fields_values',
);
if( !$this->_tableExists($toDb, $required_to_tables) ) {
throw new Engine_Exception(sprintf('One of the target tables does not exist: %s', join(', ', $required_to_tables)));
}
// Truncate existing tables
$toDb->query('TRUNCATE TABLE' . $toDb->quoteIdentifier('engine4_' . $this->_toResourceType . '_fields_meta'));
$toDb->query('TRUNCATE TABLE' . $toDb->quoteIdentifier('engine4_' . $this->_toResourceType . '_fields_maps'));
$toDb->query('TRUNCATE TABLE' . $toDb->quoteIdentifier('engine4_' . $this->_toResourceType . '_fields_options'));
$toDb->query('TRUNCATE TABLE' . $toDb->quoteIdentifier('engine4_' . $this->_toResourceType . '_fields_search'));
$toDb->query('TRUNCATE TABLE' . $toDb->quoteIdentifier('engine4_' . $this->_toResourceType . '_fields_values'));
// Remove search columns
$searchCols = $toDb->query('SHOW COLUMNS FROM ' . $toDb->quoteIdentifier('engine4_' . $this->_toResourceType . '_fields_search'))->fetchAll();
foreach( $searchCols as $searchCol ) {
if( $searchCol['Field'] != 'item_id' ) {
try {
$toDb->query('ALTER TABLE ' . $toDb->quoteIdentifier('engine4_' . $this->_toResourceType . '_fields_search')
. ' DROP COLUMN ' . $toDb->quoteIdentifier($searchCol['Field']));
} catch( Exception $e ) {
$this->_warning($e->getMessage(), 0);
}
}
}
// Hack to prevent it running again
$this->_runCount += 1;
// -------------------------------------------------------------------------
// Step 2: Import profile types --------------------------------------------
// Create profile type field
if( null === $this->_profileTypeFieldIdentity ) {
$toDb->insert('engine4_' . $this->_toResourceType . '_fields_meta', array(
'type' => 'profile_type',
'label' => 'Profile Type',
'alias' => 'profile_type',
'required' => 1,
'display' => 0,
//'publish' => 0,
'search' => 2,
'order' => 1,
));
$this->_profileTypeFieldIdentity = $toDb->lastInsertId();
$toDb->insert('engine4_' . $this->_toResourceType . '_fields_maps', array(
'field_id' => 0,
'option_id' => 0,
'child_id' => $this->_profileTypeFieldIdentity,
));
$this->_profileFieldCount++;
}
// Add profile type options
if( null === $this->_profileTypeOptionMap &&
null === $this->_profileTypeSubcatMap &&
null === $this->_profileTypeHeadingMap ) {
$this->_profileTypeOptionMap = array();
$this->_profileTypeSubcatMap = array();
$this->_profileTypeHeadingMap = array();
$stmt = $fromDb->select()
->from('se_' . $this->_fromResourceType . 'cats')
->order($this->_fromResourceType . 'cat_order')
->query();
foreach( $stmt->fetchAll() as $data ) {
// Don't add subcategories
if( !empty($data[$this->_fromResourceType . 'cat_dependency']) ) {
$this->_profileTypeSubcatMap[$data[$this->_fromResourceType . 'cat_id']] = $data[$this->_fromResourceType . 'cat_dependency'];
$this->_profileTypeHeadingMap[$data[$this->_fromResourceType . 'cat_id']] = $data;
continue;
}
$title = $this->_getLanguageValue($data[$this->_fromResourceType . 'cat_title']);
$toDb->insert('engine4_' . $this->_toResourceType . '_fields_options', array(
'field_id' => $this->_profileTypeFieldIdentity,
'label' => $title,
));
$this->_profileTypeOptionMap[$data[$this->_fromResourceType . 'cat_id']] = $toDb->lastInsertId();
}
}
// -------------------------------------------------------------------------
// Step 3: Import profile fields -------------------------------------------
if( null === $this->_profileFieldsMap &&
null === $this->_profileFieldsOptionsMap &&
null === $this->_profileDependentFieldsMap &&
null === $this->_profileOrderIndex &&
null === $this->_profileOptionOrderIndex ) {
$this->_profileFieldsMap = array();
$this->_profileFieldsOptionsMap = array();
$this->_profileDependentFieldsMap = array();
$this->_profileOrderIndex = 1;
$this->_profileOptionOrderIndex = 1;
// Import profile fields
$stmt = $fromDb->select()
->from('se_' . $this->_fromResourceType . 'fields')
->joinLeft('se_' . $this->_fromResourceType . 'cats', $this->_fromResourceType . 'field_' . $this->_fromResourceType . 'cat_id=' . $this->_fromResourceType . 'cat_id', $this->_fromResourceType . 'cat_order')
->order($this->_fromResourceType . 'cat_order ASC')
//->order($this->_fromResourceType . 'field_' . $this->_fromResourceType . 'cat_id ASC')
->order($this->_fromResourceType . 'field_dependency ASC')
->order($this->_fromResourceType . 'field_order ASC')
->query();
foreach( $stmt->fetchAll() as $data ) {
$title = $this->_getLanguageValue($data[$this->_fromResourceType . 'field_title']);
$description = $this->_getLanguageValue($data[$this->_fromResourceType . 'field_desc']);
$error = $this->_getLanguageValue($data[$this->_fromResourceType . 'field_error']);
// Dependent field
if( isset($this->_profileDependentFieldsMap[$data[$this->_fromResourceType . 'field_id']]) ) {
$currentFieldParentFieldId = $this->_profileDependentFieldsMap[$data[$this->_fromResourceType . 'field_id']]['field_id'];
$currentFieldParentOptionId = $this->_profileDependentFieldsMap[$data[$this->_fromResourceType . 'field_id']]['option_id'];
}
// Category
else if( isset($this->_profileTypeOptionMap[$data[$this->_fromResourceType . 'field_' . $this->_fromResourceType . 'cat_id']]) ) {
$currentFieldParentFieldId = $this->_profileTypeFieldIdentity;
$currentFieldParentOptionId = $this->_profileTypeOptionMap[$data[$this->_fromResourceType . 'field_' . $this->_fromResourceType . 'cat_id']];
} else if( isset($this->_profileTypeSubcatMap[$data[$this->_fromResourceType . 'field_' . $this->_fromResourceType . 'cat_id']]) &&
isset($this->_profileTypeOptionMap[$this->_profileTypeSubcatMap[$data[$this->_fromResourceType . 'field_' . $this->_fromResourceType . 'cat_id']]]) ) {
$currentFieldParentFieldId = $this->_profileTypeFieldIdentity;
$currentFieldParentOptionId = $this->_profileTypeOptionMap[$this->_profileTypeSubcatMap[$data[$this->_fromResourceType . 'field_' . $this->_fromResourceType . 'cat_id']]];
} else {
$this->_error('Missing parent category in field: ' . $data[$this->_fromResourceType . 'field_id']);
continue;
}
// Insert heading before this field
if( isset($this->_profileTypeHeadingMap[$data[$this->_fromResourceType . 'field_' . $this->_fromResourceType . 'cat_id']]) ) {
$toDb->insert('engine4_' . $this->_toResourceType . '_fields_meta', array(
'type' => 'heading',
'label' => $this->_getLanguageValue($this->_profileTypeHeadingMap[$data[$this->_fromResourceType . 'field_' . $this->_fromResourceType . 'cat_id']][$this->_fromResourceType . 'cat_title']),
'order' => $this->_profileOrderIndex++,
'display' => 1, // 2?
));
$headingId = $toDb->lastInsertId();
$toDb->insert('engine4_' . $this->_toResourceType . '_fields_maps', array(
'field_id' => $this->_profileTypeFieldIdentity,
'option_id' => $currentFieldParentOptionId,
'child_id' => $headingId,
'order' => $this->_profileOrderIndex++,
));
// Remove?
unset($this->_profileTypeHeadingMap[$data[$this->_fromResourceType . 'field_' . $this->_fromResourceType . 'cat_id']]);
}
$options = null;
$alias = null;
switch( $data[$this->_fromResourceType . 'field_type'] ) {
case 1: // TEXT FIELD
if( $data[$this->_fromResourceType . 'field_special'] == 2 ) {
$alias = 'first_name';
$type = 'first_name';
} else if( $data[$this->_fromResourceType . 'field_special'] == 3 ) {
$alias = 'last_name';
$type = 'last_name';
} else {
$type = 'text';
}
break;
case 2: // TEXTAREA
$type = 'textarea';
break;
case 3: // SELECT BOX
$type = 'select';
try {
$options = $this->_unserialize($data[$this->_fromResourceType . 'field_options']);
} catch( Exception $e ) {
$options = null;
}
break;
case 4: // RADIO BUTTON
$type = 'radio';
try {
$options = $this->_unserialize($data[$this->_fromResourceType . 'field_options']);
} catch( Exception $e ) {
$options = null;
}
break;
case 5: // DATE FIELD
if( $data[$this->_fromResourceType . 'field_special'] == 1 ) {
$alias = 'birthdate';
$type = 'birthdate';
} else {
$type = 'date';
}
break;
case 6: // CHECKBOXES
$type = 'multi_checkbox';
try {
$options = $this->_unserialize($data[$this->_fromResourceType . 'field_options']);
} catch( Exception $e ) {
$options = null;
}
break;
default:
$this->_warning('Unknown field type: ' . $data[$this->_fromResourceType . 'field_type'] . ' for field: ' . $data[$this->_fromResourceType . 'field_id'], 0);
continue;
break;
}
$newData = array(
'type' => (string) $type,
'label' => (string) $title,
'description' => (string) $description,
'alias' => (string) $alias,
'required' => $data[$this->_fromResourceType . 'field_required'],
'display' => ( $data[$this->_fromResourceType . 'field_display'] ? 1 : 0 ), // ?
//'publish' => 0, // ?
'search' => ( !$data[$this->_fromResourceType . 'field_search'] ? 0 : ( in_array($type, array('profile_type', 'first_name', 'last_name'))) ? 2 : 1 ),
'order' => $this->_profileOrderIndex++,
'error' => $error,
'style' => $data[$this->_fromResourceType . 'field_style'],
);
try {
$toDb->insert('engine4_' . $this->_toResourceType . '_fields_meta', $newData);
} catch( Exception $e ) {
$this->_error($e);
continue;
}
$fieldId = $toDb->lastInsertId();
$this->_profileFieldsMap[$data[$this->_fromResourceType . 'field_id']] = $fieldId;
// Do options
if( !empty($options) && is_array($options) ) {
$this->_profileFieldsOptionsMap[$data[$this->_fromResourceType . 'field_id']] = array();
foreach( $options as $fieldOptionIndex => $option ) {
$this->_profileOptionCount++;
try {
$toDb->insert('engine4_' . $this->_toResourceType . '_fields_options', array(
'field_id' => $fieldId,
'label' => $this->_getLanguageValue($option['label']),
'order' => $this->_profileOptionOrderIndex++,
));
$optionIdentity = $toDb->lastInsertId();
$this->_profileFieldsOptionsMap[$data[$this->_fromResourceType . 'field_id']][$option['value']] = $optionIdentity;
} catch( Exception $e ) {
$this->_error($e);
continue;
}
// Check for dependent field
if( !empty($option['dependency']) || !empty($option['dependent_id']) || !empty($option['dependent_label']) ) {
$this->_message('Dependent option: ' . Zend_Json::encode($option), 2);
$this->_profileDependentFieldsMap[$option['dependent_id']] = array(
'field_id' => $fieldId,
'option_id' => $optionIdentity,
);
}
}
}
// Do map
try {
$toDb->insert('engine4_' . $this->_toResourceType . '_fields_maps', array(
'field_id' => $currentFieldParentFieldId,
'option_id' => $currentFieldParentOptionId,
'child_id' => $fieldId,
'order' => $this->_profileOrderIndex++,
));
$this->_profileFieldCount++;
} catch( Exception $e ) {
$this->_error($e);
continue;
}
}
}
// -------------------------------------------------------------------------
// Step 4: Import profile search structure ---------------------------------
if( null === $this->_profileSearchColumns &&
null === $this->_profileSearchIndex ) {
$this->_profileSearchColumns = array();
$this->_profileSearchIndex = array();
$stmt = $toDb->select()
->from('engine4_' . $this->_toResourceType . '_fields_meta')
->where('search > ?', 0)
->query();
foreach( $stmt->fetchAll() as $tmpFieldMeta ) {
// Not searchable
if( empty($tmpFieldMeta['search']) ) {
continue;
}
$searchColName = null;
if( !empty($tmpFieldMeta['alias']) ) {
$searchColName = $tmpFieldMeta['alias'];
} else {
$searchColName = sprintf('field_%d', $tmpFieldMeta['field_id']);
}
// Already made?
if( isset($this->_profileSearchColumns[$searchColName]) ) {
continue;
}
$sql = 'ALTER TABLE ' . $toDb->quoteIdentifier('engine4_' . $this->_toResourceType . '_fields_search')
. ' ADD COLUMN ' . $toDb->quoteIdentifier($searchColName);
$keySql = 'ALTER TABLE ' . $toDb->quoteIdentifier('engine4_' . $this->_toResourceType . '_fields_search')
. ' ADD INDEX (' . $toDb->quoteIdentifier($searchColName) . ')';
switch( $tmpFieldMeta['type'] ) {
case 'first_name':
case 'last_name':
case 'text':
$sql .= ' varchar(255) NULL';
break;
case 'textarea':
$sql .= ' varchar(255) NULL'; // ?
break;
case 'date':
case 'birthdate':
$sql .= ' DATETIME NULL';
break;
case 'profile_type':
case 'select':
case 'radio':
case 'gender':
$tmpFieldOptions = $toDb->select()
->from('engine4_' . $this->_toResourceType . '_fields_options', 'option_id')
->where('field_id = ?', $tmpFieldMeta['field_id'])
->query()
->fetchAll();
if( empty($tmpFieldOptions) ) {
continue 2;
}
$optStr = '';
foreach( $tmpFieldOptions as $tmpFieldOption ) {
if( $optStr != '' ) {
$optStr .= ',';
}
$optStr .= sprintf("'%d'", $tmpFieldOption['option_id']); // blegh
}
$sql .= ' ENUM(' . $optStr . ') NULL';
break;
case 'multi_checkbox':
$tmpFieldOptions = $toDb->select()
->from('engine4_' . $this->_toResourceType . '_fields_options', 'option_id')
->where('field_id = ?', $tmpFieldMeta['field_id'])
->query()
->fetchAll();
if( empty($tmpFieldOptions) ) {
continue 2;
}
$optStr = '';
foreach( $tmpFieldOptions as $tmpFieldOption ) {
if( $optStr != '' ) {
$optStr .= ',';
}
$optStr .= sprintf("'%d'", $tmpFieldOption['option_id']); // blegh
}
$sql .= ' SET(' . $optStr . ') NULL';
break;
default:
continue 2;
break;
}
// Add column
try {
$toDb->query($sql);
} catch( Exception $e ) {
$this->_error($e->getMessage() . ', SQL was: ' . $sql);
continue;
}
// Add as created
$this->_profileSearchCount++;
$this->_profileSearchColumns[$searchColName] = true;
$this->_profileSearchIndex[$tmpFieldMeta['field_id']] = $searchColName;
// Add key
try {
$toDb->query($keySql);
} catch( Exception $e ) {
$this->_error($e->getMessage() . ', SQL was: ' . $keySql);
continue;
}
}
}
// -------------------------------------------------------------------------
$this->_message(sprintf('Imported %d profile fields', $this->_profileFieldCount));
$this->_message(sprintf('Imported %d profile options', $this->_profileOptionCount));
$this->_message(sprintf('Imported %d profile search columns', $this->_profileSearchCount));
}
protected function _translateRow(array $data, $key = null)
{
// Data should have two keys:
// $this->_fromAlternateResourceType . '_id'
// $this->_fromAlternateResourceType . '_' . $this->_fromResourceType .'cat_id'
$fromDb = $this->getFromDb();
$toDb = $this->getToDb();
$resourceIdentity = @$data[$this->_fromAlternateResourceType . '_id'];
$resourceProfileIdentity = @$data[$this->_fromAlternateResourceType . '_' . $this->_fromResourceType .'cat_id'];
// Add profile type
if( !$resourceProfileIdentity ) {
$this->_warning('Missing profile type id: ' . $resourceProfileIdentity . ' for object: ' . $resourceIdentity);
return false;
}
if( isset($this->_profileTypeOptionMap[$resourceProfileIdentity]) ) {
$currentFieldParentOptionId = $this->_profileTypeOptionMap[$resourceProfileIdentity];
} else if( isset($this->_profileTypeSubcatMap[$resourceProfileIdentity]) &&
isset($this->_profileTypeOptionMap[$this->_profileTypeSubcatMap[$resourceProfileIdentity]]) ) {
$currentFieldParentOptionId = $this->_profileTypeOptionMap[$this->_profileTypeSubcatMap[$resourceProfileIdentity]];
} else {
throw new Engine_Exception('Missing profile type id: ' . $resourceProfileIdentity . ' for object: ' . $resourceIdentity);
return false;
}
$toDb->insert('engine4_' . $this->_toResourceType . '_fields_values', array(
'item_id' => $resourceIdentity,
'field_id' => $this->_profileTypeFieldIdentity,
'value' => $currentFieldParentOptionId,
));
// Get the user's profile values
$profileValues = $fromDb->select()
->from('se_' . $this->_fromResourceType . 'values')
->where($this->_fromResourceType . 'value_' . $this->_fromAlternateResourceType .'_id' . ' = ?', $resourceIdentity)
->limit(1)
->query()
->fetch();
// Nothing?
if( !$profileValues || empty($profileValues) ) {
return false;
}
// Import profile values
$valId = $profileValues[$this->_fromResourceType . 'value_id'];
$valUserId = $profileValues[$this->_fromResourceType . 'value_' . $this->_fromAlternateResourceType .'_id'];
unset($profileValues[$this->_fromResourceType . 'value_id']);
unset($profileValues[$this->_fromResourceType . 'value_' . $this->_fromAlternateResourceType .'_id']);
$userDatum = array();
$userDatum['item_id'] = $valUserId;
foreach( $profileValues as $key => $value ) {
$valFieldId = array_pop(explode('_', $key));
// Missing field
if( !isset($this->_profileFieldsMap[$valFieldId]) ) {
$this->_message('No field for value: ' . $key);
continue;
}
$valNewFieldId = $this->_profileFieldsMap[$valFieldId];
// Value by option
if( isset($this->_profileFieldsOptionsMap[$valFieldId]) ) {
// Nothing selected
if( $value == -1 || '' === $value /* || Not sure about empty empty($value) */ ) {
continue;
}
// Normal select or multi_checkbox with one value selected
else if( is_numeric($value) ) {
if( isset($this->_profileFieldsOptionsMap[$valFieldId][$value]) ) {
$valFieldValue = $this->_profileFieldsOptionsMap[$valFieldId][$value];
} else {
//$this->_message('No corresponding value for field value: ' . $value . ' for field id: ' . $valFieldId);
continue;
}
}
// Multi select?
else if( strpos($value, ',') !== false &&
($value = array_filter(array_map('trim', explode(',', $value)))) &&
is_array($value) &&
!empty($value) ) {
$valFieldValue = array();
foreach( $value as $valueArrVal ) {
if( isset($this->_profileFieldsOptionsMap[$valFieldId][$valueArrVal]) ) {
$valFieldValue[] = $this->_profileFieldsOptionsMap[$valFieldId][$valueArrVal];
} else {
$this->_message('No corresponding value for multi select field value: ' . $valueArrVal . ' for field id: ' . $valFieldId);
}
}
}
// Unknown
else {
//$this->_message('Unknown option value type for field value: ' . $value . ' for field id: ' . $valFieldId);
continue;
}
}
// Value by value
else {
$valFieldValue = $value;
}
// Insert
if( null !== $valFieldValue ) {
// multi select
if( is_array($valFieldValue) ) {
if( !empty($valFieldValue) ) {
$tmpValFieldValueIndex = 0;
if( isset($this->_profileSearchIndex[$valNewFieldId]) ) {
$userDatum[$this->_profileSearchIndex[$valNewFieldId]] = array();
}
foreach( $valFieldValue as $valFieldValueIn ) {
if( isset($this->_profileSearchIndex[$valNewFieldId]) ) {
$userDatum[$this->_profileSearchIndex[$valNewFieldId]][] = $valFieldValueIn;
}
try {
$toDb->insert('engine4_' . $this->_toResourceType . '_fields_values', array(
'item_id' => $valUserId,
'field_id' => $valNewFieldId,
'index' => $tmpValFieldValueIndex++,
'value' => $valFieldValueIn,
));
} catch( Exception $e ) {
$this->_error($e);
}
}
}
}
// Scalar
else {
if( isset($this->_profileSearchIndex[$valNewFieldId]) ) {
$userDatum[$this->_profileSearchIndex[$valNewFieldId]] = $valFieldValue;
}
try {
$toDb->insert('engine4_' . $this->_toResourceType . '_fields_values', array(
'item_id' => $valUserId,
'field_id' => $valNewFieldId,
'value' => $valFieldValue,
));
} catch( Exception $e ) {
$this->_error($e);
}
}
}
}
// Add fields search values
if( !empty($userDatum) && is_array($userDatum) && count($userDatum) > 1 ) {
try {
foreach( $userDatum as $k => $v ) {
if( is_array($v) ) {
$userDatum[$k] = $toDb->quote($v);
}
}
$toDb->insert('engine4_' . $this->_toResourceType . '_fields_search', $userDatum);
} catch( Exception $e ) {
$this->_error($e);
}
}
return true;
}
}