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:
Paul Baranowski 2011-04-14 18:55:04 -04:00
parent 514777e8d2
commit b11cbd8159
4546 changed files with 138 additions and 51 deletions

View file

@ -0,0 +1,133 @@
<?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
*/
/**
* Changes the coding standard of Propel generated Model classes
* - Opening brackets always use newline, e.g.
* if ($foo) {
* ...
* } else {
* ...
* }
* Becomes:
* if ($foo)
* {
* ...
* }
* else
* {
* ...
* }
* - closing comments are removed, e.g.
* } // save()
* Becomes:
* }
* - tabs are replaced by 2 whitespaces
* - comments are stripped (optional)
*
* @author François Zaninotto
* @version $Revision: 1612 $
* @package propel.generator.behavior
*/
class AlternativeCodingStandardsBehavior extends Behavior
{
// default parameters value
protected $parameters = array(
'brackets_newline' => 'true',
'remove_closing_comments' => 'true',
'use_whitespace' => 'true',
'tab_size' => 2,
'strip_comments' => 'false'
);
public function objectFilter(&$script)
{
return $this->filter($script);
}
public function extensionObjectFilter(&$script)
{
return $this->filter($script);
}
public function queryFilter(&$script)
{
return $this->filter($script);
}
public function extensionQueryFilter(&$script)
{
return $this->filter($script);
}
public function peerFilter(&$script)
{
return $this->filter($script);
}
public function extensionPeerFilter(&$script)
{
return $this->filter($script);
}
public function tableMapFilter(&$script)
{
return $this->filter($script);
}
/**
* Transform the coding standards of a PHP sourcecode string
*
* @param string $script A script string to be filtered, passed as reference
*/
protected function filter(&$script)
{
$filter = array();
if($this->getParameter('brackets_newline') == 'true') {
$filter['#^(\t*)\}\h(else|elseif|catch)(.*)\h\{$#m'] = "$1}
$1$2$3
$1{";
$filter['#^(\t*)(\w.*)\h\{$#m'] = "$1$2
$1{";
}
if ($this->getParameter('remove_closing_comments') == 'true') {
$filter['#^(\t*)} //.*$#m'] = "$1}";
}
if ($this->getParameter('use_whitespace') == 'true') {
$filter['#\t#'] = str_repeat(' ', $this->getParameter('tab_size'));
}
$script = preg_replace(array_keys($filter), array_values($filter), $script);
if ($this->getParameter('strip_comments') == 'true') {
$script = self::stripComments($script);
}
}
/**
* Remove inline and codeblock comments from a PHP code string
* @param string $code The input code
* @return string The input code, without comments
*/
public static function stripComments($code)
{
$output = '';
$commentTokens = array(T_COMMENT, T_DOC_COMMENT);
foreach (token_get_all($code) as $token) {
if (is_array($token)) {
if (in_array($token[0], $commentTokens)) continue;
$token = $token[1];
}
$output .= $token;
}
return $output;
}
}

View file

@ -0,0 +1,54 @@
<?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
*/
/**
* Adds a primary key to models defined without one
*
* @author François Zaninotto
* @version $Revision: 1745 $
* @package propel.generator.behavior
*/
class AutoAddPkBehavior extends Behavior
{
protected $isEarly = true;
// default parameters value
protected $parameters = array(
'name' => 'id',
'autoIncrement' => 'true',
'type' => 'INTEGER'
);
/**
* Copy the behavior to the database tables
* Only for tables that have no Pk
*/
public function modifyDatabase()
{
foreach ($this->getDatabase()->getTables() as $table) {
if(!$table->hasPrimaryKey()) {
$b = clone $this;
$table->addBehavior($b);
}
}
}
/**
* Add the primary key to the current table
*/
public function modifyTable()
{
$table = $this->getTable();
if (!$table->hasPrimaryKey() && !$table->hasBehavior('concrete_inheritance')) {
$columnAttributes = array_merge(array('primaryKey' => 'true'), $this->getParameters());
$this->getTable()->addColumn($columnAttributes);
}
}
}

View file

@ -0,0 +1,467 @@
<?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
*/
/**
* Gives a model class the ability to remain in database even when the user deletes object
* Uses an additional column storing the deletion date
* And an additional condition for every read query to only consider rows with no deletion date
*
* @author François Zaninotto
* @version $Revision: 1807 $
* @package propel.generator.behavior
*/
class SoftDeleteBehavior extends Behavior
{
// default parameters value
protected $parameters = array(
'deleted_column' => 'deleted_at',
);
/**
* Add the deleted_column to the current table
*/
public function modifyTable()
{
if(!$this->getTable()->containsColumn($this->getParameter('deleted_column'))) {
$this->getTable()->addColumn(array(
'name' => $this->getParameter('deleted_column'),
'type' => 'TIMESTAMP'
));
}
}
protected function getColumnSetter()
{
return 'set' . $this->getColumnForParameter('deleted_column')->getPhpName();
}
public function objectMethods($builder)
{
$script = '';
$this->addObjectForceDelete($script);
$this->addObjectUndelete($script);
return $script;
}
public function addObjectForceDelete(&$script)
{
$script .= "
/**
* Bypass the soft_delete behavior and force a hard delete of the current object
*/
public function forceDelete(PropelPDO \$con = null)
{
{$this->getTable()->getPhpName()}Peer::disableSoftDelete();
\$this->delete(\$con);
}
";
}
public function addObjectUndelete(&$script)
{
$script .= "
/**
* Undelete a row that was soft_deleted
*
* @return int The number of rows affected by this update and any referring fk objects' save() operations.
*/
public function unDelete(PropelPDO \$con = null)
{
\$this->{$this->getColumnSetter()}(null);
return \$this->save(\$con);
}
";
}
public function preDelete($builder)
{
return <<<EOT
if (!empty(\$ret) && {$builder->getStubQueryBuilder()->getClassname()}::isSoftDeleteEnabled()) {
\$this->{$this->getColumnSetter()}(time());
\$this->save(\$con);
\$con->commit();
{$builder->getStubPeerBuilder()->getClassname()}::removeInstanceFromPool(\$this);
return;
}
EOT;
}
public function queryAttributes()
{
return "protected static \$softDelete = true;
protected \$localSoftDelete = true;
";
}
public function queryMethods($builder)
{
$this->builder = $builder;
$script = '';
$this->addQueryIncludeDeleted($script);
$this->addQuerySoftDelete($script);
$this->addQueryForceDelete($script);
$this->addQueryForceDeleteAll($script);
$this->addQueryUnDelete($script);
$this->addQueryEnableSoftDelete($script);
$this->addQueryDisableSoftDelete($script);
$this->addQueryIsSoftDeleteEnabled($script);
return $script;
}
public function addQueryIncludeDeleted(&$script)
{
$script .= "
/**
* Temporarily disable the filter on deleted rows
* Valid only for the current query
*
* @see {$this->builder->getStubQueryBuilder()->getClassname()}::disableSoftDelete() to disable the filter for more than one query
*
* @return {$this->builder->getStubQueryBuilder()->getClassname()} The current query, for fuid interface
*/
public function includeDeleted()
{
\$this->localSoftDelete = false;
return \$this;
}
";
}
public function addQuerySoftDelete(&$script)
{
$script .= "
/**
* Soft delete the selected rows
*
* @param PropelPDO \$con an optional connection object
*
* @return int Number of updated rows
*/
public function softDelete(PropelPDO \$con = null)
{
return \$this->update(array('{$this->getColumnForParameter('deleted_column')->getPhpName()}' => time()), \$con);
}
";
}
public function addQueryForceDelete(&$script)
{
$script .= "
/**
* Bypass the soft_delete behavior and force a hard delete of the selected rows
*
* @param PropelPDO \$con an optional connection object
*
* @return int Number of deleted rows
*/
public function forceDelete(PropelPDO \$con = null)
{
return {$this->builder->getPeerClassname()}::doForceDelete(\$this, \$con);
}
";
}
public function addQueryForceDeleteAll(&$script)
{
$script .= "
/**
* Bypass the soft_delete behavior and force a hard delete of all the rows
*
* @param PropelPDO \$con an optional connection object
*
* @return int Number of deleted rows
*/
public function forceDeleteAll(PropelPDO \$con = null)
{
return {$this->builder->getPeerClassname()}::doForceDeleteAll(\$con);}
";
}
public function addQueryUnDelete(&$script)
{
$script .= "
/**
* Undelete selected rows
*
* @param PropelPDO \$con an optional connection object
*
* @return int The number of rows affected by this update and any referring fk objects' save() operations.
*/
public function unDelete(PropelPDO \$con = null)
{
return \$this->update(array('{$this->getColumnForParameter('deleted_column')->getPhpName()}' => null), \$con);
}
";
}
public function addQueryEnableSoftDelete(&$script)
{
$script .= "
/**
* Enable the soft_delete behavior for this model
*/
public static function enableSoftDelete()
{
self::\$softDelete = true;
}
";
}
public function addQueryDisableSoftDelete(&$script)
{
$script .= "
/**
* Disable the soft_delete behavior for this model
*/
public static function disableSoftDelete()
{
self::\$softDelete = false;
}
";
}
public function addQueryIsSoftDeleteEnabled(&$script)
{
$script .= "
/**
* Check the soft_delete behavior for this model
*
* @return boolean true if the soft_delete behavior is enabled
*/
public static function isSoftDeleteEnabled()
{
return self::\$softDelete;
}
";
}
public function preSelectQuery($builder)
{
return <<<EOT
if ({$builder->getStubQueryBuilder()->getClassname()}::isSoftDeleteEnabled() && \$this->localSoftDelete) {
\$this->addUsingAlias({$this->getColumnForParameter('deleted_column')->getConstantName()}, null, Criteria::ISNULL);
} else {
{$this->getTable()->getPhpName()}Peer::enableSoftDelete();
}
EOT;
}
public function preDeleteQuery($builder)
{
return <<<EOT
if ({$builder->getStubQueryBuilder()->getClassname()}::isSoftDeleteEnabled() && \$this->localSoftDelete) {
return \$this->softDelete(\$con);
} else {
return \$this->hasWhereClause() ? \$this->forceDelete(\$con) : \$this->forceDeleteAll(\$con);
}
EOT;
}
public function staticMethods($builder)
{
$builder->declareClassFromBuilder($builder->getStubQueryBuilder());
$this->builder = $builder;
$script = '';
$this->addPeerEnableSoftDelete($script);
$this->addPeerDisableSoftDelete($script);
$this->addPeerIsSoftDeleteEnabled($script);
$this->addPeerDoSoftDelete($script);
$this->addPeerDoDelete2($script);
$this->addPeerDoSoftDeleteAll($script);
$this->addPeerDoDeleteAll2($script);
return $script;
}
public function addPeerEnableSoftDelete(&$script)
{
$script .= "
/**
* Enable the soft_delete behavior for this model
*/
public static function enableSoftDelete()
{
{$this->builder->getStubQueryBuilder()->getClassname()}::enableSoftDelete();
// some soft_deleted objects may be in the instance pool
{$this->builder->getStubPeerBuilder()->getClassname()}::clearInstancePool();
}
";
}
public function addPeerDisableSoftDelete(&$script)
{
$script .= "
/**
* Disable the soft_delete behavior for this model
*/
public static function disableSoftDelete()
{
{$this->builder->getStubQueryBuilder()->getClassname()}::disableSoftDelete();
}
";
}
public function addPeerIsSoftDeleteEnabled(&$script)
{
$script .= "
/**
* Check the soft_delete behavior for this model
* @return boolean true if the soft_delete behavior is enabled
*/
public static function isSoftDeleteEnabled()
{
return {$this->builder->getStubQueryBuilder()->getClassname()}::isSoftDeleteEnabled();
}
";
}
public function addPeerDoSoftDelete(&$script)
{
$script .= "
/**
* Soft delete records, given a {$this->getTable()->getPhpName()} or Criteria object OR a primary key value.
*
* @param mixed \$values Criteria or {$this->getTable()->getPhpName()} object or primary key or array of primary keys
* which is used to create the DELETE statement
* @param PropelPDO \$con the connection to use
* @return int The number of affected rows (if supported by underlying database driver).
* @throws PropelException Any exceptions caught during processing will be
* rethrown wrapped into a PropelException.
*/
public static function doSoftDelete(\$values, PropelPDO \$con = null)
{
if (\$values instanceof Criteria) {
// rename for clarity
\$criteria = clone \$values;
} elseif (\$values instanceof {$this->getTable()->getPhpName()}) {
// create criteria based on pk values
\$criteria = \$values->buildPkeyCriteria();
} else {
// it must be the primary key
\$criteria = new Criteria(self::DATABASE_NAME);";
$pks = $this->getTable()->getPrimaryKey();
if (count($pks)>1) {
$i = 0;
foreach ($pks as $col) {
$script .= "
\$criteria->add({$col->getConstantName()}, \$values[$i], Criteria::EQUAL);";
$i++;
}
} else {
$col = $pks[0];
$script .= "
\$criteria->add({$col->getConstantName()}, (array) \$values, Criteria::IN);";
}
$script .= "
}
\$criteria->add({$this->getColumnForParameter('deleted_column')->getConstantName()}, time());
return {$this->getTable()->getPhpName()}Peer::doUpdate(\$criteria, \$con);
}
";
}
public function addPeerDoDelete2(&$script)
{
$script .= "
/**
* Delete or soft delete records, depending on {$this->getTable()->getPhpName()}Peer::\$softDelete
*
* @param mixed \$values Criteria or {$this->getTable()->getPhpName()} object or primary key or array of primary keys
* which is used to create the DELETE statement
* @param PropelPDO \$con the connection to use
* @return int The number of affected rows (if supported by underlying database driver).
* @throws PropelException Any exceptions caught during processing will be
* rethrown wrapped into a PropelException.
*/
public static function doDelete2(\$values, PropelPDO \$con = null)
{
if ({$this->getTable()->getPhpName()}Peer::isSoftDeleteEnabled()) {
return {$this->getTable()->getPhpName()}Peer::doSoftDelete(\$values, \$con);
} else {
return {$this->getTable()->getPhpName()}Peer::doForceDelete(\$values, \$con);
}
}";
}
public function addPeerDoSoftDeleteAll(&$script)
{
$script .= "
/**
* Method to soft delete all rows from the {$this->getTable()->getName()} table.
*
* @param PropelPDO \$con the connection to use
* @return int The number of affected rows (if supported by underlying database driver).
* @throws PropelException Any exceptions caught during processing will be
* rethrown wrapped into a PropelException.
*/
public static function doSoftDeleteAll(PropelPDO \$con = null)
{
if (\$con === null) {
\$con = Propel::getConnection({$this->getTable()->getPhpName()}Peer::DATABASE_NAME, Propel::CONNECTION_WRITE);
}
\$selectCriteria = new Criteria();
\$selectCriteria->add({$this->getColumnForParameter('deleted_column')->getConstantName()}, null, Criteria::ISNULL);
\$selectCriteria->setDbName({$this->getTable()->getPhpName()}Peer::DATABASE_NAME);
\$modifyCriteria = new Criteria();
\$modifyCriteria->add({$this->getColumnForParameter('deleted_column')->getConstantName()}, time());
return BasePeer::doUpdate(\$selectCriteria, \$modifyCriteria, \$con);
}
";
}
public function addPeerDoDeleteAll2(&$script)
{
$script .= "
/**
* Delete or soft delete all records, depending on {$this->getTable()->getPhpName()}Peer::\$softDelete
*
* @param PropelPDO \$con the connection to use
* @return int The number of affected rows (if supported by underlying database driver).
* @throws PropelException Any exceptions caught during processing will be
* rethrown wrapped into a PropelException.
*/
public static function doDeleteAll2(PropelPDO \$con = null)
{
if ({$this->getTable()->getPhpName()}Peer::isSoftDeleteEnabled()) {
return {$this->getTable()->getPhpName()}Peer::doSoftDeleteAll(\$con);
} else {
return {$this->getTable()->getPhpName()}Peer::doForceDeleteAll(\$con);
}
}
";
}
public function preSelect($builder)
{
return <<<EOT
if ({$builder->getStubQueryBuilder()->getClassname()}::isSoftDeleteEnabled()) {
\$criteria->add({$this->getColumnForParameter('deleted_column')->getConstantName()}, null, Criteria::ISNULL);
} else {
{$this->getTable()->getPhpName()}Peer::enableSoftDelete();
}
EOT;
}
public function peerFilter(&$script)
{
$script = str_replace(array(
'public static function doDelete(',
'public static function doDelete2(',
'public static function doDeleteAll(',
'public static function doDeleteAll2('
), array(
'public static function doForceDelete(',
'public static function doDelete(',
'public static function doForceDeleteAll(',
'public static function doDeleteAll('
), $script);
}
}

View file

@ -0,0 +1,171 @@
<?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
*/
/**
* Gives a model class the ability to track creation and last modification dates
* Uses two additional columns storing the creation and update date
*
* @author François Zaninotto
* @version $Revision: 1683 $
* @package propel.generator.behavior
*/
class TimestampableBehavior extends Behavior
{
// default parameters value
protected $parameters = array(
'create_column' => 'created_at',
'update_column' => 'updated_at'
);
/**
* Add the create_column and update_columns to the current table
*/
public function modifyTable()
{
if(!$this->getTable()->containsColumn($this->getParameter('create_column'))) {
$this->getTable()->addColumn(array(
'name' => $this->getParameter('create_column'),
'type' => 'TIMESTAMP'
));
}
if(!$this->getTable()->containsColumn($this->getParameter('update_column'))) {
$this->getTable()->addColumn(array(
'name' => $this->getParameter('update_column'),
'type' => 'TIMESTAMP'
));
}
}
/**
* Get the setter of one of the columns of the behavior
*
* @param string $column One of the behavior colums, 'create_column' or 'update_column'
* @return string The related setter, 'setCreatedOn' or 'setUpdatedOn'
*/
protected function getColumnSetter($column)
{
return 'set' . $this->getColumnForParameter($column)->getPhpName();
}
/**
* Add code in ObjectBuilder::preUpdate
*
* @return string The code to put at the hook
*/
public function preUpdate()
{
return "if (\$this->isModified() && !\$this->isColumnModified(" . $this->getColumnForParameter('update_column')->getConstantName() . ")) {
\$this->" . $this->getColumnSetter('update_column') . "(time());
}";
}
/**
* Add code in ObjectBuilder::preInsert
*
* @return string The code to put at the hook
*/
public function preInsert()
{
return "if (!\$this->isColumnModified(" . $this->getColumnForParameter('create_column')->getConstantName() . ")) {
\$this->" . $this->getColumnSetter('create_column') . "(time());
}
if (!\$this->isColumnModified(" . $this->getColumnForParameter('update_column')->getConstantName() . ")) {
\$this->" . $this->getColumnSetter('update_column') . "(time());
}";
}
public function objectMethods($builder)
{
return "
/**
* Mark the current object so that the update date doesn't get updated during next save
*
* @return " . $builder->getStubObjectBuilder()->getClassname() . " The current object (for fluent API support)
*/
public function keepUpdateDateUnchanged()
{
\$this->modifiedColumns[] = " . $this->getColumnForParameter('update_column')->getConstantName() . ";
return \$this;
}
";
}
public function queryMethods($builder)
{
$queryClassName = $builder->getStubQueryBuilder()->getClassname();
$updateColumnConstant = $this->getColumnForParameter('update_column')->getConstantName();
$createColumnConstant = $this->getColumnForParameter('create_column')->getConstantName();
return "
/**
* Filter by the latest updated
*
* @param int \$nbDays Maximum age of the latest update in days
*
* @return $queryClassName The current query, for fuid interface
*/
public function recentlyUpdated(\$nbDays = 7)
{
return \$this->addUsingAlias($updateColumnConstant, time() - \$nbDays * 24 * 60 * 60, Criteria::GREATER_EQUAL);
}
/**
* Filter by the latest created
*
* @param int \$nbDays Maximum age of in days
*
* @return $queryClassName The current query, for fuid interface
*/
public function recentlyCreated(\$nbDays = 7)
{
return \$this->addUsingAlias($createColumnConstant, time() - \$nbDays * 24 * 60 * 60, Criteria::GREATER_EQUAL);
}
/**
* Order by update date desc
*
* @return $queryClassName The current query, for fuid interface
*/
public function lastUpdatedFirst()
{
return \$this->addDescendingOrderByColumn($updateColumnConstant);
}
/**
* Order by update date asc
*
* @return $queryClassName The current query, for fuid interface
*/
public function firstUpdatedFirst()
{
return \$this->addAscendingOrderByColumn($updateColumnConstant);
}
/**
* Order by create date desc
*
* @return $queryClassName The current query, for fuid interface
*/
public function lastCreatedFirst()
{
return \$this->addDescendingOrderByColumn($createColumnConstant);
}
/**
* Order by create date asc
*
* @return $queryClassName The current query, for fuid interface
*/
public function firstCreatedFirst()
{
return \$this->addAscendingOrderByColumn($createColumnConstant);
}
";
}
}

View file

@ -0,0 +1,122 @@
<?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 'AggregateColumnRelationBehavior.php';
/**
* Keeps an aggregate column updated with related table
*
* @author François Zaninotto
* @version $Revision: 1785 $
* @package propel.generator.behavior.aggregate_column
*/
class AggregateColumnBehavior extends Behavior
{
// default parameters value
protected $parameters = array(
'name' => null,
'expression' => null,
'foreign_table' => null,
);
/**
* Add the aggregate key to the current table
*/
public function modifyTable()
{
$table = $this->getTable();
if (!$columnName = $this->getParameter('name')) {
throw new InvalidArgumentException(sprintf('You must define a \'name\' parameter for the \'aggregate_column\' behavior in the \'%s\' table', $table->getName()));
}
// add the aggregate column if not present
if(!$this->getTable()->containsColumn($columnName)) {
$column = $this->getTable()->addColumn(array(
'name' => $columnName,
'type' => 'INTEGER',
));
}
// add a behavior in the foreign table to autoupdate the aggregate column
$foreignTable = $this->getForeignTable();
if (!$foreignTable->hasBehavior('concrete_inheritance_parent')) {
$relationBehavior = new AggregateColumnRelationBehavior();
$relationBehavior->setName('aggregate_column_relation');
$foreignKey = $this->getForeignKey();
$relationBehavior->addParameter(array('name' => 'foreign_table', 'value' => $table->getName()));
$relationBehavior->addParameter(array('name' => 'update_method', 'value' => 'update' . $this->getColumn()->getPhpName()));
$foreignTable->addBehavior($relationBehavior);
}
}
public function objectMethods($builder)
{
if (!$foreignTableName = $this->getParameter('foreign_table')) {
throw new InvalidArgumentException(sprintf('You must define a \'foreign_table\' parameter for the \'aggregate_column\' behavior in the \'%s\' table', $this->getTable()->getName()));
}
$script = '';
$script .= $this->addObjectCompute();
$script .= $this->addObjectUpdate();
return $script;
}
protected function addObjectCompute()
{
$conditions = array();
$bindings = array();
foreach ($this->getForeignKey()->getColumnObjectsMapping() as $index => $columnReference) {
$conditions[] = $columnReference['local']->getFullyQualifiedName() . ' = :p' . ($index + 1);
$bindings[$index + 1] = $columnReference['foreign']->getPhpName();
}
$sql = sprintf('SELECT %s FROM %s WHERE %s',
$this->getParameter('expression'),
$this->getTable()->getDatabase()->getPlatform()->quoteIdentifier($this->getParameter('foreign_table')),
implode(' AND ', $conditions)
);
return $this->renderTemplate('objectCompute', array(
'column' => $this->getColumn(),
'sql' => $sql,
'bindings' => $bindings,
));
}
protected function addObjectUpdate()
{
return $this->renderTemplate('objectUpdate', array(
'column' => $this->getColumn(),
));
}
protected function getForeignTable()
{
return $this->getTable()->getDatabase()->getTable($this->getParameter('foreign_table'));
}
protected function getForeignKey()
{
$foreignTable = $this->getForeignTable();
// let's infer the relation from the foreign table
$fks = $foreignTable->getForeignKeysReferencingTable($this->getTable()->getName());
if (!$fks) {
throw new InvalidArgumentException(sprintf('You must define a foreign key to the \'%s\' table in the \'%s\' table to enable the \'aggregate_column\' behavior', $this->getTable()->getName(), $foreignTable->getName()));
}
// FIXME doesn't work when more than one fk to the same table
return array_shift($fks);
}
protected function getColumn()
{
return $this->getTable()->getColumn($this->getParameter('name'));
}
}

View file

@ -0,0 +1,164 @@
<?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 'AggregateColumnRelationBehavior.php';
/**
* Keeps an aggregate column updated with related table
*
* @author François Zaninotto
* @version $Revision: 1785 $
* @package propel.generator.behavior.aggregate_column
*/
class AggregateColumnRelationBehavior extends Behavior
{
// default parameters value
protected $parameters = array(
'foreign_table' => '',
'update_method' => '',
);
public function postSave($builder)
{
$relationName = $this->getRelationName($builder);
return "\$this->updateRelated{$relationName}(\$con);";
}
// no need for a postDelete() hook, since delete() uses Query::delete(),
// which already has a hook
public function objectAttributes($builder)
{
$relationName = $this->getRelationName($builder);
return "protected \$old{$relationName};
";
}
public function objectMethods($builder)
{
return $this->addObjectUpdateRelated($builder);
}
protected function addObjectUpdateRelated($builder)
{
$relationName = $this->getRelationName($builder);
$updateMethodName = $this->getParameter('update_method');
return $this->renderTemplate('objectUpdateRelated', array(
'relationName' => $relationName,
'variableName' => self::lcfirst($relationName),
'updateMethodName' => $this->getParameter('update_method'),
));
}
public function objectFilter(&$script, $builder)
{
$relationName = $this->getRelationName($builder);
$relatedClass = $this->getForeignTable()->getPhpName();
$search = " public function set{$relationName}({$relatedClass} \$v = null)
{";
$replace = $search . "
// aggregate_column_relation behavior
if (null !== \$this->a{$relationName} && \$v !== \$this->a{$relationName}) {
\$this->old{$relationName} = \$this->a{$relationName};
}";
$script = str_replace($search, $replace, $script);
}
public function preUpdateQuery($builder)
{
return $this->getFindRelated($builder);
}
public function preDeleteQuery($builder)
{
return $this->getFindRelated($builder);
}
protected function getFindRelated($builder)
{
$relationName = $this->getRelationName($builder);
return "\$this->findRelated{$relationName}s(\$con);";
}
public function postUpdateQuery($builder)
{
return $this->getUpdateRelated($builder);
}
public function postDeleteQuery($builder)
{
return $this->getUpdateRelated($builder);
}
protected function getUpdateRelated($builder)
{
$relationName = $this->getRelationName($builder);
return "\$this->updateRelated{$relationName}s(\$con);";
}
public function queryMethods($builder)
{
$script = '';
$script .= $this->addQueryFindRelated($builder);
$script .= $this->addQueryUpdateRelated($builder);
return $script;
}
protected function addQueryFindRelated($builder)
{
$foreignKey = $this->getForeignKey();
$relationName = $this->getRelationName($builder);
return $this->renderTemplate('queryFindRelated', array(
'foreignTable' => $this->getForeignTable(),
'relationName' => $relationName,
'variableName' => self::lcfirst($relationName),
'foreignQueryName' => $foreignKey->getForeignTable()->getPhpName() . 'Query',
'refRelationName' => $builder->getRefFKPhpNameAffix($foreignKey),
));
}
protected function addQueryUpdateRelated($builder)
{
$relationName = $this->getRelationName($builder);
return $this->renderTemplate('queryUpdateRelated', array(
'relationName' => $relationName,
'variableName' => self::lcfirst($relationName),
'updateMethodName' => $this->getParameter('update_method'),
));
}
protected function getForeignTable()
{
return $this->getTable()->getDatabase()->getTable($this->getParameter('foreign_table'));
}
protected function getForeignKey()
{
$foreignTable = $this->getForeignTable();
// let's infer the relation from the foreign table
$fks = $this->getTable()->getForeignKeysReferencingTable($foreignTable->getName());
// FIXME doesn't work when more than one fk to the same table
return array_shift($fks);
}
protected function getRelationName($builder)
{
return $builder->getFKPhpNameAffix($this->getForeignKey());
}
protected static function lcfirst($input)
{
// no lcfirst in php<5.3...
$input[0] = strtolower($input[0]);
return $input;
}
}

View file

@ -0,0 +1,17 @@
/**
* Computes the value of the aggregate column <?php echo $column->getName() ?>
*
* @param PropelPDO $con A connection object
*
* @return mixed The scalar result from the aggregate query
*/
public function compute<?php echo $column->getPhpName() ?>(PropelPDO $con)
{
$stmt = $con->prepare('<?php echo $sql ?>');
<?php foreach ($bindings as $key => $binding): ?>
$stmt->bindValue(':p<?php echo $key ?>', $this->get<?php echo $binding ?>());
<?php endforeach; ?>
$stmt->execute();
return $stmt->fetchColumn();
}

View file

@ -0,0 +1,11 @@
/**
* Updates the aggregate column <?php echo $column->getName() ?>
*
* @param PropelPDO $con A connection object
*/
public function update<?php echo $column->getPhpName() ?>(PropelPDO $con)
{
$this->set<?php echo $column->getPhpName() ?>($this->compute<?php echo $column->getPhpName() ?>($con));
$this->save($con);
}

View file

@ -0,0 +1,16 @@
/**
* Update the aggregate column in the related <?php echo $relationName ?> object
*
* @param PropelPDO $con A connection object
*/
protected function updateRelated<?php echo $relationName ?>(PropelPDO $con)
{
if ($<?php echo $variableName ?> = $this->get<?php echo $relationName ?>()) {
$<?php echo $variableName ?>-><?php echo $updateMethodName ?>($con);
}
if ($this->old<?php echo $relationName ?>) {
$this->old<?php echo $relationName ?>-><?php echo $updateMethodName ?>($con);
$this->old<?php echo $relationName ?> = null;
}
}

View file

@ -0,0 +1,20 @@
/**
* Finds the related <?php echo $foreignTable->getPhpName() ?> objects and keep them for later
*
* @param PropelPDO $con A connection object
*/
protected function findRelated<?php echo $relationName ?>s($con)
{
$criteria = clone $this;
if ($this->useAliasInSQL) {
$alias = $this->getModelAlias();
$criteria->removeAlias($alias);
} else {
$alias = '';
}
$this-><?php echo $variableName ?>s = <?php echo $foreignQueryName ?>::create()
->join<?php echo $refRelationName ?>($alias)
->mergeWith($criteria)
->find($con);
}

View file

@ -0,0 +1,8 @@
protected function updateRelated<?php echo $relationName ?>s($con)
{
foreach ($this-><?php echo $variableName ?>s as $<?php echo $variableName ?>) {
$<?php echo $variableName ?>-><?php echo $updateMethodName ?>($con);
}
$this-><?php echo $variableName ?>s = array();
}

View file

@ -0,0 +1,235 @@
<?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 'ConcreteInheritanceParentBehavior.php';
/**
* Gives a model class the ability to remain in database even when the user deletes object
* Uses an additional column storing the deletion date
* And an additional condition for every read query to only consider rows with no deletion date
*
* @author François Zaninotto
* @version $Revision: 1774 $
* @package propel.generator.behavior.concrete_inheritance
*/
class ConcreteInheritanceBehavior extends Behavior
{
// default parameters value
protected $parameters = array(
'extends' => '',
'descendant_column' => 'descendant_class',
'copy_data_to_parent' => 'true'
);
public function modifyTable()
{
$table = $this->getTable();
$parentTable = $this->getParentTable();
if ($this->isCopyData()) {
// tell the parent table that it has a descendant
if (!$parentTable->hasBehavior('concrete_inheritance_parent')) {
$parentBehavior = new ConcreteInheritanceParentBehavior();
$parentBehavior->setName('concrete_inheritance_parent');
$parentBehavior->addParameter(array('name' => 'descendant_column', 'value' => $this->getParameter('descendant_column')));
$parentTable->addBehavior($parentBehavior);
// The parent table's behavior modifyTable() must be executed before this one
$parentBehavior->getTableModifier()->modifyTable();
$parentBehavior->setTableModified(true);
}
}
// Add the columns of the parent table
foreach ($parentTable->getColumns() as $column) {
if ($column->getName() == $this->getParameter('descendant_column')) {
continue;
}
if ($table->containsColumn($column->getName())) {
continue;
}
$copiedColumn = clone $column;
if ($column->isAutoIncrement() && $this->isCopyData()) {
$copiedColumn->setAutoIncrement(false);
}
$table->addColumn($copiedColumn);
if ($column->isPrimaryKey() && $this->isCopyData()) {
$fk = new ForeignKey();
$fk->setForeignTableName($column->getTable()->getName());
$fk->setOnDelete('CASCADE');
$fk->setOnUpdate(null);
$fk->addReference($copiedColumn, $column);
$fk->isParentChild = true;
$table->addForeignKey($fk);
}
}
// add the foreign keys of the parent table
foreach ($parentTable->getForeignKeys() as $fk) {
$copiedFk = clone $fk;
$copiedFk->setName('');
$copiedFk->setRefPhpName('');
$this->getTable()->addForeignKey($copiedFk);
}
// add the validators of the parent table
foreach ($parentTable->getValidators() as $validator) {
$copiedValidator = clone $validator;
$this->getTable()->addValidator($copiedValidator);
}
// add the indices of the parent table
foreach ($parentTable->getIndices() as $index) {
$copiedIndex = clone $index;
$copiedIndex->setName('');
$this->getTable()->addIndex($copiedIndex);
}
// add the unique indices of the parent table
foreach ($parentTable->getUnices() as $unique) {
$copiedUnique = clone $unique;
$copiedUnique->setName('');
$this->getTable()->addUnique($copiedUnique);
}
// give name to newly added foreign keys and indices
// (this is already done for other elements of the current table)
$table->doNaming();
// add the Behaviors of the parent table
foreach ($parentTable->getBehaviors() as $behavior) {
if ($behavior->getName() == 'concrete_inheritance_parent' || $behavior->getName() == 'concrete_inheritance') {
continue;
}
$copiedBehavior = clone $behavior;
$this->getTable()->addBehavior($copiedBehavior);
}
}
protected function getParentTable()
{
return $this->getTable()->getDatabase()->getTable($this->getParameter('extends'));
}
protected function isCopyData()
{
return $this->getParameter('copy_data_to_parent') == 'true';
}
public function parentClass($builder)
{
switch (get_class($builder)) {
case 'PHP5ObjectBuilder':
return $builder->getNewStubObjectBuilder($this->getParentTable())->getClassname();
break;
case 'QueryBuilder':
return $builder->getNewStubQueryBuilder($this->getParentTable())->getClassname();
break;
default:
return null;
break;
}
}
public function preSave($script)
{
if ($this->isCopyData()) {
return "\$parent = \$this->getSyncParent(\$con);
\$parent->save(\$con);
\$this->setPrimaryKey(\$parent->getPrimaryKey());
";
}
}
public function postDelete($script)
{
if ($this->isCopyData()) {
return "\$this->getParentOrCreate(\$con)->delete(\$con);
";
}
}
public function objectMethods($builder)
{
if (!$this->isCopyData()) {
return;
}
$this->builder = $builder;
$script .= '';
$this->addObjectGetParentOrCreate($script);
$this->addObjectGetSyncParent($script);
return $script;
}
protected function addObjectGetParentOrCreate(&$script)
{
$parentTable = $this->getParentTable();
$parentClass = $this->builder->getNewStubObjectBuilder($parentTable)->getClassname();
$script .= "
/**
* Get or Create the parent " . $parentClass . " object of the current object
*
* @return " . $parentClass . " The parent object
*/
public function getParentOrCreate(\$con = null)
{
if (\$this->isNew()) {
\$parent = new " . $parentClass . "();
\$parent->set" . $this->getParentTable()->getColumn($this->getParameter('descendant_column'))->getPhpName() . "('" . $this->builder->getStubObjectBuilder()->getClassname() . "');
return \$parent;
} else {
return " . $this->builder->getNewStubQueryBuilder($parentTable)->getClassname() . "::create()->findPk(\$this->getPrimaryKey(), \$con);
}
}
";
}
protected function addObjectGetSyncParent(&$script)
{
$parentTable = $this->getParentTable();
$pkeys = $parentTable->getPrimaryKey();
$cptype = $pkeys[0]->getPhpType();
$script .= "
/**
* Create or Update the parent " . $parentTable->getPhpName() . " object
* And return its primary key
*
* @return " . $cptype . " The primary key of the parent object
*/
public function getSyncParent(\$con = null)
{
\$parent = \$this->getParentOrCreate(\$con);";
foreach ($parentTable->getColumns() as $column) {
if ($column->isPrimaryKey() || $column->getName() == $this->getParameter('descendant_column')) {
continue;
}
$phpName = $column->getPhpName();
$script .= "
\$parent->set{$phpName}(\$this->get{$phpName}());";
}
foreach ($parentTable->getForeignKeys() as $fk) {
if (isset($fk->isParentChild) && $fk->isParentChild) {
continue;
}
$refPhpName = $this->builder->getFKPhpNameAffix($fk, $plural = false);
$script .= "
if (\$this->get" . $refPhpName . "() && \$this->get" . $refPhpName . "()->isNew()) {
\$parent->set" . $refPhpName . "(\$this->get" . $refPhpName . "());
}";
}
$script .= "
return \$parent;
}
";
}
}

View file

@ -0,0 +1,89 @@
<?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
*/
/**
* Gives a model class the ability to remain in database even when the user deletes object
* Uses an additional column storing the deletion date
* And an additional condition for every read query to only consider rows with no deletion date
*
* @author François Zaninotto
* @version $Revision: 1612 $
* @package propel.generator.behavior.concrete_inheritance
*/
class ConcreteInheritanceParentBehavior extends Behavior
{
// default parameters value
protected $parameters = array(
'descendant_column' => 'descendant_class'
);
public function modifyTable()
{
$table = $this->getTable();
if (!$table->containsColumn($this->getParameter('descendant_column'))) {
$table->addColumn(array(
'name' => $this->getParameter('descendant_column'),
'type' => 'VARCHAR',
'size' => 100
));
}
}
protected function getColumnGetter()
{
return 'get' . $this->getColumnForParameter('descendant_column')->getPhpName();
}
public function objectMethods($builder)
{
$this->builder = $builder;
$script .= '';
$this->addHasChildObject($script);
$this->addGetChildObject($script);
return $script;
}
protected function addHasChildObject(&$script)
{
$script .= "
/**
* Whether or not this object is the parent of a child object
*
* @return bool
*/
public function hasChildObject()
{
return \$this->" . $this->getColumnGetter() . "() !== null;
}
";
}
protected function addGetChildObject(&$script)
{
$script .= "
/**
* Get the child object of this object
*
* @return mixed
*/
public function getChildObject()
{
if (!\$this->hasChildObject()) {
return null;
}
\$childObjectClass = \$this->" . $this->getColumnGetter() . "();
\$childObject = PropelQuery::from(\$childObjectClass)->findPk(\$this->getPrimaryKey());
return \$childObject->hasChildObject() ? \$childObject->getChildObject() : \$childObject;
}
";
}
}

View file

@ -0,0 +1,99 @@
<?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 dirname(__FILE__) . '/NestedSetBehaviorObjectBuilderModifier.php';
require_once dirname(__FILE__) . '/NestedSetBehaviorQueryBuilderModifier.php';
require_once dirname(__FILE__) . '/NestedSetBehaviorPeerBuilderModifier.php';
/**
* Behavior to adds nested set tree structure columns and abilities
*
* @author François Zaninotto
* @package propel.generator.behavior.nestedset
*/
class NestedSetBehavior extends Behavior
{
// default parameters value
protected $parameters = array(
'left_column' => 'tree_left',
'right_column' => 'tree_right',
'level_column' => 'tree_level',
'use_scope' => 'false',
'scope_column' => 'tree_scope',
'method_proxies' => 'false'
);
protected $objectBuilderModifier, $queryBuilderModifier, $peerBuilderModifier;
/**
* Add the left, right and scope to the current table
*/
public function modifyTable()
{
if(!$this->getTable()->containsColumn($this->getParameter('left_column'))) {
$this->getTable()->addColumn(array(
'name' => $this->getParameter('left_column'),
'type' => 'INTEGER'
));
}
if(!$this->getTable()->containsColumn($this->getParameter('right_column'))) {
$this->getTable()->addColumn(array(
'name' => $this->getParameter('right_column'),
'type' => 'INTEGER'
));
}
if(!$this->getTable()->containsColumn($this->getParameter('level_column'))) {
$this->getTable()->addColumn(array(
'name' => $this->getParameter('level_column'),
'type' => 'INTEGER'
));
}
if ($this->getParameter('use_scope') == 'true' &&
!$this->getTable()->containsColumn($this->getParameter('scope_column'))) {
$this->getTable()->addColumn(array(
'name' => $this->getParameter('scope_column'),
'type' => 'INTEGER'
));
}
}
public function getObjectBuilderModifier()
{
if (is_null($this->objectBuilderModifier))
{
$this->objectBuilderModifier = new NestedSetBehaviorObjectBuilderModifier($this);
}
return $this->objectBuilderModifier;
}
public function getQueryBuilderModifier()
{
if (is_null($this->queryBuilderModifier))
{
$this->queryBuilderModifier = new NestedSetBehaviorQueryBuilderModifier($this);
}
return $this->queryBuilderModifier;
}
public function getPeerBuilderModifier()
{
if (is_null($this->peerBuilderModifier))
{
$this->peerBuilderModifier = new NestedSetBehaviorPeerBuilderModifier($this);
}
return $this->peerBuilderModifier;
}
public function useScope()
{
return $this->getParameter('use_scope') == 'true';
}
}

View file

@ -0,0 +1,555 @@
<?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
*/
/**
* Behavior to adds nested set tree structure columns and abilities
*
* @author François Zaninotto
* @author heltem <heltem@o2php.com>
* @package propel.generator.behavior.nestedset
*/
class NestedSetBehaviorPeerBuilderModifier
{
protected $behavior, $table, $builder, $objectClassname, $peerClassname;
public function __construct($behavior)
{
$this->behavior = $behavior;
$this->table = $behavior->getTable();
}
protected function getParameter($key)
{
return $this->behavior->getParameter($key);
}
protected function getColumn($name)
{
return $this->behavior->getColumnForParameter($name);
}
protected function getColumnAttribute($name)
{
return strtolower($this->getColumn($name)->getName());
}
protected function getColumnConstant($name)
{
return strtoupper($this->getColumn($name)->getName());
}
protected function getColumnPhpName($name)
{
return $this->getColumn($name)->getPhpName();
}
protected function setBuilder($builder)
{
$this->builder = $builder;
$this->objectClassname = $builder->getStubObjectBuilder()->getClassname();
$this->peerClassname = $builder->getStubPeerBuilder()->getClassname();
}
public function staticAttributes($builder)
{
$tableName = $this->table->getName();
$script = "
/**
* Left column for the set
*/
const LEFT_COL = '" . $tableName . '.' . $this->getColumnConstant('left_column') . "';
/**
* Right column for the set
*/
const RIGHT_COL = '" . $tableName . '.' . $this->getColumnConstant('right_column') . "';
/**
* Level column for the set
*/
const LEVEL_COL = '" . $tableName . '.' . $this->getColumnConstant('level_column') . "';
";
if ($this->behavior->useScope()) {
$script .= "
/**
* Scope column for the set
*/
const SCOPE_COL = '" . $tableName . '.' . $this->getColumnConstant('scope_column') . "';
";
}
return $script;
}
public function staticMethods($builder)
{
$this->setBuilder($builder);
$script = '';
if ($this->getParameter('use_scope') == 'true')
{
$this->addRetrieveRoots($script);
}
$this->addRetrieveRoot($script);
$this->addRetrieveTree($script);
$this->addIsValid($script);
$this->addDeleteTree($script);
$this->addShiftRLValues($script);
$this->addShiftLevel($script);
$this->addUpdateLoadedNodes($script);
$this->addMakeRoomForLeaf($script);
$this->addFixLevels($script);
return $script;
}
protected function addRetrieveRoots(&$script)
{
$peerClassname = $this->peerClassname;
$script .= "
/**
* Returns the root nodes for the tree
*
* @param PropelPDO \$con Connection to use.
* @return {$this->objectClassname} Propel object for root node
*/
public static function retrieveRoots(Criteria \$criteria = null, PropelPDO \$con = null)
{
if (\$criteria === null) {
\$criteria = new Criteria($peerClassname::DATABASE_NAME);
}
\$criteria->add($peerClassname::LEFT_COL, 1, Criteria::EQUAL);
return $peerClassname::doSelect(\$criteria, \$con);
}
";
}
protected function addRetrieveRoot(&$script)
{
$peerClassname = $this->peerClassname;
$useScope = $this->behavior->useScope();
$script .= "
/**
* Returns the root node for a given scope
*";
if($useScope) {
$script .= "
* @param int \$scope Scope to determine which root node to return";
}
$script .= "
* @param PropelPDO \$con Connection to use.
* @return {$this->objectClassname} Propel object for root node
*/
public static function retrieveRoot(" . ($useScope ? "\$scope = null, " : "") . "PropelPDO \$con = null)
{
\$c = new Criteria($peerClassname::DATABASE_NAME);
\$c->add($peerClassname::LEFT_COL, 1, Criteria::EQUAL);";
if($useScope) {
$script .= "
\$c->add($peerClassname::SCOPE_COL, \$scope, Criteria::EQUAL);";
}
$script .= "
return $peerClassname::doSelectOne(\$c, \$con);
}
";
}
protected function addRetrieveTree(&$script)
{
$peerClassname = $this->peerClassname;
$useScope = $this->behavior->useScope();
$script .= "
/**
* Returns the whole tree node for a given scope
*";
if($useScope) {
$script .= "
* @param int \$scope Scope to determine which root node to return";
}
$script .= "
* @param Criteria \$criteria Optional Criteria to filter the query
* @param PropelPDO \$con Connection to use.
* @return {$this->objectClassname} Propel object for root node
*/
public static function retrieveTree(" . ($useScope ? "\$scope = null, " : "") . "Criteria \$criteria = null, PropelPDO \$con = null)
{
if (\$criteria === null) {
\$criteria = new Criteria($peerClassname::DATABASE_NAME);
}
\$criteria->addAscendingOrderByColumn($peerClassname::LEFT_COL);";
if($useScope) {
$script .= "
\$criteria->add($peerClassname::SCOPE_COL, \$scope, Criteria::EQUAL);";
}
$script .= "
return $peerClassname::doSelect(\$criteria, \$con);
}
";
}
protected function addIsValid(&$script)
{
$objectClassname = $this->objectClassname;
$script .= "
/**
* Tests if node is valid
*
* @param $objectClassname \$node Propel object for src node
* @return bool
*/
public static function isValid($objectClassname \$node = null)
{
if (is_object(\$node) && \$node->getRightValue() > \$node->getLeftValue()) {
return true;
} else {
return false;
}
}
";
}
protected function addDeleteTree(&$script)
{
$peerClassname = $this->peerClassname;
$useScope = $this->behavior->useScope();
$script .= "
/**
* Delete an entire tree
* ";
if($useScope) {
$script .= "
* @param int \$scope Scope to determine which tree to delete";
}
$script .= "
* @param PropelPDO \$con Connection to use.
*
* @return int The number of deleted nodes
*/
public static function deleteTree(" . ($useScope ? "\$scope = null, " : "") . "PropelPDO \$con = null)
{";
if($useScope) {
$script .= "
\$c = new Criteria($peerClassname::DATABASE_NAME);
\$c->add($peerClassname::SCOPE_COL, \$scope, Criteria::EQUAL);
return $peerClassname::doDelete(\$c, \$con);";
} else {
$script .= "
return $peerClassname::doDeleteAll(\$con);";
}
$script .= "
}
";
}
protected function addShiftRLValues(&$script)
{
$peerClassname = $this->peerClassname;
$useScope = $this->behavior->useScope();
$script .= "
/**
* Adds \$delta to all L and R values that are >= \$first and <= \$last.
* '\$delta' can also be negative.
*
* @param int \$delta Value to be shifted by, can be negative
* @param int \$first First node to be shifted
* @param int \$last Last node to be shifted (optional)";
if($useScope) {
$script .= "
* @param int \$scope Scope to use for the shift";
}
$script .= "
* @param PropelPDO \$con Connection to use.
*/
public static function shiftRLValues(\$delta, \$first, \$last = null" . ($useScope ? ", \$scope = null" : ""). ", PropelPDO \$con = null)
{
if (\$con === null) {
\$con = Propel::getConnection($peerClassname::DATABASE_NAME, Propel::CONNECTION_WRITE);
}
// Shift left column values
\$whereCriteria = new Criteria($peerClassname::DATABASE_NAME);
\$criterion = \$whereCriteria->getNewCriterion($peerClassname::LEFT_COL, \$first, Criteria::GREATER_EQUAL);
if (null !== \$last) {
\$criterion->addAnd(\$whereCriteria->getNewCriterion($peerClassname::LEFT_COL, \$last, Criteria::LESS_EQUAL));
}
\$whereCriteria->add(\$criterion);";
if ($useScope) {
$script .= "
\$whereCriteria->add($peerClassname::SCOPE_COL, \$scope, Criteria::EQUAL);";
}
$script .= "
\$valuesCriteria = new Criteria($peerClassname::DATABASE_NAME);
\$valuesCriteria->add($peerClassname::LEFT_COL, array('raw' => $peerClassname::LEFT_COL . ' + ?', 'value' => \$delta), Criteria::CUSTOM_EQUAL);
{$this->builder->getBasePeerClassname()}::doUpdate(\$whereCriteria, \$valuesCriteria, \$con);
// Shift right column values
\$whereCriteria = new Criteria($peerClassname::DATABASE_NAME);
\$criterion = \$whereCriteria->getNewCriterion($peerClassname::RIGHT_COL, \$first, Criteria::GREATER_EQUAL);
if (null !== \$last) {
\$criterion->addAnd(\$whereCriteria->getNewCriterion($peerClassname::RIGHT_COL, \$last, Criteria::LESS_EQUAL));
}
\$whereCriteria->add(\$criterion);";
if ($useScope) {
$script .= "
\$whereCriteria->add($peerClassname::SCOPE_COL, \$scope, Criteria::EQUAL);";
}
$script .= "
\$valuesCriteria = new Criteria($peerClassname::DATABASE_NAME);
\$valuesCriteria->add($peerClassname::RIGHT_COL, array('raw' => $peerClassname::RIGHT_COL . ' + ?', 'value' => \$delta), Criteria::CUSTOM_EQUAL);
{$this->builder->getBasePeerClassname()}::doUpdate(\$whereCriteria, \$valuesCriteria, \$con);
}
";
}
protected function addShiftLevel(&$script)
{
$peerClassname = $this->peerClassname;
$useScope = $this->behavior->useScope();
$script .= "
/**
* Adds \$delta to level for nodes having left value >= \$first and right value <= \$last.
* '\$delta' can also be negative.
*
* @param int \$delta Value to be shifted by, can be negative
* @param int \$first First node to be shifted
* @param int \$last Last node to be shifted";
if($useScope) {
$script .= "
* @param int \$scope Scope to use for the shift";
}
$script .= "
* @param PropelPDO \$con Connection to use.
*/
public static function shiftLevel(\$delta, \$first, \$last" . ($useScope ? ", \$scope = null" : ""). ", PropelPDO \$con = null)
{
if (\$con === null) {
\$con = Propel::getConnection($peerClassname::DATABASE_NAME, Propel::CONNECTION_WRITE);
}
\$whereCriteria = new Criteria($peerClassname::DATABASE_NAME);
\$whereCriteria->add($peerClassname::LEFT_COL, \$first, Criteria::GREATER_EQUAL);
\$whereCriteria->add($peerClassname::RIGHT_COL, \$last, Criteria::LESS_EQUAL);";
if ($useScope) {
$script .= "
\$whereCriteria->add($peerClassname::SCOPE_COL, \$scope, Criteria::EQUAL);";
}
$script .= "
\$valuesCriteria = new Criteria($peerClassname::DATABASE_NAME);
\$valuesCriteria->add($peerClassname::LEVEL_COL, array('raw' => $peerClassname::LEVEL_COL . ' + ?', 'value' => \$delta), Criteria::CUSTOM_EQUAL);
{$this->builder->getBasePeerClassname()}::doUpdate(\$whereCriteria, \$valuesCriteria, \$con);
}
";
}
protected function addUpdateLoadedNodes(&$script)
{
$peerClassname = $this->peerClassname;
$script .= "
/**
* Reload all already loaded nodes to sync them with updated db
*
* @param PropelPDO \$con Connection to use.
*/
public static function updateLoadedNodes(PropelPDO \$con = null)
{
if (Propel::isInstancePoolingEnabled()) {
\$keys = array();
foreach ($peerClassname::\$instances as \$obj) {
\$keys[] = \$obj->getPrimaryKey();
}
if (!empty(\$keys)) {
// We don't need to alter the object instance pool; we're just modifying these ones
// already in the pool.
\$criteria = new Criteria($peerClassname::DATABASE_NAME);";
if (count($this->table->getPrimaryKey()) === 1) {
$pkey = $this->table->getPrimaryKey();
$col = array_shift($pkey);
$script .= "
\$criteria->add(".$this->builder->getColumnConstant($col).", \$keys, Criteria::IN);
";
} else {
$fields = array();
foreach ($this->table->getPrimaryKey() as $k => $col) {
$fields[] = $this->builder->getColumnConstant($col);
};
$script .= "
// Loop on each instances in pool
foreach (\$keys as \$values) {
// Create initial Criterion
\$cton = \$criteria->getNewCriterion(" . $fields[0] . ", \$values[0]);";
unset($fields[0]);
foreach ($fields as $k => $col) {
$script .= "
// Create next criterion
\$nextcton = \$criteria->getNewCriterion(" . $col . ", \$values[$k]);
// And merge it with the first
\$cton->addAnd(\$nextcton);";
}
$script .= "
// Add final Criterion to Criteria
\$criteria->addOr(\$cton);
}";
}
$script .= "
\$stmt = $peerClassname::doSelectStmt(\$criteria, \$con);
while (\$row = \$stmt->fetch(PDO::FETCH_NUM)) {
\$key = $peerClassname::getPrimaryKeyHashFromRow(\$row, 0);
if (null !== (\$object = $peerClassname::getInstanceFromPool(\$key))) {";
$n = 0;
foreach ($this->table->getColumns() as $col) {
if ($col->getPhpName() == $this->getColumnPhpName('left_column')) {
$script .= "
\$object->setLeftValue(\$row[$n]);";
} else if ($col->getPhpName() == $this->getColumnPhpName('right_column')) {
$script .= "
\$object->setRightValue(\$row[$n]);";
} else if ($col->getPhpName() == $this->getColumnPhpName('level_column')) {
$script .= "
\$object->setLevel(\$row[$n]);
\$object->clearNestedSetChildren();";
}
$n++;
}
$script .= "
}
}
\$stmt->closeCursor();
}
}
}
";
}
protected function addMakeRoomForLeaf(&$script)
{
$peerClassname = $this->peerClassname;
$useScope = $this->behavior->useScope();
$script .= "
/**
* Update the tree to allow insertion of a leaf at the specified position
*
* @param int \$left left column value";
if ($useScope) {
$script .= "
* @param integer \$scope scope column value";
}
$script .= "
* @param PropelPDO \$con Connection to use.
*/
public static function makeRoomForLeaf(\$left" . ($useScope ? ", \$scope" : ""). ", PropelPDO \$con = null)
{
// Update database nodes
$peerClassname::shiftRLValues(2, \$left, null" . ($useScope ? ", \$scope" : "") . ", \$con);
// Update all loaded nodes
$peerClassname::updateLoadedNodes(\$con);
}
";
}
protected function addFixLevels(&$script)
{
$peerClassname = $this->peerClassname;
$useScope = $this->behavior->useScope();
$script .= "
/**
* Update the tree to allow insertion of a leaf at the specified position
*";
if ($useScope) {
$script .= "
* @param integer \$scope scope column value";
}
$script .= "
* @param PropelPDO \$con Connection to use.
*/
public static function fixLevels(" . ($useScope ? "\$scope, " : ""). "PropelPDO \$con = null)
{
\$c = new Criteria();";
if ($useScope) {
$script .= "
\$c->add($peerClassname::SCOPE_COL, \$scope, Criteria::EQUAL);";
}
$script .= "
\$c->addAscendingOrderByColumn($peerClassname::LEFT_COL);
\$stmt = $peerClassname::doSelectStmt(\$c, \$con);
";
if (!$this->table->getChildrenColumn()) {
$script .= "
// set the class once to avoid overhead in the loop
\$cls = $peerClassname::getOMClass(false);";
}
$script .= "
\$level = null;
// iterate over the statement
while (\$row = \$stmt->fetch(PDO::FETCH_NUM)) {
// hydrate object
\$key = $peerClassname::getPrimaryKeyHashFromRow(\$row, 0);
if (null === (\$obj = $peerClassname::getInstanceFromPool(\$key))) {";
if ($this->table->getChildrenColumn()) {
$script .= "
// class must be set each time from the record row
\$cls = $peerClassname::getOMClass(\$row, 0);
\$cls = substr('.'.\$cls, strrpos('.'.\$cls, '.') + 1);
" . $this->builder->buildObjectInstanceCreationCode('$obj', '$cls') . "
\$obj->hydrate(\$row);
$peerClassname::addInstanceToPool(\$obj, \$key);";
} else {
$script .= "
" . $this->builder->buildObjectInstanceCreationCode('$obj', '$cls') . "
\$obj->hydrate(\$row);
$peerClassname::addInstanceToPool(\$obj, \$key);";
}
$script .= "
}
// compute level
// Algorithm shamelessly stolen from sfPropelActAsNestedSetBehaviorPlugin
// Probably authored by Tristan Rivoallan
if (\$level === null) {
\$level = 0;
\$i = 0;
\$prev = array(\$obj->getRightValue());
} else {
while (\$obj->getRightValue() > \$prev[\$i]) {
\$i--;
}
\$level = ++\$i;
\$prev[\$i] = \$obj->getRightValue();
}
// update level in node if necessary
if (\$obj->getLevel() !== \$level) {
\$obj->setLevel(\$level);
\$obj->save(\$con);
}
}
\$stmt->closeCursor();
}
";
}
}

View file

@ -0,0 +1,357 @@
<?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
*/
/**
* Behavior to adds nested set tree structure columns and abilities
*
* @author François Zaninotto
* @package propel.generator.behavior.nestedset
*/
class NestedSetBehaviorQueryBuilderModifier
{
protected $behavior, $table, $builder, $objectClassname, $peerClassname;
public function __construct($behavior)
{
$this->behavior = $behavior;
$this->table = $behavior->getTable();
}
protected function getParameter($key)
{
return $this->behavior->getParameter($key);
}
protected function getColumn($name)
{
return $this->behavior->getColumnForParameter($name);
}
protected function setBuilder($builder)
{
$this->builder = $builder;
$this->objectClassname = $builder->getStubObjectBuilder()->getClassname();
$this->queryClassname = $builder->getStubQueryBuilder()->getClassname();
$this->peerClassname = $builder->getStubPeerBuilder()->getClassname();
}
public function queryMethods($builder)
{
$this->setBuilder($builder);
$script = '';
// select filters
if ($this->behavior->useScope()) {
$this->addTreeRoots($script);
$this->addInTree($script);
}
$this->addDescendantsOf($script);
$this->addBranchOf($script);
$this->addChildrenOf($script);
$this->addSiblingsOf($script);
$this->addAncestorsOf($script);
$this->addRootsOf($script);
// select orders
$this->addOrderByBranch($script);
$this->addOrderByLevel($script);
// select termination methods
$this->addFindRoot($script);
$this->addFindTree($script);
return $script;
}
protected function addTreeRoots(&$script)
{
$script .= "
/**
* Filter the query to restrict the result to root objects
*
* @return {$this->queryClassname} The current query, for fuid interface
*/
public function treeRoots()
{
return \$this->addUsingAlias({$this->peerClassname}::LEFT_COL, 1, Criteria::EQUAL);
}
";
}
protected function addInTree(&$script)
{
$script .= "
/**
* Returns the objects in a certain tree, from the tree scope
*
* @param int \$scope Scope to determine which objects node to return
*
* @return {$this->queryClassname} The current query, for fuid interface
*/
public function inTree(\$scope = null)
{
return \$this->addUsingAlias({$this->peerClassname}::SCOPE_COL, \$scope, Criteria::EQUAL);
}
";
}
protected function addDescendantsOf(&$script)
{
$objectName = '$' . $this->table->getStudlyPhpName();
$script .= "
/**
* Filter the query to restrict the result to descendants of an object
*
* @param {$this->objectClassname} $objectName The object to use for descendant search
*
* @return {$this->queryClassname} The current query, for fuid interface
*/
public function descendantsOf($objectName)
{
return \$this";
if ($this->behavior->useScope()) {
$script .= "
->inTree({$objectName}->getScopeValue())";
}
$script .= "
->addUsingAlias({$this->peerClassname}::LEFT_COL, {$objectName}->getLeftValue(), Criteria::GREATER_THAN)
->addUsingAlias({$this->peerClassname}::RIGHT_COL, {$objectName}->getRightValue(), Criteria::LESS_THAN);
}
";
}
protected function addBranchOf(&$script)
{
$objectName = '$' . $this->table->getStudlyPhpName();
$script .= "
/**
* Filter the query to restrict the result to the branch of an object.
* Same as descendantsOf(), except that it includes the object passed as parameter in the result
*
* @param {$this->objectClassname} $objectName The object to use for branch search
*
* @return {$this->queryClassname} The current query, for fuid interface
*/
public function branchOf($objectName)
{
return \$this";
if ($this->behavior->useScope()) {
$script .= "
->inTree({$objectName}->getScopeValue())";
}
$script .= "
->addUsingAlias({$this->peerClassname}::LEFT_COL, {$objectName}->getLeftValue(), Criteria::GREATER_EQUAL)
->addUsingAlias({$this->peerClassname}::RIGHT_COL, {$objectName}->getRightValue(), Criteria::LESS_EQUAL);
}
";
}
protected function addChildrenOf(&$script)
{
$objectName = '$' . $this->table->getStudlyPhpName();
$script .= "
/**
* Filter the query to restrict the result to children of an object
*
* @param {$this->objectClassname} $objectName The object to use for child search
*
* @return {$this->queryClassname} The current query, for fuid interface
*/
public function childrenOf($objectName)
{
return \$this
->descendantsOf($objectName)
->addUsingAlias({$this->peerClassname}::LEVEL_COL, {$objectName}->getLevel() + 1, Criteria::EQUAL);
}
";
}
protected function addSiblingsOf(&$script)
{
$objectName = '$' . $this->table->getStudlyPhpName();
$script .= "
/**
* Filter the query to restrict the result to siblings of an object.
* The result does not include the object passed as parameter.
*
* @param {$this->objectClassname} $objectName The object to use for sibling search
* @param PropelPDO \$con Connection to use.
*
* @return {$this->queryClassname} The current query, for fuid interface
*/
public function siblingsOf($objectName, PropelPDO \$con = null)
{
if ({$objectName}->isRoot()) {
return \$this->
add({$this->peerClassname}::LEVEL_COL, '1<>1', Criteria::CUSTOM);
} else {
return \$this
->childrenOf({$objectName}->getParent(\$con))
->prune($objectName);
}
}
";
}
protected function addAncestorsOf(&$script)
{
$objectName = '$' . $this->table->getStudlyPhpName();
$script .= "
/**
* Filter the query to restrict the result to ancestors of an object
*
* @param {$this->objectClassname} $objectName The object to use for ancestors search
*
* @return {$this->queryClassname} The current query, for fuid interface
*/
public function ancestorsOf($objectName)
{
return \$this";
if ($this->behavior->useScope()) {
$script .= "
->inTree({$objectName}->getScopeValue())";
}
$script .= "
->addUsingAlias({$this->peerClassname}::LEFT_COL, {$objectName}->getLeftValue(), Criteria::LESS_THAN)
->addUsingAlias({$this->peerClassname}::RIGHT_COL, {$objectName}->getRightValue(), Criteria::GREATER_THAN);
}
";
}
protected function addRootsOf(&$script)
{
$objectName = '$' . $this->table->getStudlyPhpName();
$script .= "
/**
* Filter the query to restrict the result to roots of an object.
* Same as ancestorsOf(), except that it includes the object passed as parameter in the result
*
* @param {$this->objectClassname} $objectName The object to use for roots search
*
* @return {$this->queryClassname} The current query, for fuid interface
*/
public function rootsOf($objectName)
{
return \$this";
if ($this->behavior->useScope()) {
$script .= "
->inTree({$objectName}->getScopeValue())";
}
$script .= "
->addUsingAlias({$this->peerClassname}::LEFT_COL, {$objectName}->getLeftValue(), Criteria::LESS_EQUAL)
->addUsingAlias({$this->peerClassname}::RIGHT_COL, {$objectName}->getRightValue(), Criteria::GREATER_EQUAL);
}
";
}
protected function addOrderByBranch(&$script)
{
$script .= "
/**
* Order the result by branch, i.e. natural tree order
*
* @param bool \$reverse if true, reverses the order
*
* @return {$this->queryClassname} The current query, for fuid interface
*/
public function orderByBranch(\$reverse = false)
{
if (\$reverse) {
return \$this
->addDescendingOrderByColumn({$this->peerClassname}::LEFT_COL);
} else {
return \$this
->addAscendingOrderByColumn({$this->peerClassname}::LEFT_COL);
}
}
";
}
protected function addOrderByLevel(&$script)
{
$script .= "
/**
* Order the result by level, the closer to the root first
*
* @param bool \$reverse if true, reverses the order
*
* @return {$this->queryClassname} The current query, for fuid interface
*/
public function orderByLevel(\$reverse = false)
{
if (\$reverse) {
return \$this
->addAscendingOrderByColumn({$this->peerClassname}::RIGHT_COL);
} else {
return \$this
->addDescendingOrderByColumn({$this->peerClassname}::RIGHT_COL);
}
}
";
}
protected function addFindRoot(&$script)
{
$useScope = $this->behavior->useScope();
$script .= "
/**
* Returns " . ($useScope ? 'a' : 'the') ." root node for the tree
*";
if($useScope) {
$script .= "
* @param int \$scope Scope to determine which root node to return";
}
$script .= "
* @param PropelPDO \$con Connection to use.
*
* @return {$this->objectClassname} The tree root object
*/
public function findRoot(" . ($useScope ? "\$scope = null, " : "") . "\$con = null)
{
return \$this
->addUsingAlias({$this->peerClassname}::LEFT_COL, 1, Criteria::EQUAL)";
if ($useScope) {
$script .= "
->inTree(\$scope)";
}
$script .= "
->findOne(\$con);
}
";
}
protected function addFindTree(&$script)
{
$useScope = $this->behavior->useScope();
$script .= "
/**
* Returns " . ($useScope ? 'a' : 'the') ." tree of objects
*";
if($useScope) {
$script .= "
* @param int \$scope Scope to determine which tree node to return";
}
$script .= "
* @param PropelPDO \$con Connection to use.
*
* @return mixed the list of results, formatted by the current formatter
*/
public function findTree(" . ($useScope ? "\$scope = null, " : "") . "\$con = null)
{
return \$this";
if ($useScope) {
$script .= "
->inTree(\$scope)";
}
$script .= "
->orderByBranch()
->find(\$con);
}
";
}
}

View file

@ -0,0 +1,262 @@
<?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
*/
/**
* Speeds up queries on a model by caching the query
*
* @author François Zaninotto
* @version $Revision: 1746 $
* @package propel.generator.behavior.cacheable
*/
class QueryCacheBehavior extends Behavior
{
// default parameters value
protected $parameters = array(
'backend' => 'apc',
'lifetime' => 3600,
);
public function queryAttributes($builder)
{
$script = "protected \$queryKey = '';
";
switch ($this->getParameter('backend')) {
case 'backend':
$script .= "protected static \$cacheBackend = array();
";
break;
case 'apc':
break;
case 'custom':
default:
$script .= "protected static \$cacheBackend;
";
break;
}
return $script;
}
public function queryMethods($builder)
{
$this->peerClassname = $builder->getStubPeerBuilder()->getClassname();
$script = '';
$this->addSetQueryKey($script);
$this->addGetQueryKey($script);
$this->addCacheContains($script);
$this->addCacheFetch($script);
$this->addCacheStore($script);
$this->addGetSelectStatement($script);
$this->addGetCountStatement($script);
return $script;
}
protected function addSetQueryKey(&$script)
{
$script .= "
public function setQueryKey(\$key)
{
\$this->queryKey = \$key;
return \$this;
}
";
}
protected function addGetQueryKey(&$script)
{
$script .= "
public function getQueryKey()
{
return \$this->queryKey;
}
";
}
protected function addCacheContains(&$script)
{
$script .= "
public function cacheContains(\$key)
{";
switch ($this->getParameter('backend')) {
case 'apc':
$script .= "
return apc_fetch(\$key);";
break;
case 'array':
$script .= "
return isset(self::\$cacheBackend[\$key]);";
break;
case 'custom':
default:
$script .= "
throw new PropelException('You must override the cacheContains(), cacheStore(), and cacheFetch() methods to enable query cache');";
break;
}
$script .= "
}
";
}
protected function addCacheStore(&$script)
{
$script .= "
public function cacheStore(\$key, \$value, \$lifetime = " .$this->getParameter('lifetime') . ")
{";
switch ($this->getParameter('backend')) {
case 'apc':
$script .= "
apc_store(\$key, \$value, \$lifetime);";
break;
case 'array':
$script .= "
self::\$cacheBackend[\$key] = \$value;";
break;
case 'custom':
default:
$script .= "
throw new PropelException('You must override the cacheContains(), cacheStore(), and cacheFetch() methods to enable query cache');";
break;
}
$script .= "
}
";
}
protected function addCacheFetch(&$script)
{
$script .= "
public function cacheFetch(\$key)
{";
switch ($this->getParameter('backend')) {
case 'apc':
$script .= "
return apc_fetch(\$key);";
break;
case 'array':
$script .= "
return isset(self::\$cacheBackend[\$key]) ? self::\$cacheBackend[\$key] : null;";
break;
case 'custom':
default:
$script .= "
throw new PropelException('You must override the cacheContains(), cacheStore(), and cacheFetch() methods to enable query cache');";
break;
}
$script .= "
}
";
}
protected function addGetSelectStatement(&$script)
{
$script .= "
protected function getSelectStatement(\$con = null)
{
\$dbMap = Propel::getDatabaseMap(" . $this->peerClassname ."::DATABASE_NAME);
\$db = Propel::getDB(" . $this->peerClassname ."::DATABASE_NAME);
if (\$con === null) {
\$con = Propel::getConnection(" . $this->peerClassname ."::DATABASE_NAME, Propel::CONNECTION_READ);
}
if (!\$this->hasSelectClause()) {
\$this->addSelfSelectColumns();
}
\$con->beginTransaction();
try {
\$this->basePreSelect(\$con);
\$key = \$this->getQueryKey();
if (\$key && \$this->cacheContains(\$key)) {
\$params = \$this->getParams();
\$sql = \$this->cacheFetch(\$key);
} else {
\$params = array();
\$sql = BasePeer::createSelectSql(\$this, \$params);
if (\$key) {
\$this->cacheStore(\$key, \$sql);
}
}
\$stmt = \$con->prepare(\$sql);
BasePeer::populateStmtValues(\$stmt, \$params, \$dbMap, \$db);
\$stmt->execute();
\$con->commit();
} catch (PropelException \$e) {
\$con->rollback();
throw \$e;
}
return \$stmt;
}
";
}
protected function addGetCountStatement(&$script)
{
$script .= "
protected function getCountStatement(\$con = null)
{
\$dbMap = Propel::getDatabaseMap(\$this->getDbName());
\$db = Propel::getDB(\$this->getDbName());
if (\$con === null) {
\$con = Propel::getConnection(\$this->getDbName(), Propel::CONNECTION_READ);
}
\$con->beginTransaction();
try {
\$this->basePreSelect(\$con);
\$key = \$this->getQueryKey();
if (\$key && \$this->cacheContains(\$key)) {
\$params = \$this->getParams();
\$sql = \$this->cacheFetch(\$key);
} else {
if (!\$this->hasSelectClause() && !\$this->getPrimaryCriteria()) {
\$this->addSelfSelectColumns();
}
\$params = array();
\$needsComplexCount = \$this->getGroupByColumns()
|| \$this->getOffset()
|| \$this->getLimit()
|| \$this->getHaving()
|| in_array(Criteria::DISTINCT, \$this->getSelectModifiers());
if (\$needsComplexCount) {
if (BasePeer::needsSelectAliases(\$this)) {
if (\$this->getHaving()) {
throw new PropelException('Propel cannot create a COUNT query when using HAVING and duplicate column names in the SELECT part');
}
BasePeer::turnSelectColumnsToAliases(\$this);
}
\$selectSql = BasePeer::createSelectSql(\$this, \$params);
\$sql = 'SELECT COUNT(*) FROM (' . \$selectSql . ') propelmatch4cnt';
} else {
// Replace SELECT columns with COUNT(*)
\$this->clearSelectColumns()->addSelectColumn('COUNT(*)');
\$sql = BasePeer::createSelectSql(\$this, \$params);
}
if (\$key) {
\$this->cacheStore(\$key, \$sql);
}
}
\$stmt = \$con->prepare(\$sql);
BasePeer::populateStmtValues(\$stmt, \$params, \$dbMap, \$db);
\$stmt->execute();
\$con->commit();
} catch (PropelException \$e) {
\$con->rollback();
throw \$e;
}
return \$stmt;
}
";
}
}

View file

@ -0,0 +1,332 @@
<?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
*/
/**
* Adds a slug column
*
* @author Francois Zaninotto
* @author Massimiliano Arione
* @version $Revision: 1629 $
* @package propel.generator.behavior.sluggable
*/
class SluggableBehavior extends Behavior
{
// default parameters value
protected $parameters = array(
'slug_column' => 'slug',
'slug_pattern' => '',
'replace_pattern' => '/\W+/', // Tip: use '/[^\\pL\\d]+/u' instead if you're in PHP5.3
'replacement' => '-',
'separator' => '-',
'permanent' => 'false'
);
/**
* Add the slug_column to the current table
*/
public function modifyTable()
{
if(!$this->getTable()->containsColumn($this->getParameter('slug_column'))) {
$this->getTable()->addColumn(array(
'name' => $this->getParameter('slug_column'),
'type' => 'VARCHAR',
'size' => 255
));
// add a unique to column
$unique = new Unique($this->getColumnForParameter('slug_column'));
$unique->setName($this->getTable()->getName() . '_slug');
$unique->addColumn($this->getTable()->getColumn($this->getParameter('slug_column')));
$this->getTable()->addUnique($unique);
}
}
/**
* Get the getter of the column of the behavior
*
* @return string The related getter, e.g. 'getSlug'
*/
protected function getColumnGetter()
{
return 'get' . $this->getColumnForParameter('slug_column')->getPhpName();
}
/**
* Get the setter of the column of the behavior
*
* @return string The related setter, e.g. 'setSlug'
*/
protected function getColumnSetter()
{
return 'set' . $this->getColumnForParameter('slug_column')->getPhpName();
}
/**
* Add code in ObjectBuilder::preSave
*
* @return string The code to put at the hook
*/
public function preSave($builder)
{
$const = $builder->getColumnConstant($this->getColumnForParameter('slug_column'), $this->getTable()->getPhpName() . 'Peer');
$script = "
if (\$this->isColumnModified($const) && \$this->{$this->getColumnGetter()}()) {
\$this->{$this->getColumnSetter()}(\$this->makeSlugUnique(\$this->{$this->getColumnGetter()}()));";
if ($this->getParameter('permanent') == 'true') {
$script .= "
} elseif (!\$this->{$this->getColumnGetter()}()) {
\$this->{$this->getColumnSetter()}(\$this->createSlug());
}";
} else {
$script .= "
} else {
\$this->{$this->getColumnSetter()}(\$this->createSlug());
}";
}
return $script;
}
public function objectMethods($builder)
{
$this->builder = $builder;
$script = '';
if ($this->getParameter('slug_column') != 'slug') {
$this->addSlugSetter($script);
$this->addSlugGetter($script);
}
$this->addCreateSlug($script);
$this->addCreateRawSlug($script);
$this->addCleanupSlugPart($script);
$this->addLimitSlugSize($script);
$this->addMakeSlugUnique($script);
return $script;
}
protected function addSlugSetter(&$script)
{
$script .= "
/**
* Wrap the setter for slug value
*
* @param string
* @return " . $this->getTable()->getPhpName() . "
*/
public function setSlug(\$v)
{
return \$this->" . $this->getColumnSetter() . "(\$v);
}
";
}
protected function addSlugGetter(&$script)
{
$script .= "
/**
* Wrap the getter for slug value
*
* @return string
*/
public function getSlug()
{
return \$this->" . $this->getColumnGetter() . "();
}
";
}
protected function addCreateSlug(&$script)
{
$script .= "
/**
* Create a unique slug based on the object
*
* @return string The object slug
*/
protected function createSlug()
{
\$slug = \$this->createRawSlug();
\$slug = \$this->limitSlugSize(\$slug);
\$slug = \$this->makeSlugUnique(\$slug);
return \$slug;
}
";
}
protected function addCreateRawSlug(&$script)
{
$pattern = $this->getParameter('slug_pattern');
$script .= "
/**
* Create the slug from the appropriate columns
*
* @return string
*/
protected function createRawSlug()
{
";
if ($pattern) {
$script .= "return '" . str_replace(array('{', '}'), array('\' . $this->cleanupSlugPart($this->get', '()) . \''), $pattern). "';";
} else {
$script .= "return \$this->cleanupSlugPart(\$this->__toString());";
}
$script .= "
}
";
return $script;
}
public function addCleanupSlugPart(&$script)
{
$script .= "
/**
* Cleanup a string to make a slug of it
* Removes special characters, replaces blanks with a separator, and trim it
*
* @param string \$text the text to slugify
* @param string \$separator the separator used by slug
* @return string the slugified text
*/
protected static function cleanupSlugPart(\$slug, \$replacement = '" . $this->getParameter('replacement') . "')
{
// transliterate
if (function_exists('iconv')) {
\$slug = iconv('utf-8', 'us-ascii//TRANSLIT', \$slug);
}
// lowercase
if (function_exists('mb_strtolower')) {
\$slug = mb_strtolower(\$slug);
} else {
\$slug = strtolower(\$slug);
}
// remove accents resulting from OSX's iconv
\$slug = str_replace(array('\'', '`', '^'), '', \$slug);
// replace non letter or digits with separator
\$slug = preg_replace('" . $this->getParameter('replace_pattern') . "', \$replacement, \$slug);
// trim
\$slug = trim(\$slug, \$replacement);
if (empty(\$slug)) {
return 'n-a';
}
return \$slug;
}
";
}
public function addLimitSlugSize(&$script)
{
$size = $this->getColumnForParameter('slug_column')->getSize();
$script .= "
/**
* Make sure the slug is short enough to accomodate the column size
*
* @param string \$slug the slug to check
*
* @return string the truncated slug
*/
protected static function limitSlugSize(\$slug, \$incrementReservedSpace = 3)
{
// check length, as suffix could put it over maximum
if (strlen(\$slug) > ($size - \$incrementReservedSpace)) {
\$slug = substr(\$slug, 0, $size - \$incrementReservedSpace);
}
return \$slug;
}
";
}
public function addMakeSlugUnique(&$script)
{
$script .= "
/**
* Get the slug, ensuring its uniqueness
*
* @param string \$slug the slug to check
* @param string \$separator the separator used by slug
* @return string the unique slug
*/
protected function makeSlugUnique(\$slug, \$separator = '" . $this->getParameter('separator') ."', \$increment = 0)
{
\$slug2 = empty(\$increment) ? \$slug : \$slug . \$separator . \$increment;
\$slugAlreadyExists = " . $this->builder->getStubQueryBuilder()->getClassname() . "::create()
->filterBySlug(\$slug2)
->prune(\$this)";
// watch out: some of the columns may be hidden by the soft_delete behavior
if ($this->table->hasBehavior('soft_delete')) {
$script .= "
->includeDeleted()";
}
$script .= "
->count();
if (\$slugAlreadyExists) {
return \$this->makeSlugUnique(\$slug, \$separator, ++\$increment);
} else {
return \$slug2;
}
}
";
}
public function queryMethods($builder)
{
$this->builder = $builder;
$script = '';
if ($this->getParameter('slug_column') != 'slug') {
$this->addFilterBySlug($script);
}
$this->addFindOneBySlug($script);
return $script;
}
protected function addFilterBySlug(&$script)
{
$script .= "
/**
* Filter the query on the slug column
*
* @param string \$slug The value to use as filter.
*
* @return " . $this->builder->getStubQueryBuilder()->getClassname() . " The current query, for fluid interface
*/
public function filterBySlug(\$slug)
{
return \$this->addUsingAlias(" . $this->builder->getColumnConstant($this->getColumnForParameter('slug_column')) . ", \$slug, Criteria::EQUAL);
}
";
}
protected function addFindOneBySlug(&$script)
{
$script .= "
/**
* Find one object based on its slug
*
* @param string \$slug The value to use as filter.
* @param PropelPDO \$con The optional connection object
*
* @return " . $this->builder->getStubObjectBuilder()->getClassname() . " the result, formatted by the current formatter
*/
public function findOneBySlug(\$slug, \$con = null)
{
return \$this->filterBySlug(\$slug)->findOne(\$con);
}
";
}
}

View file

@ -0,0 +1,83 @@
<?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 dirname(__FILE__) . '/SortableBehaviorObjectBuilderModifier.php';
require_once dirname(__FILE__) . '/SortableBehaviorQueryBuilderModifier.php';
require_once dirname(__FILE__) . '/SortableBehaviorPeerBuilderModifier.php';
/**
* Gives a model class the ability to be ordered
* Uses one additional column storing the rank
*
* @author Massimiliano Arione
* @version $Revision: 1612 $
* @package propel.generator.behavior.sortable
*/
class SortableBehavior extends Behavior
{
// default parameters value
protected $parameters = array(
'rank_column' => 'sortable_rank',
'use_scope' => 'false',
'scope_column' => 'sortable_scope',
);
protected $objectBuilderModifier, $queryBuilderModifier, $peerBuilderModifier;
/**
* Add the rank_column to the current table
*/
public function modifyTable()
{
if (!$this->getTable()->containsColumn($this->getParameter('rank_column'))) {
$this->getTable()->addColumn(array(
'name' => $this->getParameter('rank_column'),
'type' => 'INTEGER'
));
}
if ($this->getParameter('use_scope') == 'true' &&
!$this->getTable()->containsColumn($this->getParameter('scope_column'))) {
$this->getTable()->addColumn(array(
'name' => $this->getParameter('scope_column'),
'type' => 'INTEGER'
));
}
}
public function getObjectBuilderModifier()
{
if (is_null($this->objectBuilderModifier)) {
$this->objectBuilderModifier = new SortableBehaviorObjectBuilderModifier($this);
}
return $this->objectBuilderModifier;
}
public function getQueryBuilderModifier()
{
if (is_null($this->queryBuilderModifier)) {
$this->queryBuilderModifier = new SortableBehaviorQueryBuilderModifier($this);
}
return $this->queryBuilderModifier;
}
public function getPeerBuilderModifier()
{
if (is_null($this->peerBuilderModifier)) {
$this->peerBuilderModifier = new SortableBehaviorPeerBuilderModifier($this);
}
return $this->peerBuilderModifier;
}
public function useScope()
{
return $this->getParameter('use_scope') == 'true';
}
}

View file

@ -0,0 +1,636 @@
<?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
*/
/**
* Behavior to add sortable columns and abilities
*
* @author François Zaninotto
* @author heltem <heltem@o2php.com>
* @package propel.generator.behavior.sortable
*/
class SortableBehaviorObjectBuilderModifier
{
protected $behavior, $table, $builder, $objectClassname, $peerClassname;
public function __construct($behavior)
{
$this->behavior = $behavior;
$this->table = $behavior->getTable();
}
protected function getParameter($key)
{
return $this->behavior->getParameter($key);
}
protected function getColumnAttribute($name)
{
return strtolower($this->behavior->getColumnForParameter($name)->getName());
}
protected function getColumnPhpName($name)
{
return $this->behavior->getColumnForParameter($name)->getPhpName();
}
protected function setBuilder($builder)
{
$this->builder = $builder;
$this->objectClassname = $builder->getStubObjectBuilder()->getClassname();
$this->queryClassname = $builder->getStubQueryBuilder()->getClassname();
$this->peerClassname = $builder->getStubPeerBuilder()->getClassname();
}
/**
* Get the getter of the column of the behavior
*
* @return string The related getter, e.g. 'getRank'
*/
protected function getColumnGetter($columnName = 'rank_column')
{
return 'get' . $this->behavior->getColumnForParameter($columnName)->getPhpName();
}
/**
* Get the setter of the column of the behavior
*
* @return string The related setter, e.g. 'setRank'
*/
protected function getColumnSetter($columnName = 'rank_column')
{
return 'set' . $this->behavior->getColumnForParameter($columnName)->getPhpName();
}
public function preSave($builder)
{
return "\$this->processSortableQueries(\$con);";
}
public function preInsert($builder)
{
$useScope = $this->behavior->useScope();
$this->setBuilder($builder);
return "if (!\$this->isColumnModified({$this->peerClassname}::RANK_COL)) {
\$this->{$this->getColumnSetter()}({$this->queryClassname}::create()->getMaxRank(" . ($useScope ? "\$this->{$this->getColumnGetter('scope_column')}(), " : '') . "\$con) + 1);
}
";
}
public function preDelete($builder)
{
$useScope = $this->behavior->useScope();
$this->setBuilder($builder);
return "
{$this->peerClassname}::shiftRank(-1, \$this->{$this->getColumnGetter()}() + 1, null, " . ($useScope ? "\$this->{$this->getColumnGetter('scope_column')}(), " : '') . "\$con);
{$this->peerClassname}::clearInstancePool();
";
}
public function objectAttributes($builder)
{
return "
/**
* Queries to be executed in the save transaction
* @var array
*/
protected \$sortableQueries = array();
";
}
public function objectMethods($builder)
{
$this->setBuilder($builder);
$script = '';
if ($this->getParameter('rank_column') != 'rank') {
$this->addRankAccessors($script);
}
if ($this->behavior->useScope() &&
$this->getParameter('scope_column') != 'scope_value') {
$this->addScopeAccessors($script);
}
$this->addIsFirst($script);
$this->addIsLast($script);
$this->addGetNext($script);
$this->addGetPrevious($script);
$this->addInsertAtRank($script);
$this->addInsertAtBottom($script);
$this->addInsertAtTop($script);
$this->addMoveToRank($script);
$this->addSwapWith($script);
$this->addMoveUp($script);
$this->addMoveDown($script);
$this->addMoveToTop($script);
$this->addMoveToBottom($script);
$this->addRemoveFromList($script);
$this->addProcessSortableQueries($script);
return $script;
}
/**
* Get the wraps for getter/setter, if the rank column has not the default name
*
* @return string
*/
protected function addRankAccessors(&$script)
{
$script .= "
/**
* Wrap the getter for rank value
*
* @return int
*/
public function getRank()
{
return \$this->{$this->getColumnAttribute('rank_column')};
}
/**
* Wrap the setter for rank value
*
* @param int
* @return {$this->objectClassname}
*/
public function setRank(\$v)
{
return \$this->{$this->getColumnSetter()}(\$v);
}
";
}
/**
* Get the wraps for getter/setter, if the scope column has not the default name
*
* @return string
*/
protected function addScopeAccessors(&$script)
{
$script .= "
/**
* Wrap the getter for scope value
*
* @return int
*/
public function getScopeValue()
{
return \$this->{$this->getColumnAttribute('scope_column')};
}
/**
* Wrap the setter for scope value
*
* @param int
* @return {$this->objectClassname}
*/
public function setScopeValue(\$v)
{
return \$this->{$this->getColumnSetter('scope_column')}(\$v);
}
";
}
protected function addIsFirst(&$script)
{
$script .= "
/**
* Check if the object is first in the list, i.e. if it has 1 for rank
*
* @return boolean
*/
public function isFirst()
{
return \$this->{$this->getColumnGetter()}() == 1;
}
";
}
protected function addIsLast(&$script)
{
$useScope = $this->behavior->useScope();
$script .= "
/**
* Check if the object is last in the list, i.e. if its rank is the highest rank
*
* @param PropelPDO \$con optional connection
*
* @return boolean
*/
public function isLast(PropelPDO \$con = null)
{
return \$this->{$this->getColumnGetter()}() == {$this->queryClassname}::create()->getMaxRank(" . ($useScope ? "\$this->{$this->getColumnGetter('scope_column')}(), " : '') . "\$con);
}
";
}
protected function addGetNext(&$script)
{
$useScope = $this->behavior->useScope();
$script .= "
/**
* Get the next item in the list, i.e. the one for which rank is immediately higher
*
* @param PropelPDO \$con optional connection
*
* @return {$this->objectClassname}
*/
public function getNext(PropelPDO \$con = null)
{";
if ($this->behavior->getParameter('rank_column') == 'rank' && $useScope) {
$script .= "
return {$this->queryClassname}::create()
->filterByRank(\$this->{$this->getColumnGetter()}() + 1)
->inList(\$this->{$this->getColumnGetter('scope_column')}())
->findOne(\$con);";
} else {
$script .= "
return {$this->queryClassname}::create()->findOneByRank(\$this->{$this->getColumnGetter()}() + 1, " . ($useScope ? "\$this->{$this->getColumnGetter('scope_column')}(), " : '') . "\$con);";
}
$script .= "
}
";
}
protected function addGetPrevious(&$script)
{
$useScope = $this->behavior->useScope();
$script .= "
/**
* Get the previous item in the list, i.e. the one for which rank is immediately lower
*
* @param PropelPDO \$con optional connection
*
* @return {$this->objectClassname}
*/
public function getPrevious(PropelPDO \$con = null)
{";
if ($this->behavior->getParameter('rank_column') == 'rank' && $useScope) {
$script .= "
return {$this->queryClassname}::create()
->filterByRank(\$this->{$this->getColumnGetter()}() - 1)
->inList(\$this->{$this->getColumnGetter('scope_column')}())
->findOne(\$con);";
} else {
$script .= "
return {$this->queryClassname}::create()->findOneByRank(\$this->{$this->getColumnGetter()}() - 1, " . ($useScope ? "\$this->{$this->getColumnGetter('scope_column')}(), " : '') . "\$con);";
}
$script .= "
}
";
}
protected function addInsertAtRank(&$script)
{
$useScope = $this->behavior->useScope();
$peerClassname = $this->peerClassname;
$script .= "
/**
* Insert at specified rank
* The modifications are not persisted until the object is saved.
*
* @param integer \$rank rank value
* @param PropelPDO \$con optional connection
*
* @return {$this->objectClassname} the current object
*
* @throws PropelException
*/
public function insertAtRank(\$rank, PropelPDO \$con = null)
{";
if ($useScope) {
$script .= "
if (null === \$this->{$this->getColumnGetter('scope_column')}()) {
throw new PropelException('The scope must be defined before inserting an object in a suite');
}";
}
$script .= "
\$maxRank = {$this->queryClassname}::create()->getMaxRank(" . ($useScope ? "\$this->{$this->getColumnGetter('scope_column')}(), " : '') . "\$con);
if (\$rank < 1 || \$rank > \$maxRank + 1) {
throw new PropelException('Invalid rank ' . \$rank);
}
// move the object in the list, at the given rank
\$this->{$this->getColumnSetter()}(\$rank);
if (\$rank != \$maxRank + 1) {
// Keep the list modification query for the save() transaction
\$this->sortableQueries []= array(
'callable' => array('$peerClassname', 'shiftRank'),
'arguments' => array(1, \$rank, null, " . ($useScope ? "\$this->{$this->getColumnGetter('scope_column')}()" : '') . ")
);
}
return \$this;
}
";
}
protected function addInsertAtBottom(&$script)
{
$useScope = $this->behavior->useScope();
$script .= "
/**
* Insert in the last rank
* The modifications are not persisted until the object is saved.
*
* @param PropelPDO \$con optional connection
*
* @return {$this->objectClassname} the current object
*
* @throws PropelException
*/
public function insertAtBottom(PropelPDO \$con = null)
{";
if ($useScope) {
$script .= "
if (null === \$this->{$this->getColumnGetter('scope_column')}()) {
throw new PropelException('The scope must be defined before inserting an object in a suite');
}";
}
$script .= "
\$this->{$this->getColumnSetter()}({$this->queryClassname}::create()->getMaxRank(" . ($useScope ? "\$this->{$this->getColumnGetter('scope_column')}(), " : '') . "\$con) + 1);
return \$this;
}
";
}
protected function addInsertAtTop(&$script)
{
$script .= "
/**
* Insert in the first rank
* The modifications are not persisted until the object is saved.
*
* @return {$this->objectClassname} the current object
*/
public function insertAtTop()
{
return \$this->insertAtRank(1);
}
";
}
protected function addMoveToRank(&$script)
{
$useScope = $this->behavior->useScope();
$peerClassname = $this->peerClassname;
$script .= "
/**
* Move the object to a new rank, and shifts the rank
* Of the objects inbetween the old and new rank accordingly
*
* @param integer \$newRank rank value
* @param PropelPDO \$con optional connection
*
* @return {$this->objectClassname} the current object
*
* @throws PropelException
*/
public function moveToRank(\$newRank, PropelPDO \$con = null)
{
if (\$this->isNew()) {
throw new PropelException('New objects cannot be moved. Please use insertAtRank() instead');
}
if (\$con === null) {
\$con = Propel::getConnection($peerClassname::DATABASE_NAME);
}
if (\$newRank < 1 || \$newRank > {$this->queryClassname}::create()->getMaxRank(" . ($useScope ? "\$this->{$this->getColumnGetter('scope_column')}(), " : '') . "\$con)) {
throw new PropelException('Invalid rank ' . \$newRank);
}
\$oldRank = \$this->{$this->getColumnGetter()}();
if (\$oldRank == \$newRank) {
return \$this;
}
\$con->beginTransaction();
try {
// shift the objects between the old and the new rank
\$delta = (\$oldRank < \$newRank) ? -1 : 1;
$peerClassname::shiftRank(\$delta, min(\$oldRank, \$newRank), max(\$oldRank, \$newRank), " . ($useScope ? "\$this->{$this->getColumnGetter('scope_column')}(), " : '') . "\$con);
// move the object to its new rank
\$this->{$this->getColumnSetter()}(\$newRank);
\$this->save(\$con);
\$con->commit();
return \$this;
} catch (Exception \$e) {
\$con->rollback();
throw \$e;
}
}
";
}
protected function addSwapWith(&$script)
{
$script .= "
/**
* Exchange the rank of the object with the one passed as argument, and saves both objects
*
* @param {$this->objectClassname} \$object
* @param PropelPDO \$con optional connection
*
* @return {$this->objectClassname} the current object
*
* @throws Exception if the database cannot execute the two updates
*/
public function swapWith(\$object, PropelPDO \$con = null)
{
if (\$con === null) {
\$con = Propel::getConnection({$this->peerClassname}::DATABASE_NAME);
}
\$con->beginTransaction();
try {
\$oldRank = \$this->{$this->getColumnGetter()}();
\$newRank = \$object->{$this->getColumnGetter()}();
\$this->{$this->getColumnSetter()}(\$newRank);
\$this->save(\$con);
\$object->{$this->getColumnSetter()}(\$oldRank);
\$object->save(\$con);
\$con->commit();
return \$this;
} catch (Exception \$e) {
\$con->rollback();
throw \$e;
}
}
";
}
protected function addMoveUp(&$script)
{
$script .= "
/**
* Move the object higher in the list, i.e. exchanges its rank with the one of the previous object
*
* @param PropelPDO \$con optional connection
*
* @return {$this->objectClassname} the current object
*/
public function moveUp(PropelPDO \$con = null)
{
if (\$this->isFirst()) {
return \$this;
}
if (\$con === null) {
\$con = Propel::getConnection({$this->peerClassname}::DATABASE_NAME);
}
\$con->beginTransaction();
try {
\$prev = \$this->getPrevious(\$con);
\$this->swapWith(\$prev, \$con);
\$con->commit();
return \$this;
} catch (Exception \$e) {
\$con->rollback();
throw \$e;
}
}
";
}
protected function addMoveDown(&$script)
{
$script .= "
/**
* Move the object higher in the list, i.e. exchanges its rank with the one of the next object
*
* @param PropelPDO \$con optional connection
*
* @return {$this->objectClassname} the current object
*/
public function moveDown(PropelPDO \$con = null)
{
if (\$this->isLast(\$con)) {
return \$this;
}
if (\$con === null) {
\$con = Propel::getConnection({$this->peerClassname}::DATABASE_NAME);
}
\$con->beginTransaction();
try {
\$next = \$this->getNext(\$con);
\$this->swapWith(\$next, \$con);
\$con->commit();
return \$this;
} catch (Exception \$e) {
\$con->rollback();
throw \$e;
}
}
";
}
protected function addMoveToTop(&$script)
{
$script .= "
/**
* Move the object to the top of the list
*
* @param PropelPDO \$con optional connection
*
* @return {$this->objectClassname} the current object
*/
public function moveToTop(PropelPDO \$con = null)
{
if (\$this->isFirst()) {
return \$this;
}
return \$this->moveToRank(1, \$con);
}
";
}
protected function addMoveToBottom(&$script)
{
$useScope = $this->behavior->useScope();
$script .= "
/**
* Move the object to the bottom of the list
*
* @param PropelPDO \$con optional connection
*
* @return integer the old object's rank
*/
public function moveToBottom(PropelPDO \$con = null)
{
if (\$this->isLast(\$con)) {
return false;
}
if (\$con === null) {
\$con = Propel::getConnection({$this->peerClassname}::DATABASE_NAME);
}
\$con->beginTransaction();
try {
\$bottom = {$this->queryClassname}::create()->getMaxRank(" . ($useScope ? "\$this->{$this->getColumnGetter('scope_column')}(), " : '') . "\$con);
\$res = \$this->moveToRank(\$bottom, \$con);
\$con->commit();
return \$res;
} catch (Exception \$e) {
\$con->rollback();
throw \$e;
}
}
";
}
protected function addRemoveFromList(&$script)
{
$useScope = $this->behavior->useScope();
$peerClassname = $this->peerClassname;
$script .= "
/**
* Removes the current object from the list.
* The modifications are not persisted until the object is saved.
*
* @return {$this->objectClassname} the current object
*/
public function removeFromList()
{
// Keep the list modification query for the save() transaction
\$this->sortableQueries []= array(
'callable' => array('$peerClassname', 'shiftRank'),
'arguments' => array(-1, \$this->{$this->getColumnGetter()}() + 1, null" . ($useScope ? ", \$this->{$this->getColumnGetter('scope_column')}()" : '') . ")
);
// remove the object from the list
\$this->{$this->getColumnSetter('rank_column')}(null);";
if ($useScope) {
$script .= "
\$this->{$this->getColumnSetter('scope_column')}(null);";
}
$script .= "
return \$this;
}
";
}
protected function addProcessSortableQueries(&$script)
{
$script .= "
/**
* Execute queries that were saved to be run inside the save transaction
*/
protected function processSortableQueries(\$con)
{
foreach (\$this->sortableQueries as \$query) {
\$query['arguments'][]= \$con;
call_user_func_array(\$query['callable'], \$query['arguments']);
}
\$this->sortableQueries = array();
}
";
}
}

View file

@ -0,0 +1,367 @@
<?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
*/
/**
* Behavior to add sortable peer methods
*
* @author François Zaninotto
* @author heltem <heltem@o2php.com>
* @package propel.generator.behavior.sortable
*/
class SortableBehaviorPeerBuilderModifier
{
protected $behavior, $table, $builder, $objectClassname, $peerClassname;
public function __construct($behavior)
{
$this->behavior = $behavior;
$this->table = $behavior->getTable();
}
protected function getParameter($key)
{
return $this->behavior->getParameter($key);
}
protected function getColumnAttribute($name)
{
return strtolower($this->behavior->getColumnForParameter($name)->getName());
}
protected function getColumnConstant($name)
{
return strtoupper($this->behavior->getColumnForParameter($name)->getName());
}
protected function getColumnPhpName($name)
{
return $this->behavior->getColumnForParameter($name)->getPhpName();
}
protected function setBuilder($builder)
{
$this->builder = $builder;
$this->objectClassname = $builder->getStubObjectBuilder()->getClassname();
$this->peerClassname = $builder->getStubPeerBuilder()->getClassname();
}
public function staticAttributes($builder)
{
$tableName = $this->table->getName();
$script = "
/**
* rank column
*/
const RANK_COL = '" . $tableName . '.' . $this->getColumnConstant('rank_column') . "';
";
if ($this->behavior->useScope()) {
$script .= "
/**
* Scope column for the set
*/
const SCOPE_COL = '" . $tableName . '.' . $this->getColumnConstant('scope_column') . "';
";
}
return $script;
}
/**
* Static methods
*
* @return string
*/
public function staticMethods($builder)
{
$this->setBuilder($builder);
$script = '';
$this->addGetMaxRank($script);
$this->addRetrieveByRank($script);
$this->addReorder($script);
$this->addDoSelectOrderByRank($script);
if ($this->behavior->useScope()) {
$this->addRetrieveList($script);
$this->addCountList($script);
$this->addDeleteList($script);
}
$this->addShiftRank($script);
return $script;
}
protected function addGetMaxRank(&$script)
{
$useScope = $this->behavior->useScope();
$script .= "
/**
* Get the highest rank
* ";
if($useScope) {
$script .= "
* @param int \$scope Scope to determine which suite to consider";
}
$script .= "
* @param PropelPDO optional connection
*
* @return integer highest position
*/
public static function getMaxRank(" . ($useScope ? "\$scope = null, " : "") . "PropelPDO \$con = null)
{
if (\$con === null) {
\$con = Propel::getConnection({$this->peerClassname}::DATABASE_NAME);
}
// shift the objects with a position lower than the one of object
\$c = new Criteria();
\$c->addSelectColumn('MAX(' . {$this->peerClassname}::RANK_COL . ')');";
if ($useScope) {
$script .= "
\$c->add({$this->peerClassname}::SCOPE_COL, \$scope, Criteria::EQUAL);";
}
$script .= "
\$stmt = {$this->peerClassname}::doSelectStmt(\$c, \$con);
return \$stmt->fetchColumn();
}
";
}
protected function addRetrieveByRank(&$script)
{
$peerClassname = $this->peerClassname;
$useScope = $this->behavior->useScope();
$script .= "
/**
* Get an item from the list based on its rank
*
* @param integer \$rank rank";
if($useScope) {
$script .= "
* @param int \$scope Scope to determine which suite to consider";
}
$script .= "
* @param PropelPDO \$con optional connection
*
* @return {$this->objectClassname}
*/
public static function retrieveByRank(\$rank, " . ($useScope ? "\$scope = null, " : "") . "PropelPDO \$con = null)
{
if (\$con === null) {
\$con = Propel::getConnection($peerClassname::DATABASE_NAME);
}
\$c = new Criteria;
\$c->add($peerClassname::RANK_COL, \$rank);";
if($useScope) {
$script .= "
\$c->add($peerClassname::SCOPE_COL, \$scope, Criteria::EQUAL);";
}
$script .= "
return $peerClassname::doSelectOne(\$c, \$con);
}
";
}
protected function addReorder(&$script)
{
$peerClassname = $this->peerClassname;
$columnGetter = 'get' . $this->behavior->getColumnForParameter('rank_column')->getPhpName();
$columnSetter = 'set' . $this->behavior->getColumnForParameter('rank_column')->getPhpName();
$script .= "
/**
* Reorder a set of sortable objects based on a list of id/position
* Beware that there is no check made on the positions passed
* So incoherent positions will result in an incoherent list
*
* @param array \$order id => rank pairs
* @param PropelPDO \$con optional connection
*
* @return boolean true if the reordering took place, false if a database problem prevented it
*/
public static function reorder(array \$order, PropelPDO \$con = null)
{
if (\$con === null) {
\$con = Propel::getConnection($peerClassname::DATABASE_NAME);
}
\$con->beginTransaction();
try {
\$ids = array_keys(\$order);
\$objects = $peerClassname::retrieveByPKs(\$ids);
foreach (\$objects as \$object) {
\$pk = \$object->getPrimaryKey();
if (\$object->$columnGetter() != \$order[\$pk]) {
\$object->$columnSetter(\$order[\$pk]);
\$object->save(\$con);
}
}
\$con->commit();
return true;
} catch (PropelException \$e) {
\$con->rollback();
throw \$e;
}
}
";
}
protected function addDoSelectOrderByRank(&$script)
{
$peerClassname = $this->peerClassname;
$script .= "
/**
* Return an array of sortable objects ordered by position
*
* @param Criteria \$criteria optional criteria object
* @param string \$order sorting order, to be chosen between Criteria::ASC (default) and Criteria::DESC
* @param PropelPDO \$con optional connection
*
* @return array list of sortable objects
*/
public static function doSelectOrderByRank(Criteria \$criteria = null, \$order = Criteria::ASC, PropelPDO \$con = null)
{
if (\$con === null) {
\$con = Propel::getConnection($peerClassname::DATABASE_NAME);
}
if (\$criteria === null) {
\$criteria = new Criteria();
} elseif (\$criteria instanceof Criteria) {
\$criteria = clone \$criteria;
}
\$criteria->clearOrderByColumns();
if (\$order == Criteria::ASC) {
\$criteria->addAscendingOrderByColumn($peerClassname::RANK_COL);
} else {
\$criteria->addDescendingOrderByColumn($peerClassname::RANK_COL);
}
return $peerClassname::doSelect(\$criteria, \$con);
}
";
}
protected function addRetrieveList(&$script)
{
$peerClassname = $this->peerClassname;
$script .= "
/**
* Return an array of sortable objects in the given scope ordered by position
*
* @param int \$scope the scope of the list
* @param string \$order sorting order, to be chosen between Criteria::ASC (default) and Criteria::DESC
* @param PropelPDO \$con optional connection
*
* @return array list of sortable objects
*/
public static function retrieveList(\$scope, \$order = Criteria::ASC, PropelPDO \$con = null)
{
\$c = new Criteria();
\$c->add($peerClassname::SCOPE_COL, \$scope);
return $peerClassname::doSelectOrderByRank(\$c, \$order, \$con);
}
";
}
protected function addCountList(&$script)
{
$peerClassname = $this->peerClassname;
$script .= "
/**
* Return the number of sortable objects in the given scope
*
* @param int \$scope the scope of the list
* @param PropelPDO \$con optional connection
*
* @return array list of sortable objects
*/
public static function countList(\$scope, PropelPDO \$con = null)
{
\$c = new Criteria();
\$c->add($peerClassname::SCOPE_COL, \$scope);
return $peerClassname::doCount(\$c, \$con);
}
";
}
protected function addDeleteList(&$script)
{
$peerClassname = $this->peerClassname;
$script .= "
/**
* Deletes the sortable objects in the given scope
*
* @param int \$scope the scope of the list
* @param PropelPDO \$con optional connection
*
* @return int number of deleted objects
*/
public static function deleteList(\$scope, PropelPDO \$con = null)
{
\$c = new Criteria();
\$c->add($peerClassname::SCOPE_COL, \$scope);
return $peerClassname::doDelete(\$c, \$con);
}
";
}
protected function addShiftRank(&$script)
{
$useScope = $this->behavior->useScope();
$peerClassname = $this->peerClassname;
$script .= "
/**
* Adds \$delta to all Rank values that are >= \$first and <= \$last.
* '\$delta' can also be negative.
*
* @param int \$delta Value to be shifted by, can be negative
* @param int \$first First node to be shifted
* @param int \$last Last node to be shifted";
if($useScope) {
$script .= "
* @param int \$scope Scope to use for the shift";
}
$script .= "
* @param PropelPDO \$con Connection to use.
*/
public static function shiftRank(\$delta, \$first, \$last = null, " . ($useScope ? "\$scope = null, " : "") . "PropelPDO \$con = null)
{
if (\$con === null) {
\$con = Propel::getConnection($peerClassname::DATABASE_NAME, Propel::CONNECTION_WRITE);
}
\$whereCriteria = new Criteria($peerClassname::DATABASE_NAME);
\$criterion = \$whereCriteria->getNewCriterion($peerClassname::RANK_COL, \$first, Criteria::GREATER_EQUAL);
if (null !== \$last) {
\$criterion->addAnd(\$whereCriteria->getNewCriterion($peerClassname::RANK_COL, \$last, Criteria::LESS_EQUAL));
}
\$whereCriteria->add(\$criterion);";
if ($useScope) {
$script .= "
\$whereCriteria->add($peerClassname::SCOPE_COL, \$scope, Criteria::EQUAL);";
}
$script .= "
\$valuesCriteria = new Criteria($peerClassname::DATABASE_NAME);
\$valuesCriteria->add($peerClassname::RANK_COL, array('raw' => $peerClassname::RANK_COL . ' + ?', 'value' => \$delta), Criteria::CUSTOM_EQUAL);
{$this->builder->getPeerBuilder()->getBasePeerClassname()}::doUpdate(\$whereCriteria, \$valuesCriteria, \$con);
$peerClassname::clearInstancePool();
}
";
}
}

View file

@ -0,0 +1,283 @@
<?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
*/
/**
* Behavior to add sortable query methods
*
* @author François Zaninotto
* @package propel.generator.behavior.sortable
*/
class SortableBehaviorQueryBuilderModifier
{
protected $behavior, $table, $builder, $objectClassname, $peerClassname;
public function __construct($behavior)
{
$this->behavior = $behavior;
$this->table = $behavior->getTable();
}
protected function getParameter($key)
{
return $this->behavior->getParameter($key);
}
protected function getColumn($name)
{
return $this->behavior->getColumnForParameter($name);
}
protected function setBuilder($builder)
{
$this->builder = $builder;
$this->objectClassname = $builder->getStubObjectBuilder()->getClassname();
$this->queryClassname = $builder->getStubQueryBuilder()->getClassname();
$this->peerClassname = $builder->getStubPeerBuilder()->getClassname();
}
public function queryMethods($builder)
{
$this->setBuilder($builder);
$script = '';
// select filters
if ($this->behavior->useScope()) {
$this->addInList($script);
}
if ($this->getParameter('rank_column') != 'rank') {
$this->addFilterByRank($script);
$this->addOrderByRank($script);
}
// select termination methods
if ($this->getParameter('rank_column') != 'rank') {
$this->addFindOneByRank($script);
}
$this->addFindList($script);
// utilities
$this->addGetMaxRank($script);
$this->addReorder($script);
return $script;
}
protected function addInList(&$script)
{
$script .= "
/**
* Returns the objects in a certain list, from the list scope
*
* @param int \$scope Scope to determine which objects node to return
*
* @return {$this->queryClassname} The current query, for fuid interface
*/
public function inList(\$scope = null)
{
return \$this->addUsingAlias({$this->peerClassname}::SCOPE_COL, \$scope, Criteria::EQUAL);
}
";
}
protected function addFilterByRank(&$script)
{
$useScope = $this->behavior->useScope();
$peerClassname = $this->peerClassname;
$script .= "
/**
* Filter the query based on a rank in the list
*
* @param integer \$rank rank";
if($useScope) {
$script .= "
* @param int \$scope Scope to determine which suite to consider";
}
$script .= "
*
* @return " . $this->queryClassname . " The current query, for fluid interface
*/
public function filterByRank(\$rank" . ($useScope ? ", \$scope = null" : "") . ")
{
return \$this";
if ($useScope) {
$script .= "
->inList(\$scope)";
}
$script .= "
->addUsingAlias($peerClassname::RANK_COL, \$rank, Criteria::EQUAL);
}
";
}
protected function addOrderByRank(&$script)
{
$script .= "
/**
* Order the query based on the rank in the list.
* Using the default \$order, returns the item with the lowest rank first
*
* @param string \$order either Criteria::ASC (default) or Criteria::DESC
*
* @return " . $this->queryClassname . " The current query, for fluid interface
*/
public function orderByRank(\$order = Criteria::ASC)
{
\$order = strtoupper(\$order);
switch (\$order) {
case Criteria::ASC:
return \$this->addAscendingOrderByColumn(\$this->getAliasedColName(" . $this->peerClassname . "::RANK_COL));
break;
case Criteria::DESC:
return \$this->addDescendingOrderByColumn(\$this->getAliasedColName(" . $this->peerClassname . "::RANK_COL));
break;
default:
throw new PropelException('" . $this->queryClassname . "::orderBy() only accepts \"asc\" or \"desc\" as argument');
}
}
";
}
protected function addFindOneByRank(&$script)
{
$useScope = $this->behavior->useScope();
$peerClassname = $this->peerClassname;
$script .= "
/**
* Get an item from the list based on its rank
*
* @param integer \$rank rank";
if($useScope) {
$script .= "
* @param int \$scope Scope to determine which suite to consider";
}
$script .= "
* @param PropelPDO \$con optional connection
*
* @return {$this->objectClassname}
*/
public function findOneByRank(\$rank, " . ($useScope ? "\$scope = null, " : "") . "PropelPDO \$con = null)
{
return \$this
->filterByRank(\$rank" . ($useScope ? ", \$scope" : "") . ")
->findOne(\$con);
}
";
}
protected function addFindList(&$script)
{
$useScope = $this->behavior->useScope();
$script .= "
/**
* Returns " . ($useScope ? 'a' : 'the') ." list of objects
*";
if($useScope) {
$script .= "
* @param int \$scope Scope to determine which list to return";
}
$script .= "
* @param PropelPDO \$con Connection to use.
*
* @return mixed the list of results, formatted by the current formatter
*/
public function findList(" . ($useScope ? "\$scope = null, " : "") . "\$con = null)
{
return \$this";
if ($useScope) {
$script .= "
->inList(\$scope)";
}
$script .= "
->orderByRank()
->find(\$con);
}
";
}
protected function addGetMaxRank(&$script)
{
$useScope = $this->behavior->useScope();
$script .= "
/**
* Get the highest rank
* ";
if($useScope) {
$script .= "
* @param int \$scope Scope to determine which suite to consider";
}
$script .= "
* @param PropelPDO optional connection
*
* @return integer highest position
*/
public function getMaxRank(" . ($useScope ? "\$scope = null, " : "") . "PropelPDO \$con = null)
{
if (\$con === null) {
\$con = Propel::getConnection({$this->peerClassname}::DATABASE_NAME);
}
// shift the objects with a position lower than the one of object
\$this->addSelectColumn('MAX(' . {$this->peerClassname}::RANK_COL . ')');";
if ($useScope) {
$script .= "
\$this->add({$this->peerClassname}::SCOPE_COL, \$scope, Criteria::EQUAL);";
}
$script .= "
\$stmt = \$this->getSelectStatement(\$con);
return \$stmt->fetchColumn();
}
";
}
protected function addReorder(&$script)
{
$peerClassname = $this->peerClassname;
$columnGetter = 'get' . $this->behavior->getColumnForParameter('rank_column')->getPhpName();
$columnSetter = 'set' . $this->behavior->getColumnForParameter('rank_column')->getPhpName();
$script .= "
/**
* Reorder a set of sortable objects based on a list of id/position
* Beware that there is no check made on the positions passed
* So incoherent positions will result in an incoherent list
*
* @param array \$order id => rank pairs
* @param PropelPDO \$con optional connection
*
* @return boolean true if the reordering took place, false if a database problem prevented it
*/
public function reorder(array \$order, PropelPDO \$con = null)
{
if (\$con === null) {
\$con = Propel::getConnection($peerClassname::DATABASE_NAME);
}
\$con->beginTransaction();
try {
\$ids = array_keys(\$order);
\$objects = \$this->findPks(\$ids, \$con);
foreach (\$objects as \$object) {
\$pk = \$object->getPrimaryKey();
if (\$object->$columnGetter() != \$order[\$pk]) {
\$object->$columnSetter(\$order[\$pk]);
\$object->save(\$con);
}
}
\$con->commit();
return true;
} catch (PropelException \$e) {
\$con->rollback();
throw \$e;
}
}
";
}
}