diff --git a/src/NestedSetSchemaInterface.php b/src/NestedSetSchemaInterface.php new file mode 100644 index 00000000..b9d7431d --- /dev/null +++ b/src/NestedSetSchemaInterface.php @@ -0,0 +1,20 @@ +connection = $connection; + if (!$this->validTableName($tableName)) { + throw new \InvalidArgumentException("Table name must match the regex " . self::VALID_TABLE_REGEX); + } + $this->tableName = $tableName; + } + + /** + * Checks if the table name is valid. + * + * Table names must: + * - start with a letter + * - only contain letters, numbers, and underscores + * - be maximum 64 characters. + * + * @param string $tableName + * The table name. + * + * @return bool + * TRUE if the table name is valid. + */ + protected function validTableName($tableName) { + return (bool) preg_match(self::VALID_TABLE_REGEX, $tableName); + } + +} diff --git a/src/Storage/DbalNestedSet.php b/src/Storage/DbalNestedSet.php index d19c9eeb..ef6d1785 100644 --- a/src/Storage/DbalNestedSet.php +++ b/src/Storage/DbalNestedSet.php @@ -2,50 +2,14 @@ namespace PNX\NestedSet\Storage; -use Doctrine\DBAL\Connection; use Exception; -use PNX\NestedSet\Node; use PNX\NestedSet\NestedSetInterface; +use PNX\NestedSet\Node; /** * Provides a DBAL implementation of a Tree. */ -class DbalNestedSet implements NestedSetInterface { - - /** - * The regex for validating table names. - */ - const VALID_TABLE_REGEX = '/^[a-zA-Z]\w{1,64}$/'; - - /** - * The database connection. - * - * @var \Doctrine\DBAL\Connection - */ - protected $connection; - - /** - * The table name to use for storing the nested set. - * - * @var string - */ - protected $tableName; - - /** - * DbalTree constructor. - * - * @param \Doctrine\DBAL\Connection $connection - * The database connection. - * @param string $tableName - * (optional) The table name to use. - */ - public function __construct(Connection $connection, $tableName = 'tree') { - $this->connection = $connection; - if (!preg_match(self::VALID_TABLE_REGEX, $tableName)) { - throw new \InvalidArgumentException("Table name must match the regex " . self::VALID_TABLE_REGEX); - } - $this->tableName = $tableName; - } +class DbalNestedSet extends BaseDbalStorage implements NestedSetInterface { /** * {@inheritdoc} @@ -169,10 +133,10 @@ public function findDescendants(Node $node, $depth = 0) { $query->select('child.id', 'child.revision_id', 'child.left_pos', 'child.right_pos', 'child.depth') ->from($this->tableName, 'child') ->from($this->tableName, 'parent') - ->where('child.left_pos > parent.left_pos') - ->andWhere('child.right_pos < parent.right_pos') ->andWhere('parent.id = :id') ->andWhere('parent.revision_id = :revision_id') + ->andwhere('child.left_pos > parent.left_pos') + ->andWhere('child.right_pos < parent.right_pos') ->setParameter(':id', $node->getId()) ->setParameter(':revision_id', $node->getRevisionId()); if ($depth > 0) { diff --git a/src/Storage/DbalNestedSetSchema.php b/src/Storage/DbalNestedSetSchema.php new file mode 100644 index 00000000..d8e57b66 --- /dev/null +++ b/src/Storage/DbalNestedSetSchema.php @@ -0,0 +1,46 @@ +createTable($this->tableName); + $tree->addColumn("id", "integer", ["unsigned" => TRUE]); + $tree->addColumn("revision_id", "integer", ["unsigned" => TRUE]); + $tree->addColumn("left_pos", "integer", ["unsigned" => TRUE]); + $tree->addColumn("right_pos", "integer", ["unsigned" => TRUE]); + $tree->addColumn("depth", "integer", ["unsigned" => TRUE]); + + $tree->setPrimaryKey(['id', 'revision_id']); + $tree->addIndex(['id', 'revision_id', 'left_pos', 'right_pos', 'depth']); + $tree->addIndex(['left_pos', 'right_pos']); + $tree->addIndex(['right_pos']); + + foreach ($schema->toSql($this->connection->getDatabasePlatform()) as $sql) { + $this->connection->exec($sql); + } + } + + /** + * {@inheritdoc} + */ + public function drop() { + $schema = new Schema(); + $schema->dropTable($this->tableName); + foreach ($schema->toSql($this->connection->getDatabasePlatform()) as $sql) { + $this->connection->exec($sql); + } + } + +} diff --git a/tests/Functional/DbalNestedSetTest.php b/tests/Functional/DbalNestedSetTest.php index 4ab8152a..65b794e1 100644 --- a/tests/Functional/DbalNestedSetTest.php +++ b/tests/Functional/DbalNestedSetTest.php @@ -5,9 +5,9 @@ use Console_Table; use Doctrine\DBAL\Configuration; use Doctrine\DBAL\DriverManager; -use Doctrine\DBAL\Schema\Schema; use PNX\NestedSet\Node; use PNX\NestedSet\Storage\DbalNestedSet; +use PNX\NestedSet\Storage\DbalNestedSetSchema; /** * Tests the Dbal Tree implementation. @@ -37,18 +37,25 @@ class DbalNestedSetTest extends \PHPUnit_Framework_TestCase { */ protected $tableName = 'nested_set'; + /** + * The nested set schema. + * + * @var \PNX\NestedSet\Storage\DbalNestedSetSchema + */ + protected $schema; + /** * {@inheritdoc} */ protected function setUp() { - if ($this->connection === NULL) { - $this->connection = DriverManager::getConnection([ - 'url' => 'sqlite:///:memory:', - ], new Configuration()); - $this->createTable(); - $this->loadTestData(); - $this->nestedSet = new DbalNestedSet($this->connection, $this->tableName); - } + $this->connection = DriverManager::getConnection([ + 'url' => 'sqlite:///:memory:', + ], new Configuration()); + + $this->schema = new DbalNestedSetSchema($this->connection, $this->tableName); + $this->schema->create(); + $this->loadTestData(); + $this->nestedSet = new DbalNestedSet($this->connection, $this->tableName); } /** @@ -423,34 +430,6 @@ public function testValidateTableInvalidFirstChars() { $this->nestedSet = new DbalNestedSet($this->connection, "1abc"); } - /** - * Drops the table. - */ - protected function dropTable() { - $schema = new Schema(); - $schema->dropTable("tree"); - foreach ($schema->toSql($this->connection->getDatabasePlatform()) as $sql) { - $this->connection->exec($sql); - } - } - - /** - * Creates the table. - */ - protected function createTable() { - $schema = new Schema(); - $tree = $schema->createTable($this->tableName); - $tree->addColumn("id", "integer", ["unsigned" => TRUE]); - $tree->addColumn("revision_id", "integer", ["unsigned" => TRUE]); - $tree->addColumn("left_pos", "integer", ["unsigned" => TRUE]); - $tree->addColumn("right_pos", "integer", ["unsigned" => TRUE]); - $tree->addColumn("depth", "integer", ["unsigned" => TRUE]); - - foreach ($schema->toSql($this->connection->getDatabasePlatform()) as $sql) { - $this->connection->exec($sql); - } - } - /** * Loads the test data. */