Ticket #417 (in_QA enhancement)

Opened 3 years ago

Last modified 3 years ago

Nested Set support

Reported by: VexedPanda Owned by:
Priority: punt Milestone: 1.1.3
Component: Database adapter Version: 1.1 Development
Keywords: Cc:

Description

Just so it's avaliable, here's the final class files I used for nested sets. It would be cool to have these eventually included as part of core. The DB specific stuff may have a more elegant approach, I'm not sure, but I'm open to suggestions. :)

Attachments

QDatabaseNSBase.class.php Download (26.0 KB) - added by VexedPanda 3 years ago.
QMySqlDatabaseNS.class.php Download (1.1 KB) - added by VexedPanda 3 years ago.
QOracleDatabaseNS.class.php Download (1.0 KB) - added by VexedPanda 3 years ago.
QSqlServerDatabaseNS.class.php Download (1.0 KB) - added by VexedPanda 3 years ago.

Change History

Changed 3 years ago by VexedPanda

Changed 3 years ago by VexedPanda

Changed 3 years ago by VexedPanda

Changed 3 years ago by VexedPanda

Changed 3 years ago by VexedPanda

Then you need to add something like this to the bottom of each class that is for a db table with nested sets:

class QQNodeGroup extends QQNodeGroupBase {
	public function __get($strName) {
		switch ($strName) {
			case 'Ancestors':
				return new QQNodeGroupAncestors($this);
			case 'Descendants':
				return new QQNodeGroupDescendants($this);
			case 'Children':
				return new QQNodeGroupChildren($this);
//			case 'Parent':
//				return new QQNodeGroupParent($this);
			
			default:
			try {
				return parent::__get($strName);
			} catch (QCallerException $objExc) {
				$objExc->IncrementOffset();
				throw $objExc;
			}
		}
	}
}
class QQReverseReferenceNodeGroup extends QQReverseReferenceNodeGroupBase {
	public function __get($strName) {
		switch ($strName) {
			case 'Ancestors':
				return new QQNodeGroupAncestors($this);
			case 'Descendants':
				return new QQNodeGroupDescendants($this);
			case 'Children':
				return new QQNodeGroupChildren($this);
//			case 'Parent':
//				return new QQNodeGroupParent($this);
			
			default:
			try {
				return parent::__get($strName);
			} catch (QCallerException $objExc) {
				$objExc->IncrementOffset();
				throw $objExc;
			}
		}
	}
}

class QQNodeGroupAncestors extends QQAncestorsNode {
	protected $strType = 'association';
	protected $strName = '_groupancestors';
	
	protected $strTableName = 'groups';
	protected $strPrimaryKey = 'id';
	protected $strClassName = 'Group';
	
	public function __get($strName) 
	{
		try {
			return parent::__get($strName);
		} catch (QCallerException $objExc) {
			try {
				$objGroupNode = new QQNodeGroup('id', 'Id', 'integer', $this);
				return $objGroupNode->__get($strName);
			} catch (QCallerException $objExc) {
				$objExc->IncrementOffset();
				throw $objExc;
			}
		}
	}
}
class QQNodeGroupDescendants extends QQDescendantsNode {
	protected $strType = 'association';
	protected $strName = '_groupdescendants';
	
	protected $strTableName = 'groups';
	protected $strPrimaryKey = 'id';
	protected $strClassName = 'Group';
	
	public function __get($strName) 
	{
		try {
			return parent::__get($strName);
		} catch (QCallerException $objExc) {
			try {
				$objGroupNode = new QQNodeGroup('id', 'Id', 'integer', $this);
				return $objGroupNode->__get($strName);
			} catch (QCallerException $objExc) {
				$objExc->IncrementOffset();
				throw $objExc;
			}
		}
	}
}
class QQNodeGroupChildren extends QQChildrenNode {
	protected $strType = 'association';
	protected $strName = '_groupchildren';
	
	protected $strTableName = 'groups';
	protected $strPrimaryKey = 'id';
	protected $strClassName = 'Group';
	
	public function __get($strName) 
	{
		try {
			return parent::__get($strName);
		} catch (QCallerException $objExc) {
			try {
				$objGroupNode = new QQNodeGroup('id', 'Id', 'integer', $this);
				return $objGroupNode->__get($strName);
			} catch (QCallerException $objExc) {
				$objExc->IncrementOffset();
				throw $objExc;
			}
		}
	}
}
class QQNodeGroupParent extends QQParentNode {
	protected $strType = 'association';
	protected $strName = '_groupparent';
	
	protected $strTableName = 'groups';
	protected $strPrimaryKey = 'id';
	protected $strClassName = 'Group';
	
	public function __get($strName) 
	{
		try {
			return parent::__get($strName);
		} catch (QCallerException $objExc) {
			try {
				$objGroupNode = new QQNodeGroup('id', 'Id', 'integer', $this);
				return $objGroupNode->__get($strName);
			} catch (QCallerException $objExc) {
				$objExc->IncrementOffset();
				throw $objExc;
			}
		}
	}
}

Now you can do things like:

public function GetAncestors()
{
	return Group::QueryArray(
		QQ::Equal(QQN::Group()->Descendants->Lft, $this->Lft), 
		array(QQ::OrderBy(QQN::Group()->Lft))
		);
}

Changed 3 years ago by alex94040

  • status changed from new to in_QA

Vexed, what would it take to make this a plugin? It looks really interesting, but we need to wrap it into a nice "box with a bow on top" so that people can install it/use it easily.

Does the plugin infrastructure support everything necessary for this? If not, we should make additions to the infrastructure.

Changed 3 years ago by VexedPanda

Honestly I still haven't had much time to look at the plugin framework. My guess is that this should be fine, since it's just an extra class. Having the users update their data objects with the new node types/etc is probably going to be impossible to automate though.

Changed 3 years ago by alex94040

Vexed, some thoughts for you:
- Plugin framework is ridiculously easy. Lots of developers with very little QCubed experience built plugins. It should take you exactly no time to get used to it :-)
- I think it's OK for the first release of the plugin to say "copy this thing over at the bottom of your class file"
- Maybe in vNext, we could add a codegen_settings.xml setting that allows the user to specify tables with nested sets? Maybe a prefix/postfix for such tables, or just an explicit list of tables? If the user has such a table(or tables) specified, we could write codegen templates to spit out the code automatically during code generation.

What do you think?

Changed 3 years ago by VexedPanda

Agreed, something like a _ns tablename postfix would probably handle this nicely.

Unfortunately, altering codegen_settings, to identify arbitrary postfixes defined by plugins, as well as the ability to provide a custom code generation template (that checks the table name for this postfix and includes the new code) is definitely something that will require some new infrastructure.

Changed 3 years ago by alex94040

Thoughts for you:
- if today you add a section to codegen_settings.xml by hand that the QCodegen does not understand, will it crash or ignore it?
- if it ignores it, we can take a safe first step. Upon installation of the plugin, we can instruct the user to add something to their codegen settings file. That would be a reasonable start.
- in vNext, we can create infrastructure for modifying the codegen XML file from the plugins (we'll probably do something like an "include") - but this will only be necessary if many plugins require this kind of infrastructure.

What do you think?

Changed 3 years ago by VexedPanda

Sounds good to me. :)

Changed 3 years ago by alex94040

  • milestone changed from 2.0 to 1.1.2

Changed 3 years ago by alex94040

Ping-ping. Any desire to finish this up, Vexed?

Changed 3 years ago by VexedPanda

Less so than the other tickets that are being worked on at the moment, I'm afraid. It's still on my todo list, but a fairly low priority unless someone starts begging for it.

Changed 3 years ago by alex94040

  • milestone changed from 1.1.2 to 1.1.3
Note: See TracTickets for help on using tickets.