CC-2166: Packaging Improvements. Moved the Zend app into airtime_mvc. It is now installed to /var/www/airtime. Storage is now set to /srv/airtime/stor. Utils are now installed to /usr/lib/airtime/utils/. Added install/airtime-dircheck.php as a simple test to see if everything is install/uninstalled correctly.
This commit is contained in:
parent
514777e8d2
commit
b11cbd8159
4546 changed files with 138 additions and 51 deletions
|
@ -0,0 +1,593 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Propel package.
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @license MIT License
|
||||
*/
|
||||
|
||||
//include_once 'phing/tasks/ext/CapsuleTask.php';
|
||||
require_once 'phing/Task.php';
|
||||
include_once 'config/GeneratorConfig.php';
|
||||
include_once 'model/AppData.php';
|
||||
include_once 'model/Database.php';
|
||||
include_once 'builder/util/XmlToAppData.php';
|
||||
|
||||
/**
|
||||
* An abstract base Propel task to perform work related to the XML schema file.
|
||||
*
|
||||
* The subclasses invoke templates to do the actual writing of the resulting files.
|
||||
*
|
||||
* @author Hans Lellelid <hans@xmpl.org> (Propel)
|
||||
* @author Jason van Zyl <jvanzyl@zenplex.com> (Torque)
|
||||
* @author Daniel Rall <dlr@finemaltcoding.com> (Torque)
|
||||
* @package propel.generator.task
|
||||
*/
|
||||
abstract class AbstractPropelDataModelTask extends Task
|
||||
{
|
||||
|
||||
/**
|
||||
* Fileset of XML schemas which represent our data models.
|
||||
* @var array Fileset[]
|
||||
*/
|
||||
protected $schemaFilesets = array();
|
||||
|
||||
/**
|
||||
* Data models that we collect. One from each XML schema file.
|
||||
*/
|
||||
protected $dataModels = array();
|
||||
|
||||
/**
|
||||
* Have datamodels been initialized?
|
||||
* @var boolean
|
||||
*/
|
||||
private $dataModelsLoaded = false;
|
||||
|
||||
/**
|
||||
* Map of data model name to database name.
|
||||
* Should probably stick to the convention
|
||||
* of them being the same but I know right now
|
||||
* in a lot of cases they won't be.
|
||||
*/
|
||||
protected $dataModelDbMap;
|
||||
|
||||
/**
|
||||
* The target database(s) we are generating SQL
|
||||
* for. Right now we can only deal with a single
|
||||
* target, but we will support multiple targets
|
||||
* soon.
|
||||
*/
|
||||
protected $targetDatabase;
|
||||
|
||||
/**
|
||||
* DB encoding to use for XmlToAppData object
|
||||
*/
|
||||
protected $dbEncoding = 'iso-8859-1';
|
||||
|
||||
/**
|
||||
* Target PHP package to place the generated files in.
|
||||
*/
|
||||
protected $targetPackage;
|
||||
|
||||
/**
|
||||
* @var Mapper
|
||||
*/
|
||||
protected $mapperElement;
|
||||
|
||||
/**
|
||||
* Destination directory for results of template scripts.
|
||||
* @var PhingFile
|
||||
*/
|
||||
protected $outputDirectory;
|
||||
|
||||
/**
|
||||
* Whether to package the datamodels or not
|
||||
* @var PhingFile
|
||||
*/
|
||||
protected $packageObjectModel;
|
||||
|
||||
/**
|
||||
* Whether to perform validation (XSD) on the schema.xml file(s).
|
||||
* @var boolean
|
||||
*/
|
||||
protected $validate;
|
||||
|
||||
/**
|
||||
* The XSD schema file to use for validation.
|
||||
* @var PhingFile
|
||||
*/
|
||||
protected $xsdFile;
|
||||
|
||||
/**
|
||||
* XSL file to use to normalize (or otherwise transform) schema before validation.
|
||||
* @var PhingFile
|
||||
*/
|
||||
protected $xslFile;
|
||||
|
||||
/**
|
||||
* Optional database connection url.
|
||||
* @var string
|
||||
*/
|
||||
private $url = null;
|
||||
|
||||
/**
|
||||
* Optional database connection user name.
|
||||
* @var string
|
||||
*/
|
||||
private $userId = null;
|
||||
|
||||
/**
|
||||
* Optional database connection password.
|
||||
* @var string
|
||||
*/
|
||||
private $password = null;
|
||||
|
||||
/**
|
||||
* PDO Connection.
|
||||
* @var PDO
|
||||
*/
|
||||
private $conn = false;
|
||||
|
||||
/**
|
||||
* An initialized GeneratorConfig object containing the converted Phing props.
|
||||
*
|
||||
* @var GeneratorConfig
|
||||
*/
|
||||
private $generatorConfig;
|
||||
|
||||
/**
|
||||
* Return the data models that have been
|
||||
* processed.
|
||||
*
|
||||
* @return List data models
|
||||
*/
|
||||
public function getDataModels()
|
||||
{
|
||||
if (!$this->dataModelsLoaded) {
|
||||
$this->loadDataModels();
|
||||
}
|
||||
return $this->dataModels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the data model to database name map.
|
||||
*
|
||||
* @return Hashtable data model name to database name map.
|
||||
*/
|
||||
public function getDataModelDbMap()
|
||||
{
|
||||
if (!$this->dataModelsLoaded) {
|
||||
$this->loadDataModels();
|
||||
}
|
||||
return $this->dataModelDbMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a set of xml schema files (nested fileset attribute).
|
||||
*
|
||||
* @param set a Set of xml schema files
|
||||
*/
|
||||
public function addSchemaFileset(Fileset $set)
|
||||
{
|
||||
$this->schemaFilesets[] = $set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current target database.
|
||||
*
|
||||
* @return String target database(s)
|
||||
*/
|
||||
public function getTargetDatabase()
|
||||
{
|
||||
return $this->targetDatabase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current target database. (e.g. mysql, oracle, ..)
|
||||
*
|
||||
* @param v target database(s)
|
||||
*/
|
||||
public function setTargetDatabase($v)
|
||||
{
|
||||
$this->targetDatabase = $v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current target package.
|
||||
*
|
||||
* @return string target PHP package.
|
||||
*/
|
||||
public function getTargetPackage()
|
||||
{
|
||||
return $this->targetPackage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current target package. This is where generated PHP classes will
|
||||
* live.
|
||||
*
|
||||
* @param string $v target PHP package.
|
||||
*/
|
||||
public function setTargetPackage($v)
|
||||
{
|
||||
$this->targetPackage = $v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the packageObjectModel switch on/off
|
||||
*
|
||||
* @param string $v The build.property packageObjectModel
|
||||
*/
|
||||
public function setPackageObjectModel($v)
|
||||
{
|
||||
$this->packageObjectModel = ($v === '1' ? true : false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to perform validation on the datamodel schema.xml file(s).
|
||||
* @param boolean $v
|
||||
*/
|
||||
public function setValidate($v)
|
||||
{
|
||||
$this->validate = $v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the XSD schema to use for validation of any datamodel schema.xml file(s).
|
||||
* @param $v PhingFile
|
||||
*/
|
||||
public function setXsd(PhingFile $v)
|
||||
{
|
||||
$this->xsdFile = $v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the normalization XSLT to use to transform datamodel schema.xml file(s) before validation and parsing.
|
||||
* @param $v PhingFile
|
||||
*/
|
||||
public function setXsl(PhingFile $v)
|
||||
{
|
||||
$this->xslFile = $v;
|
||||
}
|
||||
|
||||
/**
|
||||
* [REQUIRED] Set the output directory. It will be
|
||||
* created if it doesn't exist.
|
||||
* @param PhingFile $outputDirectory
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public function setOutputDirectory(PhingFile $outputDirectory) {
|
||||
try {
|
||||
if (!$outputDirectory->exists()) {
|
||||
$this->log("Output directory does not exist, creating: " . $outputDirectory->getPath(),Project::MSG_VERBOSE);
|
||||
if (!$outputDirectory->mkdirs()) {
|
||||
throw new IOException("Unable to create Ouptut directory: " . $outputDirectory->getAbsolutePath());
|
||||
}
|
||||
}
|
||||
$this->outputDirectory = $outputDirectory->getCanonicalPath();
|
||||
} catch (IOException $ioe) {
|
||||
throw new BuildException($ioe);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current target database encoding.
|
||||
*
|
||||
* @param v target database encoding
|
||||
*/
|
||||
public function setDbEncoding($v)
|
||||
{
|
||||
$this->dbEncoding = $v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the DB connection url.
|
||||
*
|
||||
* @param string $url connection url
|
||||
*/
|
||||
public function setUrl($url)
|
||||
{
|
||||
$this->url = $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the user name for the DB connection.
|
||||
*
|
||||
* @param string $userId database user
|
||||
*/
|
||||
public function setUserid($userId)
|
||||
{
|
||||
$this->userId = $userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the password for the DB connection.
|
||||
*
|
||||
* @param string $password database password
|
||||
*/
|
||||
public function setPassword($password)
|
||||
{
|
||||
$this->password = $password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the output directory.
|
||||
* @return string
|
||||
*/
|
||||
public function getOutputDirectory() {
|
||||
return $this->outputDirectory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Nested creator, creates one Mapper for this task.
|
||||
*
|
||||
* @return Mapper The created Mapper type object.
|
||||
* @throws BuildException
|
||||
*/
|
||||
public function createMapper() {
|
||||
if ($this->mapperElement !== null) {
|
||||
throw new BuildException("Cannot define more than one mapper.", $this->location);
|
||||
}
|
||||
$this->mapperElement = new Mapper($this->project);
|
||||
return $this->mapperElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps the passed in name to a new filename & returns resolved File object.
|
||||
* @param string $from
|
||||
* @return PhingFile Resolved File object.
|
||||
* @throws BuilException - if no Mapper element se
|
||||
* - if unable to map new filename.
|
||||
*/
|
||||
protected function getMappedFile($from)
|
||||
{
|
||||
if (!$this->mapperElement) {
|
||||
throw new BuildException("This task requires you to use a <mapper/> element to describe how filename changes should be handled.");
|
||||
}
|
||||
|
||||
$mapper = $this->mapperElement->getImplementation();
|
||||
$mapped = $mapper->main($from);
|
||||
if (!$mapped) {
|
||||
throw new BuildException("Cannot create new filename based on: " . $from);
|
||||
}
|
||||
// Mappers always return arrays since it's possible for some mappers to map to multiple names.
|
||||
$outFilename = array_shift($mapped);
|
||||
$outFile = new PhingFile($this->getOutputDirectory(), $outFilename);
|
||||
return $outFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the PDO connection, if URL specified.
|
||||
* @return PDO Connection to use (for quoting, Platform class, etc.) or NULL if no connection params were specified.
|
||||
*/
|
||||
public function getConnection()
|
||||
{
|
||||
if ($this->conn === false) {
|
||||
$this->conn = null;
|
||||
if ($this->url) {
|
||||
$buf = "Using database settings:\n"
|
||||
. " URL: " . $this->url . "\n"
|
||||
. ($this->userId ? " user: " . $this->userId . "\n" : "")
|
||||
. ($this->password ? " password: " . $this->password . "\n" : "");
|
||||
|
||||
$this->log($buf, Project::MSG_VERBOSE);
|
||||
|
||||
// Set user + password to null if they are empty strings
|
||||
if (!$this->userId) { $this->userId = null; }
|
||||
if (!$this->password) { $this->password = null; }
|
||||
try {
|
||||
$this->conn = new PDO($this->url, $this->userId, $this->password);
|
||||
$this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
} catch (PDOException $x) {
|
||||
$this->log("Unable to create a PDO connection: " . $x->getMessage(), Project::MSG_WARN);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this->conn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all matching XML schema files and loads them into data models for class.
|
||||
* @return void
|
||||
*/
|
||||
protected function loadDataModels()
|
||||
{
|
||||
$ads = array();
|
||||
|
||||
// Get all matched files from schemaFilesets
|
||||
foreach ($this->schemaFilesets as $fs) {
|
||||
$ds = $fs->getDirectoryScanner($this->project);
|
||||
$srcDir = $fs->getDir($this->project);
|
||||
|
||||
$dataModelFiles = $ds->getIncludedFiles();
|
||||
|
||||
$platform = $this->getGeneratorConfig()->getConfiguredPlatform();
|
||||
|
||||
// Make a transaction for each file
|
||||
foreach ($dataModelFiles as $dmFilename) {
|
||||
|
||||
$this->log("Processing: ".$dmFilename);
|
||||
$xmlFile = new PhingFile($srcDir, $dmFilename);
|
||||
|
||||
$dom = new DomDocument('1.0', 'UTF-8');
|
||||
$dom->load($xmlFile->getAbsolutePath());
|
||||
|
||||
// modify schema to include any external schemas (and remove the external-schema nodes)
|
||||
$this->includeExternalSchemas($dom, $srcDir);
|
||||
|
||||
// normalize (or transform) the XML document using XSLT
|
||||
if ($this->getGeneratorConfig()->getBuildProperty('schemaTransform') && $this->xslFile) {
|
||||
$this->log("Transforming " . $xmlFile->getPath() . " using stylesheet " . $this->xslFile->getPath(), Project::MSG_VERBOSE);
|
||||
if (!class_exists('XSLTProcessor')) {
|
||||
$this->log("Could not perform XLST transformation. Make sure PHP has been compiled/configured to support XSLT.", Project::MSG_ERR);
|
||||
} else {
|
||||
// normalize the document using normalizer stylesheet
|
||||
$xslDom = new DomDocument('1.0', 'UTF-8');
|
||||
$xslDom->load($this->xslFile->getAbsolutePath());
|
||||
$xsl = new XsltProcessor();
|
||||
$xsl->importStyleSheet($xslDom);
|
||||
$dom = $xsl->transformToDoc($dom);
|
||||
}
|
||||
}
|
||||
|
||||
// validate the XML document using XSD schema
|
||||
if ($this->validate && $this->xsdFile) {
|
||||
$this->log("Validating XML doc (".$xmlFile->getPath().") using schema file " . $this->xsdFile->getPath(), Project::MSG_VERBOSE);
|
||||
if (!$dom->schemaValidate($this->xsdFile->getAbsolutePath())) {
|
||||
throw new EngineException("XML schema file (".$xmlFile->getPath().") does not validate. See warnings above for reasons validation failed (make sure error_reporting is set to show E_WARNING if you don't see any).", $this->getLocation());
|
||||
}
|
||||
}
|
||||
|
||||
$xmlParser = new XmlToAppData($platform, $this->getTargetPackage(), $this->dbEncoding);
|
||||
$ad = $xmlParser->parseString($dom->saveXML(), $xmlFile->getAbsolutePath());
|
||||
|
||||
$ad->setName($dmFilename);
|
||||
$ads[] = $ad;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($ads)) {
|
||||
throw new BuildException("No schema files were found (matching your schema fileset definition).");
|
||||
}
|
||||
|
||||
foreach ($ads as $ad) {
|
||||
// map schema filename with database name
|
||||
$this->dataModelDbMap[$ad->getName()] = $ad->getDatabase(null, false)->getName();
|
||||
}
|
||||
|
||||
if (count($ads)>1 && $this->packageObjectModel) {
|
||||
$ad = $this->joinDataModels($ads);
|
||||
$this->dataModels = array($ad);
|
||||
} else {
|
||||
$this->dataModels = $ads;
|
||||
}
|
||||
|
||||
foreach ($this->dataModels as &$ad) {
|
||||
$ad->doFinalInitialization();
|
||||
}
|
||||
|
||||
$this->dataModelsLoaded = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces all external-schema nodes with the content of xml schema that node refers to
|
||||
*
|
||||
* Recurses to include any external schema referenced from in an included xml (and deeper)
|
||||
* Note: this function very much assumes at least a reasonable XML schema, maybe it'll proof
|
||||
* users don't have those and adding some more informative exceptions would be better
|
||||
*
|
||||
* @param DomDocument $dom
|
||||
* @param string $srcDir
|
||||
* @return void (objects, DomDocument, are references by default in PHP 5, so returning it is useless)
|
||||
**/
|
||||
protected function includeExternalSchemas(DomDocument $dom, $srcDir) {
|
||||
$databaseNode = $dom->getElementsByTagName("database")->item(0);
|
||||
$externalSchemaNodes = $dom->getElementsByTagName("external-schema");
|
||||
$fs = FileSystem::getFileSystem();
|
||||
$nbIncludedSchemas = 0;
|
||||
while ($externalSchema = $externalSchemaNodes->item(0)) {
|
||||
$include = $externalSchema->getAttribute("filename");
|
||||
$this->log("Processing external schema: ".$include);
|
||||
$externalSchema->parentNode->removeChild($externalSchema);
|
||||
if ($fs->prefixLength($include) != 0) {
|
||||
$externalSchemaFile = new PhingFile($include);
|
||||
} else {
|
||||
$externalSchemaFile = new PhingFile($srcDir, $include);
|
||||
}
|
||||
$externalSchemaDom = new DomDocument('1.0', 'UTF-8');
|
||||
$externalSchemaDom->load($externalSchemaFile->getAbsolutePath());
|
||||
// The external schema may have external schemas of its own ; recurse
|
||||
$this->includeExternalSchemas($externalSchemaDom, $srcDir);
|
||||
foreach ($externalSchemaDom->getElementsByTagName("table") as $tableNode) { // see xsd, datatase may only have table or external-schema, the latter was just deleted so this should cover everything
|
||||
$databaseNode->appendChild($dom->importNode($tableNode, true));
|
||||
}
|
||||
$nbIncludedSchemas++;
|
||||
}
|
||||
return $nbIncludedSchemas;
|
||||
}
|
||||
|
||||
/**
|
||||
* Joins the datamodels collected from schema.xml files into one big datamodel.
|
||||
* We need to join the datamodels in this case to allow for foreign keys
|
||||
* that point to tables in different packages.
|
||||
*
|
||||
* @param array[AppData] $ads The datamodels to join
|
||||
* @return AppData The single datamodel with all other datamodels joined in
|
||||
*/
|
||||
protected function joinDataModels($ads)
|
||||
{
|
||||
$mainAppData = null;
|
||||
foreach ($ads as $appData) {
|
||||
if (null === $mainAppData) {
|
||||
$mainAppData = $appData;
|
||||
$appData->setName('JoinedDataModel');
|
||||
continue;
|
||||
}
|
||||
// merge subsequent schemas to the first one
|
||||
foreach ($appData->getDatabases(false) as $addDb) {
|
||||
$addDbName = $addDb->getName();
|
||||
if ($mainAppData->hasDatabase($addDbName)) {
|
||||
$db = $mainAppData->getDatabase($addDbName, false);
|
||||
// join tables
|
||||
foreach ($addDb->getTables() as $addTable) {
|
||||
if ($db->getTable($addTable->getName())) {
|
||||
throw new BuildException('Duplicate table found: ' . $addDbName . '.');
|
||||
}
|
||||
$db->addTable($addTable);
|
||||
}
|
||||
// join database behaviors
|
||||
foreach ($addDb->getBehaviors() as $addBehavior) {
|
||||
if (!$db->hasBehavior($addBehavior->getName())) {
|
||||
$db->addBehavior($addBehavior);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$mainAppData->addDatabase($addDb);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $mainAppData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the GeneratorConfig object for this task or creates it on-demand.
|
||||
* @return GeneratorConfig
|
||||
*/
|
||||
protected function getGeneratorConfig()
|
||||
{
|
||||
if ($this->generatorConfig === null) {
|
||||
$this->generatorConfig = new GeneratorConfig();
|
||||
$this->generatorConfig->setBuildProperties($this->getProject()->getProperties());
|
||||
}
|
||||
return $this->generatorConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks this class against Basic requrements of any propel datamodel task.
|
||||
*
|
||||
* @throws BuildException - if schema fileset was not defined
|
||||
* - if no output directory was specified
|
||||
*/
|
||||
protected function validate()
|
||||
{
|
||||
if (empty($this->schemaFilesets)) {
|
||||
throw new BuildException("You must specify a fileset of XML schemas.", $this->getLocation());
|
||||
}
|
||||
|
||||
// Make sure the output directory is set.
|
||||
if ($this->outputDirectory === null) {
|
||||
throw new BuildException("The output directory needs to be defined!", $this->getLocation());
|
||||
}
|
||||
|
||||
if ($this->validate) {
|
||||
if (!$this->xsdFile) {
|
||||
throw new BuildException("'validate' set to TRUE, but no XSD specified (use 'xsd' attribute).", $this->getLocation());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,344 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Propel package.
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @license MIT License
|
||||
*/
|
||||
|
||||
require_once 'phing/Task.php';
|
||||
require_once 'task/PropelDataModelTemplateTask.php';
|
||||
require_once 'builder/om/OMBuilder.php';
|
||||
require_once 'builder/om/ClassTools.php';
|
||||
|
||||
/**
|
||||
* This Task converts the XML runtime configuration file into a PHP array for faster performance.
|
||||
*
|
||||
* @author Hans Lellelid <hans@xmpl.org>
|
||||
* @package propel.generator.task
|
||||
*/
|
||||
class PropelConvertConfTask extends AbstractPropelDataModelTask
|
||||
{
|
||||
|
||||
/**
|
||||
* @var PhingFile The XML runtime configuration file to be converted.
|
||||
*/
|
||||
private $xmlConfFile;
|
||||
|
||||
/**
|
||||
* @var string This is the file where the converted conf array dump will be placed.
|
||||
*/
|
||||
private $outputFile;
|
||||
|
||||
/**
|
||||
* @var string This is the file where the classmap manifest converted conf array dump will be placed.
|
||||
*/
|
||||
private $outputClassmapFile;
|
||||
|
||||
/**
|
||||
* [REQUIRED] Set the input XML runtime conf file.
|
||||
* @param PhingFile $v The XML runtime configuration file to be converted.
|
||||
*/
|
||||
public function setXmlConfFile(PhingFile $v)
|
||||
{
|
||||
$this->xmlConfFile = $v;
|
||||
}
|
||||
|
||||
/**
|
||||
* [REQUIRED] Set the output filename for the converted runtime conf.
|
||||
* The directory is specified using AbstractPropelDataModelTask#setOutputDirectory().
|
||||
* @param string $outputFile
|
||||
* @see AbstractPropelDataModelTask#setOutputDirectory()
|
||||
*/
|
||||
public function setOutputFile($outputFile)
|
||||
{
|
||||
// this is a string, not a file
|
||||
$this->outputFile = $outputFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* [REQUIRED] Set the output filename for the autoload classmap.
|
||||
* The directory is specified using AbstractPropelDataModelTask#setOutputDirectory().
|
||||
* @param string $outputFile
|
||||
* @see AbstractPropelDataModelTask#setOutputDirectory()
|
||||
*/
|
||||
public function setOutputClassmapFile($outputFile)
|
||||
{
|
||||
// this is a string, not a file
|
||||
$this->outputClassmapFile = $outputFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* The main method does the work of the task.
|
||||
*/
|
||||
public function main()
|
||||
{
|
||||
// Check to make sure the input and output files were specified and that the input file exists.
|
||||
|
||||
if (!$this->xmlConfFile || !$this->xmlConfFile->exists()) {
|
||||
throw new BuildException("No valid xmlConfFile specified.", $this->getLocation());
|
||||
}
|
||||
|
||||
if (!$this->outputFile) {
|
||||
throw new BuildException("No outputFile specified.", $this->getLocation());
|
||||
}
|
||||
|
||||
// Create a PHP array from the runtime-conf.xml file
|
||||
|
||||
$xmlDom = new DOMDocument();
|
||||
$xmlDom->load($this->xmlConfFile->getAbsolutePath());
|
||||
$xml = simplexml_load_string($xmlDom->saveXML());
|
||||
$phpconf = self::simpleXmlToArray($xml);
|
||||
|
||||
/* For some reason the array generated from runtime-conf.xml has separate
|
||||
* 'log' section and 'propel' sections. To maintain backward compatibility
|
||||
* we need to put 'log' back into the 'propel' section.
|
||||
*/
|
||||
$log = array();
|
||||
if (isset($phpconf['log'])) {
|
||||
$phpconf['propel']['log'] = $phpconf['log'];
|
||||
unset($phpconf['log']);
|
||||
}
|
||||
|
||||
if(isset($phpconf['propel'])) {
|
||||
$phpconf = $phpconf['propel'];
|
||||
}
|
||||
|
||||
// add generator version
|
||||
$phpconf['generator_version'] = $this->getGeneratorConfig()->getBuildProperty('version');
|
||||
|
||||
if (!$this->outputClassmapFile) {
|
||||
// We'll create a default one for BC
|
||||
$this->outputClassmapFile = 'classmap-' . $this->outputFile;
|
||||
}
|
||||
|
||||
// Write resulting PHP data to output file
|
||||
$outfile = new PhingFile($this->outputDirectory, $this->outputFile);
|
||||
$output = "<?php\n";
|
||||
$output .= "// This file generated by Propel " . $phpconf['generator_version'] . " convert-conf target".($this->getGeneratorConfig()->getBuildProperty('addTimestamp') ? " on " . strftime("%c") : '') . "\n";
|
||||
$output .= "// from XML runtime conf file " . $this->xmlConfFile->getPath() . "\n";
|
||||
$output .= "\$conf = ";
|
||||
$output .= var_export($phpconf, true);
|
||||
$output .= ";\n";
|
||||
$output .= "\$conf['classmap'] = include(dirname(__FILE__) . DIRECTORY_SEPARATOR . '".$this->outputClassmapFile."');\n";
|
||||
$output .= "return \$conf;";
|
||||
|
||||
|
||||
$this->log("Creating PHP runtime conf file: " . $outfile->getPath());
|
||||
if (!file_put_contents($outfile->getAbsolutePath(), $output)) {
|
||||
throw new BuildException("Error creating output file: " . $outfile->getAbsolutePath(), $this->getLocation());
|
||||
}
|
||||
|
||||
// add classmap
|
||||
$phpconfClassmap = $this->getClassMap();
|
||||
$outfile = new PhingFile($this->outputDirectory, $this->outputClassmapFile);
|
||||
$output = '<' . '?' . "php\n";
|
||||
$output .= "// This file generated by Propel " . $phpconf['generator_version'] . " convert-conf target".($this->getGeneratorConfig()->getBuildProperty('addTimestamp') ? " on " . strftime("%c") : '') . "\n";
|
||||
$output .= "return ";
|
||||
$output .= var_export($phpconfClassmap, true);
|
||||
$output .= ";";
|
||||
$this->log("Creating PHP classmap runtime file: " . $outfile->getPath());
|
||||
if (!file_put_contents($outfile->getAbsolutePath(), $output)) {
|
||||
throw new BuildException("Error creating output file: " . $outfile->getAbsolutePath(), $this->getLocation());
|
||||
}
|
||||
|
||||
} // main()
|
||||
|
||||
/**
|
||||
* Recursive function that converts an SimpleXML object into an array.
|
||||
* @author Christophe VG (based on code form php.net manual comment)
|
||||
* @param object SimpleXML object.
|
||||
* @return array Array representation of SimpleXML object.
|
||||
*/
|
||||
private static function simpleXmlToArray($xml)
|
||||
{
|
||||
$ar = array();
|
||||
|
||||
foreach ( $xml->children() as $k => $v ) {
|
||||
|
||||
// recurse the child
|
||||
$child = self::simpleXmlToArray( $v );
|
||||
|
||||
//print "Recursed down and found: " . var_export($child, true) . "\n";
|
||||
|
||||
// if it's not an array, then it was empty, thus a value/string
|
||||
if ( count($child) == 0 ) {
|
||||
$child = self::getConvertedXmlValue($v);
|
||||
|
||||
}
|
||||
|
||||
// add the childs attributes as if they where children
|
||||
foreach ( $v->attributes() as $ak => $av ) {
|
||||
|
||||
// if the child is not an array, transform it into one
|
||||
if ( !is_array( $child ) ) {
|
||||
$child = array( "value" => $child );
|
||||
}
|
||||
|
||||
if ($ak == 'id') {
|
||||
// special exception: if there is a key named 'id'
|
||||
// then we will name the current key after that id
|
||||
$k = self::getConvertedXmlValue($av);
|
||||
} else {
|
||||
// otherwise, just add the attribute like a child element
|
||||
$child[$ak] = self::getConvertedXmlValue($av);
|
||||
}
|
||||
}
|
||||
|
||||
// if the $k is already in our children list, we need to transform
|
||||
// it into an array, else we add it as a value
|
||||
if ( !in_array( $k, array_keys($ar) ) ) {
|
||||
$ar[$k] = $child;
|
||||
} else {
|
||||
// (This only applies to nested nodes that do not have an @id attribute)
|
||||
|
||||
// if the $ar[$k] element is not already an array, then we need to make it one.
|
||||
// this is a bit of a hack, but here we check to also make sure that if it is an
|
||||
// array, that it has numeric keys. this distinguishes it from simply having other
|
||||
// nested element data.
|
||||
|
||||
if ( !is_array($ar[$k]) || !isset($ar[$k][0]) ) { $ar[$k] = array($ar[$k]); }
|
||||
$ar[$k][] = $child;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $ar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process XML value, handling boolean, if appropriate.
|
||||
* @param object The simplexml value object.
|
||||
* @return mixed
|
||||
*/
|
||||
private static function getConvertedXmlValue($value)
|
||||
{
|
||||
$value = (string) $value; // convert from simplexml to string
|
||||
// handle booleans specially
|
||||
$lwr = strtolower($value);
|
||||
if ($lwr === "false") {
|
||||
$value = false;
|
||||
} elseif ($lwr === "true") {
|
||||
$value = true;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists data model classes and builds an associative array className => classPath
|
||||
* To be used for autoloading
|
||||
* @return array
|
||||
*/
|
||||
protected function getClassMap()
|
||||
{
|
||||
$phpconfClassmap = array();
|
||||
|
||||
$generatorConfig = $this->getGeneratorConfig();
|
||||
|
||||
foreach ($this->getDataModels() as $dataModel) {
|
||||
|
||||
foreach ($dataModel->getDatabases() as $database) {
|
||||
|
||||
$classMap = array();
|
||||
|
||||
foreach ($database->getTables() as $table) {
|
||||
|
||||
if (!$table->isForReferenceOnly()) {
|
||||
|
||||
// -----------------------------------------------------
|
||||
// Add TableMap class,
|
||||
// Peer, Object & Query stub classes,
|
||||
// and Peer, Object & Query base classes
|
||||
// -----------------------------------------------------
|
||||
// (this code is based on PropelOMTask)
|
||||
|
||||
foreach (array('tablemap', 'peerstub', 'objectstub', 'querystub', 'peer', 'object', 'query') as $target) {
|
||||
$builder = $generatorConfig->getConfiguredBuilder($table, $target);
|
||||
$this->log("Adding class mapping: " . $builder->getClassname() . ' => ' . $builder->getClassFilePath());
|
||||
$classMap[$builder->getFullyQualifiedClassname()] = $builder->getClassFilePath();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
// Add children classes for object and query,
|
||||
// as well as base child query,
|
||||
// for single tabel inheritance tables.
|
||||
// -----------------------------------------------------
|
||||
|
||||
if ($col = $table->getChildrenColumn()) {
|
||||
if ($col->isEnumeratedClasses()) {
|
||||
foreach ($col->getChildren() as $child) {
|
||||
foreach (array('objectmultiextend', 'queryinheritance', 'queryinheritancestub') as $target) {
|
||||
$builder = $generatorConfig->getConfiguredBuilder($table, $target);
|
||||
$builder->setChild($child);
|
||||
$this->log("Adding class mapping: " . $builder->getClassname() . ' => ' . $builder->getClassFilePath());
|
||||
$classMap[$builder->getFullyQualifiedClassname()] = $builder->getClassFilePath();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
// Add base classes for alias tables (undocumented)
|
||||
// -----------------------------------------------------
|
||||
|
||||
$baseClass = $table->getBaseClass();
|
||||
if ( $baseClass !== null ) {
|
||||
$className = ClassTools::classname($baseClass);
|
||||
if (!isset($classMap[$className])) {
|
||||
$classPath = ClassTools::getFilePath($baseClass);
|
||||
$this->log('Adding class mapping: ' . $className . ' => ' . $classPath);
|
||||
$classMap[$className] = $classPath;
|
||||
}
|
||||
}
|
||||
|
||||
$basePeer = $table->getBasePeer();
|
||||
if ( $basePeer !== null ) {
|
||||
$className = ClassTools::classname($basePeer);
|
||||
if (!isset($classMap[$className])) {
|
||||
$classPath = ClassTools::getFilePath($basePeer);
|
||||
$this->log('Adding class mapping: ' . $className . ' => ' . $classPath);
|
||||
$classMap[$className] = $classPath;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------
|
||||
// Add classes for interface
|
||||
// ----------------------------------------------
|
||||
|
||||
if ($table->getInterface()) {
|
||||
$builder = $generatorConfig->getConfiguredBuilder($table, 'interface');
|
||||
$this->log("Adding class mapping: " . $builder->getClassname() . ' => ' . $builder->getClassFilePath());
|
||||
$classMap[$builder->getFullyQualifiedClassname()] = $builder->getClassFilePath();
|
||||
}
|
||||
|
||||
// ----------------------------------------------
|
||||
// Add classes from old treeMode implementations
|
||||
// ----------------------------------------------
|
||||
|
||||
if ($table->treeMode() == 'MaterializedPath') {
|
||||
foreach (array('nodepeerstub', 'nodestub', 'nodepeer', 'node') as $target) {
|
||||
$builder = $generatorConfig->getConfiguredBuilder($table, $target);
|
||||
$this->log("Adding class mapping: " . $builder->getClassname() . ' => ' . $builder->getClassFilePath());
|
||||
$classMap[$builder->getFullyQualifiedClassname()] = $builder->getClassFilePath();
|
||||
}
|
||||
}
|
||||
if ($table->treeMode() == 'NestedSet') {
|
||||
foreach (array('nestedset', 'nestedsetpeer') as $target) {
|
||||
$builder = $generatorConfig->getConfiguredBuilder($table, $target);
|
||||
$this->log("Adding class mapping: " . $builder->getClassname() . ' => ' . $builder->getClassFilePath());
|
||||
$classMap[$builder->getFullyQualifiedClassname()] = $builder->getClassFilePath();
|
||||
}
|
||||
}
|
||||
|
||||
} // if (!$table->isReferenceOnly())
|
||||
}
|
||||
|
||||
$phpconfClassmap = array_merge($phpconfClassmap, $classMap);
|
||||
}
|
||||
}
|
||||
|
||||
return $phpconfClassmap;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Propel package.
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @license MIT License
|
||||
*/
|
||||
|
||||
require_once 'task/PropelDataModelTemplateTask.php';
|
||||
require_once 'builder/om/ClassTools.php';
|
||||
|
||||
/**
|
||||
* This Task creates the OM classes based on the XML schema file.
|
||||
*
|
||||
* @author Hans Lellelid <hans@xmpl.org>
|
||||
* @package propel.generator.task
|
||||
*/
|
||||
class PropelDataDTDTask extends PropelDataModelTemplateTask
|
||||
{
|
||||
|
||||
public function main()
|
||||
{
|
||||
// check to make sure task received all correct params
|
||||
$this->validate();
|
||||
|
||||
if (!$this->mapperElement) {
|
||||
throw new BuildException("You must use a <mapper/> element to describe how names should be transformed.");
|
||||
}
|
||||
|
||||
$basepath = $this->getOutputDirectory();
|
||||
|
||||
// Get new Capsule context
|
||||
$generator = $this->createContext();
|
||||
$generator->put("basepath", $basepath); // make available to other templates
|
||||
|
||||
// we need some values that were loaded into the template context
|
||||
$basePrefix = $generator->get('basePrefix');
|
||||
$project = $generator->get('project');
|
||||
|
||||
foreach ($this->getDataModels() as $dataModel) {
|
||||
|
||||
$this->log("Processing Datamodel : " . $dataModel->getName());
|
||||
|
||||
foreach ($dataModel->getDatabases() as $database) {
|
||||
|
||||
$outFile = $this->getMappedFile($dataModel->getName());
|
||||
|
||||
$generator->put("tables", $database->getTables());
|
||||
$generator->parse("data/dtd/dataset.tpl", $outFile->getAbsolutePath());
|
||||
|
||||
$this->log("Generating DTD for database: " . $database->getName());
|
||||
$this->log("Creating DTD file: " . $outFile->getPath());
|
||||
|
||||
foreach ($database->getTables() as $tbl) {
|
||||
$this->log("\t + " . $tbl->getName());
|
||||
$generator->put("table", $tbl);
|
||||
$generator->parse("data/dtd/table.tpl", $outFile->getAbsolutePath(), true);
|
||||
}
|
||||
|
||||
} // foreach database
|
||||
|
||||
} // foreach dataModel
|
||||
|
||||
|
||||
} // main()
|
||||
}
|
|
@ -0,0 +1,354 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Propel package.
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @license MIT License
|
||||
*/
|
||||
|
||||
/**
|
||||
* Dumps the contenst of selected databases to XML data dump file.
|
||||
*
|
||||
* The generated XML files can have corresponding DTD files generated using the
|
||||
* PropelDataDTDTask. The results of the data dump can be converted to SQL using
|
||||
* the PropelDataSQLTask class.
|
||||
*
|
||||
* The database may be specified (via 'databaseName' attribute) if you only want to dump
|
||||
* the contents of one database. Otherwise it is assumed that all databases described
|
||||
* by datamodel schema file(s) will be dumped.
|
||||
*
|
||||
* @author Hans Lellelid <hans@xmpl.org> (Propel)
|
||||
* @author Fedor Karpelevitch <fedor.karpelevitch@home.com> (Torque)
|
||||
* @author Jason van Zyl <jvanzyl@zenplex.com> (Torque)
|
||||
* @author Daniel Rall <dlr@finemaltcoding.com> (Torque)
|
||||
* @version $Revision: 1612 $
|
||||
* @package propel.generator.task
|
||||
*/
|
||||
class PropelDataDumpTask extends AbstractPropelDataModelTask
|
||||
{
|
||||
|
||||
/**
|
||||
* Database name.
|
||||
* The database name may be optionally specified in the XML if you only want
|
||||
* to dump the contents of one database.
|
||||
*/
|
||||
private $databaseName;
|
||||
|
||||
/**
|
||||
* Database URL used for Propel connection.
|
||||
* This is a PEAR-compatible (loosely) DSN URL.
|
||||
*/
|
||||
private $databaseUrl;
|
||||
|
||||
/**
|
||||
* Database driver used for Propel connection.
|
||||
* This should normally be left blank so that default (Propel built-in) driver for database type is used.
|
||||
*/
|
||||
private $databaseDriver;
|
||||
|
||||
/**
|
||||
* Database user used for Propel connection.
|
||||
* @deprecated Put username in databaseUrl.
|
||||
*/
|
||||
private $databaseUser;
|
||||
|
||||
/**
|
||||
* Database password used for Propel connection.
|
||||
* @deprecated Put password in databaseUrl.
|
||||
*/
|
||||
private $databasePassword;
|
||||
|
||||
/**
|
||||
* Properties file that maps a data XML file to a particular database.
|
||||
* @var PhingFile
|
||||
*/
|
||||
private $datadbmap;
|
||||
|
||||
/**
|
||||
* The database connection used to retrieve the data to dump.
|
||||
* Needs to be public so that the TableInfo class can access it.
|
||||
*/
|
||||
public $conn;
|
||||
|
||||
/**
|
||||
* The statement used to acquire the data to dump.
|
||||
*/
|
||||
private $stmt;
|
||||
|
||||
/**
|
||||
* Set the file that maps between data XML files and databases.
|
||||
*
|
||||
* @param PhingFile $sqldbmap the db map
|
||||
* @return void
|
||||
*/
|
||||
public function setDataDbMap(PhingFile $datadbmap)
|
||||
{
|
||||
$this->datadbmap = $datadbmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file that maps between data XML files and databases.
|
||||
*
|
||||
* @return PhingFile $datadbmap.
|
||||
*/
|
||||
public function getDataDbMap()
|
||||
{
|
||||
return $this->datadbmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the database name to dump
|
||||
*
|
||||
* @return The DatabaseName value
|
||||
*/
|
||||
public function getDatabaseName()
|
||||
{
|
||||
return $this->databaseName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the database name
|
||||
*
|
||||
* @param v The new DatabaseName value
|
||||
*/
|
||||
public function setDatabaseName($v)
|
||||
{
|
||||
$this->databaseName = $v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the database url
|
||||
*
|
||||
* @return The DatabaseUrl value
|
||||
*/
|
||||
public function getDatabaseUrl()
|
||||
{
|
||||
return $this->databaseUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the database url
|
||||
*
|
||||
* @param string $v The PEAR-compatible database DSN URL.
|
||||
*/
|
||||
public function setDatabaseUrl($v)
|
||||
{
|
||||
$this->databaseUrl = $v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the database user
|
||||
*
|
||||
* @return string database user
|
||||
* @deprecated
|
||||
*/
|
||||
public function getDatabaseUser()
|
||||
{
|
||||
return $this->databaseUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the database user
|
||||
*
|
||||
* @param string $v The new DatabaseUser value
|
||||
* @deprecated Specify user in DSN URL.
|
||||
*/
|
||||
public function setDatabaseUser($v)
|
||||
{
|
||||
$this->databaseUser = $v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the database password
|
||||
*
|
||||
* @return string database password
|
||||
*/
|
||||
public function getDatabasePassword()
|
||||
{
|
||||
return $this->databasePassword;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the database password
|
||||
*
|
||||
* @param string $v The new DatabasePassword value
|
||||
* @deprecated Specify database password in DSN URL.
|
||||
*/
|
||||
public function setDatabasePassword($v)
|
||||
{
|
||||
$this->databasePassword = $v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the database driver name
|
||||
*
|
||||
* @return string database driver name
|
||||
*/
|
||||
public function getDatabaseDriver()
|
||||
{
|
||||
return $this->databaseDriver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the database driver name
|
||||
*
|
||||
* @param string $v The new DatabaseDriver value
|
||||
*/
|
||||
public function setDatabaseDriver($v)
|
||||
{
|
||||
$this->databaseDriver = $v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the data XML -> database map.
|
||||
*
|
||||
* This is necessary because there is currently no other method of knowing which
|
||||
* data XML files correspond to which database. This map allows us to convert multiple
|
||||
* data XML files into SQL.
|
||||
*
|
||||
* @throws IOException - if unable to store properties
|
||||
*/
|
||||
private function createDataDbMap()
|
||||
{
|
||||
if ($this->getDataDbMap() === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Produce the sql -> database map
|
||||
$datadbmap = new Properties();
|
||||
|
||||
// Check to see if the sqldbmap has already been created.
|
||||
if ($this->getDataDbMap()->exists()) {
|
||||
$datadbmap->load($this->getDataDbMap());
|
||||
}
|
||||
|
||||
foreach ($this->getDataModels() as $dataModel) { // there is really one 1 db per datamodel
|
||||
foreach ($dataModel->getDatabases() as $database) {
|
||||
|
||||
// if database name is specified, then we only want to dump that one db.
|
||||
if (empty($this->databaseName) || ($this->databaseName && $database->getName() == $this->databaseName)) {
|
||||
$outFile = $this->getMappedFile($dataModel->getName());
|
||||
$datadbmap->setProperty($outFile->getName(), $database->getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$datadbmap->store($this->getDataDbMap(), "Data XML file -> Database map");
|
||||
} catch (IOException $e) {
|
||||
throw new IOException("Unable to store properties: ". $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates through each datamodel/database, dumps the contents of all tables and creates a DOM XML doc.
|
||||
*
|
||||
* @return void
|
||||
* @throws BuildException
|
||||
*/
|
||||
public function main()
|
||||
{
|
||||
$this->validate();
|
||||
|
||||
$buf = "Database settings:\n"
|
||||
. " driver: " . ($this->databaseDriver ? $this->databaseDriver : "(default)" ). "\n"
|
||||
. " URL: " . $this->databaseUrl . "\n"
|
||||
. ($this->databaseUser ? " user: " . $this->databaseUser . "\n" : "")
|
||||
. ($this->databasePassword ? " password: " . $this->databasePassword . "\n" : "");
|
||||
|
||||
$this->log($buf, Project::MSG_VERBOSE);
|
||||
|
||||
// 1) First create the Data XML -> database name map.
|
||||
$this->createDataDbMap();
|
||||
|
||||
// 2) Now go create the XML files from teh database(s)
|
||||
foreach ($this->getDataModels() as $dataModel) { // there is really one 1 db per datamodel
|
||||
foreach ($dataModel->getDatabases() as $database) {
|
||||
|
||||
// if database name is specified, then we only want to dump that one db.
|
||||
if (empty($this->databaseName) || ($this->databaseName && $database->getName() == $this->databaseName)) {
|
||||
|
||||
$outFile = $this->getMappedFile($dataModel->getName());
|
||||
|
||||
$this->log("Dumping data to XML for database: " . $database->getName());
|
||||
$this->log("Writing to XML file: " . $outFile->getName());
|
||||
|
||||
try {
|
||||
|
||||
$url = str_replace("@DB@", $database->getName(), $this->databaseUrl);
|
||||
|
||||
if ($url !== $this->databaseUrl) {
|
||||
$this->log("New (resolved) URL: " . $url, Project::MSG_VERBOSE);
|
||||
}
|
||||
|
||||
if (empty($url)) {
|
||||
throw new BuildException("Unable to connect to database; no PDO connection URL specified.", $this->getLocation());
|
||||
}
|
||||
|
||||
$this->conn = new PDO($url, $this->databaseUser, $this->databasePassword);
|
||||
$this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
$doc = $this->createXMLDoc($database);
|
||||
$doc->save($outFile->getAbsolutePath());
|
||||
|
||||
} catch (SQLException $se) {
|
||||
$this->log("SQLException while connecting to DB: ". $se->getMessage(), Project::MSG_ERR);
|
||||
throw new BuildException($se);
|
||||
}
|
||||
} // if databaseName && database->getName == databaseName
|
||||
} // foreach database
|
||||
} // foreach datamodel
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets PDOStatement of query to fetch all data from a table.
|
||||
* @param string $tableName
|
||||
* @param Platform $platform
|
||||
* @return PDOStatement
|
||||
*/
|
||||
private function getTableDataStmt($tableName, Platform $platform)
|
||||
{
|
||||
return $this->conn->query("SELECT * FROM " . $platform->quoteIdentifier( $tableName ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DOM document containing data for specified database.
|
||||
* @param Database $database
|
||||
* @return DOMDocument
|
||||
*/
|
||||
private function createXMLDoc(Database $database)
|
||||
{
|
||||
$doc = new DOMDocument('1.0', 'utf-8');
|
||||
$doc->formatOutput = true; // pretty printing
|
||||
$doc->appendChild($doc->createComment("Created by data/dump/Control.tpl template."));
|
||||
|
||||
$dsNode = $doc->createElement("dataset");
|
||||
$dsNode->setAttribute("name", "all");
|
||||
$doc->appendChild($dsNode);
|
||||
|
||||
$platform = $this->getGeneratorConfig()->getConfiguredPlatform($this->conn);
|
||||
|
||||
$this->log("Building DOM tree containing data from tables:");
|
||||
|
||||
foreach ($database->getTables() as $tbl) {
|
||||
$this->log("\t+ " . $tbl->getName());
|
||||
$stmt = $this->getTableDataStmt($tbl->getName(), $platform);
|
||||
while ($row = $stmt->fetch()) {
|
||||
$rowNode = $doc->createElement($tbl->getPhpName());
|
||||
foreach ($tbl->getColumns() as $col) {
|
||||
$cval = $row[$col->getName()];
|
||||
if ($cval !== null) {
|
||||
$rowNode->setAttribute($col->getPhpName(), iconv($this->dbEncoding, 'utf-8', $cval));
|
||||
}
|
||||
}
|
||||
$dsNode->appendChild($rowNode);
|
||||
unset($rowNode);
|
||||
}
|
||||
unset($stmt);
|
||||
}
|
||||
|
||||
return $doc;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,217 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Propel package.
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @license MIT License
|
||||
*/
|
||||
|
||||
require_once 'task/AbstractPropelDataModelTask.php';
|
||||
include_once 'model/AppData.php';
|
||||
include_once 'model/Database.php';
|
||||
include_once 'builder/util/XmlToAppData.php';
|
||||
|
||||
/**
|
||||
* A generic class that simply loads the data model and parses a control template.
|
||||
*
|
||||
* This class exists largely for compatibility with early Propel where this was
|
||||
* a CapsuleTask subclass. This class also makes it easy to quickly add some custom
|
||||
* datamodel-based transformations (by allowing you to put the logic in the templates).
|
||||
*
|
||||
* @author Hans Lellelid <hans@xmpl.org>
|
||||
* @package propel.generator.task
|
||||
* @version $Revision: 1612 $
|
||||
*/
|
||||
class PropelDataModelTemplateTask extends AbstractPropelDataModelTask
|
||||
{
|
||||
|
||||
/**
|
||||
* This is the file where the generated text
|
||||
* will be placed.
|
||||
* @var string
|
||||
*/
|
||||
protected $outputFile;
|
||||
|
||||
/**
|
||||
* Path where Capsule looks for templates.
|
||||
* @var PhingFile
|
||||
*/
|
||||
protected $templatePath;
|
||||
|
||||
/**
|
||||
* This is the control template that governs the output.
|
||||
* It may or may not invoke the services of worker
|
||||
* templates.
|
||||
* @var string
|
||||
*/
|
||||
protected $controlTemplate;
|
||||
|
||||
/**
|
||||
* [REQUIRED] Set the output file for the
|
||||
* generation process.
|
||||
* @param string $outputFile (TODO: change this to File)
|
||||
* @return void
|
||||
*/
|
||||
public function setOutputFile($outputFile) {
|
||||
$this->outputFile = $outputFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the output file for the
|
||||
* generation process.
|
||||
* @return string
|
||||
*/
|
||||
public function getOutputFile() {
|
||||
return $this->outputFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* [REQUIRED] Set the control template for the
|
||||
* generating process.
|
||||
* @param string $controlTemplate
|
||||
* @return void
|
||||
*/
|
||||
public function setControlTemplate ($controlTemplate) {
|
||||
$this->controlTemplate = $controlTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the control template for the
|
||||
* generating process.
|
||||
* @return string
|
||||
*/
|
||||
public function getControlTemplate() {
|
||||
return $this->controlTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* [REQUIRED] Set the path where Capsule will look
|
||||
* for templates using the file template
|
||||
* loader.
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public function setTemplatePath($templatePath) {
|
||||
$resolvedPath = "";
|
||||
$tok = strtok($templatePath, ",");
|
||||
while ( $tok ) {
|
||||
// resolve relative path from basedir and leave
|
||||
// absolute path untouched.
|
||||
$fullPath = $this->project->resolveFile($tok);
|
||||
$cpath = $fullPath->getCanonicalPath();
|
||||
if ($cpath === false) {
|
||||
$this->log("Template directory does not exist: " . $fullPath->getAbsolutePath());
|
||||
} else {
|
||||
$resolvedPath .= $cpath;
|
||||
}
|
||||
$tok = strtok(",");
|
||||
if ( $tok ) {
|
||||
$resolvedPath .= ",";
|
||||
}
|
||||
}
|
||||
$this->templatePath = $resolvedPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path where Velocity will look
|
||||
* for templates using the file template
|
||||
* loader.
|
||||
* @return string
|
||||
*/
|
||||
public function getTemplatePath() {
|
||||
return $this->templatePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Capsule context with some basic properties set.
|
||||
* (Capsule is a simple PHP encapsulation system -- aka a php "template" class.)
|
||||
* @return Capsule
|
||||
*/
|
||||
protected function createContext() {
|
||||
|
||||
$context = new Capsule();
|
||||
|
||||
// Make sure the output directory exists, if it doesn't
|
||||
// then create it.
|
||||
$outputDir = new PhingFile($this->outputDirectory);
|
||||
if (!$outputDir->exists()) {
|
||||
$this->log("Output directory does not exist, creating: " . $outputDir->getAbsolutePath());
|
||||
$outputDir->mkdirs();
|
||||
}
|
||||
|
||||
// Place our set of data models into the context along
|
||||
// with the names of the databases as a convenience for now.
|
||||
$context->put("targetDatabase", $this->getTargetDatabase());
|
||||
$context->put("targetPackage", $this->getTargetPackage());
|
||||
$context->put("now", strftime("%c"));
|
||||
|
||||
$this->log("Target database type: " . $this->getTargetDatabase());
|
||||
$this->log("Target package: " . $this->getTargetPackage());
|
||||
$this->log("Using template path: " . $this->templatePath);
|
||||
$this->log("Output directory: " . $this->getOutputDirectory());
|
||||
|
||||
$context->setTemplatePath($this->templatePath);
|
||||
$context->setOutputDirectory($this->outputDirectory);
|
||||
|
||||
$this->populateContextProperties($context);
|
||||
|
||||
return $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the propel build properties to the passed Capsule context.
|
||||
*
|
||||
* @param Capsule $context
|
||||
* @see GeneratorConfig::getBuildProperties()
|
||||
*/
|
||||
public function populateContextProperties(Capsule $context)
|
||||
{
|
||||
foreach ($this->getGeneratorConfig()->getBuildProperties() as $key => $propValue) {
|
||||
$this->log('Adding property ${' . $key . '} to context', Project::MSG_DEBUG);
|
||||
$context->put($key, $propValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs validation for single-file mode.
|
||||
* @throws BuildException - if there are any validation errors
|
||||
*/
|
||||
protected function singleFileValidate()
|
||||
{
|
||||
parent::validate();
|
||||
|
||||
// Make sure the control template is set.
|
||||
if ($this->controlTemplate === null) {
|
||||
throw new BuildException("The control template needs to be defined!");
|
||||
}
|
||||
// Make sure there is an output file.
|
||||
if ($this->outputFile === null) {
|
||||
throw new BuildException("The output file needs to be defined!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates Capsule context and parses control template.
|
||||
* @return void
|
||||
*/
|
||||
public function main()
|
||||
{
|
||||
$this->singleFileValidate();
|
||||
$context = $this->createContext();
|
||||
|
||||
$context->put("dataModels", $this->getDataModels());
|
||||
|
||||
$path = $this->outputDirectory . DIRECTORY_SEPARATOR . $this->outputFile;
|
||||
$this->log("Generating to file " . $path);
|
||||
|
||||
try {
|
||||
$this->log("Parsing control template: " . $this->controlTemplate);
|
||||
$context->parse($this->controlTemplate, $path);
|
||||
} catch (Exception $ioe) {
|
||||
throw new BuildException("Cannot write parsed template: ". $ioe->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,193 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Propel package.
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @license MIT License
|
||||
*/
|
||||
|
||||
require_once 'model/AppData.php';
|
||||
require_once 'model/Database.php';
|
||||
require_once 'builder/util/XmlToAppData.php';
|
||||
require_once 'builder/util/XmlToDataSQL.php';
|
||||
|
||||
/**
|
||||
* Task that transforms XML datadump files into files containing SQL INSERT statements.
|
||||
*
|
||||
* @author Hans Lellelid <hans@xmpl.org> (Propel)
|
||||
* @author Jason van Zyl <jvanzyl@periapt.com> (Torque)
|
||||
* @author John McNally <jmcnally@collab.net> (Torque)
|
||||
* @author Fedor Karpelevitch <fedor.karpelevitch@home.com> (Torque)
|
||||
* @version $Revision: 1612 $
|
||||
* @package propel.generator.task
|
||||
*/
|
||||
class PropelDataSQLTask extends AbstractPropelDataModelTask
|
||||
{
|
||||
|
||||
/**
|
||||
* Properties file that maps an SQL file to a particular database.
|
||||
* @var PhingFile
|
||||
*/
|
||||
private $sqldbmap;
|
||||
|
||||
/**
|
||||
* Properties file that maps a data XML file to a particular database.
|
||||
* @var PhingFile
|
||||
*/
|
||||
private $datadbmap;
|
||||
|
||||
/**
|
||||
* The base directory in which to find data XML files.
|
||||
* @var PhingFile
|
||||
*/
|
||||
private $srcDir;
|
||||
|
||||
/**
|
||||
* Set the file that maps between SQL files and databases.
|
||||
*
|
||||
* @param PhingFile $sqldbmap the sql -> db map.
|
||||
* @return void
|
||||
*/
|
||||
public function setSqlDbMap(PhingFile $sqldbmap)
|
||||
{
|
||||
$this->sqldbmap = $sqldbmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file that maps between SQL files and databases.
|
||||
*
|
||||
* @return PhingFile sqldbmap.
|
||||
*/
|
||||
public function getSqlDbMap()
|
||||
{
|
||||
return $this->sqldbmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the file that maps between data XML files and databases.
|
||||
*
|
||||
* @param PhingFile $sqldbmap the db map
|
||||
* @return void
|
||||
*/
|
||||
public function setDataDbMap(PhingFile $datadbmap)
|
||||
{
|
||||
$this->datadbmap = $datadbmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file that maps between data XML files and databases.
|
||||
*
|
||||
* @return PhingFile $datadbmap.
|
||||
*/
|
||||
public function getDataDbMap()
|
||||
{
|
||||
return $this->datadbmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the src directory for the data xml files listed in the datadbmap file.
|
||||
* @param PhingFile $srcDir data xml source directory
|
||||
*/
|
||||
public function setSrcDir(PhingFile $srcDir)
|
||||
{
|
||||
$this->srcDir = $srcDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the src directory for the data xml files listed in the datadbmap file.
|
||||
*
|
||||
* @return PhingFile data xml source directory
|
||||
*/
|
||||
public function getSrcDir()
|
||||
{
|
||||
return $this->srcDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search through all data models looking for matching database.
|
||||
* @return Database or NULL if none found.
|
||||
*/
|
||||
private function getDatabase($name)
|
||||
{
|
||||
foreach ($this->getDataModels() as $dm) {
|
||||
foreach ($dm->getDatabases() as $db) {
|
||||
if ($db->getName() == $name) {
|
||||
return $db;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main method parses the XML files and creates SQL files.
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception If there is an error parsing the data xml.
|
||||
*/
|
||||
public function main()
|
||||
{
|
||||
$this->validate();
|
||||
|
||||
$targetDatabase = $this->getTargetDatabase();
|
||||
|
||||
$platform = $this->getGeneratorConfig()->getConfiguredPlatform();
|
||||
|
||||
// Load the Data XML -> DB Name properties
|
||||
$map = new Properties();
|
||||
try {
|
||||
$map->load($this->getDataDbMap());
|
||||
} catch (IOException $ioe) {
|
||||
throw new BuildException("Cannot open and process the datadbmap!", $ioe);
|
||||
}
|
||||
|
||||
// Parse each file in the data -> db map
|
||||
foreach ($map->keys() as $dataXMLFilename) {
|
||||
|
||||
$dataXMLFile = new PhingFile($this->srcDir, $dataXMLFilename);
|
||||
|
||||
// if file exists then proceed
|
||||
if ($dataXMLFile->exists()) {
|
||||
|
||||
$dbname = $map->get($dataXMLFilename);
|
||||
|
||||
$db = $this->getDatabase($dbname);
|
||||
|
||||
if (!$db) {
|
||||
throw new BuildException("Cannot find instantiated Database for name '$dbname' from datadbmap file.");
|
||||
}
|
||||
|
||||
$db->setPlatform($platform);
|
||||
|
||||
$outFile = $this->getMappedFile($dataXMLFilename);
|
||||
$sqlWriter = new FileWriter($outFile);
|
||||
|
||||
$this->log("Creating SQL from XML data dump file: " . $dataXMLFile->getAbsolutePath());
|
||||
|
||||
try {
|
||||
$dataXmlParser = new XmlToDataSQL($db, $this->getGeneratorConfig(), $this->dbEncoding);
|
||||
$dataXmlParser->transform($dataXMLFile, $sqlWriter);
|
||||
} catch (Exception $e) {
|
||||
throw new BuildException("Exception parsing data XML: " . $e->getMessage(), $x);
|
||||
}
|
||||
|
||||
// Place the generated SQL file(s)
|
||||
$p = new Properties();
|
||||
if ($this->getSqlDbMap()->exists()) {
|
||||
$p->load($this->getSqlDbMap());
|
||||
}
|
||||
|
||||
$p->setProperty($outFile->getName(), $db->getName());
|
||||
$p->store($this->getSqlDbMap(), "Sqlfile -> Database map");
|
||||
|
||||
} else {
|
||||
$this->log("File '" . $dataXMLFile->getAbsolutePath()
|
||||
. "' in datadbmap does not exist, so skipping it.", Project::MSG_WARN);
|
||||
}
|
||||
|
||||
} // foreach data xml file
|
||||
|
||||
} // main()
|
||||
|
||||
}
|
|
@ -0,0 +1,170 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Propel package.
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @license MIT License
|
||||
*/
|
||||
|
||||
require_once 'task/AbstractPropelDataModelTask.php';
|
||||
require_once 'model/AppData.php';
|
||||
|
||||
/**
|
||||
* A task to generate Graphviz dot files from Propel datamodel.
|
||||
*
|
||||
* @author Mark Kimsal
|
||||
* @version $Revision: 1612 $
|
||||
* @package propel.generator.task
|
||||
*/
|
||||
class PropelGraphvizTask extends AbstractPropelDataModelTask
|
||||
{
|
||||
|
||||
/**
|
||||
* The properties file that maps an SQL file to a particular database.
|
||||
* @var PhingFile
|
||||
*/
|
||||
private $sqldbmap;
|
||||
|
||||
/**
|
||||
* Name of the database.
|
||||
*/
|
||||
private $database;
|
||||
|
||||
/**
|
||||
* Name of the output directory.
|
||||
*/
|
||||
private $outDir;
|
||||
|
||||
|
||||
/**
|
||||
* Set the sqldbmap.
|
||||
* @param PhingFile $sqldbmap The db map.
|
||||
*/
|
||||
public function setOutputDirectory(PhingFile $out)
|
||||
{
|
||||
if (!$out->exists()) {
|
||||
$out->mkdirs();
|
||||
}
|
||||
$this->outDir = $out;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the sqldbmap.
|
||||
* @param PhingFile $sqldbmap The db map.
|
||||
*/
|
||||
public function setSqlDbMap(PhingFile $sqldbmap)
|
||||
{
|
||||
$this->sqldbmap = $sqldbmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the sqldbmap.
|
||||
* @return PhingFile $sqldbmap.
|
||||
*/
|
||||
public function getSqlDbMap()
|
||||
{
|
||||
return $this->sqldbmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the database name.
|
||||
* @param string $database
|
||||
*/
|
||||
public function setDatabase($database)
|
||||
{
|
||||
$this->database = $database;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the database name.
|
||||
* @return string
|
||||
*/
|
||||
public function getDatabase()
|
||||
{
|
||||
return $this->database;
|
||||
}
|
||||
|
||||
|
||||
public function main()
|
||||
{
|
||||
|
||||
$count = 0;
|
||||
|
||||
$dotSyntax = '';
|
||||
|
||||
// file we are going to create
|
||||
|
||||
$dbMaps = $this->getDataModelDbMap();
|
||||
|
||||
foreach ($this->getDataModels() as $dataModel) {
|
||||
|
||||
$dotSyntax .= "digraph G {\n";
|
||||
foreach ($dataModel->getDatabases() as $database) {
|
||||
|
||||
$this->log("db: " . $database->getName());
|
||||
|
||||
//print the tables
|
||||
foreach ($database->getTables() as $tbl) {
|
||||
|
||||
$this->log("\t+ " . $tbl->getName());
|
||||
|
||||
++$count;
|
||||
$dotSyntax .= 'node'.$tbl->getName().' [label="{<table>'.$tbl->getName().'|<cols>';
|
||||
|
||||
foreach ($tbl->getColumns() as $col) {
|
||||
$dotSyntax .= $col->getName() . ' (' . $col->getType() . ')';
|
||||
if (count($col->getForeignKeys()) > 0) {
|
||||
$dotSyntax .= ' [FK]';
|
||||
} elseif ($col->isPrimaryKey()) {
|
||||
$dotSyntax .= ' [PK]';
|
||||
}
|
||||
$dotSyntax .= '\l';
|
||||
}
|
||||
$dotSyntax .= '}", shape=record];';
|
||||
$dotSyntax .= "\n";
|
||||
}
|
||||
|
||||
//print the relations
|
||||
|
||||
$count = 0;
|
||||
$dotSyntax .= "\n";
|
||||
foreach ($database->getTables() as $tbl) {
|
||||
++$count;
|
||||
|
||||
foreach ($tbl->getColumns() as $col) {
|
||||
$fk = $col->getForeignKeys();
|
||||
if ( count($fk) == 0 or $fk === null ) continue;
|
||||
if ( count($fk) > 1 ) throw( new Exception("not sure what to do here...") );
|
||||
$fk = $fk[0]; // try first one
|
||||
$dotSyntax .= 'node'.$tbl->getName() .':cols -> node'.$fk->getForeignTableName() . ':table [label="' . $col->getName() . '=' . implode(',', $fk->getForeignColumns()) . ' "];';
|
||||
$dotSyntax .= "\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // foreach database
|
||||
$dotSyntax .= "}\n";
|
||||
|
||||
$this->writeDot($dotSyntax,$this->outDir,$database->getName());
|
||||
|
||||
$dotSyntax = '';
|
||||
|
||||
} //foreach datamodels
|
||||
|
||||
} // main()
|
||||
|
||||
|
||||
/**
|
||||
* probably insecure
|
||||
*/
|
||||
function writeDot($dotSyntax, PhingFile $outputDir, $baseFilename) {
|
||||
$file = new PhingFile($outputDir, $baseFilename . '.schema.dot');
|
||||
$this->log("Writing dot file to " . $file->getAbsolutePath());
|
||||
file_put_contents($file->getAbsolutePath(), $dotSyntax);
|
||||
}
|
||||
|
||||
}
|
231
airtime_mvc/library/propel/generator/lib/task/PropelOMTask.php
Normal file
231
airtime_mvc/library/propel/generator/lib/task/PropelOMTask.php
Normal file
|
@ -0,0 +1,231 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Propel package.
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @license MIT License
|
||||
*/
|
||||
|
||||
require_once 'task/AbstractPropelDataModelTask.php';
|
||||
require_once 'builder/om/ClassTools.php';
|
||||
require_once 'builder/om/OMBuilder.php';
|
||||
|
||||
/**
|
||||
* This Task creates the OM classes based on the XML schema file.
|
||||
*
|
||||
* @author Hans Lellelid <hans@xmpl.org>
|
||||
* @package propel.generator.task
|
||||
*/
|
||||
class PropelOMTask extends AbstractPropelDataModelTask
|
||||
{
|
||||
|
||||
/**
|
||||
* The platform (php4, php5, etc.) for which the om is being built.
|
||||
* @var string
|
||||
*/
|
||||
private $targetPlatform;
|
||||
|
||||
/**
|
||||
* Sets the platform (php4, php5, etc.) for which the om is being built.
|
||||
* @param string $v
|
||||
*/
|
||||
public function setTargetPlatform($v) {
|
||||
$this->targetPlatform = $v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the platform (php4, php5, etc.) for which the om is being built.
|
||||
* @return string
|
||||
*/
|
||||
public function getTargetPlatform() {
|
||||
return $this->targetPlatform;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to create directory for package if it doesn't already exist.
|
||||
* @param string $path The [relative] package path.
|
||||
* @throws BuildException - if there is an error creating directories
|
||||
*/
|
||||
protected function ensureDirExists($path)
|
||||
{
|
||||
$f = new PhingFile($this->getOutputDirectory(), $path);
|
||||
if (!$f->exists()) {
|
||||
if (!$f->mkdirs()) {
|
||||
throw new BuildException("Error creating directories: ". $f->getPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses a builder class to create the output class.
|
||||
* This method assumes that the DataModelBuilder class has been initialized with the build properties.
|
||||
* @param OMBuilder $builder
|
||||
* @param boolean $overwrite Whether to overwrite existing files with te new ones (default is YES).
|
||||
* @todo -cPropelOMTask Consider refactoring build() method into AbstractPropelDataModelTask (would need to be more generic).
|
||||
*/
|
||||
protected function build(OMBuilder $builder, $overwrite = true)
|
||||
{
|
||||
$path = $builder->getClassFilePath();
|
||||
$this->ensureDirExists(dirname($path));
|
||||
|
||||
$_f = new PhingFile($this->getOutputDirectory(), $path);
|
||||
|
||||
// skip files already created once
|
||||
if ($_f->exists() && !$overwrite) {
|
||||
$this->log("\t\t-> (exists) " . $builder->getClassname(), Project::MSG_VERBOSE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
$script = $builder->build();
|
||||
foreach ($builder->getWarnings() as $warning) {
|
||||
$this->log($warning, Project::MSG_WARN);
|
||||
}
|
||||
|
||||
// skip unchanged files
|
||||
if ($_f->exists() && $script == $_f->contents()) {
|
||||
$this->log("\t\t-> (unchanged) " . $builder->getClassname(), Project::MSG_VERBOSE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// write / overwrite new / changed files
|
||||
$this->log("\t\t-> " . $builder->getClassname() . " [builder: " . get_class($builder) . "]");
|
||||
file_put_contents($_f->getAbsolutePath(), $script);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main method builds all the targets for a typical propel project.
|
||||
*/
|
||||
public function main()
|
||||
{
|
||||
// check to make sure task received all correct params
|
||||
$this->validate();
|
||||
|
||||
$generatorConfig = $this->getGeneratorConfig();
|
||||
$totalNbFiles = 0;
|
||||
|
||||
foreach ($this->getDataModels() as $dataModel) {
|
||||
$this->log("Processing Datamodel : " . $dataModel->getName());
|
||||
|
||||
foreach ($dataModel->getDatabases() as $database) {
|
||||
|
||||
$this->log(" - processing database : " . $database->getName());
|
||||
|
||||
foreach ($database->getTables() as $table) {
|
||||
|
||||
if (!$table->isForReferenceOnly()) {
|
||||
|
||||
$nbWrittenFiles = 0;
|
||||
|
||||
$this->log("\t+ " . $table->getName());
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// Create Peer, Object, and TableMap classes
|
||||
// -----------------------------------------------------------------------------------------
|
||||
|
||||
// these files are always created / overwrite any existing files
|
||||
foreach (array('peer', 'object', 'tablemap', 'query') as $target) {
|
||||
$builder = $generatorConfig->getConfiguredBuilder($table, $target);
|
||||
$nbWrittenFiles += $this->build($builder);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// Create [empty] stub Peer and Object classes if they don't exist
|
||||
// -----------------------------------------------------------------------------------------
|
||||
|
||||
// these classes are only generated if they don't already exist
|
||||
foreach (array('peerstub', 'objectstub', 'querystub') as $target) {
|
||||
$builder = $generatorConfig->getConfiguredBuilder($table, $target);
|
||||
$nbWrittenFiles += $this->build($builder, $overwrite=false);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// Create [empty] stub child Object classes if they don't exist
|
||||
// -----------------------------------------------------------------------------------------
|
||||
|
||||
// If table has enumerated children (uses inheritance) then create the empty child stub classes if they don't already exist.
|
||||
if ($table->getChildrenColumn()) {
|
||||
$col = $table->getChildrenColumn();
|
||||
if ($col->isEnumeratedClasses()) {
|
||||
foreach ($col->getChildren() as $child) {
|
||||
foreach (array('queryinheritance') as $target) {
|
||||
if (!$child->getAncestor()) {
|
||||
continue;
|
||||
}
|
||||
$builder = $generatorConfig->getConfiguredBuilder($table, $target);
|
||||
$builder->setChild($child);
|
||||
$nbWrittenFiles += $this->build($builder, $overwrite=true);
|
||||
}
|
||||
foreach (array('objectmultiextend', 'queryinheritancestub') as $target) {
|
||||
$builder = $generatorConfig->getConfiguredBuilder($table, $target);
|
||||
$builder->setChild($child);
|
||||
$nbWrittenFiles += $this->build($builder, $overwrite=false);
|
||||
}
|
||||
} // foreach
|
||||
} // if col->is enumerated
|
||||
} // if tbl->getChildrenCol
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// Create [empty] Interface if it doesn't exist
|
||||
// -----------------------------------------------------------------------------------------
|
||||
|
||||
// Create [empty] interface if it does not already exist
|
||||
if ($table->getInterface()) {
|
||||
$builder = $generatorConfig->getConfiguredBuilder($table, 'interface');
|
||||
$nbWrittenFiles += $this->build($builder, $overwrite=false);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// Create tree Node classes
|
||||
// -----------------------------------------------------------------------------------------
|
||||
|
||||
if ($table->treeMode()) {
|
||||
switch($table->treeMode()) {
|
||||
case 'NestedSet':
|
||||
foreach (array('nestedsetpeer', 'nestedset') as $target) {
|
||||
$builder = $generatorConfig->getConfiguredBuilder($table, $target);
|
||||
$nbWrittenFiles += $this->build($builder);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'MaterializedPath':
|
||||
foreach (array('nodepeer', 'node') as $target) {
|
||||
$builder = $generatorConfig->getConfiguredBuilder($table, $target);
|
||||
$nbWrittenFiles += $this->build($builder);
|
||||
}
|
||||
|
||||
foreach (array('nodepeerstub', 'nodestub') as $target) {
|
||||
$builder = $generatorConfig->getConfiguredBuilder($table, $target);
|
||||
$nbWrittenFiles += $this->build($builder, $overwrite=false);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'AdjacencyList':
|
||||
// No implementation for this yet.
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
} // if Table->treeMode()
|
||||
|
||||
$totalNbFiles += $nbWrittenFiles;
|
||||
if ($nbWrittenFiles == 0) {
|
||||
$this->log("\t\t(no change)");
|
||||
}
|
||||
} // if !$table->isForReferenceOnly()
|
||||
|
||||
} // foreach table
|
||||
|
||||
} // foreach database
|
||||
|
||||
} // foreach dataModel
|
||||
if ($totalNbFiles) {
|
||||
$this->log(sprintf("Object model generation complete - %d files written", $totalNbFiles));
|
||||
} else {
|
||||
$this->log("Object model generation complete - All files already up to date");
|
||||
}
|
||||
} // main()
|
||||
}
|
701
airtime_mvc/library/propel/generator/lib/task/PropelSQLExec.php
Normal file
701
airtime_mvc/library/propel/generator/lib/task/PropelSQLExec.php
Normal file
|
@ -0,0 +1,701 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Propel package.
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @license MIT License
|
||||
*/
|
||||
|
||||
require_once 'phing/Task.php';
|
||||
|
||||
/**
|
||||
* Executes all SQL files referenced in the sqldbmap file against their mapped databases.
|
||||
*
|
||||
* This task uses an SQL -> Database map in the form of a properties
|
||||
* file to insert each SQL file listed into its designated database.
|
||||
*
|
||||
* @author Hans Lellelid <hans@xmpl.org>
|
||||
* @author Dominik del Bondio
|
||||
* @author Jeff Martin <jeff@custommonkey.org> (Torque)
|
||||
* @author Michael McCallum <gholam@xtra.co.nz> (Torque)
|
||||
* @author Tim Stephenson <tim.stephenson@sybase.com> (Torque)
|
||||
* @author Jason van Zyl <jvanzyl@apache.org> (Torque)
|
||||
* @author Martin Poeschl <mpoeschl@marmot.at> (Torque)
|
||||
* @version $Revision: 1612 $
|
||||
* @package propel.generator.task
|
||||
*/
|
||||
class PropelSQLExec extends Task
|
||||
{
|
||||
|
||||
private $goodSql = 0;
|
||||
private $totalSql = 0;
|
||||
|
||||
const DELIM_ROW = "row";
|
||||
const DELIM_NORMAL = "normal";
|
||||
|
||||
/**
|
||||
* The delimiter type indicating whether the delimiter will
|
||||
* only be recognized on a line by itself
|
||||
*/
|
||||
private $delimiterType = "normal"; // can't use constant just defined
|
||||
|
||||
//private static $delimiterTypes = array(DELIM_NORMAL, DELIM_ROW);
|
||||
//private static $errorActions = array("continue", "stop", "abort");
|
||||
|
||||
/** PDO Database connection */
|
||||
private $conn = null;
|
||||
|
||||
/** Autocommit flag. Default value is false */
|
||||
private $autocommit = false;
|
||||
|
||||
/** DB url. */
|
||||
private $url = null;
|
||||
|
||||
/** User name. */
|
||||
private $userId = null;
|
||||
|
||||
/** Password */
|
||||
private $password = null;
|
||||
|
||||
/** SQL input command */
|
||||
private $sqlCommand = "";
|
||||
|
||||
/** SQL transactions to perform */
|
||||
private $transactions = array();
|
||||
|
||||
/** SQL Statement delimiter */
|
||||
private $delimiter = ";";
|
||||
|
||||
/** Print SQL results. */
|
||||
private $print = false;
|
||||
|
||||
/** Print header columns. */
|
||||
private $showheaders = true;
|
||||
|
||||
/** Results Output file. */
|
||||
private $output = null;
|
||||
|
||||
/** RDBMS Product needed for this SQL. */
|
||||
private $rdbms = null;
|
||||
|
||||
/** RDBMS Version needed for this SQL. */
|
||||
private $version = null;
|
||||
|
||||
/** Action to perform if an error is found */
|
||||
private $onError = "abort";
|
||||
|
||||
/** Encoding to use when reading SQL statements from a file */
|
||||
private $encoding = null;
|
||||
|
||||
/** Src directory for the files listed in the sqldbmap. */
|
||||
private $srcDir;
|
||||
|
||||
/** Properties file that maps an individual SQL file to a database. */
|
||||
private $sqldbmap;
|
||||
|
||||
/**
|
||||
* Set the sqldbmap properties file.
|
||||
*
|
||||
* @param sqldbmap filename for the sqldbmap
|
||||
*/
|
||||
public function setSqlDbMap($sqldbmap)
|
||||
{
|
||||
$this->sqldbmap = $this->project->resolveFile($sqldbmap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the sqldbmap properties file.
|
||||
*
|
||||
* @return filename for the sqldbmap
|
||||
*/
|
||||
public function getSqlDbMap()
|
||||
{
|
||||
return $this->sqldbmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the src directory for the sql files listed in the sqldbmap file.
|
||||
*
|
||||
* @param PhingFile $srcDir sql source directory
|
||||
*/
|
||||
public function setSrcDir(PhingFile $srcDir)
|
||||
{
|
||||
$this->srcDir = $srcDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the src directory for the sql files listed in the sqldbmap file.
|
||||
*
|
||||
* @return PhingFile SQL Source directory
|
||||
*/
|
||||
public function getSrcDir()
|
||||
{
|
||||
return $this->srcDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the sql command to execute
|
||||
*
|
||||
* @param sql sql command to execute
|
||||
*/
|
||||
public function addText($sql)
|
||||
{
|
||||
$this->sqlCommand .= $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the DB connection url.
|
||||
*
|
||||
* @param string $url connection url
|
||||
*/
|
||||
public function setUrl($url)
|
||||
{
|
||||
$this->url = $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the user name for the DB connection.
|
||||
*
|
||||
* @param string $userId database user
|
||||
* @deprecated Specify userid in the DSN URL.
|
||||
*/
|
||||
public function setUserid($userId)
|
||||
{
|
||||
$this->userId = $userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the password for the DB connection.
|
||||
*
|
||||
* @param string $password database password
|
||||
* @deprecated Specify password in the DSN URL.
|
||||
*/
|
||||
public function setPassword($password)
|
||||
{
|
||||
$this->password = $password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the autocommit flag for the DB connection.
|
||||
*
|
||||
* @param boolean $autocommit the autocommit flag
|
||||
*/
|
||||
public function setAutoCommit($autocommit)
|
||||
{
|
||||
$this->autocommit = (boolean) $autocommit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the statement delimiter.
|
||||
*
|
||||
* <p>For example, set this to "go" and delimitertype to "ROW" for
|
||||
* Sybase ASE or MS SQL Server.</p>
|
||||
*
|
||||
* @param string $delimiter
|
||||
*/
|
||||
public function setDelimiter($delimiter)
|
||||
{
|
||||
$this->delimiter = $delimiter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Delimiter type for this sql task. The delimiter type takes two
|
||||
* values - normal and row. Normal means that any occurence of the delimiter
|
||||
* terminate the SQL command whereas with row, only a line containing just
|
||||
* the delimiter is recognized as the end of the command.
|
||||
*
|
||||
* @param string $delimiterType
|
||||
*/
|
||||
public function setDelimiterType($delimiterType)
|
||||
{
|
||||
$this->delimiterType = $delimiterType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the print flag.
|
||||
*
|
||||
* @param boolean $print
|
||||
*/
|
||||
public function setPrint($print)
|
||||
{
|
||||
$this->print = (boolean) $print;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the showheaders flag.
|
||||
*
|
||||
* @param boolean $showheaders
|
||||
*/
|
||||
public function setShowheaders($showheaders)
|
||||
{
|
||||
$this->showheaders = (boolean) $showheaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the output file.
|
||||
*
|
||||
* @param PhingFile $output
|
||||
*/
|
||||
public function setOutput(PhingFile $output)
|
||||
{
|
||||
$this->output = $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the action to perform onerror
|
||||
*
|
||||
* @param string $action
|
||||
*/
|
||||
public function setOnerror($action)
|
||||
{
|
||||
$this->onError = $action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the sql file and then execute it
|
||||
*
|
||||
* @throws BuildException
|
||||
*/
|
||||
public function main()
|
||||
{
|
||||
$this->sqlCommand = trim($this->sqlCommand);
|
||||
|
||||
if ($this->sqldbmap === null || $this->getSqlDbMap()->exists() === false) {
|
||||
throw new BuildException("You haven't provided an sqldbmap, or "
|
||||
. "the one you specified doesn't exist: " . $this->sqldbmap->getPath());
|
||||
}
|
||||
|
||||
if ($this->url === null) {
|
||||
throw new BuildException("DSN url attribute must be set!");
|
||||
}
|
||||
|
||||
$map = new Properties();
|
||||
|
||||
try {
|
||||
$map->load($this->getSqlDbMap());
|
||||
} catch (IOException $ioe) {
|
||||
throw new BuildException("Cannot open and process the sqldbmap!");
|
||||
}
|
||||
|
||||
$databases = array();
|
||||
|
||||
foreach ($map->keys() as $sqlfile) {
|
||||
|
||||
$database = $map->getProperty($sqlfile);
|
||||
|
||||
// Q: already there?
|
||||
if (!isset($databases[$database])) {
|
||||
// A: No.
|
||||
$databases[$database] = array();
|
||||
}
|
||||
|
||||
// We want to make sure that the base schemas
|
||||
// are inserted first.
|
||||
if (strpos($sqlfile, "schema.sql") !== false) {
|
||||
// add to the beginning of the array
|
||||
array_unshift($databases[$database], $sqlfile);
|
||||
} else {
|
||||
array_push($databases[$database], $sqlfile);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($databases as $db => $files) {
|
||||
$transactions = array();
|
||||
|
||||
foreach ($files as $fileName) {
|
||||
|
||||
$file = new PhingFile($this->srcDir, $fileName);
|
||||
|
||||
if ($file->exists()) {
|
||||
$this->log("Executing statements in file: " . $file->__toString());
|
||||
$transaction = new PropelSQLExecTransaction($this);
|
||||
$transaction->setSrc($file);
|
||||
$transactions[] = $transaction;
|
||||
} else {
|
||||
$this->log("File '" . $file->__toString()
|
||||
. "' in sqldbmap does not exist, so skipping it.");
|
||||
}
|
||||
}
|
||||
$this->insertDatabaseSqlFiles($this->url, $db, $transactions);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Take the base url, the target database and insert a set of SQL
|
||||
* files into the target database.
|
||||
*
|
||||
* @param string $url
|
||||
* @param string $database
|
||||
* @param array $transactions
|
||||
*/
|
||||
private function insertDatabaseSqlFiles($url, $database, $transactions)
|
||||
{
|
||||
$url = str_replace("@DB@", $database, $url);
|
||||
$this->log("Our new url -> " . $url);
|
||||
|
||||
try {
|
||||
|
||||
$buf = "Database settings:" . PHP_EOL
|
||||
. " URL: " . $url . PHP_EOL
|
||||
. ($this->userId ? " user: " . $this->userId . PHP_EOL : "")
|
||||
. ($this->password ? " password: " . $this->password . PHP_EOL : "");
|
||||
|
||||
$this->log($buf, Project::MSG_VERBOSE);
|
||||
|
||||
// Set user + password to null if they are empty strings
|
||||
if (!$this->userId) { $this->userId = null; }
|
||||
|
||||
if (!$this->password) { $this->password = null; }
|
||||
|
||||
$this->conn = new PDO($url, $this->userId, $this->password);
|
||||
$this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
// $this->conn->setAutoCommit($this->autocommit);
|
||||
// $this->statement = $this->conn->createStatement();
|
||||
|
||||
$out = null;
|
||||
|
||||
try {
|
||||
if ($this->output !== null) {
|
||||
$this->log("Opening PrintStream to output file " . $this->output->__toString(), Project::MSG_VERBOSE);
|
||||
$out = new FileWriter($this->output);
|
||||
}
|
||||
|
||||
// Process all transactions
|
||||
for ($i=0,$size=count($transactions); $i < $size; $i++) {
|
||||
$transactions[$i]->runTransaction($out);
|
||||
if (!$this->autocommit) {
|
||||
$this->log("Commiting transaction", Project::MSG_VERBOSE);
|
||||
$this->conn->commit();
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
if ($out) $out->close();
|
||||
}
|
||||
|
||||
} catch (IOException $e) {
|
||||
|
||||
if (!$this->autocommit && $this->conn !== null && $this->onError == "abort") {
|
||||
try {
|
||||
$this->conn->rollBack();
|
||||
} catch (PDOException $ex) {
|
||||
// do nothing.
|
||||
System::println("Rollback failed.");
|
||||
}
|
||||
}
|
||||
if ($this->statement) $this->statement = null; // close
|
||||
throw new BuildException($e);
|
||||
} catch (PDOException $e) {
|
||||
if (!$this->autocommit && $this->conn !== null && $this->onError == "abort") {
|
||||
try {
|
||||
$this->conn->rollBack();
|
||||
} catch (PDOException $ex) {
|
||||
// do nothing.
|
||||
System::println("Rollback failed");
|
||||
}
|
||||
}
|
||||
if ($this->statement) $this->statement = null; // close
|
||||
throw new BuildException($e);
|
||||
}
|
||||
|
||||
$this->statement = null; // close
|
||||
|
||||
$this->log($this->goodSql . " of " . $this->totalSql
|
||||
. " SQL statements executed successfully");
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the statements from the .sql file and execute them.
|
||||
* Lines starting with '//', '--' or 'REM ' are ignored.
|
||||
*
|
||||
* Developer note: must be public in order to be called from
|
||||
* sudo-"inner" class PropelSQLExecTransaction.
|
||||
*
|
||||
* @param Reader $reader
|
||||
* @param $out Optional output stream.
|
||||
* @throws PDOException
|
||||
* @throws IOException
|
||||
*/
|
||||
public function runStatements(Reader $reader, $out = null)
|
||||
{
|
||||
$sql = "";
|
||||
$line = "";
|
||||
$sqlBacklog = "";
|
||||
$hasQuery = false;
|
||||
|
||||
$in = new BufferedReader($reader);
|
||||
|
||||
$parser['pointer'] = 0;
|
||||
$parser['isInString'] = false;
|
||||
$parser['stringQuotes'] = "";
|
||||
$parser['backslashCount'] = 0;
|
||||
$parser['parsedString'] = "";
|
||||
|
||||
$sqlParts = array();
|
||||
|
||||
while (($line = $in->readLine()) !== null) {
|
||||
|
||||
$line = trim($line);
|
||||
$line = ProjectConfigurator::replaceProperties($this->project, $line,
|
||||
$this->project->getProperties());
|
||||
|
||||
if (StringHelper::startsWith("//", $line)
|
||||
|| StringHelper::startsWith("--", $line)
|
||||
|| StringHelper::startsWith("#", $line)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strlen($line) > 4 && strtoupper(substr($line,0, 4)) == "REM ") {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($sqlBacklog !== "") {
|
||||
$sql = $sqlBacklog;
|
||||
$sqlBacklog = "";
|
||||
}
|
||||
|
||||
$sql .= " " . $line . PHP_EOL;
|
||||
|
||||
// SQL defines "--" as a comment to EOL
|
||||
// and in Oracle it may contain a hint
|
||||
// so we cannot just remove it, instead we must end it
|
||||
if (strpos($line, "--") !== false) {
|
||||
$sql .= PHP_EOL;
|
||||
}
|
||||
|
||||
// DELIM_ROW doesn't need this (as far as i can tell)
|
||||
if ($this->delimiterType == self::DELIM_NORMAL) {
|
||||
|
||||
// old regex, being replaced due to segfaults:
|
||||
// See: http://propel.phpdb.org/trac/ticket/294
|
||||
//$reg = "#((?:\"(?:\\\\.|[^\"])*\"?)+|'(?:\\\\.|[^'])*'?|" . preg_quote($this->delimiter) . ")#";
|
||||
//$sqlParts = preg_split($reg, $sql, 0, PREG_SPLIT_DELIM_CAPTURE);
|
||||
|
||||
$i = $parser['pointer'];
|
||||
$c = strlen($sql);
|
||||
while ($i < $c) {
|
||||
|
||||
$char = $sql[$i];
|
||||
|
||||
switch($char) {
|
||||
case "\\":
|
||||
$parser['backslashCount']++;
|
||||
$this->log("c$i: found ".$parser['backslashCount']." backslash(es)", Project::MSG_VERBOSE);
|
||||
break;
|
||||
case "'":
|
||||
case "\"":
|
||||
if ($parser['isInString'] && $parser['stringQuotes'] == $char) {
|
||||
if (($parser['backslashCount'] & 1) == 0) {
|
||||
#$this->log("$i: out of string", Project::MSG_VERBOSE);
|
||||
$parser['isInString'] = false;
|
||||
} else {
|
||||
$this->log("c$i: rejected quoted delimiter", Project::MSG_VERBOSE);
|
||||
}
|
||||
|
||||
} elseif (!$parser['isInString']) {
|
||||
$parser['stringQuotes'] = $char;
|
||||
$parser['isInString'] = true;
|
||||
#$this->log("$i: into string with $parser['stringQuotes']", Project::MSG_VERBOSE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if ($char == $this->delimiter && !$parser['isInString']) {
|
||||
$this->log("c$i: valid end of command found!", Project::MSG_VERBOSE);
|
||||
$sqlParts[] = $parser['parsedString'];
|
||||
$sqlParts[] = $this->delimiter;
|
||||
break;
|
||||
}
|
||||
$parser['parsedString'] .= $char;
|
||||
if ($char !== "\\") {
|
||||
if ($parser['backslashCount']) $this->log("$i: backslash reset", Project::MSG_VERBOSE);
|
||||
$parser['backslashCount'] = 0;
|
||||
}
|
||||
$i++;
|
||||
$parser['pointer']++;
|
||||
}
|
||||
|
||||
$sqlBacklog = "";
|
||||
foreach ($sqlParts as $sqlPart) {
|
||||
// we always want to append, even if it's a delim (which will be stripped off later)
|
||||
$sqlBacklog .= $sqlPart;
|
||||
|
||||
// we found a single (not enclosed by ' or ") delimiter, so we can use all stuff before the delim as the actual query
|
||||
if ($sqlPart === $this->delimiter) {
|
||||
$sql = $sqlBacklog;
|
||||
$sqlBacklog = "";
|
||||
$hasQuery = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($hasQuery || ($this->delimiterType == self::DELIM_ROW && $line == $this->delimiter)) {
|
||||
// this assumes there is always a delimter on the end of the SQL statement.
|
||||
$sql = StringHelper::substring($sql, 0, strlen($sql) - 1 - strlen($this->delimiter));
|
||||
$this->log("SQL: " . $sql, Project::MSG_VERBOSE);
|
||||
$this->execSQL($sql, $out);
|
||||
$sql = "";
|
||||
$hasQuery = false;
|
||||
|
||||
$parser['pointer'] = 0;
|
||||
$parser['isInString'] = false;
|
||||
$parser['stringQuotes'] = "";
|
||||
$parser['backslashCount'] = 0;
|
||||
$parser['parsedString'] = "";
|
||||
$sqlParts = array();
|
||||
}
|
||||
}
|
||||
|
||||
// Catch any statements not followed by ;
|
||||
if ($sql !== "") {
|
||||
$this->execSQL($sql, $out);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exec the sql statement.
|
||||
*
|
||||
* @param sql
|
||||
* @param out
|
||||
* @throws PDOException
|
||||
*/
|
||||
protected function execSQL($sql, $out = null)
|
||||
{
|
||||
// Check and ignore empty statements
|
||||
if (trim($sql) == "") {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->totalSql++;
|
||||
|
||||
if (!$this->autocommit) $this->conn->beginTransaction();
|
||||
|
||||
$stmt = $this->conn->prepare($sql);
|
||||
$stmt->execute();
|
||||
$this->log($stmt->rowCount() . " rows affected", Project::MSG_VERBOSE);
|
||||
|
||||
if (!$this->autocommit) $this->conn->commit();
|
||||
|
||||
$this->goodSql++;
|
||||
} catch (PDOException $e) {
|
||||
$this->log("Failed to execute: " . $sql, Project::MSG_ERR);
|
||||
if ($this->onError != "continue") {
|
||||
throw $e;
|
||||
}
|
||||
$this->log($e->getMessage(), Project::MSG_ERR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* print any results in the statement.
|
||||
*
|
||||
* @param out
|
||||
* @throws PDOException
|
||||
*/
|
||||
protected function printResults($out = null)
|
||||
{
|
||||
$rs = null;
|
||||
|
||||
do {
|
||||
$rs = $this->statement->getResultSet();
|
||||
|
||||
if ($rs !== null) {
|
||||
|
||||
$this->log("Processing new result set.", Project::MSG_VERBOSE);
|
||||
|
||||
$line = "";
|
||||
|
||||
$colsprinted = false;
|
||||
|
||||
while ($rs->next()) {
|
||||
|
||||
if (!$colsprinted && $this->showheaders) {
|
||||
$first = true;
|
||||
foreach ($this->fields as $fieldName => $ignore) {
|
||||
if ($first) $first = false; else $line .= ",";
|
||||
$line .= $fieldName;
|
||||
}
|
||||
} // if show headers
|
||||
|
||||
$first = true;
|
||||
foreach ($rs->fields as $columnValue) {
|
||||
|
||||
if ($columnValue != null) {
|
||||
$columnValue = trim($columnValue);
|
||||
}
|
||||
|
||||
if ($first) {
|
||||
$first = false;
|
||||
} else {
|
||||
$line .= ",";
|
||||
}
|
||||
$line .= $columnValue;
|
||||
}
|
||||
|
||||
if ($out !== null) {
|
||||
$out->write($line);
|
||||
$out->newLine();
|
||||
}
|
||||
|
||||
System::println($line);
|
||||
$line = "";
|
||||
} // while rs->next()
|
||||
}
|
||||
} while ($this->statement->getMoreResults());
|
||||
System::println();
|
||||
if ($out !== null) $out->newLine();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* "Inner" class that contains the definition of a new transaction element.
|
||||
* Transactions allow several files or blocks of statements
|
||||
* to be executed using the same Propel connection and commit
|
||||
* operation in between.
|
||||
* @package propel.generator.task
|
||||
*/
|
||||
class PropelSQLExecTransaction
|
||||
{
|
||||
|
||||
private $tSrcFile = null;
|
||||
private $tSqlCommand = "";
|
||||
private $parent;
|
||||
|
||||
function __construct($parent)
|
||||
{
|
||||
// Parent is required so that we can log things ...
|
||||
$this->parent = $parent;
|
||||
}
|
||||
|
||||
public function setSrc(PhingFile $src)
|
||||
{
|
||||
$this->tSrcFile = $src;
|
||||
}
|
||||
|
||||
public function addText($sql)
|
||||
{
|
||||
$this->tSqlCommand .= $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws IOException, PDOException
|
||||
*/
|
||||
public function runTransaction($out = null)
|
||||
{
|
||||
if (!empty($this->tSqlCommand)) {
|
||||
$this->parent->log("Executing commands", Project::MSG_INFO);
|
||||
$this->parent->runStatements($this->tSqlCommand, $out);
|
||||
}
|
||||
|
||||
if ($this->tSrcFile !== null) {
|
||||
$this->parent->log("Executing file: " . $this->tSrcFile->getAbsolutePath(), Project::MSG_INFO);
|
||||
$reader = new FileReader($this->tSrcFile);
|
||||
$this->parent->runStatements($reader, $out);
|
||||
$reader->close();
|
||||
}
|
||||
}
|
||||
}
|
250
airtime_mvc/library/propel/generator/lib/task/PropelSQLTask.php
Normal file
250
airtime_mvc/library/propel/generator/lib/task/PropelSQLTask.php
Normal file
|
@ -0,0 +1,250 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Propel package.
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @license MIT License
|
||||
*/
|
||||
|
||||
require_once 'model/AppData.php';
|
||||
|
||||
/**
|
||||
* The task for building SQL DDL based on the XML datamodel.
|
||||
*
|
||||
* This class uses the new DDLBuilder classes instead of the Capsule PHP templates.
|
||||
*
|
||||
* @author Hans Lellelid <hans@xmpl.org>
|
||||
* @package propel.generator.task
|
||||
*/
|
||||
class PropelSQLTask extends AbstractPropelDataModelTask
|
||||
{
|
||||
|
||||
/**
|
||||
* The properties file that maps an SQL file to a particular database.
|
||||
* @var PhingFile
|
||||
*/
|
||||
private $sqldbmap;
|
||||
|
||||
/**
|
||||
* Name of the database.
|
||||
*/
|
||||
private $database;
|
||||
|
||||
/**
|
||||
* Set the sqldbmap.
|
||||
* @param PhingFile $sqldbmap The db map.
|
||||
*/
|
||||
public function setSqlDbMap(PhingFile $sqldbmap)
|
||||
{
|
||||
$this->sqldbmap = $sqldbmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the sqldbmap.
|
||||
* @return PhingFile $sqldbmap.
|
||||
*/
|
||||
public function getSqlDbMap()
|
||||
{
|
||||
return $this->sqldbmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the database name.
|
||||
* @param string $database
|
||||
*/
|
||||
public function setDatabase($database)
|
||||
{
|
||||
$this->database = $database;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the database name.
|
||||
* @return string
|
||||
*/
|
||||
public function getDatabase()
|
||||
{
|
||||
return $this->database;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the sql -> database map.
|
||||
*
|
||||
* @throws IOException - if unable to store properties
|
||||
*/
|
||||
protected function createSqlDbMap()
|
||||
{
|
||||
if ($this->getSqlDbMap() === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Produce the sql -> database map
|
||||
$sqldbmap = new Properties();
|
||||
|
||||
// Check to see if the sqldbmap has already been created.
|
||||
if ($this->getSqlDbMap()->exists()) {
|
||||
$sqldbmap->load($this->getSqlDbMap());
|
||||
}
|
||||
|
||||
if ($this->packageObjectModel) {
|
||||
// in this case we'll get the sql file name from the package attribute
|
||||
$dataModels = $this->packageDataModels();
|
||||
foreach ($dataModels as $package => $dataModel) {
|
||||
foreach ($dataModel->getDatabases() as $database) {
|
||||
$name = ($package ? $package . '.' : '') . 'schema.xml';
|
||||
$sqlFile = $this->getMappedFile($name);
|
||||
$sqldbmap->setProperty($sqlFile->getName(), $database->getName());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// the traditional way is to map the schema.xml filenames
|
||||
$dmMap = $this->getDataModelDbMap();
|
||||
foreach (array_keys($dmMap) as $dataModelName) {
|
||||
$sqlFile = $this->getMappedFile($dataModelName);
|
||||
if ($this->getDatabase() === null) {
|
||||
$databaseName = $dmMap[$dataModelName];
|
||||
} else {
|
||||
$databaseName = $this->getDatabase();
|
||||
}
|
||||
$sqldbmap->setProperty($sqlFile->getName(), $databaseName);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$sqldbmap->store($this->getSqlDbMap(), "Sqlfile -> Database map");
|
||||
} catch (IOException $e) {
|
||||
throw new IOException("Unable to store properties: ". $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function main() {
|
||||
|
||||
$this->validate();
|
||||
|
||||
if (!$this->mapperElement) {
|
||||
throw new BuildException("You must use a <mapper/> element to describe how names should be transformed.");
|
||||
}
|
||||
|
||||
if ($this->packageObjectModel) {
|
||||
$dataModels = $this->packageDataModels();
|
||||
} else {
|
||||
$dataModels = $this->getDataModels();
|
||||
}
|
||||
|
||||
// 1) first create a map of filenames to databases; this is used by other tasks like
|
||||
// the SQLExec task.
|
||||
$this->createSqlDbMap();
|
||||
|
||||
// 2) Now actually create the DDL based on the datamodel(s) from XML schema file.
|
||||
$targetDatabase = $this->getTargetDatabase();
|
||||
|
||||
$generatorConfig = $this->getGeneratorConfig();
|
||||
|
||||
$builderClazz = $generatorConfig->getBuilderClassname('ddl');
|
||||
|
||||
foreach ($dataModels as $package => $dataModel) {
|
||||
|
||||
foreach ($dataModel->getDatabases() as $database) {
|
||||
|
||||
// Clear any start/end DLL
|
||||
call_user_func(array($builderClazz, 'reset'));
|
||||
|
||||
// file we are going to create
|
||||
if (!$this->packageObjectModel) {
|
||||
$name = $dataModel->getName();
|
||||
} else {
|
||||
$name = ($package ? $package . '.' : '') . 'schema.xml';
|
||||
}
|
||||
|
||||
$outFile = $this->getMappedFile($name);
|
||||
|
||||
$this->log("Writing to SQL file: " . $outFile->getPath());
|
||||
|
||||
// First add any "header" SQL
|
||||
$ddl = call_user_func(array($builderClazz, 'getDatabaseStartDDL'));
|
||||
|
||||
foreach ($database->getTables() as $table) {
|
||||
|
||||
if (!$table->isSkipSql()) {
|
||||
$builder = $generatorConfig->getConfiguredBuilder($table, 'ddl');
|
||||
$this->log("\t+ " . $table->getName() . " [builder: " . get_class($builder) . "]");
|
||||
$ddl .= $builder->build();
|
||||
foreach ($builder->getWarnings() as $warning) {
|
||||
$this->log($warning, Project::MSG_WARN);
|
||||
}
|
||||
} else {
|
||||
$this->log("\t + (skipping) " . $table->getName());
|
||||
}
|
||||
|
||||
} // foreach database->getTables()
|
||||
|
||||
// Finally check to see if there is any "footer" SQL
|
||||
$ddl .= call_user_func(array($builderClazz, 'getDatabaseEndDDL'));
|
||||
|
||||
#var_dump($outFile->getAbsolutePath());
|
||||
// Now we're done. Write the file!
|
||||
file_put_contents($outFile->getAbsolutePath(), $ddl);
|
||||
|
||||
} // foreach database
|
||||
} //foreach datamodels
|
||||
|
||||
} // main()
|
||||
|
||||
/**
|
||||
* Packages the datamodels to one datamodel per package
|
||||
*
|
||||
* This applies only when the the packageObjectModel option is set. We need to
|
||||
* re-package the datamodels to allow the database package attribute to control
|
||||
* which tables go into which SQL file.
|
||||
*
|
||||
* @return array The packaged datamodels
|
||||
*/
|
||||
protected function packageDataModels() {
|
||||
|
||||
static $packagedDataModels;
|
||||
|
||||
if (is_null($packagedDataModels)) {
|
||||
|
||||
$dataModels = $this->getDataModels();
|
||||
$dataModel = array_shift($dataModels);
|
||||
$packagedDataModels = array();
|
||||
|
||||
$platform = $this->getGeneratorConfig()->getConfiguredPlatform();
|
||||
|
||||
foreach ($dataModel->getDatabases() as $db) {
|
||||
foreach ($db->getTables() as $table) {
|
||||
$package = $table->getPackage();
|
||||
if (!isset($packagedDataModels[$package])) {
|
||||
$dbClone = $this->cloneDatabase($db);
|
||||
$dbClone->setPackage($package);
|
||||
$ad = new AppData($platform);
|
||||
$ad->setName($dataModel->getName());
|
||||
$ad->addDatabase($dbClone);
|
||||
$packagedDataModels[$package] = $ad;
|
||||
}
|
||||
$packagedDataModels[$package]->getDatabase($db->getName())->addTable($table);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $packagedDataModels;
|
||||
}
|
||||
|
||||
protected function cloneDatabase($db) {
|
||||
|
||||
$attributes = array (
|
||||
'name' => $db->getName(),
|
||||
'baseClass' => $db->getBaseClass(),
|
||||
'basePeer' => $db->getBasePeer(),
|
||||
'defaultIdMethod' => $db->getDefaultIdMethod(),
|
||||
'defaultPhpNamingMethod' => $db->getDefaultPhpNamingMethod(),
|
||||
'defaultTranslateMethod' => $db->getDefaultTranslateMethod(),
|
||||
'heavyIndexing' => $db->getHeavyIndexing(),
|
||||
);
|
||||
|
||||
$clone = new Database();
|
||||
$clone->loadFromXML($attributes);
|
||||
return $clone;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,548 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Propel package.
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @license MIT License
|
||||
*/
|
||||
|
||||
require_once 'phing/tasks/ext/pdo/PDOTask.php';
|
||||
require_once 'config/GeneratorConfig.php';
|
||||
require_once 'model/PropelTypes.php';
|
||||
|
||||
/**
|
||||
* This class generates an XML schema of an existing database from
|
||||
* the database metadata.
|
||||
*
|
||||
* @author Hans Lellelid <hans@xmpl.org>
|
||||
* @version $Revision: 1716 $
|
||||
* @package propel.generator.task
|
||||
*/
|
||||
class PropelSchemaReverseTask extends PDOTask
|
||||
{
|
||||
|
||||
/**
|
||||
* Zero bit for no validators
|
||||
*/
|
||||
const VALIDATORS_NONE = 0;
|
||||
|
||||
/**
|
||||
* Bit for maxLength validator
|
||||
*/
|
||||
const VALIDATORS_MAXLENGTH = 1;
|
||||
|
||||
/**
|
||||
* Bit for maxValue validator
|
||||
*/
|
||||
const VALIDATORS_MAXVALUE = 2;
|
||||
|
||||
/**
|
||||
* Bit for type validator
|
||||
*/
|
||||
const VALIDATORS_TYPE = 4;
|
||||
|
||||
/**
|
||||
* Bit for required validator
|
||||
*/
|
||||
const VALIDATORS_REQUIRED = 8;
|
||||
|
||||
/**
|
||||
* Bit for unique validator
|
||||
*/
|
||||
const VALIDATORS_UNIQUE = 16;
|
||||
|
||||
/**
|
||||
* Bit for all validators
|
||||
*/
|
||||
const VALIDATORS_ALL = 255;
|
||||
|
||||
/**
|
||||
* File to contain XML database schema.
|
||||
* @var PhingFIle
|
||||
*/
|
||||
protected $xmlSchema;
|
||||
|
||||
/**
|
||||
* DB encoding to use
|
||||
* @var string
|
||||
*/
|
||||
protected $dbEncoding = 'iso-8859-1';
|
||||
|
||||
/**
|
||||
* DB schema to use.
|
||||
* @var string
|
||||
*/
|
||||
protected $dbSchema;
|
||||
|
||||
/**
|
||||
* The datasource name (used for <database name=""> in schema.xml)
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $databaseName;
|
||||
|
||||
/**
|
||||
* DOM document produced.
|
||||
* @var DOMDocument
|
||||
*/
|
||||
protected $doc;
|
||||
|
||||
/**
|
||||
* The document root element.
|
||||
* @var DOMElement
|
||||
*/
|
||||
protected $databaseNode;
|
||||
|
||||
/**
|
||||
* Hashtable of columns that have primary keys.
|
||||
* @var array
|
||||
*/
|
||||
protected $primaryKeys;
|
||||
|
||||
/**
|
||||
* Whether to use same name for phpName or not.
|
||||
* @var boolean
|
||||
*/
|
||||
protected $samePhpName;
|
||||
|
||||
/**
|
||||
* whether to add vendor info or not
|
||||
* @var boolean
|
||||
*/
|
||||
protected $addVendorInfo;
|
||||
|
||||
/**
|
||||
* Bitfield to switch on/off which validators will be created.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $validatorBits = PropelSchemaReverseTask::VALIDATORS_NONE;
|
||||
|
||||
/**
|
||||
* Collect validatorInfos to create validators.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $validatorInfos;
|
||||
|
||||
/**
|
||||
* An initialized GeneratorConfig object containing the converted Phing props.
|
||||
*
|
||||
* @var GeneratorConfig
|
||||
*/
|
||||
private $generatorConfig;
|
||||
|
||||
/**
|
||||
* Maps validator type tokens to bits
|
||||
*
|
||||
* The tokens are used in the propel.addValidators property to define
|
||||
* which validators are to be added
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
static protected $validatorBitMap = array (
|
||||
'none' => PropelSchemaReverseTask::VALIDATORS_NONE,
|
||||
'maxlength' => PropelSchemaReverseTask::VALIDATORS_MAXLENGTH,
|
||||
'maxvalue' => PropelSchemaReverseTask::VALIDATORS_MAXVALUE,
|
||||
'type' => PropelSchemaReverseTask::VALIDATORS_TYPE,
|
||||
'required' => PropelSchemaReverseTask::VALIDATORS_REQUIRED,
|
||||
'unique' => PropelSchemaReverseTask::VALIDATORS_UNIQUE,
|
||||
'all' => PropelSchemaReverseTask::VALIDATORS_ALL,
|
||||
);
|
||||
|
||||
/**
|
||||
* Defines messages that are added to validators
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
static protected $validatorMessages = array (
|
||||
'maxlength' => array (
|
||||
'msg' => 'The field %s must be not longer than %s characters.',
|
||||
'var' => array('colName', 'value')
|
||||
),
|
||||
'maxvalue' => array (
|
||||
'msg' => 'The field %s must be not greater than %s.',
|
||||
'var' => array('colName', 'value')
|
||||
),
|
||||
'type' => array (
|
||||
'msg' => 'The column %s must be an %s value.',
|
||||
'var' => array('colName', 'value')
|
||||
),
|
||||
'required' => array (
|
||||
'msg' => 'The field %s is required.',
|
||||
'var' => array('colName')
|
||||
),
|
||||
'unique' => array (
|
||||
'msg' => 'This %s already exists in table %s.',
|
||||
'var' => array('colName', 'tableName')
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Gets the (optional) schema name to use.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDbSchema()
|
||||
{
|
||||
return $this->dbSchema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of a database schema to use (optional).
|
||||
*
|
||||
* @param string $dbSchema
|
||||
*/
|
||||
public function setDbSchema($dbSchema)
|
||||
{
|
||||
$this->dbSchema = $dbSchema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the database encoding.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDbEncoding($v)
|
||||
{
|
||||
return $this->dbEncoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the database encoding.
|
||||
*
|
||||
* @param string $v
|
||||
*/
|
||||
public function setDbEncoding($v)
|
||||
{
|
||||
$this->dbEncoding = $v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the datasource name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDatabaseName()
|
||||
{
|
||||
return $this->databaseName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the datasource name.
|
||||
*
|
||||
* This will be used as the <database name=""> value in the generated schema.xml
|
||||
*
|
||||
* @param string $v
|
||||
*/
|
||||
public function setDatabaseName($v)
|
||||
{
|
||||
$this->databaseName = $v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the output name for the XML file.
|
||||
*
|
||||
* @param PhingFile $v
|
||||
*/
|
||||
public function setOutputFile(PhingFile $v)
|
||||
{
|
||||
$this->xmlSchema = $v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to use the column name as phpName without any translation.
|
||||
*
|
||||
* @param boolean $v
|
||||
*/
|
||||
public function setSamePhpName($v)
|
||||
{
|
||||
$this->samePhpName = $v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to add vendor info to the schema.
|
||||
*
|
||||
* @param boolean $v
|
||||
*/
|
||||
public function setAddVendorInfo($v)
|
||||
{
|
||||
$this->addVendorInfo = (boolean) $v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets set validator bitfield from a comma-separated list of "validator bit" names.
|
||||
*
|
||||
* @param string $v The comma-separated list of which validators to add.
|
||||
* @return void
|
||||
*/
|
||||
public function setAddValidators($v)
|
||||
{
|
||||
$validKeys = array_keys(self::$validatorBitMap);
|
||||
|
||||
// lowercase input
|
||||
$v = strtolower($v);
|
||||
|
||||
$bits = self::VALIDATORS_NONE;
|
||||
|
||||
$exprs = explode(',', $v);
|
||||
foreach ($exprs as $expr) {
|
||||
$expr = trim($expr);
|
||||
if(!empty($expr)) {
|
||||
if (!isset(self::$validatorBitMap[$expr])) {
|
||||
throw new BuildException("Unable to interpret validator in expression ('$v'): " . $expr);
|
||||
}
|
||||
$bits |= self::$validatorBitMap[$expr];
|
||||
}
|
||||
}
|
||||
|
||||
$this->validatorBits = $bits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether to add validators of specified type or not
|
||||
*
|
||||
* @param int $type The validator type constant.
|
||||
* @return boolean
|
||||
*/
|
||||
protected function isValidatorRequired($type)
|
||||
{
|
||||
return (($this->validatorBits & $type) === $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to use the column name as phpName without any translation.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isSamePhpName()
|
||||
{
|
||||
return $this->samePhpName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws BuildException
|
||||
*/
|
||||
public function main()
|
||||
{
|
||||
if (!$this->getDatabaseName()) {
|
||||
throw new BuildException("databaseName attribute is required for schema reverse engineering", $this->getLocation());
|
||||
}
|
||||
|
||||
//(not yet supported) $this->log("schema : " . $this->dbSchema);
|
||||
//DocumentTypeImpl docType = new DocumentTypeImpl(null, "database", null,
|
||||
// "http://jakarta.apache.org/turbine/dtd/database.dtd");
|
||||
|
||||
$this->doc = new DOMDocument('1.0', 'utf-8');
|
||||
$this->doc->formatOutput = true; // pretty printing
|
||||
|
||||
$this->doc->appendChild($this->doc->createComment("Autogenerated by ".get_class($this)." class."));
|
||||
|
||||
try {
|
||||
|
||||
$database = $this->buildModel();
|
||||
|
||||
if ($this->validatorBits !== self::VALIDATORS_NONE) {
|
||||
$this->addValidators($database);
|
||||
}
|
||||
|
||||
$database->appendXml($this->doc);
|
||||
|
||||
$this->log("Writing XML to file: " . $this->xmlSchema->getPath());
|
||||
$out = new FileWriter($this->xmlSchema);
|
||||
$xmlstr = $this->doc->saveXML();
|
||||
$out->write($xmlstr);
|
||||
$out->close();
|
||||
|
||||
} catch (Exception $e) {
|
||||
$this->log("There was an error building XML from metadata: " . $e->getMessage(), Project::MSG_ERR);
|
||||
}
|
||||
|
||||
$this->log("Schema reverse engineering finished");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the GeneratorConfig object for this task or creates it on-demand.
|
||||
* @return GeneratorConfig
|
||||
*/
|
||||
protected function getGeneratorConfig()
|
||||
{
|
||||
if ($this->generatorConfig === null) {
|
||||
$this->generatorConfig = new GeneratorConfig();
|
||||
$this->generatorConfig->setBuildProperties($this->getProject()->getProperties());
|
||||
}
|
||||
return $this->generatorConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the model classes from the database schema.
|
||||
* @return Database The built-out Database (with all tables, etc.)
|
||||
*/
|
||||
protected function buildModel()
|
||||
{
|
||||
$config = $this->getGeneratorConfig();
|
||||
$con = $this->getConnection();
|
||||
|
||||
$database = new Database($this->getDatabaseName());
|
||||
$database->setPlatform($config->getConfiguredPlatform($con));
|
||||
|
||||
// Some defaults ...
|
||||
$database->setDefaultIdMethod(IDMethod::NATIVE);
|
||||
|
||||
$parser = $config->getConfiguredSchemaParser($con);
|
||||
|
||||
$nbTables = $parser->parse($database, $this);
|
||||
|
||||
$this->log("Successfully Reverse Engineered " . $nbTables . " tables");
|
||||
|
||||
return $database;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds any requested validators to the data model.
|
||||
*
|
||||
* We will add the following type specific validators:
|
||||
*
|
||||
* for notNull columns: required validator
|
||||
* for unique indexes: unique validator
|
||||
* for varchar types: maxLength validators (CHAR, VARCHAR, LONGVARCHAR)
|
||||
* for numeric types: maxValue validators (BIGINT, SMALLINT, TINYINT, INTEGER, FLOAT, DOUBLE, NUMERIC, DECIMAL, REAL)
|
||||
* for integer and timestamp types: notMatch validator with [^\d]+ (BIGINT, SMALLINT, TINYINT, INTEGER, TIMESTAMP)
|
||||
* for float types: notMatch validator with [^\d\.]+ (FLOAT, DOUBLE, NUMERIC, DECIMAL, REAL)
|
||||
*
|
||||
* @param Database $database The Database model.
|
||||
* @return void
|
||||
* @todo find out how to evaluate the appropriate size and adjust maxValue rule values appropriate
|
||||
* @todo find out if float type column values must always notMatch('[^\d\.]+'), i.e. digits and point for any db vendor, language etc.
|
||||
*/
|
||||
protected function addValidators(Database $database)
|
||||
{
|
||||
|
||||
$platform = $this->getGeneratorConfig()->getConfiguredPlatform();
|
||||
|
||||
foreach ($database->getTables() as $table) {
|
||||
|
||||
$set = new PropelSchemaReverse_ValidatorSet();
|
||||
|
||||
foreach ($table->getColumns() as $col) {
|
||||
|
||||
if ($col->isNotNull() && $this->isValidatorRequired(self::VALIDATORS_REQUIRED)) {
|
||||
$validator = $set->getValidator($col);
|
||||
$validator->addRule($this->getValidatorRule($col, 'required'));
|
||||
}
|
||||
|
||||
if (in_array($col->getType(), array(PropelTypes::CHAR, PropelTypes::VARCHAR, PropelTypes::LONGVARCHAR))
|
||||
&& $col->getSize() && $this->isValidatorRequired(self::VALIDATORS_MAXLENGTH)) {
|
||||
$validator = $set->getValidator($col);
|
||||
$validator->addRule($this->getValidatorRule($col, 'maxLength', $col->getSize()));
|
||||
}
|
||||
|
||||
if ($col->isNumericType() && $this->isValidatorRequired(self::VALIDATORS_MAXVALUE)) {
|
||||
$this->log("WARNING: maxValue validator added for column ".$col->getName().". You will have to adjust the size value manually.", Project::MSG_WARN);
|
||||
$validator = $set->getValidator($col);
|
||||
$validator->addRule($this->getValidatorRule($col, 'maxValue', 'REPLACEME'));
|
||||
}
|
||||
|
||||
if ($col->isPhpPrimitiveType() && $this->isValidatorRequired(self::VALIDATORS_TYPE)) {
|
||||
$validator = $set->getValidator($col);
|
||||
$validator->addRule($this->getValidatorRule($col, 'type', $col->getPhpType()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
foreach ($table->getUnices() as $unique) {
|
||||
$colnames = $unique->getColumns();
|
||||
if (count($colnames) == 1) { // currently 'unique' validator only works w/ single columns.
|
||||
$col = $table->getColumn($colnames[0]);
|
||||
$validator = $set->getValidator($col);
|
||||
$validator->addRule($this->getValidatorRule($col, 'unique'));
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($set->getValidators() as $validator) {
|
||||
$table->addValidator($validator);
|
||||
}
|
||||
|
||||
} // foreach table
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets validator rule for specified type (string).
|
||||
*
|
||||
* @param Column $column The column that is being validated.
|
||||
* @param string $type The type (string) for validator (e.g. 'required').
|
||||
* @param mixed $value The value for the validator (if applicable)
|
||||
*/
|
||||
protected function getValidatorRule(Column $column, $type, $value = null)
|
||||
{
|
||||
$rule = new Rule();
|
||||
$rule->setName($type);
|
||||
if ($value !== null) {
|
||||
$rule->setValue($value);
|
||||
}
|
||||
$rule->setMessage($this->getRuleMessage($column, $type, $value));
|
||||
return $rule;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the message for a specified rule.
|
||||
*
|
||||
* @param Column $column
|
||||
* @param string $type
|
||||
* @param mixed $value
|
||||
*/
|
||||
protected function getRuleMessage(Column $column, $type, $value)
|
||||
{
|
||||
// create message
|
||||
$colName = $column->getName();
|
||||
$tableName = $column->getTable()->getName();
|
||||
$msg = self::$validatorMessages[strtolower($type)];
|
||||
$tmp = compact($msg['var']);
|
||||
array_unshift($tmp, $msg['msg']);
|
||||
$msg = call_user_func_array('sprintf', $tmp);
|
||||
return $msg;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper class to store validator sets indexed by column.
|
||||
* @package propel.generator.task
|
||||
*/
|
||||
class PropelSchemaReverse_ValidatorSet
|
||||
{
|
||||
|
||||
/**
|
||||
* Map of column names to validators.
|
||||
*
|
||||
* @var array Validator[]
|
||||
*/
|
||||
private $validators = array();
|
||||
|
||||
/**
|
||||
* Gets a single validator for specified column name.
|
||||
* @param Column $column
|
||||
* @return Validator
|
||||
*/
|
||||
public function getValidator(Column $column)
|
||||
{
|
||||
$key = $column->getName();
|
||||
if (!isset($this->validators[$key])) {
|
||||
$this->validators[$key] = new Validator();
|
||||
$this->validators[$key]->setColumn($column);
|
||||
}
|
||||
return $this->validators[$key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all validators.
|
||||
* @return array Validator[]
|
||||
*/
|
||||
public function getValidators()
|
||||
{
|
||||
return $this->validators;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue