Overview

Packages

  • Actions
  • Codegen
  • Controls
    • Base
  • DatabaseAdapters
  • Events
  • None
  • Sessions
  • Tests

Classes

  • AbstractControl_CodeGenerator
  • AjaxTimingForm
  • BasicForm
  • BasicOrmTests
  • BBCodeParser
  • CacheTests
  • CalculatorForm
  • CalculatorWidget
  • ComplexColumn
  • DataRepeaterExample
  • Event
  • ExampleCheckColumn1
  • ExampleCheckColumn2
  • ExampleForm
  • Examples
  • ExampleService
  • ExamplesForm
  • ExampleSingleForm
  • ExpandAsArrayTests
  • HtmlJqDoc
  • InjectForm
  • JavaScriptHelper
  • JqAttributes
  • JqControlGen
  • JqDoc
  • JqIcon
  • Method
  • ModelConnectorTests
  • MyControl
  • MyQSlider_ChangeEvent
  • NavPanel
  • NestedTabForm
  • Option
  • Order
  • PaginatorForm
  • ParamsForm
  • PersistentExampleForm
  • PersonEditPanel
  • PluginEditForm
  • PluginManagerForm
  • Project
  • ProjectEditPanel
  • ProjectListForm
  • ProjectPickerListBox
  • ProjectViewPanel
  • QAbstractCacheProvider
  • QAbstractHtmlTableColumn
  • QAbstractHtmlTableDataColumn
  • QAccordion
  • QAccordion_ActivateEvent
  • QAccordion_BeforeActivateEvent
  • QAccordion_CreateEvent
  • QAccordionBase
  • QAccordionGen
  • QAction
  • QActionControl
  • QAjaxAction
  • QAjaxControlAction
  • QAjaxResponse
  • QAlertAction
  • QApplication
  • QApplicationBase
  • QArchive
  • QAutocomplete
  • QAutocomplete_ChangeEvent
  • QAutocomplete_CloseEvent
  • QAutocomplete_CodeGenerator
  • QAutocomplete_CreateEvent
  • QAutocomplete_FocusEvent
  • QAutocomplete_OpenEvent
  • QAutocomplete_ResponseEvent
  • QAutocomplete_SearchEvent
  • QAutocomplete_SelectEvent
  • QAutocomplete_SourceEvent
  • QAutocompleteBase
  • QAutocompleteBase_CodeGenerator
  • QAutocompleteGen
  • QAutocompleteListItem
  • QBackspaceKeyEvent
  • QBaseClass
  • QBlockControl
  • QBlurControlAction
  • QBlurEvent
  • QBorderCollapse
  • QBorderStyle
  • QBrowserType
  • QButton
  • QButtonBase
  • QCache
  • QCacheDeleteAction
  • QCacheDeleteAllAction
  • QCacheProviderAPC
  • QCacheProviderLocalMemory
  • QCacheProviderLocalMemoryTest
  • QCacheProviderMemcache
  • QCacheProviderNoCache
  • QCacheProviderProxy
  • QCacheSetAction
  • QCalendar
  • QCalendarType
  • QCallType
  • QCausesValidation
  • QCellClickEvent
  • QChangeEvent
  • QCheckBox
  • QCheckBox_CodeGenerator
  • QCheckBoxBase_CodeGenerator
  • QCheckBoxLegacyColumn
  • QCheckBoxList
  • QCheckBoxList_CodeGenerator
  • QCheckBoxListBase_CodeGenerator
  • QClickEvent
  • QCodeGen
  • QCodeGenBase
  • QConfirmAction
  • QContextMenuEvent
  • QControl
  • QControl_CodeGenerator
  • QControlBase
  • QControlBase_CodeGenerator
  • QControlBaseTests
  • QControlCategoryType
  • QControlGrouping
  • QControlLabel
  • QControlProxy
  • QConvertNotation
  • QConvertNotationBase
  • QCrossScripting
  • QCryptography
  • QCss
  • QCssAction
  • QCssClassAction
  • QCssTests
  • QCsvTextBox
  • QCursor
  • QDatabaseBase
  • QDatabaseCodeGen
  • QDatabaseFieldBase
  • QDatabaseFieldType
  • QDatabaseForeignKey
  • QDatabaseIndex
  • QDatabaseResultBase
  • QDatabaseRowBase
  • QDatabaseTests
  • QDataGrid
  • QDataGrid_CheckBoxColumn
  • QDataGrid_CodeGenerator
  • QDataGrid_SortEvent
  • QDataGridBase
  • QDataGridBase_CodeGenerator
  • QDataGridLegacy
  • QDataGridLegacyBase
  • QDataGridLegacyColumn
  • QDataGridLegacyRow
  • QDataGridLegacyRowStyle
  • QDataRepeater
  • QDatepicker
  • QDatepicker_BeforeShowDayEvent
  • QDatepicker_BeforeShowEvent
  • QDatepicker_CalculateWeekEvent
  • QDatepicker_ChangeMonthYearEvent
  • QDatepicker_CloseEvent
  • QDatepicker_SelectEvent
  • QDatepicker_SelectEvent2
  • QDatepickerBase
  • QDatepickerBox
  • QDatepickerBox_BeforeShowDayEvent
  • QDatepickerBox_BeforeShowEvent
  • QDatepickerBox_CalculateWeekEvent
  • QDatepickerBox_ChangeMonthYearEvent
  • QDatepickerBox_CloseEvent
  • QDatepickerBox_CodeGenerator
  • QDatepickerBox_SelectEvent
  • QDatepickerBoxBase
  • QDatepickerBoxBase_CodeGenerator
  • QDatepickerBoxGen
  • QDatepickerGen
  • QDateTime
  • QDateTimePicker
  • QDateTimePicker_CodeGenerator
  • QDateTimePickerBase_CodeGenerator
  • QDateTimePickerFormat
  • QDateTimePickerType
  • QDateTimeSpan
  • QDateTimeTests
  • QDateTimeTextBox
  • QDbBackedFormStateHandler
  • QDbBackedSessionHandler
  • QDialog
  • QDialog_BeforeCloseEvent
  • QDialog_ButtonEvent
  • QDialog_CloseEvent
  • QDialog_CreateEvent
  • QDialog_DragEvent
  • QDialog_DragStartEvent
  • QDialog_DragStopEvent
  • QDialog_FocusEvent
  • QDialog_OpenEvent
  • QDialog_ResizeEvent
  • QDialog_ResizeStartEvent
  • QDialog_ResizeStopEvent
  • QDialogBase
  • QDialogBox
  • QDialogGen
  • QDisplayStyle
  • QDoubleClickEvent
  • QDownArrowKeyEvent
  • QDragDropEvent
  • QDraggable
  • QDraggable_CreateEvent
  • QDraggable_DragEvent
  • QDraggable_StartEvent
  • QDraggable_StopEvent
  • QDraggableBase
  • QDraggableGen
  • QDroppable
  • QDroppable_ActivateEvent
  • QDroppable_CreateEvent
  • QDroppable_DeactivateEvent
  • QDroppable_DropEvent
  • QDroppable_OutEvent
  • QDroppable_OverEvent
  • QDroppableBase
  • QDroppableGen
  • QDropZoneGrouping
  • QEmailAttachment
  • QEmailMessage
  • QEmailServer
  • QEmailStringAttachment
  • QEmailTextBox
  • QEnterKeyEvent
  • QErrorAttribute
  • QEscapeKeyEvent
  • QEvent
  • QFieldset
  • QFile
  • QFileAsset
  • QFileAssetBase
  • QFileAssetDialog
  • QFileAssetType
  • QFileControl
  • QFileFormStateHandler
  • QFilterType
  • QFloatTextBox
  • QFloatTextBox_CodeGenerator
  • QFloatTextBoxBase_CodeGenerator
  • QFocusControlAction
  • QFocusEvent
  • QFocusInEvent
  • QFocusOutEvent
  • QFolder
  • QFontFamily
  • QForm
  • QFormBase
  • QFormGen
  • QFormStateHandler
  • QGridLines
  • QHideCalendarAction
  • QHideDialog
  • QHideDialogBox
  • QHListControl
  • QHListItem
  • QHorizontalAlign
  • QHtml
  • QHtmlAttributeManager
  • QHtmlAttributeManagerBase
  • QHtmlReporter
  • QHtmlTable
  • QHtmlTable_CodeGenerator
  • QHtmlTableBase
  • QHtmlTableCallableColumn
  • QHtmlTableCheckBoxColumn
  • QHtmlTableCheckBoxColumn_ClickEvent
  • QHtmlTableIndexedColumn
  • QHtmlTableLinkColumn
  • QHtmlTableNodeColumn
  • QHtmlTablePropertyColumn
  • QI18n
  • QI18nTests
  • QImageBase
  • QImageBrowser
  • QImageBrowserBase
  • QImageBrowserNav
  • QImageBrowserThumbnails
  • QImageButton
  • QImageControl
  • QImageControlBase
  • QImageFileAsset
  • QImageLabel
  • QImageLabelBase
  • QImageRollover
  • QImageType
  • QIndex
  • QInformixPdoDatabase
  • QInformixPdoDatabaseField
  • QInformixPdoDatabaseResult
  • QInformixPdoDatabaseRow
  • QInputEvent
  • QInstallationValidationResult
  • QInstallationValidator
  • QIntegerTextBox
  • QIntegerTextBox_CodeGenerator
  • QIntegerTextBoxBase_CodeGenerator
  • QJavaScriptAction
  • QJQAction
  • QJQBounceAction
  • QJqButton
  • QJqButton_CreateEvent
  • QJqButtonBase
  • QJqButtonGen
  • QJqCheckBox
  • QJqCheckBox_CreateEvent
  • QJqCheckBoxBase
  • QJqCheckBoxGen
  • QJQHideAction
  • QJQHideEffectAction
  • QJQHighlightAction
  • QJQPulsateAction
  • QJqRadioButton
  • QJqRadioButton_CreateEvent
  • QJqRadioButtonBase
  • QJqRadioButtonGen
  • QJQShakeAction
  • QJQShowAction
  • QJQShowEffectAction
  • QJQSizeAction
  • QJQToggleAction
  • QJQToggleEffectAction
  • QJQTransferAction
  • QJqUiEvent
  • QJqUiPropertyEvent
  • QJsClosure
  • QJsFunction
  • QJsNoQuoteKey
  • QJsParameterList
  • QJsPriority
  • QJsTimer
  • QJsTimerBase
  • QJsVarName
  • QKeyDownEvent
  • QKeyPressEvent
  • QKeyUpEvent
  • QLabel
  • QLabel_CodeGenerator
  • QLabelBase_CodeGenerator
  • QLexer
  • QLinkButton
  • QListBox
  • QListBox_CodeGenerator
  • QListBoxBase
  • QListBoxBase_CodeGenerator
  • QListControl
  • QListControl_CodeGenerator
  • QListControlBase_CodeGenerator
  • QListItem
  • QListItemBase
  • QListItemStyle
  • QManyToManyReference
  • QMenu
  • QMenu_BlurEvent
  • QMenu_CreateEvent
  • QMenu_FocusEvent
  • QMenu_SelectEvent
  • QMenuBase
  • QMenuGen
  • QMimeType
  • QModelConnectorArgumentType
  • QModelConnectorCreateType
  • QModelConnectorEditDlg
  • QModelConnectorOptions
  • QModelConnectorParam
  • QMouseDownEvent
  • QMouseEnterEvent
  • QMouseLeaveEvent
  • QMouseMoveEvent
  • QMouseOutEvent
  • QMouseOverEvent
  • QMouseUpEvent
  • QMultiLevelCacheProvider
  • QMySqlDatabase
  • QMySqlDatabaseField
  • QMySqlDatabaseResult
  • QMySqli5ClusterDatabase
  • QMySqli5Database
  • QMySqli5DatabaseField
  • QMySqli5DatabaseResult
  • QMySqliDatabase
  • QMySqliDatabaseField
  • QMySqliDatabaseResult
  • QMySqliDatabaseRow
  • QNoScriptAjaxAction
  • QNumericTextBox
  • QOnEvent
  • QOracleDatabase
  • QOracleDatabaseField
  • QOracleDatabaseResult
  • QOracleDatabaseRow
  • QOrderedListType
  • QOverflow
  • QPaginatedControl
  • QPaginator
  • QPaginatorBase
  • QPanel
  • QPartialQueryBuilder
  • QPdoDatabase
  • QPdoDatabaseResult
  • QPgConditionILike
  • QPgConditionJsonContains
  • QPgQ
  • QPosition
  • QPostgreSqlDatabase
  • QPostgreSqlDatabaseField
  • QPostgreSqlDatabaseResult
  • QPostgreSqlDatabaseRow
  • QPostgreSqlPdoDatabase
  • QPostgreSqlPdoDatabaseField
  • QPostgreSqlPdoDatabaseResult
  • QPostgreSqlPdoDatabaseRow
  • QProgressbar
  • QProgressbar_ChangeEvent
  • QProgressbar_CompleteEvent
  • QProgressbar_CreateEvent
  • QProgressbarBase
  • QProgressbarGen
  • QQ
  • QQAggregationClause
  • QQAliasTests
  • QQAssociationNode
  • QQAverage
  • QQClause
  • QQColumnNode
  • QQCondition
  • QQConditionAll
  • QQConditionAnd
  • QQConditionBetween
  • QQConditionComparison
  • QQConditionEqual
  • QQConditionExists
  • QQConditionGreaterOrEqual
  • QQConditionGreaterThan
  • QQConditionIn
  • QQConditionIsNotNull
  • QQConditionIsNull
  • QQConditionLessOrEqual
  • QQConditionLessThan
  • QQConditionLike
  • QQConditionLogical
  • QQConditionNone
  • QQConditionNot
  • QQConditionNotBetween
  • QQConditionNotEqual
  • QQConditionNotExists
  • QQConditionNotIn
  • QQConditionNotLike
  • QQConditionOr
  • QQCount
  • QQDistinct
  • QQExpand
  • QQExpandAsArray
  • QQExpandVirtualNode
  • QQFuncTests
  • QQFunctionNode
  • QQGroupBy
  • QQHavingClause
  • QQLimitInfo
  • QQMathNode
  • QQMathOpTests
  • QQMaximum
  • QQMinimum
  • QQNamedValue
  • QQNode
  • QQNoParentNode
  • QQOrderBy
  • QQReverseReferenceNode
  • QQSelect
  • QQSubQueryCountNode
  • QQSubQueryNode
  • QQSubQuerySqlNode
  • QQSum
  • QQTableNode
  • QQuery
  • QQueryBuilder
  • QQueryExpansion
  • QQVirtualNode
  • QRadioButton
  • QRadioButtonList
  • QRadioButtonList_CodeGenerator
  • QRadioButtonListBase_CodeGenerator
  • QRedirectAction
  • QReference
  • QRegex
  • QRegisterClickPositionAction
  • QRepeatDirection
  • QRequestMode
  • QResetTimerAction
  • QResizable
  • QResizable_CreateEvent
  • QResizable_ResizeEvent
  • QResizable_StartEvent
  • QResizable_StopEvent
  • QResizableBase
  • QResizableGen
  • QResizeHandleDirection
  • QRestServiceCodeGen
  • QReverseReference
  • QRssCategory
  • QRssFeed
  • QRssImage
  • QRssItem
  • QSampleControl
  • QSampleTranslation
  • QSelectable
  • QSelectable_CreateEvent
  • QSelectable_SelectedEvent
  • QSelectable_SelectingEvent
  • QSelectable_StartEvent
  • QSelectable_StopEvent
  • QSelectable_UnselectedEvent
  • QSelectable_UnselectingEvent
  • QSelectableBase
  • QSelectableGen
  • QSelectControlAction
  • QSelectEvent
  • QSelectionMode
  • QSelectMenu
  • QSelectMenu_ChangeEvent
  • QSelectMenu_CloseEvent
  • QSelectMenu_CreateEvent
  • QSelectMenu_FocusEvent
  • QSelectMenu_OpenEvent
  • QSelectMenu_SelectEvent
  • QSelectMenuBase
  • QSelectMenuGen
  • QServerAction
  • QServerControlAction
  • QSessionFormStateHandler
  • QSetValueAction
  • QShowCalendarAction
  • QShowDialog
  • QShowDialogBox
  • QSlider
  • QSlider_ChangeEvent
  • QSlider_CodeGenerator
  • QSlider_CreateEvent
  • QSlider_SlideEvent
  • QSlider_StartEvent
  • QSlider_StopEvent
  • QSliderBase
  • QSliderBase_CodeGenerator
  • QSliderGen
  • QSoapMethod
  • QSoapParameter
  • QSoapService
  • QSortable
  • QSortable_ActivateEvent
  • QSortable_BeforeStopEvent
  • QSortable_ChangeEvent
  • QSortable_CreateEvent
  • QSortable_DeactivateEvent
  • QSortable_OutEvent
  • QSortable_OverEvent
  • QSortable_ReceiveEvent
  • QSortable_RemoveEvent
  • QSortable_SortEvent
  • QSortable_StartEvent
  • QSortable_StopEvent
  • QSortable_UpdateEvent
  • QSortableBase
  • QSortableGen
  • QSpinner
  • QSpinner_ChangeEvent
  • QSpinner_CreateEvent
  • QSpinner_SpinEvent
  • QSpinner_StartEvent
  • QSpinner_StopEvent
  • QSpinnerBase
  • QSpinnerGen
  • QSqlColumn
  • QSqLite3PdoDatabase
  • QSqLite3PdoDatabaseField
  • QSqLite3PdoDatabaseResult
  • QSqLite3PdoDatabaseRow
  • QSqlServer2005Database
  • QSqlServer2005DatabaseField
  • QSqlServer2005DatabaseResult
  • QSqlServer2005DatabaseRow
  • QSqlServerDatabase
  • QSqlServerDatabaseField
  • QSqlServerDatabaseResult
  • QSqlServerDatabaseRow
  • QSqlTable
  • QStack
  • QStopPropagationAction
  • QString
  • QStringTest
  • QTabKeyEvent
  • QTabs
  • QTabs_ActivateEvent
  • QTabs_BeforeActivateEvent
  • QTabs_BeforeLoadEvent
  • QTabs_CreateEvent
  • QTabs_LoadEvent
  • QTabsBase
  • QTabsGen
  • QTag
  • QTagStyler
  • QTerminateAction
  • QTestControl
  • QTestForm
  • QTextAlign
  • QTextBox
  • QTextBox_CodeGenerator
  • QTextBoxBase
  • QTextBoxBase_CodeGenerator
  • QTextMode
  • QTimer
  • QTimerExpiredEvent
  • QTimerTests
  • QToggleCssClassAction
  • QToggleDisplayAction
  • QToggleEnableAction
  • QTranslationPoParser
  • QTreeNav
  • QTreeNavItem
  • QType
  • QTypeTable
  • QTypeTests
  • QUnitTestCaseBase
  • QUnorderedListStyle
  • QUpArrowKeyEvent
  • QUrlTextBox
  • QVerticalAlign
  • QVirtualAttributeColumn
  • QWaitIcon
  • QWatcher
  • QWatcherBase
  • QWatcherCache
  • QWatcherDB
  • QWatcherNone
  • QWriteBox
  • RecordsSummary
  • RefreshForm
  • SampleComposite
  • SampleForm
  • SelectableLabel
  • SelectForm
  • SpeedForm
  • TestImageBrowser
  • UrlForm

Interfaces

  • ICacheAction
  • QDataList_CodeGenerator_Interface
  • QTranslationBase

Traits

  • QDataBinder
  • QListItemManager
  • QModelTrait

Exceptions

  • QCallerException
  • QCrossScriptingException
  • QCryptographyException
  • QDatabaseExceptionBase
  • QDataBindException
  • QDateTimeNullException
  • QEmailException
  • QIndexOutOfRangeException
  • QInformixPdoDatabaseException
  • QInvalidCastException
  • QInvalidFormStateException
  • QMySqliDatabaseException
  • QOptimisticLockingException
  • QOracleDatabaseException
  • QPdoDatabaseException
  • QPoParserException
  • QPostgreSqlDatabaseException
  • QPostgreSqlPdoDatabaseException
  • QRemoteAdminDeniedException
  • QSqLite3PdoDatabaseException
  • QSqlServer2005DatabaseException
  • QSqlServerDatabaseException
  • QUndefinedMethodException
  • QUndefinedPrimaryKeyException
  • QUndefinedPropertyException

Functions

  • __database_check_error
  • __QForm_EvaluateTemplate_ObHandler
  • _b
  • _indent
  • _nl
  • _p
  • _r
  • _t
  • _tp
  • _tr
  • array_trim
  • beginsWith
  • CamelCaseFromDash
  • CastToInt
  • DataGridEvalHandleError
  • DisplayMonospacedText
  • endsWith
  • GO_BACK
  • jq_anytime_gen
  • jq_control_gen
  • jq_inc_gen
  • jq_indent
  • PrintExplainStatement
  • PrintInstructions
  • QcubedHandleCodeGenParseError
  • QcubedHandleError
  • QcubedHandleException
  • QCubedShutdown
  • QDateTimeErrorHandler
  • trimOffEnd
  • trimOffFront
  • Overview
  • Package
  • Class
   1: <?php
   2: 
   3:     /*
   4:     *   QQuery.class.php
   5:     *
   6:     *   Classes to simplify the creation of SQL statements.
   7:     */
   8: 
   9: 
  10:     /**
  11:      * The abstract QQNode base class. This represents an "object" in a SQL join tree. There are a number of different subclasses of
  12:      * the QQNode, depending on the kind of object represented. The top of the join tree is generally a table node, and
  13:      * the bottom is generally a column node, but that depends on the context in which the node is being used.
  14:      *
  15:      * The properties begin with underscores to prevent name conflicts with codegenerated subclasses.
  16:      *
  17:      * @property-read QQNode $_ParentNode       // Parent object in tree.
  18:      * @property-read string $_Name             // Default SQL name in query, or default alias
  19:      * @property-read string $_Alias                // Actual alias. Usually the name, unless changed by QQ::Alias() call
  20:      * @property-read string $_PropertyName     // The name as used in PHP
  21:      * @property-read string $_Type             // The type of object. A SQL type if referring to a column.
  22:      * @property-read string $_RootTableName        // The name of the table at the top of the tree. Rednundant, since it could be found be following the chain.
  23:      * @property-read string $_TableName            // The name of the table associated with this node, if its not a column node.
  24:      * @property-read string $_PrimaryKey
  25:      * @property-read string $_ClassName
  26:      * @property-read QQNode $_PrimaryKeyNode
  27:      * @property bool $ExpandAsArray True if this node should be array expanded.
  28:      * @property-read bool $IsType Is a type table node. For association type arrays.
  29:      */
  30:     abstract class QQNode extends QBaseClass {
  31:         /** @var null|QQNode|bool  */
  32:         protected $objParentNode;
  33:         /** @var  string Type node. SQL type or table type*/
  34:         protected $strType;
  35:         /** @var  string SQL Name of related object in the database */
  36:         protected $strName;
  37:         /** @var  string Alias, if one was assigned using QQ::Alias(). Otherwise, same as name. */
  38:         protected $strAlias;
  39:         /** @var  string resolved alias that includes parent join tables. */
  40:         protected $strFullAlias;
  41:         /** @var  string PHP property name of the related PHP object */
  42:         protected $strPropertyName;
  43:         /** @var  string copy of the root table name at the top of the node tree. */
  44:         protected $strRootTableName;
  45:         /** @var  string name of SQL table associated with this node. Generally set by subclasses. */
  46:         protected $strTableName;
  47: 
  48:         /** @var  string SQL primary key, for nodes that have primary keys */
  49:         protected $strPrimaryKey;
  50:         /** @var  string PHP class name */
  51:         protected $strClassName;
  52: 
  53:         // used by expansion nodes
  54:         /** @var  bool True if this is an expand as array node point */
  55:         protected $blnExpandAsArray;
  56:         /** @var  QQNode[] the array of child nodes if this is an expand as array point */
  57:         protected $objChildNodeArray;
  58:         /** @var  bool True if this is a Type node */
  59:         protected $blnIsType;
  60: 
  61:         abstract public function Join(QQueryBuilder $objBuilder, $blnExpandSelection = false, QQCondition $objJoinCondition = null, QQSelect $objSelect = null);
  62: 
  63:         /**
  64:          * Return the variable type. Should be a QDatabaseFieldType enum.
  65:          * @return string
  66:          */
  67:         public function GetType() {
  68:             return $this->strType;
  69:         }
  70: 
  71:         /**
  72:          * Change the alias of the node, primarily for joining the same table more than once.
  73:          *
  74:          * @param $strAlias
  75:          * @throws Exception
  76:          * @throws QCallerException
  77:          */
  78:         public function SetAlias($strAlias) {
  79:             if ($this->strFullAlias) {
  80:                 throw new Exception ("You cannot set an alias on a node after you have used it in a query. See the examples doc. You must set the alias while creating the node.");
  81:             }
  82:             try {
  83:                 // Changing the alias of the node. Must change pointers to the node too.
  84:                 $strNewAlias = QType::Cast($strAlias, QType::String);
  85:                 if ($this->objParentNode) {
  86:                     assert (is_object($this->objParentNode));
  87:                     unset($this->objParentNode->objChildNodeArray[$this->strAlias]);
  88:                     $this->objParentNode->objChildNodeArray[$strNewAlias] = $this;
  89:                 }
  90:                 $this->strAlias = $strNewAlias;
  91:             } catch (QCallerException $objExc) {
  92:                 $objExc->IncrementOffset();
  93:                 throw $objExc;
  94:             }
  95:         }
  96: 
  97:         /**
  98:          * Aid to generating full aliases. Recursively gets and sets the parent alias, eventually creating, caching and returning
  99:          * an alias for itself.
 100:          * @return string
 101:          */
 102:         public function FullAlias() {
 103:             if ($this->strFullAlias) {
 104:                 return $this->strFullAlias;
 105:             } else {
 106:                 assert (!empty($this->strAlias));   // Alias should always be set by default
 107:                 if ($this->objParentNode) {
 108:                     assert (is_object($this->objParentNode));
 109:                     return $this->objParentNode->FullAlias() . '__' . $this->strAlias;
 110:                 }
 111:                 else {
 112:                     return $this->strAlias;
 113:                 }
 114:             }
 115:         }
 116: 
 117:         /**
 118:          * Returns the fields in this node. Assumes its a table node.
 119:          * @return string[]
 120:          */
 121:         public function Fields() {return [];}
 122: 
 123:         /**
 124:          * Returns the primary key fields in this node. Assumes its a table node.
 125:          * @return string[]
 126:          */
 127:         public function PrimaryKeyFields() {return [];}
 128: 
 129:         /**
 130:          * Merges a node tree into this node, building the child nodes. The node being received
 131:          * is assumed to be specially built node such that only one child node exists, if any,
 132:          * and the last node in the chain is designated as array expansion. The goal of all of this
 133:          * is to set up a node chain where intermediate nodes can be designated as being array
 134:          * expansion nodes, as well as the leaf nodes.
 135:          *
 136:          * @param QQNode $objNewNode
 137:          * @throws QCallerException
 138:          */
 139:         public function _MergeExpansionNode (QQNode $objNewNode) {
 140:             if (!$objNewNode || empty($objNewNode->objChildNodeArray)) {
 141:                 return;
 142:             }
 143:             if ($objNewNode->strName != $this->strName) {
 144:                 throw new QCallerException('Expansion node tables must match.');
 145:             }
 146: 
 147:             if (!$this->objChildNodeArray) {
 148:                 $this->objChildNodeArray = $objNewNode->objChildNodeArray;
 149:             } else {
 150:                 $objChildNode = reset($objNewNode->objChildNodeArray);
 151:                 if (isset ($this->objChildNodeArray[$objChildNode->strAlias])) {
 152:                     if ($objChildNode->blnExpandAsArray) {
 153:                         $this->objChildNodeArray[$objChildNode->strAlias]->blnExpandAsArray = true;
 154:                         // assume this is a leaf node, so don't follow any more.
 155:                     }
 156:                     else {
 157:                         $this->objChildNodeArray[$objChildNode->strAlias]->_MergeExpansionNode ($objChildNode);
 158:                     }
 159:                 } else {
 160:                     $this->objChildNodeArray[$objChildNode->strAlias] = $objChildNode;
 161:                 }
 162:             }
 163:         }
 164: 
 165:         /**
 166:          * Puts the "Select" clause fields for this node into builder.
 167:          *
 168:          * @param QQueryBuilder $objBuilder
 169:          * @param null|string $strPrefix
 170:          * @param null|QQSelect $objSelect
 171:          */
 172:         public function PutSelectFields($objBuilder, $strPrefix = null, $objSelect = null) {
 173:             if ($strPrefix) {
 174:                 $strTableName = $strPrefix;
 175:                 $strAliasPrefix = $strPrefix . '__';
 176:             } else {
 177:                 $strTableName = $this->strTableName;
 178:                 $strAliasPrefix = '';
 179:             }
 180: 
 181:             if ($objSelect) {
 182:                 if (!$objSelect->SkipPrimaryKey()) {
 183:                     $strFields = $this->PrimaryKeyFields();
 184:                     foreach ($strFields as $strField) {
 185:                         $objBuilder->AddSelectItem($strTableName, $strField, $strAliasPrefix . $strField);
 186:                     }
 187:                 }
 188:                 $objSelect->AddSelectItems($objBuilder, $strTableName, $strAliasPrefix);
 189:             } else {
 190:                 $strFields = $this->Fields();
 191:                 foreach ($strFields as $strField) {
 192:                     $objBuilder->AddSelectItem($strTableName, $strField, $strAliasPrefix . $strField);
 193:                 }
 194:             }
 195:         }
 196: 
 197:         /**
 198:          * @return QQNode|null
 199:          */
 200:         public function FirstChild() {
 201:             $a = $this->objChildNodeArray;
 202:             if ($a) {
 203:                 return reset ($a);
 204:             } else {
 205:                 return null;
 206:             }
 207:         }
 208: 
 209:         /**
 210:          * Returns the extended table associated with the node.
 211:          * @return string
 212:          */
 213:         public function GetTable() {
 214:             return $this->FullAlias();
 215:         }
 216: 
 217:         /**
 218:          * @param mixed $mixValue
 219:          * @param QQueryBuilder $objBuilder
 220:          * @param boolean $blnEqualityType can be null (for no equality), true (to add a standard "equal to") or false (to add a standard "not equal to")
 221:          * @return string
 222:          * @throws Exception
 223:          * @throws QCallerException
 224:          */
 225:         public static function GetValue($mixValue, QQueryBuilder $objBuilder, $blnEqualityType = null) {
 226:             if ($mixValue instanceof QQNamedValue) {
 227:                 /** @var QQNamedValue $mixValue */
 228:                 return $mixValue->Parameter($blnEqualityType);
 229:             }
 230: 
 231:             if ($mixValue instanceof QQNode) {
 232:                 /** @var QQNode $mixValue */
 233:                 if ($n = $mixValue->_PrimaryKeyNode) {
 234:                     $mixValue = $n; // Convert table node to column node
 235:                 }
 236:                 /** @var QQColumnNode $mixValue */
 237:                 if (is_null($blnEqualityType))
 238:                     $strToReturn = '';
 239:                 else if ($blnEqualityType)
 240:                     $strToReturn = '= ';
 241:                 else
 242:                     $strToReturn = '!= ';
 243: 
 244:                 try {
 245:                     return $strToReturn . $mixValue->GetColumnAlias($objBuilder);
 246:                 } catch (QCallerException $objExc) {
 247:                     $objExc->IncrementOffset();
 248:                     throw $objExc;
 249:                 }
 250:             } else {
 251:                 if (is_null($blnEqualityType)) {
 252:                     $blnIncludeEquality = false;
 253:                     $blnReverseEquality = false;
 254:                 } else {
 255:                     $blnIncludeEquality = true;
 256:                     if ($blnEqualityType)
 257:                         $blnReverseEquality = false;
 258:                     else
 259:                         $blnReverseEquality = true;
 260:                 }
 261: 
 262:                 return $objBuilder->Database->SqlVariable($mixValue, $blnIncludeEquality, $blnReverseEquality);
 263:             }
 264:         }
 265: 
 266:         public function __get($strName) {
 267:             switch ($strName) {
 268:                 case '_ParentNode':
 269:                     return $this->objParentNode;
 270:                 case '_Name':
 271:                     return $this->strName;
 272:                 case '_Alias':
 273:                     return $this->strAlias;
 274:                 case '_PropertyName':
 275:                     return $this->strPropertyName;
 276:                 case '_Type':
 277:                     return $this->strType;
 278:                 case '_RootTableName':
 279:                     return $this->strRootTableName;
 280:                 case '_TableName':
 281:                     return $this->strTableName;
 282:                 case '_PrimaryKey':
 283:                     return $this->strPrimaryKey;
 284:                 case '_ClassName':
 285:                     return $this->strClassName;
 286:                 case '_PrimaryKeyNode':
 287:                     return null;
 288:                     
 289:                 case 'ExpandAsArray':
 290:                     return $this->blnExpandAsArray;
 291:                 case 'IsType':
 292:                     return $this->blnIsType;
 293:                     
 294:                 case 'ChildNodeArray':
 295:                     return $this->objChildNodeArray;
 296:                     
 297:                 default:
 298:                     try {
 299:                         return parent::__get($strName);
 300:                     } catch (QCallerException $objExc) {
 301:                         $objExc->IncrementOffset();
 302:                         throw $objExc;
 303:                     }
 304:             }
 305:         }
 306: 
 307:         public function __set($strName, $mixValue) {
 308:             switch ($strName) {
 309:                 case 'ExpandAsArray':
 310:                     try {
 311:                         return ($this->blnExpandAsArray = QType::Cast($mixValue, QType::Boolean));
 312:                     } catch (QCallerException $objExc) {
 313:                         $objExc->IncrementOffset();
 314:                         throw $objExc;
 315:                     }
 316:                                         
 317:                 default:
 318:                     try {
 319:                         return parent::__set($strName, $mixValue);
 320:                     } catch (QCallerException $objExc) {
 321:                         $objExc->IncrementOffset();
 322:                         throw $objExc;
 323:                     }
 324:             }
 325:         }
 326: 
 327:         //////////////////
 328:         // Helpers for Orm-generated DataGrids
 329: 
 330:         // Deprecated, soon to be removed.
 331:         //////////////////
 332:         /**
 333:          * @return string
 334:          * @throws Exception
 335:          */
 336:         public function GetDataGridHtml() {
 337:             // Array-ify Node Hierarchy
 338:             $objNodeArray = array();
 339: 
 340:             $objNodeArray[] = $this;
 341:             while ($objNodeArray[count($objNodeArray) - 1]->objParentNode)
 342:                 $objNodeArray[] = $objNodeArray[count($objNodeArray) - 1]->objParentNode;
 343: 
 344:             $objNodeArray = array_reverse($objNodeArray, false);
 345: 
 346:             // Go through the objNodeArray to build out the DataGridHtml
 347: 
 348:             // Error Behavior
 349:             if (count($objNodeArray) < 2)
 350:                 throw new Exception('Invalid QQNode to GetDataGridHtml on');
 351: 
 352:             // Simple Two-Step Node
 353:             else if (count($objNodeArray) == 2) {
 354:                 $strToReturn = '$_ITEM->' . $objNodeArray[1]->strPropertyName;
 355:                 if (class_exists($this->strClassName)) {
 356:                     $strToReturn = sprintf('(%s) ? %s->__toString() : null;', $strToReturn, $strToReturn);
 357:                 }
 358:             }
 359:             // Complex N-Step Node
 360:             else {
 361:                 $strNodeLabelArray[0] = '$_ITEM->' . $objNodeArray[1]->strPropertyName;
 362:                 for ($intIndex = 2; $intIndex < count($objNodeArray); $intIndex++) {
 363:                     $strNodeLabelArray[$intIndex - 1] = $strNodeLabelArray[$intIndex - 2] . '->' . $objNodeArray[$intIndex]->strPropertyName;
 364:                 }
 365: 
 366:                 $slice_count = count ($objNodeArray) - 2;
 367:                 $blnIsClass = class_exists($this->strClassName);
 368: 
 369:                 if ($blnIsClass) {
 370:                     $slice_count++;
 371:                 }
 372: 
 373:                 $aTest = array_slice ($strNodeLabelArray, 0, $slice_count);
 374:                 $strTest = implode (' && ', $aTest);
 375:                 $strLastNode = $strNodeLabelArray[count($strNodeLabelArray) - 1];
 376: 
 377:                 if ($blnIsClass) {
 378:                     return sprintf ('(%s) ? %s->__toString() : null', $strTest, $strLastNode);
 379:                 } else {
 380:                     $strToReturn = sprintf ('(%s) ? %s : null', $strTest, $strLastNode);
 381:                 }
 382:             }
 383: 
 384:             if($this->strType == QDatabaseFieldType::Time)
 385:                 return sprintf('(%s) ? %s->qFormat(QDateTime::$DefaultTimeFormat) : null', $strToReturn, $strToReturn);
 386: 
 387:             if ($this->strType == QDatabaseFieldType::Bit)
 388:                 return sprintf('(null === %s)? "" : ((%s)? "%s" : "%s")', $strToReturn, $strToReturn, QApplication::Translate('True'), QApplication::Translate('False'));
 389: 
 390: 
 391:             return $strToReturn;
 392:         }
 393: 
 394:         public function GetDataGridOrderByNode() {
 395:             if ($this instanceof QQReverseReferenceNode)
 396:                 return $this->_PrimaryKeyNode;
 397:             else
 398:                 return $this;
 399:         }
 400: 
 401:         public function SetFilteredDataGridColumnFilter(QDataGridLegacyColumn $col)
 402:         {
 403:             if ($this->_PrimaryKeyNode) {
 404:                 $objNode = $this->_PrimaryKeyNode;
 405:             } else {
 406:                 $objNode = $this;
 407:             }
 408: 
 409:             switch($objNode->strType)
 410:             {
 411:                 case QDatabaseFieldType::Bit:
 412:                     //List of true / false / any
 413:                     $col->FilterType = QFilterType::ListFilter;
 414:                     $col->FilterAddListItem("True", QQ::Equal($objNode, true));
 415:                     $col->FilterAddListItem("False", QQ::Equal($objNode, false));
 416:                     $col->FilterAddListItem("Set", QQ::IsNotNull($objNode));
 417:                     $col->FilterAddListItem("Unset", QQ::IsNull($objNode));
 418:                     break;
 419:                 case QDatabaseFieldType::Blob:
 420:                 case QDatabaseFieldType::Char:
 421:                 case QDatabaseFieldType::Time:
 422:                 case QDatabaseFieldType::VarChar:
 423:                 case QDatabaseFieldType::Date:
 424:                 case QDatabaseFieldType::DateTime:
 425:                     //LIKE
 426:                     $col->FilterType = QFilterType::TextFilter;
 427:                     $col->FilterPrefix = '%';
 428:                     $col->FilterPostfix = '%';
 429:                     $col->Filter = QQ::Like($objNode, null);
 430:                     break;
 431:                 case QDatabaseFieldType::Float:
 432:                 case QDatabaseFieldType::Integer:
 433:                     //EQUAL
 434:                     $col->FilterType = QFilterType::TextFilter;
 435:                     $col->Filter = QQ::Equal($objNode, null);
 436:                     break;
 437:                 case QType::Object:
 438:                 case QType::Resource:
 439:                 default:
 440:                     //this node points to a class, there's no way to know what to filter on
 441:                     $col->FilterType = QFilterType::None;
 442:                     $col->ClearFilter();
 443:                     break;
 444:             }
 445:         }
 446: 
 447:     }
 448: 
 449:     /**
 450:      * Class QQColumnNode
 451:      * A node that represents a column in a table.
 452:      */
 453:     class QQColumnNode extends QQNode {
 454:         /**
 455:          * Initialize a column node.
 456:          * @param string $strName
 457:          * @param string $strPropertyName
 458:          * @param string $strType
 459:          * @param QQNode|null $objParentNode
 460:          */
 461:         public function __construct($strName, $strPropertyName, $strType, QQNode $objParentNode = null) {
 462:             $this->objParentNode = $objParentNode;
 463:             $this->strName = $strName;
 464:             $this->strAlias = $strName;
 465:             if ($objParentNode) $objParentNode->objChildNodeArray[$strName] = $this;
 466: 
 467:             $this->strPropertyName = $strPropertyName;
 468:             $this->strType = $strType;
 469:             if ($objParentNode) {
 470:                 $this->strRootTableName = $objParentNode->strRootTableName;
 471:             } else
 472:                 $this->strRootTableName = $strName;
 473:         }
 474: 
 475:         /**
 476:          * @return string
 477:          */
 478:         public function GetColumnAlias(QQueryBuilder $objBuilder) {
 479:             $this->Join($objBuilder);
 480:             $strParentAlias = $this->objParentNode->FullAlias();
 481:             $strTableAlias = $objBuilder->GetTableAlias($strParentAlias);
 482:             // Pull the Begin and End Escape Identifiers from the Database Adapter
 483:             return $this->MakeColumnAlias($objBuilder, $strTableAlias);
 484:         }
 485: 
 486:         /**
 487:          * @return string
 488:          */
 489:         public function MakeColumnAlias(QQueryBuilder $objBuilder, $strTableAlias) {
 490:             $strBegin = $objBuilder->Database->EscapeIdentifierBegin;
 491:             $strEnd = $objBuilder->Database->EscapeIdentifierEnd;
 492: 
 493:             return sprintf('%s%s%s.%s%s%s',
 494:                 $strBegin, $strTableAlias, $strEnd,
 495:                 $strBegin, $this->strName, $strEnd);
 496:         }
 497: 
 498: 
 499:         /**
 500:          * @return string
 501:          */
 502:         public function GetTable() {
 503:             return $this->objParentNode->FullAlias();
 504:         }
 505: 
 506:         /**
 507:          * Join the node to the given query. Since this is a leaf node, we pass on the join to the parent.
 508:          *
 509:          * @param QQueryBuilder $objBuilder
 510:          * @param bool $blnExpandSelection
 511:          * @param QQCondition|null $objJoinCondition
 512:          * @param QQSelect|null $objSelect
 513:          * @throws QCallerException
 514:          */
 515:         public function Join(QQueryBuilder $objBuilder, $blnExpandSelection = false, QQCondition $objJoinCondition = null, QQSelect $objSelect = null) {
 516:             $objParentNode = $this->objParentNode;
 517:             if (!$objParentNode) {
 518:                 throw new QCallerException('A column node must have a parent node.');
 519:             } else {
 520:                 // Here we pass the join condition on to the parent object
 521:                 $objParentNode->Join($objBuilder, $blnExpandSelection, $objJoinCondition, $objSelect);
 522:             }
 523:         }
 524: 
 525:         /**
 526:          * Get the unaliased column name. For special situations, like order by, since you can't order by aliases.
 527:          * @return string
 528:          */
 529:         public function GetAsManualSqlColumn() {
 530:             if ($this->strTableName)
 531:                 return $this->strTableName . '.' . $this->strName;
 532:             else if (($this->objParentNode) && ($this->objParentNode->strTableName))
 533:                 return $this->objParentNode->strTableName . '.' . $this->strName;
 534:             else
 535:                 return $this->strName;
 536:         }
 537: 
 538:     }
 539: 
 540:     /**
 541:      * Class QQTableNode
 542:      * A node that represents a regular table. This can either be a root of the query node chain, or a forward looking
 543:      * foreign key (as in one-to-one relationship).
 544:      */
 545:     abstract class QQTableNode extends QQNode {
 546:         /**
 547:          * Initialize a table node. The subclass should fill in the table name, primary key and class name.
 548:          *
 549:          * @param $strName
 550:          * @param null|string $strPropertyName  If it has a parent, the property the parent uses to refer to this node.
 551:          * @param null|string $strType If it has a parent, the type of the column in the parent that is the fk to this node. (Likely Integer).
 552:          * @param QQNode|null $objParentNode
 553:          */
 554:         public function __construct($strName, $strPropertyName = null, $strType = null, QQNode $objParentNode = null) {
 555:             $this->objParentNode = $objParentNode;
 556:             $this->strName = $strName;
 557:             $this->strAlias = $strName;
 558:             if ($objParentNode) $objParentNode->objChildNodeArray[$strName] = $this;
 559: 
 560:             $this->strPropertyName = $strPropertyName;
 561:             $this->strType = $strType;
 562:             if ($objParentNode) {
 563:                 $this->strRootTableName = $objParentNode->strRootTableName;
 564:             } else
 565:                 $this->strRootTableName = $strName;
 566:         }
 567: 
 568:         /**
 569:          * Join the node to the query.
 570:          * Otherwise, its a straightforward
 571:          * one-to-one join. Conditional joins in this situation are really only useful when combined with condition
 572:          * clauses that select out rows that were not joined (null FK).
 573:          *
 574:          * @param QQueryBuilder $objBuilder
 575:          * @param bool $blnExpandSelection
 576:          * @param QQCondition|null $objJoinCondition
 577:          * @param QQSelect|null $objSelect
 578:          * @throws Exception
 579:          * @throws QCallerException
 580:          */
 581:         public function Join(QQueryBuilder $objBuilder, $blnExpandSelection = false, QQCondition $objJoinCondition = null, QQSelect $objSelect = null) {
 582:             $objParentNode = $this->objParentNode;
 583:             if (!$objParentNode) {
 584:                 if ($this->strTableName != $objBuilder->RootTableName) {
 585:                     throw new QCallerException('Cannot use QQNode for "' . $this->strTableName . '" when querying against the "' . $objBuilder->RootTableName . '" table', 3);
 586:                 }
 587:             } else {
 588: 
 589:                 // Special case situation to allow applying a join condition on an association table.
 590:                 // The condition must be testing against the primary key of the joined table.
 591:                 if ($objJoinCondition &&
 592:                     $this->objParentNode instanceof QQAssociationNode &&
 593:                     $objJoinCondition->EqualTables($this->objParentNode->FullAlias())) {
 594: 
 595:                     $objParentNode->Join($objBuilder, $blnExpandSelection, $objJoinCondition, $objSelect);
 596:                     $objJoinCondition = null; // prevent passing join condition to this level
 597:                 } else {
 598:                     $objParentNode->Join($objBuilder, $blnExpandSelection, null, $objSelect);
 599:                     if ($objJoinCondition && !$objJoinCondition->EqualTables($this->FullAlias())) {
 600:                         throw new QCallerException("The join condition on the \"" . $this->strTableName . "\" table must only contain conditions for that table.");
 601:                     }
 602:                 }
 603: 
 604:                 try {
 605:                     $strParentAlias = $objParentNode->FullAlias();
 606:                     $strAlias = $this->FullAlias();
 607:                     //$strJoinTableAlias = $strParentAlias . '__' . ($this->strAlias ? $this->strAlias : $this->strName);
 608:                     $objBuilder->AddJoinItem($this->strTableName, $strAlias,
 609:                         $strParentAlias, $this->strName, $this->strPrimaryKey, $objJoinCondition);
 610: 
 611:                     if ($blnExpandSelection) {
 612:                         $this->PutSelectFields($objBuilder, $strAlias, $objSelect);
 613:                     }
 614:                 } catch (QCallerException $objExc) {
 615:                     $objExc->IncrementOffset();
 616:                     throw $objExc;
 617:                 }
 618:             }
 619:         }
 620:     }
 621: 
 622:     /**
 623:      * Class QQReverseReferenceNode
 624:      *
 625:      * Describes a foreign key relationship that links to the primary key in the parent table. Relationship can be unique (one-to-one) or
 626:      * not unique (many-to-one).
 627:      */
 628:     class QQReverseReferenceNode extends QQTableNode {
 629:         /** @var string The name of the foreign key in the linked table.  */
 630:         protected $strForeignKey;
 631: 
 632:         /**
 633:          * Construct the reverse reference.
 634:          *
 635:          * @param QQNode $objParentNode
 636:          * @param null|string $strName
 637:          * @param null|string $strType
 638:          * @param null|QQNode $strForeignKey
 639:          * @param null $strPropertyName     If a unique reverse relationship, the name of property that will be used in the model class.
 640:          * @throws QCallerException
 641:          */
 642:         public function __construct(QQNode $objParentNode, $strName, $strType, $strForeignKey, $strPropertyName = null) {
 643:             parent::__construct($strName, $strPropertyName, $strType, $objParentNode);
 644:             if (!$objParentNode) {
 645:                 throw new QCallerException('ReverseReferenceNodes must have a Parent Node');
 646:             }
 647:             $objParentNode->objChildNodeArray[$strName] = $this;
 648:             $this->strForeignKey = $strForeignKey;
 649:         }
 650: 
 651:         /**
 652:          * Return true if this is a unique reverse relationship.
 653:          *
 654:          * @return bool
 655:          */
 656:         public function IsUnique() {
 657:             return !empty($this->strPropertyName);
 658:         }
 659: 
 660:         /**
 661:          * Join a node to the query. Since this is a reverse looking node, conditions control which items are joined.
 662:          *
 663:          * @param QQueryBuilder $objBuilder
 664:          * @param bool $blnExpandSelection
 665:          * @param QQCondition|null $objJoinCondition
 666:          * @param QQSelect|null $objSelect
 667:          * @throws Exception
 668:          * @throws QCallerException
 669:          */
 670:         public function Join(QQueryBuilder $objBuilder, $blnExpandSelection = false, QQCondition $objJoinCondition = null, QQSelect $objSelect = null) {
 671:             $objParentNode = $this->objParentNode;
 672:             $objParentNode->Join($objBuilder, $blnExpandSelection, null, $objSelect);
 673:             if ($objJoinCondition && !$objJoinCondition->EqualTables($this->FullAlias())) {
 674:                 throw new QCallerException("The join condition on the \"" . $this->strTableName . "\" table must only contain conditions for that table.");
 675:             }
 676: 
 677:             try {
 678:                 $strParentAlias = $objParentNode->FullAlias();
 679:                 $strAlias = $this->FullAlias();
 680:                 //$strJoinTableAlias = $strParentAlias . '__' . ($this->strAlias ? $this->strAlias : $this->strName);
 681:                 $objBuilder->AddJoinItem($this->strTableName, $strAlias,
 682:                     $strParentAlias, $this->objParentNode->_PrimaryKey, $this->strForeignKey, $objJoinCondition);
 683: 
 684:                 if ($blnExpandSelection) {
 685:                     $this->PutSelectFields($objBuilder, $strAlias, $objSelect);
 686:                 }
 687:             } catch (QCallerException $objExc) {
 688:                 $objExc->IncrementOffset();
 689:                 throw $objExc;
 690:             }
 691:         }
 692: 
 693:     }
 694: 
 695:     /**
 696:      * Class QQAssociationNode
 697:      *
 698:      * Describes a many-to-many relationship in the database that uses an association table to link two other tables together.
 699:      */
 700:     class QQAssociationNode extends QQNode {
 701:         /**
 702:          * @param QQNode $objParentNode
 703:          * @throws Exception
 704:          */
 705:         public function __construct(QQNode $objParentNode) {
 706:             $this->objParentNode = $objParentNode;
 707:             if ($objParentNode) {
 708:                 $this->strRootTableName = $objParentNode->_RootTableName;
 709:                 $this->strAlias = $this->strName;
 710:                 $objParentNode->objChildNodeArray[$this->strAlias] = $this;
 711:             } else {
 712:                 throw new Exception ("Association Nodes must always have a parent node");
 713:             }
 714:         }
 715: 
 716:         /**
 717:          * Join the node to the query. Join condition here gets applied to parent item.
 718:          *
 719:          * @param QQueryBuilder $objBuilder
 720:          * @param bool $blnExpandSelection
 721:          * @param QQCondition|null $objJoinCondition
 722:          * @param QQSelect|null $objSelect
 723:          * @throws Exception
 724:          * @throws QCallerException
 725:          */
 726:         public function Join(QQueryBuilder $objBuilder, $blnExpandSelection = false, QQCondition $objJoinCondition = null, QQSelect $objSelect = null) {
 727:             $objParentNode = $this->objParentNode;
 728:             $objParentNode->Join($objBuilder, $blnExpandSelection, null, $objSelect);
 729:             if ($objJoinCondition && !$objJoinCondition->EqualTables($this->FullAlias())) {
 730:                 throw new QCallerException("The join condition on the \"" . $this->strTableName . "\" table must only contain conditions for that table.");
 731:             }
 732: 
 733:             try {
 734:                 $strParentAlias = $objParentNode->FullAlias();
 735:                 $strAlias = $this->FullAlias();
 736:                 //$strJoinTableAlias = $strParentAlias . '__' . ($this->strAlias ? $this->strAlias : $this->strName);
 737:                 $objBuilder->AddJoinItem($this->strTableName, $strAlias,
 738:                     $strParentAlias, $objParentNode->_PrimaryKey, $this->strPrimaryKey, $objJoinCondition);
 739: 
 740:                 if ($blnExpandSelection) {
 741:                     $this->PutSelectFields($objBuilder, $strAlias, $objSelect);
 742:                 }
 743:             } catch (QCallerException $objExc) {
 744:                 $objExc->IncrementOffset();
 745:                 throw $objExc;
 746:             }
 747:         }
 748:     }
 749: 
 750: 
 751:     /**
 752:      * Class QQNamedValue
 753:      *
 754:      * Special node for referring to a node within a custom SQL clause.
 755:      */
 756:     class QQNamedValue extends QQNode
 757:     {
 758:         const DelimiterCode = 3;
 759: 
 760:         /**
 761:          * @param $strName
 762:          */
 763:         public function __construct($strName) {
 764:             $this->strName = $strName;
 765:         }
 766: 
 767:         /**
 768:          * @param null $blnEqualityType
 769:          * @return string
 770:          */
 771:         public function Parameter($blnEqualityType = null)
 772:         {
 773:             if (is_null($blnEqualityType))
 774:                 return chr(QQNamedValue::DelimiterCode) . '{' . $this->strName . '}';
 775:             else if ($blnEqualityType)
 776:                 return chr(QQNamedValue::DelimiterCode) . '{=' . $this->strName . '=}';
 777:             else
 778:                 return chr(QQNamedValue::DelimiterCode) . '{!' . $this->strName . '!}';
 779:         }
 780: 
 781:         /**
 782:          * @param QQueryBuilder $objBuilder
 783:          * @param bool|false $blnExpandSelection
 784:          * @param QQCondition|null $objJoinCondition
 785:          * @param QQSelect|null $objSelect
 786:          */
 787:         public function Join(QQueryBuilder $objBuilder, $blnExpandSelection = false, QQCondition $objJoinCondition = null, QQSelect $objSelect = null) {
 788:             assert(0);    // This kind of node is never a parent.
 789:         }
 790:     }
 791: 
 792:     abstract class QQCondition extends QBaseClass {
 793:         protected $strOperator;
 794:         abstract public function UpdateQueryBuilder(QQueryBuilder $objBuilder);
 795:         public function __toString() {
 796:             return 'QQCondition Object';
 797:         }
 798: 
 799:         protected $blnProcessed;
 800: 
 801:         /**
 802:          * Used internally by QCubed Query to get an individual where clause for a given condition
 803:          * Mostly used for conditional joins.
 804:          *
 805:          * @param QQueryBuilder $objBuilder
 806:          * @param bool|false $blnProcessOnce
 807:          * @return null|string
 808:          * @throws Exception
 809:          * @throws QCallerException
 810:          */
 811:         public function GetWhereClause(QQueryBuilder $objBuilder, $blnProcessOnce = false) {
 812:             if ($blnProcessOnce && $this->blnProcessed)
 813:                 return null;
 814: 
 815:             $this->blnProcessed = true;
 816: 
 817:             try {
 818:                 $objConditionBuilder = new QPartialQueryBuilder($objBuilder);
 819:                 $this->UpdateQueryBuilder($objConditionBuilder);
 820:                 return $objConditionBuilder->GetWhereStatement();
 821:             } catch (QCallerException $objExc) {
 822:                 $objExc->IncrementOffset();
 823:                 $objExc->IncrementOffset();
 824:                 throw $objExc;
 825:             }
 826:         }
 827: 
 828:         /**
 829:          * @param string $strTableName
 830:          * @return bool
 831:          */
 832:         public function EqualTables($strTableName) {
 833:             return true;
 834:         }
 835:     }
 836:     class QQConditionAll extends QQCondition {
 837:         /**
 838:          * @param $mixParameterArray
 839:          * @throws QCallerException
 840:          */
 841:         public function __construct($mixParameterArray) {
 842:             if (count($mixParameterArray))
 843:                 throw new QCallerException('All clause takes in no parameters', 3);
 844:         }
 845: 
 846:         /**
 847:          * @param QQueryBuilder $objBuilder
 848:          */
 849:         public function UpdateQueryBuilder(QQueryBuilder $objBuilder) {
 850:             $objBuilder->AddWhereItem('1=1');
 851:         }
 852:     }
 853:     class QQConditionNone extends QQCondition {
 854:         /**
 855:          * @param $mixParameterArray
 856:          * @throws QCallerException
 857:          */
 858:         public function __construct($mixParameterArray) {
 859:             if (count($mixParameterArray))
 860:                 throw new QCallerException('None clause takes in no parameters', 3);
 861:         }
 862:         public function UpdateQueryBuilder(QQueryBuilder $objBuilder) {
 863:             $objBuilder->AddWhereItem('1=0');
 864:         }
 865:     }
 866:     abstract class QQConditionLogical extends QQCondition {
 867:         /** @var QQCondition[] */
 868:         protected $objConditionArray;
 869:         protected function CollapseConditions($mixParameterArray) {
 870:             $objConditionArray = array();
 871:             foreach ($mixParameterArray as $mixParameter) {
 872:                 if (is_array($mixParameter))
 873:                     $objConditionArray = array_merge($objConditionArray, $mixParameter);
 874:                 else
 875:                     array_push($objConditionArray, $mixParameter);
 876:             }
 877: 
 878:             foreach ($objConditionArray as $objCondition)
 879:                 if (!($objCondition instanceof QQCondition))
 880:                     throw new QCallerException('Logical Or/And clause parameters must all be QQCondition objects', 3);
 881: 
 882:             if (count($objConditionArray))
 883:                 return $objConditionArray;
 884:             else
 885:                 throw new QCallerException('No parameters passed in to logical Or/And clause', 3);
 886:         }
 887:         public function __construct($mixParameterArray) {
 888:             $objConditionArray = $this->CollapseConditions($mixParameterArray);
 889:             try {
 890:                 $this->objConditionArray = QType::Cast($objConditionArray, QType::ArrayType);
 891:             } catch (QCallerException $objExc) {
 892:                 $objExc->IncrementOffset();
 893:                 throw $objExc;
 894:             }
 895:         }
 896:         public function UpdateQueryBuilder(QQueryBuilder $objBuilder) {
 897:             $intLength = count($this->objConditionArray);
 898:             if ($intLength) {
 899:                 $objBuilder->AddWhereItem('(');
 900:                 for ($intIndex = 0; $intIndex < $intLength; $intIndex++) {
 901:                     if (!($this->objConditionArray[$intIndex] instanceof QQCondition))
 902:                         throw new QCallerException($this->strOperator . ' clause has elements that are not Conditions');
 903:                     try {
 904:                         $this->objConditionArray[$intIndex]->UpdateQueryBuilder($objBuilder);
 905:                     } catch (QCallerException $objExc) {
 906:                         $objExc->IncrementOffset();
 907:                         throw $objExc;
 908:                     }
 909:                     if (($intIndex + 1) != $intLength)
 910:                         $objBuilder->AddWhereItem($this->strOperator);
 911:                 }
 912:                 $objBuilder->AddWhereItem(')');
 913:             }
 914:         }
 915: 
 916:         public function EqualTables($strTableName) {
 917:             foreach ($this->objConditionArray as $objCondition) {
 918:                 if (!$objCondition->EqualTables($strTableName)) {
 919:                     return false;
 920:                 }
 921:             }
 922:             return true;
 923:         }
 924:     }
 925:     class QQConditionOr extends QQConditionLogical {
 926:         protected $strOperator = 'OR';
 927:     }
 928:     class QQConditionAnd extends QQConditionLogical {
 929:         protected $strOperator = 'AND';
 930:     }
 931: 
 932:     class QQConditionNot extends QQCondition {
 933:         protected $objCondition;
 934:         public function __construct(QQCondition $objCondition) {
 935:             $this->objCondition = $objCondition;
 936:         }
 937:         public function UpdateQueryBuilder(QQueryBuilder $objBuilder) {
 938:             $objBuilder->AddWhereItem('(NOT');
 939:             try {
 940:                 $this->objCondition->UpdateQueryBuilder($objBuilder);
 941:             } catch (QCallerException $objExc) {
 942:                 $objExc->IncrementOffset();
 943:                 throw $objExc;
 944:             }
 945:             $objBuilder->AddWhereItem(')');
 946:         }
 947:     }
 948: 
 949:     abstract class QQConditionComparison extends QQCondition {
 950:         /** @var QQColumnNode */
 951:         public $objQueryNode;
 952:         public $mixOperand;
 953: 
 954:         /**
 955:          * @param QQColumnNode $objQueryNode
 956:          * @param mixed $mixOperand
 957:          * @throws QInvalidCastException
 958:          */
 959:         public function __construct(QQColumnNode $objQueryNode, $mixOperand = null) {
 960:             $this->objQueryNode = $objQueryNode;
 961: 
 962:             if ($mixOperand instanceof QQNamedValue || $mixOperand === null)
 963:                 $this->mixOperand = $mixOperand;
 964:             else if ($mixOperand instanceof QQAssociationNode)
 965:                 throw new QInvalidCastException('Comparison operand cannot be an Association-based QQNode', 3);
 966:             else if ($mixOperand instanceof QQCondition)
 967:                 throw new QInvalidCastException('Comparison operand cannot be a QQCondition', 3);
 968:             else if ($mixOperand instanceof QQClause)
 969:                 throw new QInvalidCastException('Comparison operand cannot be a QQClause', 3);
 970:             else if (!($mixOperand instanceof QQNode)) {
 971:                 $this->mixOperand = $mixOperand;
 972:             } else {
 973:                 if (!($mixOperand instanceof QQColumnNode))
 974:                     throw new QInvalidCastException('Unable to cast "' . $mixOperand->_Name . '" table to Column-based QQNode', 3);
 975:                 $this->mixOperand = $mixOperand;
 976:             }
 977:         }
 978:         public function UpdateQueryBuilder(QQueryBuilder $objBuilder) {
 979:             $objBuilder->AddWhereItem($this->objQueryNode->GetColumnAlias($objBuilder) . $this->strOperator . QQNode::GetValue($this->mixOperand, $objBuilder));
 980:         }
 981: 
 982:         /**
 983:          * Used by conditional joins to make sure the join conditions only apply to given table.
 984:          * @param $strTableName
 985:          * @returns bool
 986:          */
 987:         public function EqualTables($strTableName) {
 988:             return $this->objQueryNode->GetTable() == $strTableName;
 989:         }
 990:     }
 991: 
 992:     /**
 993:      * Class QQConditionIsNull
 994:      * Represent a test for a null item in the database.
 995:      */
 996:     class QQConditionIsNull extends QQConditionComparison {
 997:         /**
 998:          * @param QQColumnNode $objQueryNode
 999:          */
1000:         public function __construct(QQColumnNode $objQueryNode) {
1001:             parent::__construct($objQueryNode);
1002:         }
1003: 
1004:         /**
1005:          * @param QQueryBuilder $objBuilder
1006:          */
1007:         public function UpdateQueryBuilder(QQueryBuilder $objBuilder) {
1008:             $objBuilder->AddWhereItem($this->objQueryNode->GetColumnAlias($objBuilder) . ' IS NULL');
1009:         }
1010:     }
1011: 
1012:     class QQConditionIsNotNull extends QQConditionComparison {
1013:         public function __construct(QQColumnNode $objQueryNode) {
1014:             parent::__construct($objQueryNode);
1015:         }
1016: 
1017:         /**
1018:          * @param QQueryBuilder $objBuilder
1019:          */
1020:         public function UpdateQueryBuilder(QQueryBuilder $objBuilder) {
1021:             $objBuilder->AddWhereItem($this->objQueryNode->GetColumnAlias($objBuilder) . ' IS NOT NULL');
1022:         }
1023:     }
1024: 
1025:     class QQConditionIn extends QQConditionComparison {
1026:         /**
1027:          * @param QQColumnNode $objQueryNode
1028:          * @param mixed $mixValuesArray
1029:          * @throws Exception
1030:          * @throws QCallerException
1031:          * @throws QInvalidCastException
1032:          */
1033:         public function __construct(QQColumnNode $objQueryNode, $mixValuesArray) {
1034:             parent::__construct($objQueryNode);
1035: 
1036:             if ($mixValuesArray instanceof QQNamedValue)
1037:                 $this->mixOperand = $mixValuesArray;
1038:             else if ($mixValuesArray instanceof QQSubQueryNode)
1039:                 $this->mixOperand = $mixValuesArray;
1040:             else {
1041:                 try {
1042:                     $this->mixOperand = QType::Cast($mixValuesArray, QType::ArrayType);
1043:                 } catch (QCallerException $objExc) {
1044:                     $objExc->IncrementOffset();
1045:                     $objExc->IncrementOffset();
1046:                     throw $objExc;
1047:                 }
1048:             }
1049:         }
1050: 
1051:         /**
1052:          * @param QQueryBuilder $objBuilder
1053:          */
1054:         public function UpdateQueryBuilder(QQueryBuilder $objBuilder) {
1055:             $mixOperand = $this->mixOperand;
1056:             if ($mixOperand instanceof QQNamedValue) {
1057:                 /** @var QQNamedValue $mixOperand */
1058:                 $objBuilder->AddWhereItem($this->objQueryNode->GetColumnAlias($objBuilder) . ' IN (' . $mixOperand->Parameter() . ')');
1059:             } else if ($mixOperand instanceof QQSubQueryNode) {
1060:                 /** @var QQSubQueryNode $mixOperand */
1061:                 $objBuilder->AddWhereItem($this->objQueryNode->GetColumnAlias($objBuilder) . ' IN ' . $mixOperand->GetColumnAlias($objBuilder));
1062:             } else {
1063:                 $strParameters = array();
1064:                 foreach ($mixOperand as $mixParameter) {
1065:                     array_push($strParameters, $objBuilder->Database->SqlVariable($mixParameter));
1066:                 }
1067:                 if (count($strParameters))
1068:                     $objBuilder->AddWhereItem($this->objQueryNode->GetColumnAlias($objBuilder) . ' IN (' . implode(',', $strParameters) . ')');
1069:                 else
1070:                     $objBuilder->AddWhereItem('1=0');
1071:             }
1072:         }
1073:     }
1074: 
1075:     class QQConditionNotIn extends QQConditionComparison {
1076:         /**
1077:          * @param QQColumnNode $objQueryNode
1078:          * @param mixed|null $mixValuesArray
1079:          * @throws Exception
1080:          * @throws QCallerException
1081:          */
1082:         public function __construct(QQColumnNode $objQueryNode, $mixValuesArray) {
1083:             parent::__construct($objQueryNode);
1084: 
1085:             if ($mixValuesArray instanceof QQNamedValue)
1086:                 $this->mixOperand = $mixValuesArray;
1087:             else if ($mixValuesArray instanceof QQSubQueryNode)
1088:                 $this->mixOperand = $mixValuesArray;
1089:             else {
1090:                 try {
1091:                     $this->mixOperand = QType::Cast($mixValuesArray, QType::ArrayType);
1092:                 } catch (QCallerException $objExc) {
1093:                     $objExc->IncrementOffset();
1094:                     $objExc->IncrementOffset();
1095:                     throw $objExc;
1096:                 }
1097:             }
1098:         }
1099: 
1100:         /**
1101:          * @param QQueryBuilder $objBuilder
1102:          */
1103:         public function UpdateQueryBuilder(QQueryBuilder $objBuilder) {
1104:             $mixOperand = $this->mixOperand;
1105:             if ($mixOperand instanceof QQNamedValue) {
1106:                 /** @var QQNamedValue $mixOperand */
1107:                 $objBuilder->AddWhereItem($this->objQueryNode->GetColumnAlias($objBuilder) . ' NOT IN (' . $mixOperand->Parameter() . ')');
1108:             } else if ($mixOperand instanceof QQSubQueryNode) {
1109:                 /** @var QQSubQueryNode $mixOperand */
1110:                 $objBuilder->AddWhereItem($this->objQueryNode->GetColumnAlias($objBuilder) . ' NOT IN ' . $mixOperand->GetColumnAlias($objBuilder));
1111:             } else {
1112:                 $strParameters = array();
1113:                 foreach ($mixOperand as $mixParameter) {
1114:                     array_push($strParameters, $objBuilder->Database->SqlVariable($mixParameter));
1115:                 }
1116:                 if (count($strParameters))
1117:                     $objBuilder->AddWhereItem($this->objQueryNode->GetColumnAlias($objBuilder) . ' NOT IN (' . implode(',', $strParameters) . ')');
1118:                 else
1119:                     $objBuilder->AddWhereItem('1=1');
1120:             }
1121:         }
1122:     }
1123: 
1124:     class QQConditionLike extends QQConditionComparison {
1125:         /**
1126:          * @param QQColumnNode $objQueryNode
1127:          * @param string $strValue
1128:          * @throws Exception
1129:          * @throws QCallerException
1130:          * @throws QInvalidCastException
1131:          */
1132:         public function __construct(QQColumnNode $objQueryNode, $strValue) {
1133:             parent::__construct($objQueryNode);
1134: 
1135:             if ($strValue instanceof QQNamedValue)
1136:                 $this->mixOperand = $strValue;
1137:             else {
1138:                 try {
1139:                     $this->mixOperand = QType::Cast($strValue, QType::String);
1140:                 } catch (QCallerException $objExc) {
1141:                     $objExc->IncrementOffset();
1142:                     $objExc->IncrementOffset();
1143:                     throw $objExc;
1144:                 }
1145:             }
1146:         }
1147: 
1148:         /**
1149:          * @param QQueryBuilder $objBuilder
1150:          */
1151:         public function UpdateQueryBuilder(QQueryBuilder $objBuilder) {
1152:             $mixOperand = $this->mixOperand;
1153:             if ($mixOperand instanceof QQNamedValue) {
1154:                 /** @var QQNamedValue $mixOperand */
1155:                 $objBuilder->AddWhereItem($this->objQueryNode->GetColumnAlias($objBuilder) . ' LIKE ' . $mixOperand->Parameter());
1156:             } else {
1157:                 $objBuilder->AddWhereItem($this->objQueryNode->GetColumnAlias($objBuilder) . ' LIKE ' . $objBuilder->Database->SqlVariable($mixOperand));
1158:             }
1159:         }
1160:     }
1161: 
1162:     class QQConditionNotLike extends QQConditionComparison {
1163:         /**
1164:          * @param QQColumnNode $objQueryNode
1165:          * @param mixed|null $strValue
1166:          * @throws Exception
1167:          * @throws QCallerException
1168:          */
1169:         public function __construct(QQColumnNode $objQueryNode, $strValue) {
1170:             parent::__construct($objQueryNode);
1171: 
1172:             if ($strValue instanceof QQNamedValue)
1173:                 $this->mixOperand = $strValue;
1174:             else {
1175:                 try {
1176:                     $this->mixOperand = QType::Cast($strValue, QType::String);
1177:                 } catch (QCallerException $objExc) {
1178:                     $objExc->IncrementOffset();
1179:                     $objExc->IncrementOffset();
1180:                     throw $objExc;
1181:                 }
1182:             }
1183:         }
1184: 
1185:         /**
1186:          * @param QQueryBuilder $objBuilder
1187:          */
1188:         public function UpdateQueryBuilder(QQueryBuilder $objBuilder) {
1189:             $mixOperand = $this->mixOperand;
1190:             if ($mixOperand instanceof QQNamedValue) {
1191:                 /** @var QQNamedValue $mixOperand */
1192:                 $objBuilder->AddWhereItem($this->objQueryNode->GetColumnAlias($objBuilder) . ' NOT LIKE ' . $mixOperand->Parameter());
1193:             } else {
1194:                 $objBuilder->AddWhereItem($this->objQueryNode->GetColumnAlias($objBuilder) . ' NOT LIKE ' . $objBuilder->Database->SqlVariable($mixOperand));
1195:             }
1196:         }
1197:     }
1198: 
1199:     class QQConditionBetween extends QQConditionComparison {
1200:         /** @var  mixed */
1201:         protected $mixOperandTwo;
1202: 
1203:         /**
1204:          * @param QQColumnNode $objQueryNode
1205:          * @param mixed|null $mixMinValue
1206:          * @param $mixMaxValue
1207:          * @throws Exception
1208:          * @throws QCallerException
1209:          */
1210:         public function __construct(QQColumnNode $objQueryNode, $mixMinValue, $mixMaxValue) {
1211:             parent::__construct($objQueryNode);
1212:             try {
1213:                 $this->mixOperand = $mixMinValue;
1214:                 $this->mixOperandTwo = $mixMaxValue;
1215:             } catch (QCallerException $objExc) {
1216:                 $objExc->IncrementOffset();
1217:                 $objExc->IncrementOffset();
1218:                 throw $objExc;
1219:             }
1220:         }
1221: 
1222:         /**
1223:          * @param QQueryBuilder $objBuilder
1224:          */
1225:         public function UpdateQueryBuilder(QQueryBuilder $objBuilder) {
1226:             $mixOperand = $this->mixOperand;
1227:             $mixOperandTwo = $this->mixOperandTwo;
1228:             if ($mixOperand instanceof QQNamedValue) {
1229:                 /** @var QQNamedValue $mixOperand */
1230:                 /** @var QQNamedValue $mixOperandTwo */
1231:                 $objBuilder->AddWhereItem($this->objQueryNode->GetColumnAlias($objBuilder) . ' BETWEEN ' . $mixOperand->Parameter() . ' AND ' . $mixOperandTwo->Parameter());
1232:             } else {
1233:                 $objBuilder->AddWhereItem($this->objQueryNode->GetColumnAlias($objBuilder) . ' BETWEEN ' . $objBuilder->Database->SqlVariable($mixOperand) . ' AND ' . $objBuilder->Database->SqlVariable($mixOperandTwo));
1234:             }
1235:         }
1236:     }
1237: 
1238:     class QQConditionNotBetween extends QQConditionComparison {
1239:         /** @var mixed  */
1240:         protected $mixOperandTwo;
1241: 
1242:         /**
1243:          * @param QQColumnNode $objQueryNode
1244:          * @param string $strMinValue
1245:          * @param string $strMaxValue
1246:          * @throws Exception
1247:          * @throws QCallerException
1248:          */
1249:         public function __construct(QQColumnNode $objQueryNode, $strMinValue, $strMaxValue) {
1250:             parent::__construct($objQueryNode);
1251:             try {
1252:                 $this->mixOperand = QType::Cast($strMinValue, QType::String);
1253:                 $this->mixOperandTwo = QType::Cast($strMaxValue, QType::String);
1254:             } catch (QCallerException $objExc) {
1255:                 $objExc->IncrementOffset();
1256:                 $objExc->IncrementOffset();
1257:                 throw $objExc;
1258:             }
1259: 
1260:             if ($strMinValue instanceof QQNamedValue)
1261:                 $this->mixOperand = $strMinValue;
1262:             if ($strMaxValue instanceof QQNamedValue)
1263:                 $this->mixOperandTwo = $strMaxValue;
1264: 
1265:         }
1266: 
1267:         /**
1268:          * @param QQueryBuilder $objBuilder
1269:          */
1270:         public function UpdateQueryBuilder(QQueryBuilder $objBuilder) {
1271:             $mixOperand = $this->mixOperand;
1272:             $mixOperandTwo = $this->mixOperandTwo;
1273:             if ($mixOperand instanceof QQNamedValue) {
1274:                 /** @var QQNamedValue $mixOperand */
1275:                 /** @var QQNamedValue $mixOperandTwo */
1276:                 $objBuilder->AddWhereItem($this->objQueryNode->GetColumnAlias($objBuilder) . ' NOT BETWEEN ' . $mixOperand->Parameter() . ' AND ' . $mixOperandTwo->Parameter());
1277:             } else {
1278:                 $objBuilder->AddWhereItem($this->objQueryNode->GetColumnAlias($objBuilder) . ' NOT BETWEEN ' . $objBuilder->Database->SqlVariable($mixOperand) . ' AND ' . $objBuilder->Database->SqlVariable($mixOperandTwo));
1279:             }
1280:         }
1281:     }
1282: 
1283:     class QQConditionEqual extends QQConditionComparison {
1284:         protected $strOperator = ' = ';
1285: 
1286:         /**
1287:          * @param QQueryBuilder $objBuilder
1288:          * @throws Exception
1289:          * @throws QCallerException
1290:          */
1291:         public function UpdateQueryBuilder(QQueryBuilder $objBuilder) {
1292:             $objBuilder->AddWhereItem($this->objQueryNode->GetColumnAlias($objBuilder) . ' ' . QQNode::GetValue($this->mixOperand, $objBuilder, true));
1293:         }
1294:     }
1295:     class QQConditionNotEqual extends QQConditionComparison {
1296:         protected $strOperator = ' != ';
1297: 
1298:         /**
1299:          * @param QQueryBuilder $objBuilder
1300:          * @throws Exception
1301:          * @throws QCallerException
1302:          */
1303:         public function UpdateQueryBuilder(QQueryBuilder $objBuilder) {
1304:             $objBuilder->AddWhereItem($this->objQueryNode->GetColumnAlias($objBuilder) . ' ' . QQNode::GetValue($this->mixOperand, $objBuilder, false));
1305:         }
1306:     }
1307: 
1308:     class QQConditionExists extends QQCondition {
1309:         /** @var QQSubQuerySqlNode  */
1310:         protected $objNode;
1311: 
1312:         /**
1313:          * @param QQSubQuerySqlNode $objNode
1314:          */
1315:         public function __construct(QQSubQuerySqlNode $objNode) {
1316:             $this->objNode = $objNode;
1317:         }
1318: 
1319:         /**
1320:          * @param QQueryBuilder $objBuilder
1321:          */
1322:         public function UpdateQueryBuilder(QQueryBuilder $objBuilder) {
1323:             $objBuilder->AddWhereItem('EXISTS ' . $this->objNode->GetColumnAlias($objBuilder));
1324:         }
1325:     }
1326: 
1327:     class QQConditionNotExists extends QQCondition {
1328:         /** @var QQSubQuerySqlNode  */
1329:         protected $objNode;
1330: 
1331:         /**
1332:          * @param QQSubQuerySqlNode $objNode
1333:          */
1334:         public function __construct(QQSubQuerySqlNode $objNode) {
1335:             $this->objNode = $objNode;
1336:         }
1337: 
1338:         /**
1339:          * @param QQueryBuilder $objBuilder
1340:          */
1341:         public function UpdateQueryBuilder(QQueryBuilder $objBuilder) {
1342:             $objBuilder->AddWhereItem('NOT EXISTS ' . $this->objNode->GetColumnAlias($objBuilder));
1343:         }
1344:     }
1345: 
1346: 
1347: 
1348:     class QQConditionGreaterThan extends QQConditionComparison {
1349:         protected $strOperator = ' > ';
1350:     }
1351:     class QQConditionLessThan extends QQConditionComparison {
1352:         protected $strOperator = ' < ';
1353:     }
1354:     class QQConditionGreaterOrEqual extends QQConditionComparison {
1355:         protected $strOperator = ' >= ';
1356:     }
1357:     class QQConditionLessOrEqual extends QQConditionComparison {
1358:         protected $strOperator = ' <= ';
1359:     }
1360: 
1361:     class QQ {
1362:         /////////////////////////
1363:         // QQCondition Factories
1364:         /////////////////////////
1365: 
1366:         static public function All() {
1367:             return new QQConditionAll(func_get_args());
1368:         }
1369: 
1370:         static public function None() {
1371:             return new QQConditionNone(func_get_args());
1372:         }
1373: 
1374:         static public function OrCondition(/* array and/or parameterized list of QLoad objects*/) {
1375:             return new QQConditionOr(func_get_args());
1376:         }
1377: 
1378:         static public function AndCondition(/* array and/or parameterized list of QLoad objects*/) {
1379:             return new QQConditionAnd(func_get_args());
1380:         }
1381: 
1382:         static public function Not(QQCondition $objCondition) {
1383:             return new QQConditionNot($objCondition);
1384:         }
1385: 
1386:         static public function Equal(QQColumnNode $objQueryNode, $mixValue) {
1387:             return new QQConditionEqual($objQueryNode, $mixValue);
1388:         }
1389:         static public function NotEqual(QQColumnNode $objQueryNode, $mixValue) {
1390:             return new QQConditionNotEqual($objQueryNode, $mixValue);
1391:         }
1392:         static public function GreaterThan(QQColumnNode $objQueryNode, $mixValue) {
1393:             return new QQConditionGreaterThan($objQueryNode, $mixValue);
1394:         }
1395:         static public function GreaterOrEqual(QQColumnNode $objQueryNode, $mixValue) {
1396:             return new QQConditionGreaterOrEqual($objQueryNode, $mixValue);
1397:         }
1398:         static public function LessThan(QQColumnNode $objQueryNode, $mixValue) {
1399:             return new QQConditionLessThan($objQueryNode, $mixValue);
1400:         }
1401:         static public function LessOrEqual(QQColumnNode $objQueryNode, $mixValue) {
1402:             return new QQConditionLessOrEqual($objQueryNode, $mixValue);
1403:         }
1404:         static public function IsNull(QQColumnNode $objQueryNode) {
1405:             return new QQConditionIsNull($objQueryNode);
1406:         }
1407:         static public function IsNotNull(QQColumnNode $objQueryNode) {
1408:             return new QQConditionIsNotNull($objQueryNode);
1409:         }
1410:         static public function In(QQColumnNode $objQueryNode, $mixValuesArray) {
1411:             return new QQConditionIn($objQueryNode, $mixValuesArray);
1412:         }
1413:         static public function NotIn(QQColumnNode $objQueryNode, $mixValuesArray) {
1414:             return new QQConditionNotIn($objQueryNode, $mixValuesArray);
1415:         }
1416:         static public function Like(QQColumnNode $objQueryNode, $strValue) {
1417:             return new QQConditionLike($objQueryNode, $strValue);
1418:         }
1419:         static public function NotLike(QQColumnNode $objQueryNode, $strValue) {
1420:             return new QQConditionNotLike($objQueryNode, $strValue);
1421:         }
1422:         static public function Between(QQColumnNode $objQueryNode, $mixMinValue, $mixMaxValue) {
1423:             return new QQConditionBetween($objQueryNode, $mixMinValue, $mixMaxValue);
1424:         }
1425:         static public function NotBetween(QQColumnNode $objQueryNode, $strMinValue, $strMaxValue) {
1426:             return new QQConditionNotBetween($objQueryNode, $strMinValue, $strMaxValue);
1427:         }
1428:         static public function Exists(QQSubQuerySqlNode $objQueryNode) {
1429:             return new QQConditionExists($objQueryNode);
1430:         }
1431:         static public function NotExists(QQSubQuerySqlNode $objQueryNode) {
1432:             return new QQConditionNotExists($objQueryNode);
1433:         }
1434: 
1435: 
1436:         ////////////////////////
1437:         // QQCondition Shortcuts
1438:         ////////////////////////
1439:         /**
1440:          * @param QQColumnNode $objQueryNode
1441:          * @param $strSymbol
1442:          * @param mixed|null $mixValue
1443:          * @param mixed|null $mixValueTwo
1444:          * @return QQCondition
1445:          * @throws Exception
1446:          * @throws QCallerException
1447:          */
1448:         static public function _(QQColumnNode $objQueryNode, $strSymbol, $mixValue = null, $mixValueTwo = null) {
1449:             try {
1450:                 switch(strtolower(trim($strSymbol))) {
1451:                     case '=': return QQ::Equal($objQueryNode, $mixValue);
1452:                     case '!=': return QQ::NotEqual($objQueryNode, $mixValue);
1453:                     case '>': return QQ::GreaterThan($objQueryNode, $mixValue);
1454:                     case '<': return QQ::LessThan($objQueryNode, $mixValue);
1455:                     case '>=': return QQ::GreaterOrEqual($objQueryNode, $mixValue);
1456:                     case '<=': return QQ::LessOrEqual($objQueryNode, $mixValue);
1457:                     case 'in': return QQ::In($objQueryNode, $mixValue);
1458:                     case 'not in': return QQ::NotIn($objQueryNode, $mixValue);
1459:                     case 'like': return QQ::Like($objQueryNode, $mixValue);
1460:                     case 'not like': return QQ::NotLike($objQueryNode, $mixValue);
1461:                     case 'is null': return QQ::IsNull($objQueryNode);
1462:                     case 'is not null': return QQ::IsNotNull($objQueryNode);
1463:                     case 'between': return QQ::Between($objQueryNode, $mixValue, $mixValueTwo);
1464:                     case 'not between': return QQ::NotBetween($objQueryNode, $mixValue, $mixValueTwo);
1465:                     default:
1466:                         throw new QCallerException('Unknown Query Comparison Operation: ' . $strSymbol, 0);
1467:                 }
1468:             } catch (QCallerException $objExc) {
1469:                 $objExc->IncrementOffset();
1470:                 throw $objExc;
1471:             }
1472:         }
1473: 
1474:         /////////////////////////
1475:         // QQSubQuery Factories
1476:         /////////////////////////
1477: 
1478:         /**
1479:          * @param string $strSql Sql string. Use {1}, {2}, etc. to represent nodes inside of the sql string.
1480:          * @param null|QQNode[] $objParentQueryNodes    Array of nodes to specify replacement value in the sql.
1481:          * @return QQSubQuerySqlNode
1482:          */
1483:         static public function SubSql($strSql, $objParentQueryNodes = null) {
1484:             $objParentQueryNodeArray = func_get_args();
1485:             return new QQSubQuerySqlNode($strSql, $objParentQueryNodeArray);
1486:         }
1487: 
1488:         static public function Virtual($strName, QQSubQueryNode $objSubQueryDefinition = null) {
1489:             return new QQVirtualNode($strName, $objSubQueryDefinition);
1490:         }
1491: 
1492:         /**
1493:          * Converts a virtual attribute name to an alias used in the query. The name is converted to an identifier
1494:          * that will work on any SQL database. In the query itself, the name
1495:          * will have two underscores in front of the alias name to prevent conflicts with column names.
1496:          *
1497:          * @param $strName
1498:          * @return mixed|string
1499:          */
1500:         static public function GetVirtualAlias($strName) {
1501:             $strName = trim($strName);
1502:             $strName = str_replace(" ", "_", $strName);
1503:             $strName = strtolower($strName);
1504:             return $strName;
1505:         }
1506: 
1507:         /////////////////////////
1508:         // QQClause Factories
1509:         /////////////////////////
1510: 
1511:         static public function Clause(/* parameterized list of QQClause objects */) {
1512:             $objClauseArray = array();
1513: 
1514:             foreach (func_get_args() as $objClause)
1515:                 if ($objClause) {
1516:                     if (!($objClause instanceof QQClause))
1517:                         throw new QCallerException('Non-QQClause object was passed in to QQ::Clause');
1518:                     else
1519:                         array_push($objClauseArray, $objClause);
1520:                 }
1521: 
1522:             return $objClauseArray;
1523:         }
1524: 
1525:         static public function OrderBy(/* array and/or parameterized list of QQNode objects*/) {
1526:             return new QQOrderBy(func_get_args());
1527:         }
1528: 
1529:         static public function GroupBy(/* array and/or parameterized list of QQNode objects*/) {
1530:             return new QQGroupBy(func_get_args());
1531:         }
1532: 
1533:         static public function Having(QQSubQuerySqlNode $objNode) {
1534:             return new QQHavingClause($objNode);
1535:         }
1536: 
1537:         static public function Count(QQColumnNode $objNode, $strAttributeName) {
1538:             return new QQCount($objNode, $strAttributeName);
1539:         }
1540: 
1541:         static public function Sum(QQColumnNode $objNode, $strAttributeName) {
1542:             return new QQSum($objNode, $strAttributeName);
1543:         }
1544: 
1545:         static public function Minimum(QQColumnNode $objNode, $strAttributeName) {
1546:             return new QQMinimum($objNode, $strAttributeName);
1547:         }
1548: 
1549:         static public function Maximum(QQColumnNode $objNode, $strAttributeName) {
1550:             return new QQMaximum($objNode, $strAttributeName);
1551:         }
1552: 
1553:         static public function Average(QQColumnNode $objNode, $strAttributeName) {
1554:             return new QQAverage($objNode, $strAttributeName);
1555:         }
1556: 
1557:         static public function Expand(QQNode $objNode, QQCondition $objJoinCondition = null, QQSelect $objSelect = null) {
1558: //          if (gettype($objNode) == 'string')
1559: //              return new QQExpandVirtualNode(new QQVirtualNode($objNode));
1560: 
1561:             if ($objNode instanceof QQVirtualNode)
1562:                 return new QQExpandVirtualNode($objNode);
1563:             else
1564:                 return new QQExpand($objNode, $objJoinCondition, $objSelect);
1565:         }
1566: 
1567:         static public function ExpandAsArray(QQNode $objNode, $objCondition = null, QQSelect $objSelect = null) {
1568:             return new QQExpandAsArray($objNode, $objCondition, $objSelect);
1569:         }
1570: 
1571:         static public function Select(/* array and/or parameterized list of QQNode objects*/) {
1572:             if (func_num_args() == 1 && is_array($a = func_get_arg(0))) {
1573:                 return new QQSelect($a);
1574:             } else {
1575:                 return new QQSelect(func_get_args());
1576:             }
1577:         }
1578: 
1579:         static public function LimitInfo($intMaxRowCount, $intOffset = 0) {
1580:             return new QQLimitInfo($intMaxRowCount, $intOffset);
1581:         }
1582: 
1583:         static public function Distinct() {
1584:             return new QQDistinct();
1585:         }
1586: 
1587:         /**
1588:          * Searches for all the QQSelect clauses and merges them into one clause and returns that clause.
1589:          * Returns null if none found.
1590:          *
1591:          * @param QQClause[]|QQClause|null $objClauses QQClause object or array of QQClause objects
1592:          * @return QQSelect QQSelect clause containing all the nodes from all the QQSelect clauses from $objClauses,
1593:          * or null if $objClauses contains no QQSelect clauses
1594:          */
1595:         public static function ExtractSelectClause($objClauses) {
1596:             if ($objClauses instanceof QQSelect)
1597:                 return $objClauses;
1598: 
1599:             if (is_array($objClauses)) {
1600:                 $hasSelects = false;
1601:                 $objSelect = QQuery::Select();
1602:                 foreach ($objClauses as $objClause) {
1603:                     if ($objClause instanceof QQSelect) {
1604:                         $hasSelects = true;
1605:                         $objSelect->Merge($objClause);
1606:                     }
1607:                 }
1608:                 if (!$hasSelects)
1609:                     return null;
1610:                 return $objSelect;
1611:             }
1612:             return null;
1613:         }
1614: 
1615:         /////////////////////////
1616:         // Aliased QQ Node
1617:         /////////////////////////
1618:         /**
1619:          * Returns the supplied node object, after setting its alias to the value supplied
1620:          *
1621:          * @param QQNode $objNode The node object to set alias on
1622:          * @param string $strAlias The alias to set
1623:          * @return mixed The same node that was passed in, but with the alias set
1624:          *
1625:          */
1626:         static public function Alias(QQNode $objNode, $strAlias)
1627:         {
1628:             $objNode->SetAlias($strAlias);
1629:             return $objNode;
1630:         }
1631: 
1632:         /////////////////////////
1633:         // NamedValue QQ Node
1634:         /////////////////////////
1635:         static public function NamedValue($strName) {
1636:             return new QQNamedValue($strName);
1637:         }
1638: 
1639:         /**
1640:          * Apply an arbitrary scalar function using the given parameters. See below for functions that let you apply
1641:          * common SQL functions. The list below only includes sql operations that are generic to all supported versions
1642:          * of SQL. However, you can call Func directly with any named function that works in your current SQL version,
1643:          * knowing that it might not be cross platform compatible if you ever change SQL engines.
1644:          *
1645:          * @param $strName The function name, like ABS or POWER
1646:          * @param QQNode|mixed $param1 The function parameter. Can be a qq node or a number.
1647:          * @return QQFunctionNode The resulting wrapper node
1648:          */
1649:         static public function Func($strName, $param1 /** ... */) {
1650:             $args = func_get_args();
1651:             $strFunc = array_shift($args);
1652:             return new QQFunctionNode($strFunc, $args);
1653:         }
1654: 
1655:         //////////////////////////////
1656:         // Various common functions
1657:         //////////////////////////////
1658: 
1659:         /**
1660:          * Return the absolute value
1661:          * 
1662:          * @param QQNode $param The qq node to apply the function to.
1663:          * @return QQFunctionNode The resulting wrapper node
1664:          */
1665:         static public function Abs($param) {
1666:             return QQ::Func('ABS', $param);
1667:         }
1668:         /**
1669:          * Return the smallest integer value not less than the argument
1670:          * 
1671:          * @param QQNode $param The qq node to apply the function to.
1672:          * @return QQFunctionNode The resulting wrapper node
1673:          */
1674:         static public function Ceil($param) {
1675:             return QQ::Func('CEIL', $param);
1676:         }
1677:         /**
1678:          * Return the largest integer value not greater than the argument
1679:          * 
1680:          * @param QQNode $param The qq node to apply the function to.
1681:          * @return QQFunctionNode The resulting wrapper node
1682:          */
1683:         static public function Floor($param) {
1684:             return QQ::Func('FLOOR', $param);
1685:         }
1686:         /**
1687:          * Return the remainder
1688:          * 
1689:          * @param QQNode $param The qq node to apply the function to.
1690:          * @return QQFunctionNode The resulting wrapper node
1691:          */
1692:         static public function Mod($dividend, $divider) {
1693:             return QQ::Func('MOD', $dividend, $divider);
1694:         }
1695:         /**
1696:          * Return the argument raised to the specified power
1697:          * 
1698:          * @param QQNode $param The qq node to apply the function to.
1699:          * @return QQFunctionNode The resulting wrapper node
1700:          */
1701:         static public function Power($base, $exponent) {
1702:             return QQ::Func('POWER', $base, $exponent);
1703:         }
1704:         /**
1705:          *  Return the square root of the argument
1706:          * 
1707:          * @param QQNode $param The qq node to apply the function to.
1708:          * @return QQFunctionNode The resulting wrapper node
1709:          */
1710:         static public function Sqrt($param) {
1711:             return QQ::Func('SQRT', $param);
1712:         }
1713: 
1714:         /**
1715:          * Apply an arbitrary math operation to 2 or more operands. Operands can be scalar values, or column nodes.
1716:          * 
1717:          * @param $strOperation The operation symbol, like + or *
1718:          * @param QQNode|mixed $param1 The first parameter
1719:          * @return \QQMathNode The resulting wrapper node
1720:          */
1721:         static public function MathOp($strOperation, $param1 /** ... */) {
1722:             $args = func_get_args();
1723:             $strFunc = array_shift($args);
1724:             return new QQMathNode($strFunc, $args);
1725:         }
1726: 
1727:         /**
1728:          * The multiplication operation
1729:          * 
1730:          * @param QQNode|mixed $op1 The first operand
1731:          * @param QQNode|mixed $op2 The second operand
1732:          * @return \QQMathNode The resulting wrapper node
1733:          */
1734:         static public function Mul($op1, $op2 /** ... */) {
1735:             return new QQMathNode('*', func_get_args());
1736:         }
1737:         /**
1738:          * The division operation
1739:          * 
1740:          * @param QQNode|mixed $op1 The first operand
1741:          * @param QQNode|mixed $op2 The second operand
1742:          * @return \QQMathNode The resulting wrapper node
1743:          */
1744:         static public function Div($op1, $op2 /** ... */) {
1745:             return new QQMathNode('/', func_get_args());
1746:         }
1747:         /**
1748:          * The subtraction operation
1749:          * 
1750:          * @param QQNode|mixed $op1 The first operand
1751:          * @param QQNode|mixed $op2 The second operand
1752:          * @return \QQMathNode The resulting wrapper node
1753:          */
1754:         static public function Sub($op1, $op2 /** ... */) {
1755:             return new QQMathNode('-', func_get_args());
1756:         }
1757:         /**
1758:          * The addition operation
1759:          * 
1760:          * @param QQNode|mixed $op1 The first operand
1761:          * @param QQNode|mixed $op2 The second operand
1762:          * @return \QQMathNode The resulting wrapper node
1763:          */
1764:         static public function Add($op1, $op2 /** ... */) {
1765:             return new QQMathNode('+', func_get_args());
1766:         }
1767:         /**
1768:          * The negation unary operation
1769:          *
1770:          * @param QQNode|mixed $op1 The first operand
1771:          * @return \QQMathNode The resulting wrapper node
1772:          */
1773:         static public function Neg($op1) {
1774:             return new QQMathNode('-', [$op1]);
1775:         }
1776: 
1777:     }
1778: 
1779:     abstract class QQSubQueryNode extends QQColumnNode {
1780:     }
1781: 
1782:     abstract class QQNoParentNode extends QQSubQueryNode {
1783:         /**
1784:          * @return string
1785:          */
1786:         public function GetTable() {
1787:             return $this->FullAlias();
1788:         }
1789:         /**
1790:          * Change the alias of the node, primarily for joining the same table more than once.
1791:          *
1792:          * @param $strAlias
1793:          * @throws Exception
1794:          * @throws QCallerException
1795:          */
1796:         public function SetAlias($strAlias) {
1797:             if ($this->strFullAlias) {
1798:                 throw new Exception ("You cannot set an alias on a node after you have used it in a query. See the examples doc. You must set the alias while creating the node.");
1799:             }
1800:             try {
1801:                 // Changing the alias of the node. Must change pointers to the node too.
1802:                 $strNewAlias = QType::Cast($strAlias, QType::String);
1803:                 $this->strAlias = $strNewAlias;
1804:             } catch (QCallerException $objExc) {
1805:                 $objExc->IncrementOffset();
1806:                 throw $objExc;
1807:             }
1808:         }
1809:         /**
1810:          * Aid to generating full aliases. Recursively gets and sets the parent alias, eventually creating, caching and returning
1811:          * an alias for itself.
1812:          * @return string
1813:          */
1814:         public function FullAlias() {
1815:             if ($this->strFullAlias) {
1816:                 return $this->strFullAlias;
1817:             } else {
1818:                 assert (!empty($this->strAlias));   // Alias should always be set by default
1819:                 return $this->strAlias;
1820:             }
1821:         }
1822:     }
1823: 
1824:     class QQSubQueryCountNode extends QQSubQueryNode {
1825:         protected $strFunctionName = 'COUNT';
1826:     }
1827: 
1828:     class QQSubQuerySqlNode extends QQNoParentNode {
1829:         protected $strSql;
1830:         /** @var QQNode[] */
1831:         protected $objParentQueryNodes;
1832:         /**
1833:          * @param $strSql
1834:          * @param null|QQColumnNode[] $objParentQueryNodes
1835:          */
1836:         public function __construct($strSql, $objParentQueryNodes = null) {
1837:             parent::__construct('', '', '');
1838:             $this->objParentNode = true;
1839:             $this->objParentQueryNodes = $objParentQueryNodes;
1840:             $this->strSql = $strSql;
1841:         }
1842: 
1843:         /**
1844:          * @param QQueryBuilder $objBuilder
1845:          * @return string
1846:          */
1847:         public function GetColumnAlias(QQueryBuilder $objBuilder) {
1848:             $strSql = $this->strSql;
1849:             for ($intIndex = 1; $intIndex < count($this->objParentQueryNodes); $intIndex++) {
1850:                 if (!is_null($this->objParentQueryNodes[$intIndex]))
1851:                     $strSql = str_replace('{' . $intIndex . '}', $this->objParentQueryNodes[$intIndex]->GetColumnAlias($objBuilder), $strSql);
1852:             }
1853:             return '(' . $strSql . ')';
1854:         }
1855:     }
1856: 
1857:     class QQVirtualNode extends QQNoParentNode {
1858:         protected $objSubQueryDefinition;
1859: 
1860:         /**
1861:          * @param $strName
1862:          * @param QQSubQueryNode|null $objSubQueryDefinition
1863:          */
1864:         public function __construct($strName, QQSubQueryNode $objSubQueryDefinition = null) {
1865:             parent::__construct('', '', '');
1866:             $this->objParentNode = true;
1867:             $this->strName = QQ::GetVirtualAlias($strName);
1868:             $this->strAlias = $this->strName;
1869:             $this->objSubQueryDefinition = $objSubQueryDefinition;
1870:         }
1871: 
1872:         /**
1873:          * @param QQueryBuilder $objBuilder
1874:          * @return string
1875:          * @throws Exception
1876:          * @throws QCallerException
1877:          */
1878:         public function GetColumnAlias(QQueryBuilder $objBuilder) {
1879:             if ($this->objSubQueryDefinition) {
1880:                 $objBuilder->SetVirtualNode($this->strName, $this->objSubQueryDefinition);
1881:                 return $this->objSubQueryDefinition->GetColumnAlias($objBuilder);
1882:             } else {
1883:                 try {
1884:                     $objNode = $objBuilder->GetVirtualNode($this->strName);
1885:                     return $objNode->GetColumnAlias($objBuilder);
1886:                 } catch (QCallerException $objExc) {
1887:                     $objExc->IncrementOffset();
1888:                     $objExc->IncrementOffset();
1889:                     throw $objExc;
1890:                 }
1891:             }
1892:         }
1893:         public function GetAttributeName() {
1894:             return $this->strName;
1895:         }
1896: 
1897:         public function HasSubquery() {
1898:             return $this->objSubQueryDefinition != null;
1899:         }
1900:     }
1901: 
1902:     class QQFunctionNode extends QQSubQueryNode {
1903:         /** @var  string */
1904:         protected $strFunctionName;
1905:         /** @var  array Could be constants or column nodes */
1906:         protected $params;
1907: 
1908:         /**
1909:          * @param $strName
1910:          * @param QQSubQueryNode|null $objSubQueryDefinition
1911:          */
1912:         public function __construct($strFunctionName, $params) {
1913:             parent::__construct('', '', '');
1914:             $this->strFunctionName = $strFunctionName;
1915:             $this->params = $params;
1916:         }
1917: 
1918:         /**
1919:          * @param QQueryBuilder $objBuilder
1920:          * @return string
1921:          */
1922:         public function GetColumnAlias(QQueryBuilder $objBuilder) {
1923:             $strSql = $this->strFunctionName . '(';
1924:             foreach ($this->params as $param) {
1925:                 if ($param instanceof QQColumnNode) {
1926:                     $strSql .= $param->GetColumnAlias($objBuilder);
1927:                 }
1928:                 else {
1929:                     // just a basic value
1930:                     $strSql .= $param;
1931:                 }
1932:                 $strSql .= ',';
1933:             }
1934:             $strSql = substr($strSql, 0, -1);   // get rid of last comma
1935:             $strSql .= ')';
1936:             return $strSql;
1937:         }
1938: 
1939:         public function __toString() {
1940:             return 'QQFunctionNode ' . $this->strFunctionName;
1941:         }
1942:     }
1943: 
1944:     class QQMathNode extends QQSubQueryNode {
1945:         /** @var  string */
1946:         protected $strOperation;
1947:         /** @var  array Could be constants or column nodes */
1948:         protected $params;
1949: 
1950:         /**
1951:          * @param $strName
1952:          * @param QQSubQueryNode|null $objSubQueryDefinition
1953:          */
1954:         public function __construct($strOperation, $params) {
1955:             parent::__construct('', '', '');
1956:             $this->strOperation = $strOperation;
1957:             $this->params = $params;
1958:         }
1959: 
1960:         /**
1961:          * @param QQueryBuilder $objBuilder
1962:          * @return string
1963:          */
1964:         public function GetColumnAlias(QQueryBuilder $objBuilder) {
1965:             if (count($this->params) == 0) return '';
1966: 
1967:             $strSql = '(';
1968: 
1969:             if (count($this->params) == 1) {
1970:                 // unary
1971:                 $strSql .= $this->strOperation;
1972:             }
1973:             foreach ($this->params as $param) {
1974:                 if ($param instanceof QQColumnNode) {
1975:                     $strSql .= $param->GetColumnAlias($objBuilder);
1976:                 }
1977:                 else {
1978:                     // just a basic value
1979:                     $strSql .= $param;
1980:                 }
1981:                 $strSql .= ' ' . $this->strOperation . ' ';
1982:             }
1983:             $strSql = substr($strSql, 0, -(strlen($this->strOperation) + 2));   // get rid of last operation
1984:             $strSql .= ')';
1985:             return $strSql;
1986:         }
1987: 
1988:         public function __toString() {
1989:             return 'QQMathNode ' . $this->strOperation;
1990:         }
1991: 
1992:     }
1993: 
1994: 
1995:     abstract class QQClause extends QBaseClass {
1996:         abstract public function UpdateQueryBuilder(QQueryBuilder $objBuilder);
1997:         abstract public function __toString();
1998:     }
1999: 
2000:     /**
2001:      * Class QQOrderBy: Represents an 'ORDER BY' statement on SQL/DB level
2002:      */
2003:     class QQOrderBy extends QQClause {
2004:         /** @var mixed[]  */
2005:         protected $objNodeArray;
2006: 
2007:         /**
2008:          * CollapseNodes makes sure a node list is vetted, and turned into a node list.
2009:          * This also allows table nodes to be used in certain column node contexts, in which it will
2010:          * substitute the primary key node in this situation.
2011:          *
2012:          * @param $mixParameterArray
2013:          * @return array
2014:          * @throws QCallerException
2015:          * @throws QInvalidCastException
2016:          */
2017:         protected function CollapseNodes($mixParameterArray) {
2018:             /** @var QQNode[] $objNodeArray */
2019:             $objNodeArray = array();
2020:             foreach ($mixParameterArray as $mixParameter) {
2021:                 if (is_array($mixParameter)) {
2022:                     $objNodeArray = array_merge($objNodeArray, $mixParameter);
2023:                 } else {
2024:                     array_push($objNodeArray, $mixParameter);
2025:                 }
2026:             }
2027: 
2028:             $blnPreviousIsNode = false;
2029:             $objFinalNodeArray = array();
2030:             foreach ($objNodeArray as $objNode) {
2031:                 if (!($objNode instanceof QQNode || $objNode instanceof QQCondition)) {
2032:                     if (!$blnPreviousIsNode)
2033:                         throw new QCallerException('OrderBy clause parameters must all be QQNode or QQCondition objects followed by an optional true/false "Ascending Order" option', 3);
2034:                     $blnPreviousIsNode = false;
2035:                     array_push($objFinalNodeArray, $objNode);
2036:                 } elseif ($objNode instanceof QQCondition) {
2037:                     $blnPreviousIsNode = true;
2038:                     array_push($objFinalNodeArray, $objNode);
2039:                 } else {
2040:                     if (!$objNode->_ParentNode) {
2041:                         throw new QInvalidCastException('Unable to cast "' . $objNode->_Name . '" table to Column-based QQNode', 4);
2042:                     }
2043:                     if ($objNode->_PrimaryKeyNode) { // if a table node, then use the primary key of the table
2044:                         array_push($objFinalNodeArray, $objNode->_PrimaryKeyNode);
2045:                     } else {
2046:                         array_push($objFinalNodeArray, $objNode);
2047:                     }
2048:                     $blnPreviousIsNode = true;
2049:                 }
2050:             }
2051: 
2052:             if (count($objFinalNodeArray)) {
2053:                 return $objFinalNodeArray;
2054:             } else {
2055:                 throw new QCallerException('No parameters passed in to OrderBy clause', 3);
2056:             }
2057:         }
2058: 
2059:         /**
2060:          * Constructor function
2061:          *
2062:          * @param $mixParameterArray
2063:          *
2064:          * @throws QCallerException|QInvalidCastException
2065:          */
2066:         public function __construct($mixParameterArray) {
2067:             $this->objNodeArray = $this->CollapseNodes($mixParameterArray);
2068:         }
2069: 
2070:         /**
2071:          * Updates the query builder. We delay processing of orderby clauses until just before statement creation.
2072:          *
2073:          * @param QQueryBuilder $objBuilder
2074:          */
2075:         public function UpdateQueryBuilder(QQueryBuilder $objBuilder) {
2076:             $objBuilder->SetOrderByClause($this);
2077:         }
2078: 
2079:         /**
2080:          * Updates the query builder according to this clause. This is called by the query builder only.
2081:          *
2082:          * @param QQueryBuilder $objBuilder
2083:          *
2084:          * @throws Exception|QCallerException
2085:          */
2086:         public function _UpdateQueryBuilder(QQueryBuilder $objBuilder) {
2087:             $intLength = count($this->objNodeArray);
2088:             for ($intIndex = 0; $intIndex < $intLength; $intIndex++) {
2089:                 $objNode = $this->objNodeArray[$intIndex];
2090:                 if ($objNode instanceof QQVirtualNode) {
2091:                     if ($objNode->HasSubquery()) {
2092:                         throw new QCallerException('You cannot define a virtual node in an order by clause. You must use an Expand clause to define it.');
2093:                     }
2094:                     $strOrderByCommand = '__' . $objNode->GetAttributeName();
2095:                 } elseif ($objNode instanceof QQColumnNode) {
2096:                     /** @var QQColumnNode $objNode */
2097:                     $strOrderByCommand = $objNode->GetColumnAlias($objBuilder);
2098:                 } elseif ($objNode instanceof QQCondition) {
2099:                     /** @var QQCondition $objNode */
2100:                     $strOrderByCommand = $objNode->GetWhereClause($objBuilder);
2101:                 } else {
2102:                     $strOrderByCommand = '';
2103:                 }
2104: 
2105:                 // Check to see if they want a ASC/DESC declarator
2106:                 if ((($intIndex + 1) < $intLength) &&
2107:                     !($this->objNodeArray[$intIndex + 1] instanceof QQNode)) {
2108:                     if ((!$this->objNodeArray[$intIndex + 1]) ||
2109:                         (trim(strtolower($this->objNodeArray[$intIndex + 1])) == 'desc'))
2110:                         $strOrderByCommand .= ' DESC';
2111:                     else
2112:                         $strOrderByCommand .= ' ASC';
2113:                     $intIndex++;
2114:                 }
2115: 
2116:                 $objBuilder->AddOrderByItem($strOrderByCommand);
2117:             }
2118:         }
2119: 
2120: 
2121: 
2122:         /**
2123:          * This is used primarly by datagrids wanting to use the "old Beta 2" style of
2124:          * Manual Queries.  This allows a datagrid to use QQ::OrderBy even though
2125:          * the manually-written Load method takes in Beta 2 string-based SortByCommand information.
2126:          *
2127:          * @return string
2128:          */
2129:         public function GetAsManualSql() {
2130:             $strOrderByArray = array();
2131:             $intLength = count($this->objNodeArray);
2132:             for ($intIndex = 0; $intIndex < $intLength; $intIndex++) {
2133:                 $strOrderByCommand = $this->objNodeArray[$intIndex]->GetAsManualSqlColumn();
2134: 
2135:                 // Check to see if they want a ASC/DESC declarator
2136:                 if ((($intIndex + 1) < $intLength) &&
2137:                     !($this->objNodeArray[$intIndex + 1] instanceof QQNode)) {
2138:                     if ((!$this->objNodeArray[$intIndex + 1]) ||
2139:                         (trim(strtolower($this->objNodeArray[$intIndex + 1])) == 'desc'))
2140:                         $strOrderByCommand .= ' DESC';
2141:                     else
2142:                         $strOrderByCommand .= ' ASC';
2143:                     $intIndex++;
2144:                 }
2145: 
2146:                 array_push($strOrderByArray, $strOrderByCommand);
2147:             }
2148: 
2149:             return implode(',', $strOrderByArray);
2150:         }
2151: 
2152:         public function __toString() {
2153:             return 'QQOrderBy Clause';
2154:         }
2155:     }
2156: 
2157:     class QQDistinct extends QQClause {
2158:         public function UpdateQueryBuilder(QQueryBuilder $objBuilder) {
2159:             $objBuilder->SetDistinctFlag();
2160:         }
2161:         public function __toString() {
2162:             return 'QQDistinct Clause';
2163:         }
2164:     }
2165: 
2166:     class QQLimitInfo extends QQClause {
2167:         protected $intMaxRowCount;
2168:         protected $intOffset;
2169:         public function __construct($intMaxRowCount, $intOffset = 0) {
2170:             try {
2171:                 $this->intMaxRowCount = QType::Cast($intMaxRowCount, QType::Integer);
2172:                 $this->intOffset = QType::Cast($intOffset, QType::Integer);
2173:             } catch (QCallerException $objExc) {
2174:                 $objExc->IncrementOffset();
2175:                 throw $objExc;
2176:             }
2177:         }
2178:         public function UpdateQueryBuilder(QQueryBuilder $objBuilder) {
2179:             if ($this->intOffset)
2180:                 $objBuilder->SetLimitInfo($this->intOffset . ',' . $this->intMaxRowCount);
2181:             else
2182:                 $objBuilder->SetLimitInfo($this->intMaxRowCount);
2183:         }
2184:         public function __toString() {
2185:             return 'QQLimitInfo Clause';
2186:         }
2187: 
2188:         public function __get($strName) {
2189:             switch ($strName) {
2190:                 case 'MaxRowCount':
2191:                     return $this->intMaxRowCount;
2192:                 case 'Offset':
2193:                     return $this->intOffset;
2194:             default:
2195:                 try {
2196:                     return parent::__get($strName);
2197:                 } catch (QCallerException $objExc) {
2198:                     $objExc->IncrementOffset();
2199:                     throw $objExc;
2200:                 }
2201:             }
2202:         }
2203:     }
2204: 
2205:     class QQExpandVirtualNode extends QQClause {
2206:         protected $objNode;
2207:         public function __construct(QQVirtualNode $objNode) {
2208:             $this->objNode = $objNode;
2209:         }
2210:         public function UpdateQueryBuilder(QQueryBuilder $objBuilder) {
2211:             try {
2212:                 $objBuilder->AddSelectFunction(null, $this->objNode->GetColumnAlias($objBuilder), $this->objNode->GetAttributeName());
2213:             } catch (QCallerException $objExc) {
2214:                 $objExc->IncrementOffset();
2215:                 throw $objExc;
2216:             }
2217:         }
2218:         public function __toString() {
2219:             return 'QQExpandVirtualNode Clause';
2220:         }
2221:     }
2222:     class QQExpand extends QQClause {
2223:         /** @var QQNode */
2224:         protected $objNode;
2225:         protected $objJoinCondition;
2226:         protected $objSelect;
2227: 
2228:         public function __construct($objNode, QQCondition $objJoinCondition = null, QQSelect $objSelect  = null) {
2229:             // Check against root and table QQNodes
2230:             if ($objNode instanceof QQAssociationNode)
2231:                 throw new QCallerException('Expand clause parameter cannot be an association table node. Try expanding one level deeper.', 2);
2232:             else if (!($objNode instanceof QQNode))
2233:                 throw new QCallerException('Expand clause parameter must be a QQNode object', 2);
2234:             else if (!$objNode->_ParentNode)
2235:                 throw new QInvalidCastException('Cannot expand on this kind of node.', 3);
2236: 
2237:             $this->objNode = $objNode;
2238:             $this->objJoinCondition = $objJoinCondition;
2239:             $this->objSelect = $objSelect;
2240:         }
2241:         public function UpdateQueryBuilder(QQueryBuilder $objBuilder) {
2242:             $this->objNode->Join($objBuilder, true, $this->objJoinCondition, $this->objSelect);
2243:         }
2244:         public function __toString() {
2245:             return 'QQExpand Clause';
2246:         }
2247:     }
2248: 
2249:     /*
2250:      * Allows a custom sql injection as a having clause. Its up to you to make sure its correct, but you can use subquery placeholders
2251:      * to expand column names. Standard SQL has limited Having capabilities, but many SQL engines have useful extensions.
2252:      */
2253:     class QQHavingClause extends QQClause {
2254:         protected $objNode;
2255:         public function __construct(QQSubQueryNode $objSubQueryDefinition) {
2256:             $this->objNode = $objSubQueryDefinition;
2257:         }
2258:         public function UpdateQueryBuilder(QQueryBuilder $objBuilder) {
2259:             $objBuilder->AddHavingItem (
2260:                 $this->objNode->GetColumnAlias($objBuilder)
2261:             );
2262:         }
2263:         public function GetAttributeName() {
2264:             return $this->objNode->strName;
2265:         }
2266:         public function __toString() {
2267:             return "Having Clause";
2268:         }
2269: 
2270:     }
2271: 
2272:     abstract class QQAggregationClause extends QQClause {
2273:         /** @var QQNode */
2274:         protected $objNode;
2275:         protected $strAttributeName;
2276:         protected $strFunctionName;
2277:         public function __construct(QQColumnNode $objNode, $strAttributeName) {
2278:             $this->objNode = QQ::Func($this->strFunctionName, $objNode);
2279:             $this->strAttributeName = QQ::GetVirtualAlias($strAttributeName); // virtual attributes are queried lower case
2280:         }
2281:         public function UpdateQueryBuilder(QQueryBuilder $objBuilder) {
2282:             $objBuilder->SetVirtualNode($this->strAttributeName, $this->objNode);
2283:             $objBuilder->AddSelectFunction(null, $this->objNode->GetColumnAlias($objBuilder), $this->strAttributeName);
2284:         }
2285:     }
2286:     class QQCount extends QQAggregationClause {
2287:         protected $strFunctionName = 'COUNT';
2288:         public function __toString() {
2289:             return 'QQCount Clause';
2290:         }
2291:     }
2292:     class QQSum extends QQAggregationClause {
2293:         protected $strFunctionName = 'SUM';
2294:         public function __toString() {
2295:             return 'QQSum Clause';
2296:         }
2297:     }
2298:     class QQMinimum extends QQAggregationClause {
2299:         protected $strFunctionName = 'MIN';
2300:         public function __toString() {
2301:             return 'QQMinimum Clause';
2302:         }
2303:     }
2304:     class QQMaximum extends QQAggregationClause {
2305:         protected $strFunctionName = 'MAX';
2306:         public function __toString() {
2307:             return 'QQMaximum Clause';
2308:         }
2309:     }
2310:     class QQAverage extends QQAggregationClause {
2311:         protected $strFunctionName = 'AVG';
2312:         public function __toString() {
2313:             return 'QQAverage Clause';
2314:         }
2315:     }
2316: 
2317:     class QQExpandAsArray extends QQClause {
2318:         /** @var QQNode|QQAssociationNode */
2319:         protected $objNode;
2320:         protected $objCondition;
2321:         protected $objSelect;
2322: 
2323:         /**
2324:          * @param QQNode $objNode
2325:          * @param null|mixed $objCondition
2326:          * @param QQSelect|null $objSelect
2327:          * @throws QCallerException
2328:          */
2329:         public function __construct(QQNode $objNode, $objCondition = null, QQSelect $objSelect = null) {
2330:             // For backwards compatibility with v2, which did not have a condition parameter, we will detect what the 2nd param is.
2331:             // Ensure that this is an QQAssociationNode
2332:             if ((!($objNode instanceof QQAssociationNode)) && (!($objNode instanceof QQReverseReferenceNode)))
2333:                 throw new QCallerException('ExpandAsArray clause parameter must be an Association or ReverseReference node', 2);
2334: 
2335:             if ($objCondition instanceof QQSelect) {
2336:                 $this->objNode = $objNode;
2337:                 $this->objSelect = $objCondition;
2338:             } else {
2339:                 if (!is_null($objCondition)) {
2340:                     /*
2341:                     if ($objNode instanceof QQAssociationNode) {
2342:                         throw new QCallerException('Join conditions can only be applied to reverse reference nodes here. Try putting a condition on the next level down.', 2);
2343:                     }*/
2344:                     if (!($objCondition instanceof QQCondition)) {
2345:                         throw new QCallerException('Condition clause parameter must be a QQCondition dervied class.', 2);
2346:                     }
2347:                 }
2348:                 $this->objNode = $objNode;
2349:                 $this->objSelect = $objSelect;
2350:                 $this->objCondition = $objCondition;
2351:             }
2352: 
2353:         }
2354:         public function UpdateQueryBuilder(QQueryBuilder $objBuilder) {
2355:             if ($this->objNode instanceof QQAssociationNode) {
2356:                 // The below works because all code generated association nodes will have a _ChildTableNode parameter.
2357:                 $this->objNode->_ChildTableNode->Join($objBuilder, true, $this->objCondition, $this->objSelect);
2358:             }
2359:             else {
2360:                 $this->objNode->Join($objBuilder, true, $this->objCondition, $this->objSelect);
2361:             }
2362:             $objBuilder->AddExpandAsArrayNode($this->objNode);
2363:         }
2364:         public function __toString() {
2365:             return 'QQExpandAsArray Clause';
2366:         }
2367:     }
2368: 
2369:     class QQGroupBy extends QQClause {
2370:         /** @var QQColumnNode[] */
2371:         protected $objNodeArray;
2372: 
2373:         /**
2374:          * CollapseNodes makes sure a node list is vetted, and turned into a node list.
2375:          * This also allows table nodes to be used in certain column node contexts, in which it will
2376:          * substitute the primary key node in this situation.
2377:          *
2378:          * @param $mixParameterArray
2379:          * @return QColumnNode[]
2380:          * @throws QCallerException
2381:          * @throws QInvalidCastException
2382:          */
2383:         protected function CollapseNodes($mixParameterArray) {
2384:             $objNodeArray = array();
2385:             foreach ($mixParameterArray as $mixParameter) {
2386:                 if (is_array($mixParameter)) {
2387:                     $objNodeArray = array_merge($objNodeArray, $mixParameter);
2388:                 } else {
2389:                     array_push($objNodeArray, $mixParameter);
2390:                 }
2391:             }
2392: 
2393:             $objFinalNodeArray = array();
2394:             foreach ($objNodeArray as $objNode) {
2395:                 /** @var QQNode $objNode */
2396:                 if ($objNode instanceof QQAssociationNode)
2397:                     throw new QCallerException('GroupBy clause parameter cannot be an association table node.', 3);
2398:                 else if (!($objNode instanceof QQNode))
2399:                     throw new QCallerException('GroupBy clause parameters must all be QQNode objects.', 3);
2400: 
2401:                 if (!$objNode->_ParentNode)
2402:                     throw new QInvalidCastException('Unable to cast "' . $objNode->_Name . '" table to Column-based QQNode', 4);
2403: 
2404:                 if ($objNode->_PrimaryKeyNode) {
2405:                     array_push($objFinalNodeArray, $objNode->_PrimaryKeyNode);  // if a table node, use the primary key of the table instead
2406:                 } else
2407:                     array_push($objFinalNodeArray, $objNode);
2408:             }
2409: 
2410:             if (count($objFinalNodeArray))
2411:                 return $objFinalNodeArray;
2412:             else
2413:                 throw new QCallerException('No parameters passed in to Expand clause', 3);
2414:         }
2415:         public function __construct($mixParameterArray) {
2416:             $this->objNodeArray = $this->CollapseNodes($mixParameterArray);
2417:         }
2418:         public function UpdateQueryBuilder(QQueryBuilder $objBuilder) {
2419:             $intLength = count($this->objNodeArray);
2420:             for ($intIndex = 0; $intIndex < $intLength; $intIndex++)
2421:                 $objBuilder->AddGroupByItem($this->objNodeArray[$intIndex]->GetColumnAlias($objBuilder));
2422:         }
2423:         public function __toString() {
2424:             return 'QQGroupBy Clause';
2425:         }
2426:     }
2427: 
2428: 
2429:     class QQSelect extends QQClause {
2430:         /** @var QQNode[] */
2431:         protected $arrNodeObj = array();
2432:         protected $blnSkipPrimaryKey = false;
2433: 
2434:         /**
2435:          * @param QQNode[] $arrNodeObj
2436:          * @throws QCallerException
2437:          */
2438:         public function __construct($arrNodeObj) {
2439:             $this->arrNodeObj = $arrNodeObj;
2440:             foreach ($this->arrNodeObj as $objNode) {
2441:                 if (!($objNode instanceof QQColumnNode)) {
2442:                     throw new QCallerException('Select nodes must be column nodes.', 3);
2443:                 }
2444:             }
2445:         }
2446: 
2447:         public function UpdateQueryBuilder(QQueryBuilder $objBuilder) {
2448:         }
2449: 
2450:         public function AddSelectItems(QQueryBuilder $objBuilder, $strTableName, $strAliasPrefix) {
2451:             foreach ($this->arrNodeObj as $objNode) {
2452:                 $strNodeTable = $objNode->GetTable();
2453:                 if ($strNodeTable == $strTableName) {
2454:                     $objBuilder->AddSelectItem($strTableName, $objNode->_Name, $strAliasPrefix . $objNode->_Name);
2455:                 }
2456:             }
2457:         }
2458: 
2459:         public function Merge(QQSelect $objSelect = null) {
2460:             if ($objSelect) {
2461:                 foreach ($objSelect->arrNodeObj as $objNode) {
2462:                     array_push($this->arrNodeObj, $objNode);
2463:                 }
2464:                 if ($objSelect->blnSkipPrimaryKey) {
2465:                     $this->blnSkipPrimaryKey = true;
2466:                 }
2467:             }
2468:         }
2469: 
2470:         /**
2471:          * @return boolean
2472:          */
2473:         public function SkipPrimaryKey() {
2474:             return $this->blnSkipPrimaryKey;
2475:         }
2476: 
2477:         /**
2478:          * @param boolean $blnSkipPrimaryKey
2479:          */
2480:         public function SetSkipPrimaryKey($blnSkipPrimaryKey) {
2481:             $this->blnSkipPrimaryKey = $blnSkipPrimaryKey;
2482:         }
2483: 
2484:         public function __toString() {
2485:             return 'QQSelectColumn Clause';
2486:         }
2487:     }
2488: 
2489:     // Users can use the QQuery or the shortcut "QQ"
2490:     class QQuery extends QQ {}
2491: 
2492:     /**
2493:      * QQueryBuilder class
2494:      * @property QDatabaseBase $Database
2495:      * @property string $RootTableName
2496:      * @property string[] $ColumnAliasArray
2497:      * @property QQNode $ExpandAsArrayNode
2498:      */
2499:     class QQueryBuilder extends QBaseClass {
2500:         /** @var string[]  */
2501:         protected $strSelectArray;
2502:         /** @var string[]  */
2503:         protected $strColumnAliasArray;
2504:         /** @var int  */
2505:         protected $intColumnAliasCount = 0;
2506:         /** @var string[]  */
2507:         protected $strTableAliasArray;
2508:         /** @var int  */
2509:         protected $intTableAliasCount = 0;
2510:         /** @var string[] */
2511:         protected $strFromArray;
2512:         /** @var string[] */
2513:         protected $strJoinArray;
2514:         /** @var string[] */
2515:         protected $strJoinConditionArray;
2516:         /** @var string[] */
2517:         protected $strWhereArray;
2518:         /** @var string[] */
2519:         protected $strOrderByArray;
2520:         /** @var string[] */
2521:         protected $strGroupByArray;
2522:         /** @var string[] */
2523:         protected $strHavingArray;
2524:         /** @var QQVirtualNode[] */
2525:         protected $objVirtualNodeArray;
2526:         /** @var  string */
2527:         protected $strLimitInfo;
2528:         /** @var  bool */
2529:         protected $blnDistinctFlag;
2530:         /** @var  QQNode */
2531:         protected $objExpandAsArrayNode;
2532:         /** @var  bool */
2533:         protected $blnCountOnlyFlag;
2534: 
2535:         /** @var QDatabaseBase  */
2536:         protected $objDatabase;
2537:         /** @var string  */
2538:         protected $strRootTableName;
2539:         /** @var string  */
2540:         protected $strEscapeIdentifierBegin;
2541:         /** @var string  */
2542:         protected $strEscapeIdentifierEnd;
2543:         /** @var  QQOrderBy */
2544:         protected $objOrderByClause;
2545: 
2546:         /**
2547:          * @param QDatabaseBase $objDatabase
2548:          * @param string $strRootTableName
2549:          */
2550:         public function __construct(QDatabaseBase $objDatabase, $strRootTableName) {
2551:             $this->objDatabase = $objDatabase;
2552:             $this->strEscapeIdentifierBegin = $objDatabase->EscapeIdentifierBegin;
2553:             $this->strEscapeIdentifierEnd = $objDatabase->EscapeIdentifierEnd;
2554:             $this->strRootTableName = $strRootTableName;
2555: 
2556:             $this->strSelectArray = array();
2557:             $this->strColumnAliasArray = array();
2558:             $this->strTableAliasArray = array();
2559:             $this->strFromArray = array();
2560:             $this->strJoinArray = array();
2561:             $this->strJoinConditionArray = array();
2562:             $this->strWhereArray = array();
2563:             $this->strOrderByArray = array();
2564:             $this->strGroupByArray = array();
2565:             $this->strHavingArray = array();
2566:             $this->objVirtualNodeArray = array();
2567:         }
2568: 
2569:         /**
2570:          * @param string $strTableName
2571:          * @param string $strColumnName
2572:          * @param string $strFullAlias
2573:          */
2574:         public function AddSelectItem($strTableName, $strColumnName, $strFullAlias) {
2575:             $strTableAlias = $this->GetTableAlias($strTableName);
2576: 
2577:             if (!array_key_exists($strFullAlias, $this->strColumnAliasArray)) {
2578:                 $strColumnAlias = 'a' . $this->intColumnAliasCount++;
2579:                 $this->strColumnAliasArray[$strFullAlias] = $strColumnAlias;
2580:             } else {
2581:                 $strColumnAlias = $this->strColumnAliasArray[$strFullAlias];
2582:             }
2583: 
2584:             $this->strSelectArray[$strFullAlias] = sprintf('%s%s%s.%s%s%s AS %s%s%s',
2585:                 $this->strEscapeIdentifierBegin, $strTableAlias, $this->strEscapeIdentifierEnd,
2586:                 $this->strEscapeIdentifierBegin, $strColumnName, $this->strEscapeIdentifierEnd,
2587:                 $this->strEscapeIdentifierBegin, $strColumnAlias, $this->strEscapeIdentifierEnd);
2588:         }
2589: 
2590:         /**
2591:          * @param string $strFunctionName
2592:          * @param string $strColumnName
2593:          * @param string $strFullAlias
2594:          */
2595:         public function AddSelectFunction($strFunctionName, $strColumnName, $strFullAlias) {
2596:             $this->strSelectArray[$strFullAlias] = sprintf('%s(%s) AS %s__%s%s',
2597:                 $strFunctionName, $strColumnName,
2598:                 $this->strEscapeIdentifierBegin, $strFullAlias, $this->strEscapeIdentifierEnd);
2599:         }
2600: 
2601:         /**
2602:          * @param string $strTableName
2603:          */
2604:         public function AddFromItem($strTableName) {
2605:             $strTableAlias = $this->GetTableAlias($strTableName);
2606: 
2607:             $this->strFromArray[$strTableName] = sprintf('%s%s%s AS %s%s%s',
2608:                 $this->strEscapeIdentifierBegin, $strTableName, $this->strEscapeIdentifierEnd,
2609:                 $this->strEscapeIdentifierBegin, $strTableAlias, $this->strEscapeIdentifierEnd);
2610:         }
2611: 
2612:         /**
2613:          * @param string $strTableName
2614:          * @return string
2615:          */
2616:         public function GetTableAlias($strTableName) {
2617:             if (!array_key_exists($strTableName, $this->strTableAliasArray)) {
2618:                 $strTableAlias = 't' . $this->intTableAliasCount++;
2619:                 $this->strTableAliasArray[$strTableName] = $strTableAlias;
2620:                 return $strTableAlias;
2621:             } else {
2622:                 return $this->strTableAliasArray[$strTableName];
2623:             }
2624:         }
2625: 
2626:         /**
2627:          * @param string $strJoinTableName
2628:          * @param  string $strJoinTableAlias
2629:          * @param  string $strTableName
2630:          * @param  string $strColumnName
2631:          * @param  string $strLinkedColumnName
2632:          * @param QQCondition|null $objJoinCondition
2633:          * @throws Exception
2634:          * @throws QCallerException
2635:          */
2636:         public function AddJoinItem($strJoinTableName, $strJoinTableAlias, $strTableName, $strColumnName, $strLinkedColumnName, QQCondition $objJoinCondition = null) {
2637:             $strJoinItem = sprintf('LEFT JOIN %s%s%s AS %s%s%s ON %s%s%s.%s%s%s = %s%s%s.%s%s%s',
2638:                 $this->strEscapeIdentifierBegin, $strJoinTableName, $this->strEscapeIdentifierEnd,
2639:                 $this->strEscapeIdentifierBegin, $this->GetTableAlias($strJoinTableAlias), $this->strEscapeIdentifierEnd,
2640: 
2641:                 $this->strEscapeIdentifierBegin, $this->GetTableAlias($strTableName), $this->strEscapeIdentifierEnd,
2642:                 $this->strEscapeIdentifierBegin, $strColumnName, $this->strEscapeIdentifierEnd,
2643: 
2644:                 $this->strEscapeIdentifierBegin, $this->GetTableAlias($strJoinTableAlias), $this->strEscapeIdentifierEnd,
2645:                 $this->strEscapeIdentifierBegin, $strLinkedColumnName, $this->strEscapeIdentifierEnd);
2646: 
2647:                 $strJoinIndex = $strJoinItem;
2648:             try {
2649:                 $strConditionClause = null;
2650:                 if ($objJoinCondition &&
2651:                     ($strConditionClause = $objJoinCondition->GetWhereClause($this, false)))
2652:                     $strJoinItem .= ' AND ' . $strConditionClause;
2653:             } catch (QCallerException $objExc) {
2654:                 $objExc->IncrementOffset();
2655:                 throw $objExc;
2656:             }
2657: 
2658:             /* If this table has already been joined, then we need to check for the following:
2659:                 1. Condition wasn't specified before and we aren't specifying one now
2660:                     Do Nothing --b/c nothing was changed or updated
2661:                 2. Condition wasn't specified before but we ARE specifying one now
2662:                     Update the indexed item in the joinArray with the new JoinItem WITH Condition
2663:                 3. Condition WAS specified before but we aren't specifying one now
2664:                     Do Nothing -- we need to keep the old condition intact
2665:                 4. Condition WAS specified before and we are specifying the SAME one now
2666:                     Do Nothing --b/c nothing was changed or updated
2667:                 5. Condition WAS specified before and we are specifying a DIFFERENT one now
2668:                     Throw exception
2669:             */
2670:             if (array_key_exists($strJoinIndex, $this->strJoinArray)) {
2671:                 // Case 1 and 2
2672:                 if (!array_key_exists($strJoinIndex, $this->strJoinConditionArray)) {
2673: 
2674:                     // Case 1
2675:                     if (!$strConditionClause) {
2676:                         return;
2677: 
2678:                     // Case 2
2679:                     } else {
2680:                         $this->strJoinArray[$strJoinIndex] = $strJoinItem;
2681:                         $this->strJoinConditionArray[$strJoinIndex] = $strConditionClause;
2682:                         return;
2683:                     }
2684:                 }
2685: 
2686:                 // Case 3
2687:                 if (!$strConditionClause)
2688:                     return;
2689: 
2690:                 // Case 4
2691:                 if ($strConditionClause == $this->strJoinConditionArray[$strJoinIndex])
2692:                     return;
2693: 
2694:                 // Case 5
2695:                 throw new QCallerException('You have two different Join Conditions on the same Expanded Table: ' . $strJoinIndex . "\r\n[" . $this->strJoinConditionArray[$strJoinIndex] . ']   vs.   [' . $strConditionClause . ']');
2696:             }
2697: 
2698:             // Create the new JoinItem in the JoinArray
2699:             $this->strJoinArray[$strJoinIndex] = $strJoinItem;
2700: 
2701:             // If there is a condition, record that condition against this JoinIndex
2702:             if ($strConditionClause)
2703:                 $this->strJoinConditionArray[$strJoinIndex] = $strConditionClause;
2704:         }
2705: 
2706:         /**
2707:          * @param  string $strJoinTableName
2708:          * @param  string $strJoinTableAlias
2709:          * @param QQCondition $objJoinCondition
2710:          * @throws Exception
2711:          * @throws QCallerException
2712:          */
2713:         public function AddJoinCustomItem($strJoinTableName, $strJoinTableAlias, QQCondition $objJoinCondition) {
2714:             $strJoinItem = sprintf('LEFT JOIN %s%s%s AS %s%s%s ON ',
2715:                 $this->strEscapeIdentifierBegin, $strJoinTableName, $this->strEscapeIdentifierEnd,
2716:                 $this->strEscapeIdentifierBegin, $this->GetTableAlias($strJoinTableAlias), $this->strEscapeIdentifierEnd
2717:             );
2718: 
2719:             $strJoinIndex = $strJoinItem;
2720: 
2721:             try {
2722:                 if (($strConditionClause = $objJoinCondition->GetWhereClause($this, true)))
2723:                     $strJoinItem .= ' AND ' . $strConditionClause;
2724:             } catch (QCallerException $objExc) {
2725:                 $objExc->IncrementOffset();
2726:                 throw $objExc;
2727:             }
2728: 
2729:             $this->strJoinArray[$strJoinIndex] = $strJoinItem;
2730:         }
2731: 
2732:         /**
2733:          * @param  string $strSql
2734:          */
2735:         public function AddJoinCustomSqlItem($strSql) {
2736:             $this->strJoinArray[$strSql] = $strSql;
2737:         }
2738: 
2739:         /**
2740:          * @param  string $strItem
2741:          */
2742:         public function AddWhereItem($strItem) {
2743:             array_push($this->strWhereArray, $strItem);
2744:         }
2745: 
2746:         /**
2747:          * @param  string $strItem
2748:          */
2749:         public function AddOrderByItem($strItem) {
2750:             array_push($this->strOrderByArray, $strItem);
2751:         }
2752: 
2753:         /**
2754:          * @param  string $strItem
2755:          */
2756:         public function AddGroupByItem($strItem) {
2757:             array_push($this->strGroupByArray, $strItem);
2758:         }
2759: 
2760:         /**
2761:          * @param  string $strItem
2762:          */
2763:         public function AddHavingItem ($strItem) {
2764:             array_push($this->strHavingArray, $strItem);
2765:         }
2766: 
2767:         /**
2768:          * @param $strLimitInfo
2769:          */
2770:         public function SetLimitInfo($strLimitInfo) {
2771:             $this->strLimitInfo = $strLimitInfo;
2772:         }
2773: 
2774:         public function SetDistinctFlag() {
2775:             $this->blnDistinctFlag = true;
2776:         }
2777: 
2778:         public function SetCountOnlyFlag() {
2779:             $this->blnCountOnlyFlag = true;
2780:         }
2781: 
2782:         /**
2783:          * @param string $strName
2784:          * @param QQSubQueryNode $objNode
2785:          */
2786:         public function SetVirtualNode($strName, QQColumnNode $objNode) {
2787:             $this->objVirtualNodeArray[QQ::GetVirtualAlias($strName)] = $objNode;
2788:         }
2789: 
2790:         /**
2791:          * @param string $strName
2792:          * @return QQColumnNode
2793:          * @throws QCallerException
2794:          */
2795:         public function GetVirtualNode($strName) {
2796:             $strName = QQ::GetVirtualAlias($strName);
2797:             if (isset($this->objVirtualNodeArray[$strName])) {
2798:                 return $this->objVirtualNodeArray[$strName];
2799:             }
2800:             else throw new QCallerException('Undefined Virtual Node: ' . $strName);
2801:         }
2802: 
2803:         /**
2804:          * @param QQNode $objNode
2805:          * @throws QCallerException
2806:          */
2807:         public function AddExpandAsArrayNode(QQNode $objNode) {
2808:             /** @var QQReverseReferenceNode|QQAssociationNode $objNode */
2809:             // build child nodes and find top node of given node
2810:             $objNode->ExpandAsArray = true;
2811:             while ($objNode->_ParentNode) {
2812:                 $objNode = $objNode->_ParentNode;
2813:             }
2814: 
2815:             if (!$this->objExpandAsArrayNode) {
2816:                 $this->objExpandAsArrayNode = $objNode;
2817:             }
2818:             else {
2819:                 // integrate the information into current nodes
2820:                 $this->objExpandAsArrayNode->_MergeExpansionNode ($objNode);
2821:             }
2822:         }
2823: 
2824:         /**
2825:          * @return string
2826:          */
2827:         public function GetStatement() {
2828:             $this->ProcessClauses();
2829: 
2830:             // SELECT Clause
2831:             if ($this->blnCountOnlyFlag) {
2832:                 if ($this->blnDistinctFlag) {
2833:                     $strSql = "SELECT\r\n    COUNT(*) AS q_row_count\r\n" .
2834:                         "FROM    (SELECT DISTINCT ";
2835:                     $strSql .= "    " . implode(",\r\n    ", $this->strSelectArray);
2836:                 } else
2837:                     $strSql = "SELECT\r\n    COUNT(*) AS q_row_count\r\n";
2838:             } else {
2839:                 if ($this->blnDistinctFlag)
2840:                     $strSql = "SELECT DISTINCT\r\n";
2841:                 else
2842:                     $strSql = "SELECT\r\n";
2843:                 if ($this->strLimitInfo)
2844:                     $strSql .= $this->objDatabase->SqlLimitVariablePrefix($this->strLimitInfo) . "\r\n";
2845:                 $strSql .= "    " . implode(",\r\n    ", $this->strSelectArray);
2846:             }
2847: 
2848:             // FROM and JOIN Clauses
2849:             $strSql .= sprintf("\r\nFROM\r\n    %s\r\n    %s",
2850:                 implode(",\r\n    ", $this->strFromArray),
2851:                 implode("\r\n    ", $this->strJoinArray));
2852: 
2853:             // WHERE Clause
2854:             if (count($this->strWhereArray)) {
2855:                 $strWhere = implode("\r\n    ", $this->strWhereArray);
2856:                 if (trim($strWhere) != '1=1')
2857:                     $strSql .= "\r\nWHERE\r\n    " . $strWhere;
2858:             }
2859: 
2860:             // Additional Ordering/Grouping/Having clauses
2861:             if (count($this->strGroupByArray))
2862:                 $strSql .= "\r\nGROUP BY\r\n    " . implode(",\r\n    ", $this->strGroupByArray);
2863:             if (count($this->strHavingArray)) {
2864:                 $strHaving = implode("\r\n    ", $this->strHavingArray);
2865:                 $strSql .= "\r\nHaving\r\n    " . $strHaving;
2866:             }
2867:             if (count($this->strOrderByArray))
2868:                 $strSql .= "\r\nORDER BY\r\n    " . implode(",\r\n    ", $this->strOrderByArray);
2869: 
2870:             // Limit Suffix (if applicable)
2871:             if ($this->strLimitInfo)
2872:                 $strSql .= "\r\n" . $this->objDatabase->SqlLimitVariableSuffix($this->strLimitInfo);
2873: 
2874:             // For Distinct Count Queries
2875:             if ($this->blnCountOnlyFlag && $this->blnDistinctFlag)
2876:                 $strSql .= "\r\n) as q_count_table";
2877: 
2878:             return $strSql;
2879:         }
2880: 
2881:         /**
2882:          * Sets the one order by clause allowed in a query. Stores it for delayed processing.
2883:          *
2884:          * @param QQOrderBy $objOrderByClause
2885:          */
2886:         public function SetOrderByClause(QQOrderBy $objOrderByClause) {
2887:             if ($this->objOrderByClause) {
2888:                 throw new QCallerException('You can only have one OrderBy clause in a query.');
2889:             }
2890:             $this->objOrderByClause = $objOrderByClause;
2891:         }
2892:         /**
2893:          * Final processing of delayed clauses. Clauses like OrderBy need to wait to be processed until the complete
2894:          * set of aliases is known.
2895:          */
2896:         protected function ProcessClauses() {
2897:             if ($this->objOrderByClause) {
2898:                 $this->objOrderByClause->_UpdateQueryBuilder($this);
2899:             }
2900:         }
2901: 
2902:         public function __get($strName) {
2903:             switch ($strName) {
2904:                 case 'Database':
2905:                     return $this->objDatabase;
2906:                 case 'RootTableName':
2907:                     return $this->strRootTableName;
2908:                 case 'ColumnAliasArray':
2909:                     return $this->strColumnAliasArray;
2910:                 case 'ExpandAsArrayNode':
2911:                     return $this->objExpandAsArrayNode;
2912: 
2913:                 default:
2914:                     try {
2915:                         return parent::__get($strName);
2916:                     } catch (QCallerException $objExc) {
2917:                         $objExc->IncrementOffset();
2918:                         throw $objExc;
2919:                     }
2920:             }
2921:         }
2922:     }
2923: 
2924:     /**
2925:      *  Subclasses QQueryBuilder to handle the building of conditions for conditional expansions, subqueries, etc.
2926:      *  Since regular queries use WhereClauses for conditions, we just use the where clause portion, and
2927:      *  only build a condition clause appropriate for a conditional expansion.
2928:      */
2929:     class QPartialQueryBuilder extends QQueryBuilder {
2930:         protected $objParentBuilder;
2931: 
2932:         /**
2933:          * @param QQueryBuilder $objBuilder
2934:          */
2935:         public function __construct(QQueryBuilder $objBuilder) {
2936:             parent::__construct($objBuilder->objDatabase, $objBuilder->strRootTableName);
2937:             $this->objParentBuilder = $objBuilder;
2938:             $this->strColumnAliasArray = &$objBuilder->strColumnAliasArray;
2939:             $this->strTableAliasArray = &$objBuilder->strTableAliasArray;
2940: 
2941:             $this->intTableAliasCount = &$objBuilder->intTableAliasCount;
2942:             $this->intColumnAliasCount = &$objBuilder->intColumnAliasCount;
2943:         }
2944: 
2945:         /**
2946:          * @return string
2947:          */
2948:         public function GetWhereStatement() {
2949:             return implode(' ', $this->strWhereArray);
2950:         }
2951: 
2952:         /**
2953:          * @return string
2954:          */
2955:         public function GetFromStatement() {
2956:             return implode(' ', $this->strFromArray) . ' ' . implode(' ', $this->strJoinArray);
2957:         }
2958:     }
2959: 
2960: 
API documentation generated by ApiGen