aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/sfIsisImporter.class.php333
-rw-r--r--lib/sfIsisImporterBase.class.php211
-rw-r--r--lib/task/isisImportTask.class.php45
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']);
+ }
+}