diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/sfIsisImporter.class.php | 333 | ||||
-rw-r--r-- | lib/sfIsisImporterBase.class.php | 211 | ||||
-rw-r--r-- | lib/task/isisImportTask.class.php | 45 |
3 files changed, 589 insertions, 0 deletions
diff --git a/lib/sfIsisImporter.class.php b/lib/sfIsisImporter.class.php new file mode 100644 index 0000000..9233f64 --- /dev/null +++ b/lib/sfIsisImporter.class.php @@ -0,0 +1,333 @@ +<?php + +/** + * IsisImporter: provides ISIS import methods for importing data into + * a Symfony project. + * + * Importing can be done either on actions or tasks. + */ +class sfIsisImporter extends sfIsisImporterBase +{ + /** + * Execute a mass import of ISIS database entries. This function reads + * each database entry and dispatch each field read to a custom + * dispatch function. + * + * @param object $caller Caller object + * @param string $section Caller section (whether an action or task) + * @param int $entries Number of entries to import (defaults to all) + */ + public function massImport($caller, $section, $entries = NULL) + { + // We hold arguments for logging purposes. + $this->caller = $caller; + $this->section = $section; + + foreach ($this->databases() as $database) + { + // Open database. + $this->log('Starting mass import procedure for config '. $database .'.'); + $this->open($this->config($database)); + + // Determine base model and max entries. + $base_model = $this->format['db']['base_model']; + $this->entries = ($entries != NULL && $entries <= $this->entries) ? $entries : $this->entries; + + if ($base_model) { + for ($entry = 1; $entry <= $this->entries; $entry++) + { + $this->addEntry($base_model, $entry); + $this->progress($entry); + } + } + + $this->afterImport(); + } + } + + /** + * Add a single entry from the database. + * + * @param string $base_model Model to use + * @param int $entry Entry number + * @todo Warn if repetition occurs for non-repeatable fields + * @todo Check if content already exist if a primary key is specified + */ + public function addEntry($base_model, $entry) + { + // Get data and setup the model. + $this->read($entry); + $model = new $base_model(); + $this->setBaseModelId($model); + + $this->log("Importing $base_model $entry..."); + + // Dispatch to custom import procedures. + foreach (new IsisMethodIterator($this) as $method => $field) + { + $this->{$method}($model, $field); + } + + $model->save(); + } + + /** + * Set the primary key for the model by getting it or saving the model. + * + * @param object $model Base model + */ + public function setBaseModelId(&$model) + { + $id = $this->getBaseModelId($model); + + if ($id) + { + $model->id = $id; + } + else + { + $model->save(); + } + } + + /** + * Get the primary key for the base model. + * + * @param object $model Base model + * @return int Base model id + * @todo Write and test + */ + public function getBaseModelId(&$model) + { + return false; + } + + /** + * Import a single field into a model. + * + * @param object $model Model + * @param array $field Field data from ISIS database schema + * @param int $row Row number + */ + public function addField(&$model, $field, $row = 0) { + $value = $this->filterBrackets($this->getMainItem($field, $row)); + + if ($value != null) + { + $map = $this->getMap($field); + $model->{$map}($value); + } + } + + /** + * Import a single subfield into a model. + * + * @param object $model Model + * @param array $field Field data from ISIS database schema + * @param string $subfield Subfield name + * @param int $row Row number + */ + public function addSubfield(&$model, $field, $subfield, $row = 0) { + $value = $this->filterBrackets($this->getSubfield($field, $subfield, $row)); + + if ($value != null) + { + $map = $this->getMap($field, $subfield); + $model->{$map}($value); + } + } + + /** + * Import single values into the model. + * + * Currently undefined mappings for a field/subfield + * are not saved as we would need to make sure a corresponding + * field exists in the model. This prevents the map + * configurations like $map = array('type' => 'value'); to work. + * + * As we are importing single values, here we don't care with + * row numbers as we assume that just the first row should be + * imported. + * + * @param object $model Model object + * @param array $field Field data + */ + public function importValues(&$model, array $field) + { + if ($this->fieldHasMap($field)) + { + $this->addField($model, $field); + } + + foreach ($this->getSubfieldList($field) as $subfield) + { + if ($this->subfieldHasMap($field, $subfield)) + { + $this->addSubfield($model, $field, $subfield); + } + } + } + + /** + * Add a new entity into the database if needed, returning + * the corresponding object. + * + * @param string $name Genre name + * @return Genre Genre data + */ + public function addEntity($entity, $name) + { + $name = $this->entityName($name); + $data = Doctrine_Core::getTable($entity)->findOneByName($name); + + if (!$data) + { + $this->log("Adding new $entity $name."); + $data = new $entity(); + $data->name = $name; + $data->save(); + } + + return $data; + } + + /** + * Import one to one data. + * + * @param object $model Model + * @param array $field Field data from ISIS database schema + * @param string $relation Relation name + */ + public function addOneToOne(&$model, array $field, $relation) + { + foreach (new IsisRowIterator($this, $field) as $row) + { + $data = new $relation(); + + foreach ($this->getSubfieldList($field) as $subfield) + { + $this->addSubfield($data, $field, $subfield, $row); + } + + $data->save(); + $key = sfInflector::underscore($relation) .'_id'; + $model->$key = $relation->id; + } + } + + /** + * Import many to many data. + * + * @param object $model Model + * @param array $values Values to be added + * @param string $relation Relation name + */ + public function addManyToMany(&$model, array $values, $relation) { + $method = 'add'. $relation; + + foreach ($values as $value) + { + // Populate related data. + if (is_callable(array($this, $method))) + { + $data = $this->{$method}($value); + } + else + { + $data = $this->addEntity($relation, $value); + } + + // Get model and relation names and id fields. + $model_id = $this->getModelId($model); + $relation_id = $this->getRelationId($relation); + $model_relation = $this->getModelRelation($model, $relation); + + // Make the relation. + $model_data = new $model_relation(); + $model_data->{$model_id} = $model->id; + $model_data->{$relation_id} = $data->id; + $model_data->save(); + } + } + + /** + * Import one to one data. + * + * @param object $model Model + * @param string $relation Relation name + * @return object Relation model object + */ + public function addOneToMany(&$model, $relation) + { + $model_id = $this->getModelId($model); + $data = new $relation(); + $data->{$model_id} = $model->id; + $data->save(); + return $data; + } + + /** + * Add simple entities data into the model. + * + * @param object $model Model + * @param array $field Field data from ISIS database schema + */ + public function addOneToManyEntities(&$model, array $field, $entity, $key = 'name') + { + foreach (new IsisMainItemIterator($this, $field) as $row => $value) + { + $this->log("Entity: $entity; Value: $value", 'debug'); + $data = $this->addOneToMany($model, $entity); + $data->{$key} = $value; + $data->save(); + } + } + + /** + * Add field values in a many-to-many relation. + * + * @param object $model Model + * @param array $field Field data from ISIS database schema + * @param string $relation Relation name + */ + public function addManyToManyField(&$model, array $field, $relation) + { + foreach (new IsisMainItemIterator($this, $field) as $value) + { + $this->addManyToMany($model, $this->explodeBrackets($value), $relation); + } + } + + /** + * Add an element into the database if needed, returning + * the resulting object. + * + * @param string $entity Entity name + * @param string $value Entity value + * @return object Entity data + */ + public function newOrExisting($entity, $value) + { + // Check for a null value. + if ($value == null) + { + $this->log("Null element value for $entity.", 'debug'); + return; + } + + // Get name. + $name = $this->parseName($value); + + // Get existing element. + $element = call_user_func(array($entity, 'getByName'), $name); + + // Create new element if needed. + if (!$element) + { + $this->log("Adding new $entity $value."); + $element = call_user_func(array($entity, 'addByName'), $name); + } + + return $element; + } +} diff --git a/lib/sfIsisImporterBase.class.php b/lib/sfIsisImporterBase.class.php new file mode 100644 index 0000000..fac694a --- /dev/null +++ b/lib/sfIsisImporterBase.class.php @@ -0,0 +1,211 @@ +<?php + +/** + * IsisImporterBase: provides base ISIS import methods for importing data into + * a Symfony project. + * + * Importing can be done either on actions or tasks. + */ +class sfIsisImporterBase extends IsisConnector +{ + /** + * @var string $loglevel Log level. + */ + var $loglevel = 'info'; + + /** + * @var int $processed Number of processed entries. + */ + var $processed = 0; + + /** + * Constructor. + */ + public function __construct($loglevel = 'info') + { + parent::__construct(); + $this->loglevel = $loglevel; + } + + /** + * Create an ISIS configuration for a given database. + * + * @param string $database Database name + * @return array Basic configuration + */ + public function config($database) + { + // Default cinisis config file. + $config = $this->isis->file(); + + if (file_exists($config)) + { + $this->log("Using Cinisis config file $config."); + $config = sfYaml::load($config); + } + else { + // Default configuration. + $this->log("Cinisis config file not found, building configuration."); + $config = array( + 'implementation' => 'BiblioIsis', + ); + } + + $config['database'] = $database; + return $config; + } + + /** + * List available ISIS databases. + * + * @return array Available databases + */ + public function databases() + { + $files = glob(sfConfig::get('sf_lib_dir') .'/cinisis/schemas/*.yaml'); + foreach ($files as $file) + { + $databases[] = basename($file, '.yaml'); + } + + return $databases; + } + + /** + * Log dispatcher. + * + * @param string $message Log message + * @param string $level Log level + */ + public function log($message, $level = 'info') + { + // Available log levels ordered by verbosity. + $levels = array_flip(array('fatal', 'info', 'warn', 'error', 'debug')); + + // Log level checking. + if (array_search($level, $levels) === FALSE) + { + $this->log("Invalid log level $level.", 'error'); + return; + } + elseif ($levels[$level] > $levels[$this->loglevel]) + { + return; + } + + // Dispatch. + if ($this->section == 'action') + { + $this->caller->logMessage($message, $level); + } + else + { + $this->caller->logSection('isisImporter', "[$level] $message"); + } + } + + /** + * Progress notifier. + * + * @param int $n Row number + */ + public function progress($n) + { + $this->processed = $n; + + if ($this->section == 'action') + { + $this->caller->output .= "Saved item $n\n"; + } + else + { + // Progress bar is just shown if loglevel is 'fatal'. + if ($this->loglevel == 'fatal') + { + $this->caller->progressBar($n, $this->entries); + } + } + } + + /** + * Guess a method name from a type. + * + * @param string $type Mapping type + * @return string Method name + */ + static function methodName($type) + { + return 'import'. ucfirst($type); + } + + /** + * Get the model foreign table id. + * + * @param object $model Model + * @return string Model table id + */ + static function getModelId($model) { + return sfInflector::underscore(get_class($model)) .'_id'; + } + + /** + * Get the relation foreign table id. + * + * @param string $model Relation name + * @return string Relation table id + */ + static function getRelationId($relation) { + return sfInflector::underscore($relation) .'_id'; + } + + /** + * Get the model and relation tablename. + * + * @param object $model Model + * @param string $model Relation name + * @return string Relation table name + */ + static function getModelRelation($model, $relation) + { + return sfInflector::camelize(self::getModelName($model)) . sfInflector::camelize($relation); + } + + /** + * Get the entity name from a subfield. + * + * @param string $subfield Subfield name + * @return string Genre name + */ + static function entityName($subfield) + { + return ucfirst($subfield); + } + + /** + * Get the model name. + * + * @param object $model Model + * @return string Model name + */ + static function getModelName($model) + { + return get_class($model); + } + + /** + * After import procedure. + */ + public function afterImport() { + // Output ISIS log messages. + if (is_array($this->isis->db->log)) + { + foreach ($this->isis->db->log as $log) + { + $this->log("[isis] $log"); + } + } + + $this->log("Finished mass import procedure."); + $this->log("Total entries processed: $this->processed."); + } +} diff --git a/lib/task/isisImportTask.class.php b/lib/task/isisImportTask.class.php new file mode 100644 index 0000000..b6d70af --- /dev/null +++ b/lib/task/isisImportTask.class.php @@ -0,0 +1,45 @@ +<?php + +class isisImportTask extends mySfTask +{ + protected function configure() + { + $this->addArgument('rows', sfCommandArgument::OPTIONAL, 'Optional number of rows to process'); + $this->addOptions(array( + new sfCommandOption('application', null, sfCommandOption::PARAMETER_REQUIRED, 'The application name'), + new sfCommandOption('env', null, sfCommandOption::PARAMETER_REQUIRED, 'The environment', 'dev'), + new sfCommandOption('connection', null, sfCommandOption::PARAMETER_REQUIRED, 'The connection name', 'doctrine'), + new sfCommandOption('loglevel', 'l', sfCommandOption::PARAMETER_REQUIRED, 'Log level', 'info'), + )); + + $this->namespace = 'isis'; + $this->name = 'import'; + $this->briefDescription = 'Imports an ISIS database into the application'; + $this->detailedDescription = <<<EOF +The [isis:import|INFO] task imports an ISIS database into the application. +Call it with: + + [php symfony isis:import|INFO] +EOF; + } + + protected function execute($arguments = array(), $options = array()) + { + // initialize the database connection + $databaseManager = new sfDatabaseManager($this->configuration); + $connection = $databaseManager->getDatabase($options['connection'])->getConnection(); + + // Initialize an IsisConnector. + $isis = new CineImporter($options['loglevel']); + + // Error handling. + if ($isis == FALSE) + { + $this->logSection('isis', 'Error opening ISIS database.'); + return FALSE; + } + + // Mass ISIS import. + $isis->massImport($this, 'task', $arguments['rows']); + } +} |