/** * 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; }