Esempio n. 1
0
	/**
	 * Delete this record from the datastore.
	 *
	 * Will IMMEDIATELY remove the record!
	 *
	 * If this model has a "deleted" column that is set as a zero value, that record is set to the current timestamp instead.
	 * This functionality is meant for advanced record tracking such as those in use in sync systems.
	 *
	 * @return bool
	 * @throws Exception
	 */
	public function delete() {

		$classname = get_called_class();
		// Allow all supplemental models to tap into this too!
		if(isset(self::$_ModelSupplementals[$classname])){
			foreach(self::$_ModelSupplementals[$classname] as $supplemental){
				if(class_exists($supplemental)){
					$ref = new ReflectionClass($supplemental);
					if($ref->hasMethod('PreDeleteHook')){
						$ref->getMethod('PreDeleteHook')->invoke(null, $this);
					}
				}
			}
		}

		foreach ($this->_columns as $c) {
			/** @var \Core\Datamodel\Columns\SchemaColumn $c */
			
			// Certain key types have certain functions.
			if($c->type == Model::ATT_TYPE_DELETED) {
				// Is this record already set as deleted?
				// If it is, then proceed with the delete as usual.
				// Otherwise, update the record instead of deleting it.
				if(!$c->value){
					$nv = Time::GetCurrentGMT();
					$this->set($c->field, $nv);
					return $this->save();
				}
				else{
					// I can safely break out of the foreach statement at this stage.
					break;
				}
			}
		}

		// Blank out any dependent records based on links.
		// Since relationships may have various levels, I need to execute delete on each of them instead of just
		// issuing a broad delete request.
		foreach ($this->_linked as $k => $l) {

			switch($l['link']){
				case Model::LINK_HASONE:
					// Delete this child, (and any of its subordinates).
					$child = $this->getLink($k);
					$child->delete();
					break;
				case Model::LINK_HASMANY:
					// Request all the children and issue a delete on them.
					$children = $this->getLink($k);
					foreach($children as $child){
						/** @var Model $child */
						$child->delete();
					}
					break;
				// There is no default behaviour... other than to ignore it.
			}

			if (isset($this->_linked[$k]['records'])) unset($this->_linked[$k]['records']);
		}


		if ($this->exists()) {

			// Check and see if this model extends another model.
			// If it does, then create/update that parent object to keep it in sync!
			if(($class = get_parent_class($this)) != 'Model'){
				$idx = self::GetIndexes();
				if(isset($idx['primary']) && sizeof($idx['primary']) == 1){
					$schema = $this->getKeySchema($idx['primary'][0]);
					if($schema['type'] == Model::ATT_TYPE_UUID){
						$refp = new ReflectionClass($class);
						$refm = $refp->getMethod('Construct');
						/** @var Model $parent */
						$parent = $refm->invoke(null, $this->get($idx['primary'][0]));
						$parent->delete();
					}
				}
			}

			$n = $this->_getTableName();
			$i = self::GetIndexes();
			// Delete the data from the database.
			$dat = new Core\Datamodel\Dataset();

			$dat->table($n);

			if (!isset($i['primary'])) {
				throw new Exception('Unable to delete model [ ' . get_class($this) . ' ] without any primary keys.');
			}

			$pri = $i['primary'];
			if(!is_array($pri)) $pri = [$pri];

			foreach ($pri as $k) {
				$dat->where([$k => $this->get($k)]);
			}

			$dat->limit(1)->delete();

			if ($dat->execute($this->interface)) {
				$this->_exists = false;
			}
		}

		return true;
	}
	/**
	 * @access  public
	 *
	 * @param bool $subSelect
	 *
	 * @return Core\Datamodel\Dataset
	 */
	public function parseSelect($subSelect = false)
	{
		$tree = new Core\Datamodel\Dataset();
		$tree->_mode = Core\Datamodel\Dataset::MODE_GET;
		$this->getTok();
		if ($this->token == 'distinct') {
			$tree->uniquerecords = true;
			$this->getTok();
		}

		// SELECT columns
		$selects = [];
		while (1) {
			$exp = $this->parseCondition()['args'][0];

			if(isset($exp['name'])){
				$this->raiseError('Datasets do not support functions as SELECT parameters!');
			}

			if(isset($exp['value'])){
				$selects[] = $exp['value'];
			}
			elseif(isset($exp['column'])){
				$selects[] = $exp['column'];
			}
			else{
				$this->raiseError('Unknown SELECT object');
			}

			if ($this->token != ',') {
				break;
			}
			$this->getTok();
		}
		$tree->select($selects);

		// FROM
		if ($this->token != 'from') {
			return $tree;
		}

		$this->getTok();
		$table = $this->parseFrom();
		if(sizeof($table['table_references']['table_factors']) != 1){
			$this->raiseError('Datasets only support one table at a time!');
		}
		$tree->table($table['table_references']['table_factors'][0]['table']);

		// WHERE

		// GROUP BY

		// HAVING

		// ORDER BY

		// LIMIT

		// UNION
		while ($this->token != ';' && ! is_null($this->token) && (!$subSelect || $this->token != ')')
			&& $this->token != ')') {
			switch ($this->token) {
				case 'where':
					$this->getTok();
					$clause = $this->parseWhereCondition();
					if (false === $clause) {
						return $clause;
					}
					$tree->_where = $clause;
					break;
				case 'order':
					$this->getTok();
					if ($this->token != 'by') {
						$this->raiseError('Expected "by"');
					}
					$this->getTok();
					while ($this->token == 'ident') {
						$arg = $this->lexer->tokText;
						$this->getTok();
						if ($this->token == '.') {
							$this->getTok();
							if ($this->token == 'ident') {
								$arg .= '.'.$this->lexer->tokText;
							} else {
								$this->raiseError('Expected a column name');
							}
						} else {
							$this->lexer->pushBack();
						}
						$col = $arg;
						//$col = $this->lexer->tokText;
						$this->getTok();
						if (isset($this->synonyms[$this->token])) {
							$order = $this->synonyms[$this->token];
							if (($order != 'asc') && ($order != 'desc')) {
								$this->raiseError('Unexpected token');
							}
							$this->getTok();
						} else {
							$order = 'asc';
						}
						if ($this->token == ',') {
							$this->getTok();
						}
						$tree->order($col . ' ' . $order);
					}
					break;
				case 'limit':
					$this->getTok();
					if ($this->token != 'int_val') {
						$this->raiseError('Expected an integer value');
					}
					$length = $this->lexer->tokText;
					$start = 0;
					$this->getTok();
					if ($this->token == ',') {
						$this->getTok();
						if ($this->token != 'int_val') {
							$this->raiseError('Expected an integer value');
						}
						$start  = $length;
						$length = $this->lexer->tokText;
						$this->getTok();
					}
					$tree->limit($start . ' ' . $length);
					break;
				case 'group':
					$this->getTok();
					if ($this->token != 'by') {
						$this->raiseError('Expected "by"');
					}
					$this->getTok();
					while ($this->token == 'ident') {
						$arg = $this->lexer->tokText;
						$this->getTok();
						if ($this->token == '.') {
							$this->getTok();
							if ($this->token == 'ident') {
								$arg .= '.'.$this->lexer->tokText;
							} else {
								$this->raiseError('Expected a column name');
							}
						} else {
							$this->lexer->pushBack();
						}
						$col = $arg;
						//$col = $this->lexer->tokText;
						$this->getTok();
						if ($this->token == ',') {
							$this->getTok();
						}
						$this->raiseError('@TODO group by statements not supported yet');
						$tree['group_by'][] = $col;
					}
					break;
				default:
					$this->raiseError('Unexpected clause');
			}
		}
		return $tree;
	}