Page MenuHomePhabricator

No OneTemporary

diff --git a/core/Collection.php b/core/Collection.php
index 63e71ff..471ab0e 100644
--- a/core/Collection.php
+++ b/core/Collection.php
@@ -1,248 +1,256 @@
<?php namespace spitfire\core;
use ArrayAccess;
use BadMethodCallException;
use spitfire\exceptions\OutOfBoundsException;
use spitfire\exceptions\OutOfRangeException;
/**
* The collection class is intended to supercede the array and provide additional
* functionality and ease of use to the programmer.
*/
class Collection implements ArrayAccess, CollectionInterface
{
private $arr;
/**
* The collection element allows to extend array functionality to provide
* programmers with simple methods to aggregate the data in the array.
*
* @param Collection|mixed $e
*/
public function __construct($e = null) {
if ($e === null) { $this->arr = []; }
elseif ($e instanceof Relation) { $this->arr = $e->toArray(); }
elseif (is_array($e)) { $this->arr = $e; }
else { $this->arr = [$e]; }
}
/**
* This method iterates over the elements of the array and applies a provided
* callback to each of them. The value your function returns if placed in the
* array.
*
* @param callable|array $callable
* @return Collection
* @throws BadMethodCallException
*/
public function each($callable) {
/*
* If the callback provided is not a valid callable then the function cannot
* properly continue.
*/
if (!is_callable($callable)) {
throw new BadMethodCallException('Invalid callable provided to collection::each()', 1703221329);
}
return new Collection(array_map($callable, $this->arr));
}
/**
* Reduces the array to a single value using a callback function.
*
* @param callable $callback
* @param mixed $initial
* @return mixed
*/
public function reduce($callback, $initial = null) {
return array_reduce($this->arr, $callback, $initial);
}
+ public function has($idx) {
+ return isset($this->arr[$idx]);
+ }
+
+ public function contains($e) {
+ return array_search($e, $this->arr);
+ }
+
/**
* This function checks whether a collection contains only elements with a
* given type. This function also accepts base types.
*
* Following base types are accepted:
*
* <ul>
* <li>int</li><li>float</li>
* <li>number</li><li>string</li>
* <li>array</li>
* <ul>
*
* @param string $type Base type or class name to check.
* @return boolean
*/
public function containsOnly($type) {
switch($type) {
case 'int' : return $this->reduce(function ($p, $c) { return $p && is_int($c); }, true);
case 'float' : return $this->reduce(function ($p, $c) { return $p && is_float($c); }, true);
case 'number': return $this->reduce(function ($p, $c) { return $p && is_numeric($c); }, true);
case 'string': return $this->reduce(function ($p, $c) { return $p && is_string($c); }, true);
case 'array' : return $this->reduce(function ($p, $c) { return $p && is_array($c); }, true);
default : return $this->reduce(function ($p, $c) use ($type) { return $p && is_a($c, $type); }, true);
}
}
/**
* Reports whether the collection is empty.
*
* @return boolean
*/
public function isEmpty() {
return empty($this->arr);
}
/**
* Filters the collection using a callback. This allows a collection to shed
* values that are not useful to the programmer.
*
* Please note that this will return a copy of the collection and the original
* collection will remain unmodified.
*
* @param callable $callback
* @return \spitfire\core\Collection
*/
public function filter($callback = null) {
#If there was no callback defined, then we filter the array without params
if ($callback === null) { return new Collection(array_filter($this->arr)); }
#Otherwise we use the callback parameter to filter the array
return new Collection(array_filter($this->arr, $callback));
}
/**
* Counts the number of elements inside the collection.
*
* @return int
*/
public function count() {
return count($this->arr);
}
/**
* Adds up the elements in the collection. Please note that this method will
* double check to see if all the provided elements are actually numeric and
* can be added together.
*
* @return int|float
* @throws BadMethodCallException
*/
public function sum() {
if ($this->isEmpty()) { throw new BadMethodCallException('Collection is empty'); }
if (!$this->containsOnly('number')) { throw new BadMethodCallException('Collection does contain non-numeric types'); }
return array_sum($this->arr);
}
/**
* Returns the average value of the elements inside the collection.
*
* @throws BadMethodCallException If the collection contains non-numeric values
* @return int|float
*/
public function avg() {
return $this->sum() / $this->count();
}
/**
* Extracts a certain key from every element in the collection. This requires
* every element in the collection to be either an object or an array.
*
* The method does not accept values that are neither array nor object, but
* will return null if the key is undefined in the array or object being used.
*
* @param mixed $key
*/
public function extract($key) {
return new Collection(array_map(function ($e) use ($key) {
if (is_array($e)) { return isset($e[$key])? $e[$key] : null; }
if (is_object($e)) { return isset($e->$key)? $e->$key : null; }
throw new OutOfBoundsException('Collection::extract requires array to contain only arrays and objects');
}, $this->arr));
}
public function push($element) {
$this->arr[] = $element;
return $element;
}
public function add($elements) {
$this->arr+= $elements;
return $this;
}
public function remove($element) {
unset($this->arr[array_search($element, $this->arr)]);
return $this;
}
public function reset() {
$this->arr = [];
return $this;
}
public function current() {
return current($this->arr);
}
public function key() {
return key($this->arr);
}
public function next() {
return next($this->arr);
}
public function offsetExists($offset) {
return array_key_exists($offset, $this->arr);
}
public function offsetGet($offset) {
if (!array_key_exists($offset, $this->arr)) {
throw new OutOfRangeException('Undefined index: ' . $offset, 1703221322);
}
return $this->arr[$offset];
}
public function offsetSet($offset, $value) {
$this->arr[$offset] = $value;
}
public function offsetUnset($offset) {
unset($this->arr[$offset]);
}
public function rewind() {
return reset($this->arr);
}
public function pluck() {
return array_shift($this->arr);
}
/**
* Indicates whether the current element in the Iterator is valid. To achieve
* this we use the key() function in PHP which will return the key the array
* is currently forwarded to or (which is interesting to us) NULL in the event
* that the array has been forwarded past it's end.
*
* @see key
* @return boolean
*/
public function valid() {
return null !== key($this->arr);
}
public function toArray() {
return $this->arr;
}
public function __isset($name) {
return isset($this->arr[$name]);
}
}
diff --git a/model/Index.php b/model/Index.php
new file mode 100644
index 0000000..62b16ae
--- /dev/null
+++ b/model/Index.php
@@ -0,0 +1,97 @@
+<?php namespace spitfire\model;
+
+use spitfire\core\Collection;
+
+/*
+ * The MIT License
+ *
+ * Copyright 2017 César de la Cal Bretschneider <cesar@magic3w.com>.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * An Index allows your application to define a series of fields that the DBMS
+ * should index in order to improve performance of data retrieval.
+ *
+ * Please note that indexes on the logical level are "suggestions" that allow
+ * the DBMS to improve performance, but these are not required to be followed.
+ */
+class Index
+{
+
+ private $fields;
+ private $name;
+ private $unique = false;
+ private $isPrimary = false;
+
+ /**
+ * Creates a new index for the schema.
+ *
+ * @param Field[] $fields
+ */
+ public function __construct($fields = null) {
+ $this->fields = new Collection($fields);
+ $this->name = 'idx_' . $this->fields->rewind()->getSchema()->getName() . rand(0, 9999);
+ }
+
+ public function getFields() {
+ return $this->fields;
+ }
+
+ public function contains(Field$f) {
+ return $this->fields->contains($f);
+ }
+
+ public function getName() {
+ return $this->name;
+ }
+
+ public function getUnique() {
+ return $this->unique;
+ }
+
+ public function isPrimary() {
+ return $this->isPrimary;
+ }
+
+ public function setFields($fields) {
+ $this->fields = $fields;
+ return $this;
+ }
+
+ public function putField(Field$field) {
+ $this->fields->push($field);
+ }
+
+ public function setName($name) {
+ $this->name = $name;
+ return $this;
+ }
+
+ public function setUnique($unique) {
+ $this->unique = $unique;
+ return $this;
+ }
+
+ public function setPrimary($isPrimary) {
+ $this->isPrimary = $isPrimary;
+ return $this;
+ }
+}
\ No newline at end of file
diff --git a/model/field.php b/model/field.php
index 871e624..9b2eb4f 100644
--- a/model/field.php
+++ b/model/field.php
@@ -1,359 +1,363 @@
<?php namespace spitfire\model;
use spitfire\Model;
use spitfire\storage\database\Schema;
use spitfire\storage\database\Table;
use spitfire\validation\ValidationError;
/**
* Represents a table's field in a database. Contains information about the
* table the field belongs to, the name of the field and if it is (or not) a
* primary key or auto-increment field.
*
* @author César de la Cal <cesar@magic3w.com>
*/
abstract class Field
{
/**
* This constants represent the list of basic data types that spitfire
* supports. This should be supported by the DBMS to be used.
*
* @todo Move or remove. The idea of these constants was to define the field's
* raw data in a way that DBMS's could understand. Since we now do support
* instanceof tests, these are just a superfluous way of restating what the
* system already knows about the field. And in the event of this data
* conflicting with the data provided by the class even causing trouble.
*/
const TYPE_INTEGER = 'int';
const TYPE_LONG = 'long';
const TYPE_FLOAT = 'float';
const TYPE_BOOLEAN = 'boolean';
const TYPE_STRING = 'string';
const TYPE_FILE = 'file';
const TYPE_TEXT = 'txt';
const TYPE_DATETIME = 'datetime';
const TYPE_REFERENCE = 'reference';
const TYPE_CHILDREN = 'children';
const TYPE_BRIDGED = 'bridged';
/**
* Contains a reference to the parent model. This allows this field to
* interact with it and to retrieve information about the model, the
* table it is in and even the DB engine that holds them.
*
* @var Schema
* @todo Rename to schema
*/
private $model;
/**
* Contains the name of the field inside of the model. This name should
* be unique and is recommended not to conatin any underscores or hyphens
* other than those generated by Spitfire itself to indicate it's relation
* with other fields and models.
*
* @var string
*/
private $name;
/**
* This indicates whether the content of this field needs to be enforced
* to be unique. This restriction is handled by the DBMS and Spitfire
* does not guarantee that this is respected.
*
* @var boolean
* @deprecated since version 0.1-dev 20170804
*/
private $unique = false;
/**
* Defines whether the field's value can be set to null or not. In case this
* happens the database will allow null values for this field.
*
* @var boolean
*/
private $nullable = true;
/**
* This property indicates whether the field belongs or not to the model
* 's primary Key. This key is the one used to identify records uniquely.
*
* @var boolean
* @deprecated since version 0.1-dev 20170804
*/
private $primary = false;
/**
* Indicates whether the field is to be incremented everytime a new
* record is inserted into the DBMS. This task is handled by the DBMS and
* Spitfire therefore does not guarantee nor enforce that the task of
* incrementing the field is handled correctly.
*
* @var boolean
*/
private $auto_increment = false;
/**
* Contains the physical (DB) fields that are used to store the data
* this Field generates. For most data types Spitfire will just generate
* one physical field for each logical one.
*
* Only common exception to this are the Referenced fields, which need to
* generate one field for each remote primary field.
*
* @var \spitfire\storage\database\Field|\spitfire\storage\database\Field[]
*/
private $physical;
/**
* Sets the physical equivalent of this field. This needs to be a instance
* of DBField or an array of those.
*
* [NOTICE] As it is uncommon for this function to be called with invalid
* data (due to be being mainly an internal function) there is no validation
* being done for the type of data that is inserted. Be careful!
*
* @param \spitfire\storage\database\Field|\spitfire\storage\database\Field[] $physical
*/
public function setPhysical($physical) {
$this->physical = $physical;
}
/**
* This method initializes the Physical equivalent(s) of the Field,
* this is done in a separate method from the contructor to ease
* overriding of this logic.
*/
public function makePhysical() {
#The instancing is redirected to the table
$table = $this->getModel()->getTable();
return $table->getDb()->getObjectFactory()->getFieldInstance($this, $this->getName());
}
/**
* This method returns an array of Fields. Even if the field has only one db
* equivalent the return value will be an array, for consistency with fields
* which return several ones.
*
* @return \spitfire\storage\database\Field[]
*/
public function getPhysical() {
if ($this->physical === null) { $this->physical = $this->makePhysical(); }
if (is_array($this->physical)) { return $this->physical; }
else { return Array($this->physical); }
}
/**
* Returns whether the field is autoincrement. This means, everytime a
* new record of this kind is created the auto_increment field is
* increased by 1.
*
* @return boolean
*/
public function isAutoIncrement() {
return $this->auto_increment;
}
/**
* Defines whether the field is used as autoincrement field. This setting
* will have no effect on any field that is not an integer field.
*
* @param boolean $ai
*
* @return self
*/
public function setAutoIncrement($ai) {
$this->auto_increment = !!$ai;
return $this;
}
/**
* Returns the name of this field. The name identifies this field among
* the rest of the fields of the same model / table.
*
* @return string
*/
public function getName() {
return $this->name;
}
/**
* Sets the name of this field. The name identifies this field among
* the rest of the fields of the same model / table.
*
* @param string $name New name of the field.
* @return \spitfire\model\Field The field, for method chaining
*/
public function setName($name) {
$this->name = $name;
return $this;
}
#@TODO: Document
public function setNullable($nullable) {
$this->nullable = $nullable;
}
public function getNullable() {
return $this->nullable;
}
-
+
+
+ public function getSchema() {
+ return $this->model;
+ }
/**
* Gets the parent model for this field. This allows the field to
* deliver data about which fields it's a sibling to and which fields
* it refers too.
*
- * @return Model
+ * @return Schema
*/
public function getModel() {
return $this->model;
}
/**
* Defines which model this field belongs to. This data basically is
* redundant but quickens development and makes it more efficient to
* find the model for the field.
*
- * @param Model $model
+ * @param Schema $model
*/
public function setModel($model) {
$this->model = $model;
}
/**
* Returns the table this field belongs to. This is just a shortcut method
* provided to allow making logical fields and DBFields compatible.
*
* @return Table
*/
public function getTable() {
return $this->model->getTable();
}
/**
* Returns true if the field belongs to the table's primary key. Keep in
* mind that a primary key can cover several fields.
*
* @deprecated since version 0.1-dev 20170804
* @return boolean
*/
public function isPrimary() {
return $this->primary;
}
/**
* Defines whether this belongs to the primary key or not. This will
* also automatically set the unique flag to true.
*
* @deprecated since version 0.1-dev 20170804
* @param boolean $primary
* @return self
*/
public function setPrimary($primary) {
$this->primary = !!$primary;
return $this;
}
/**
* Returns true if the field has a unique flag, alas it's content cannot
* be repeated among several records.
*
* [NOTICE] If the field is a primary key this will return true. Due to
* the primary having to be unique.
*
* @deprecated since version 0.1-dev 20170804
* @return boolean
*/
public function isUnique() {
//TODO: BUG, requires revision for multi field primaries if ($this->primary) return true;
return $this->unique;
}
/**
* Allows you to define whether the current field only allows unique
* (non repeated) values. When using data that only should allow
* unique values setting this flag to true will consistently increase
* the speed of database queries.
*
* @deprecated since version 0.1-dev 20170804
* @param boolean $unique
* @return self
*/
public function setUnique($unique) {
$this->unique = $unique;
return $this;
}
/**
* The return value of this method indicates whether the DBMS SHOULD
* create an index on this field. This depends on the kind of content
* and the settings of this field.
*
* Spitfire automatically chooses the right settings for you, this
* function should be overriden by methods for field types that have
* higher performance with specific values.
*
* @deprecated since version 0.1-dev 20170804
* @return boolean
*/
public function isIndexed() {
return ($this->primary || $this->unique);
}
/**
* Returns the field the bean should use to render this. In case it returns null
* it means the field has no suggestions and the Bean should choose the way
* to render it
*
* @param \CoffeeBean $bean
* @param Field $field
* @param string $caption
* @return Field|null
*/
public function getBeanField($bean, $field, $caption) {
return null;
}
public function validate($value) {
if (!$this->nullable && !$value) return new ValidationError(_t('err_field_null'));
return false;
}
/**
* Returns the adapter this field wants to use for the database interaction,
* usually this will be a BaseAdapter that allows the most basic interaction
* with the most basic kinds of fields.
*
* But you can also create custom adapters that manage complex data and return
* them here.
*
* @param Model $model The model to contain the adapter
* @return adapters\AdapterInterface The new adapter
*/
abstract public function getAdapter(Model$model);
/**
* This method informs the system what primary datatype the overriding
* Field class uses. You can define custom classes that store data into
* any DBMS by defining which primary data they use.
*
* @deprecated since version 0.1-dev 20170804
*/
abstract public function getDataType();
abstract public function getConnectorQueries(\spitfire\storage\database\Query$parent);
/**
* Returns the name of the Field, this is purely a conveiencce method
* to shorten code writing and make it easier to read.
*
* @return string
*/
public function __toString() {
return $this->name;
}
}
diff --git a/storage/database/LayoutInterface.php b/storage/database/LayoutInterface.php
index 4a1309f..d140b9c 100644
--- a/storage/database/LayoutInterface.php
+++ b/storage/database/LayoutInterface.php
@@ -1,73 +1,75 @@
<?php namespace spitfire\storage\database;
/*
* The MIT License
*
* Copyright 2017 César de la Cal Bretschneider <cesar@magic3w.com>.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* The layout is basically a list of columns + indexes that makes up the schema
* of a relation in a relational database.
*
* A driver can implement this interface to provide common operations on it's
* tables for spitfire to run.
*/
interface LayoutInterface
{
/**
* Returns the name the DBMS should use to name this table. The implementing
* class should respect user configuration including db_table_prefix
*
* @return string
*/
function getTableName() : string;
/**
*
* @param string $name
* @return Field
*/
function getField($name) : Field;
/**
*
* @return Field[] The columns in this database table
*/
function getFields();
/**
+ * This method needs to get the lost of indexes from the logical Schema and
+ * convert them to physical indexes for the DBMS to manage.
*
* @return IndexInterface[] The indexes in this layout
*/
function getIndexes();
/**
* Creates a table on the DBMS that is capable of holding the Model's data
* appropriately. This will try to normalize the data as far as possible to
* create consistent databases.
*/
function create();
function repair();
function destroy();
}
\ No newline at end of file
diff --git a/storage/database/Schema.php b/storage/database/Schema.php
index d7732ab..d9ac67a 100644
--- a/storage/database/Schema.php
+++ b/storage/database/Schema.php
@@ -1,257 +1,274 @@
<?php namespace spitfire\storage\database;
+use IntegerField;
+use spitfire\core\Collection;
+use spitfire\exceptions\PrivateException;
use spitfire\model\Field;
-use spitfire\storage\database\Table;
+use spitfire\model\Index;
use spitfire\storage\database\Query;
-use spitfire\exceptions\PrivateException;
-use IntegerField;
+use spitfire\storage\database\Table;
/**
* A Schema is a class used to define how Spitfire stores data into a DBMS. We
* usually consider a DBMS as relational database engine, but Spitfire can
* be connected to virtually any engine that stores data. Including No-SQL
* databases and directly on the file system. You should even be able to use
* tapes, although that would be extra slow.
*
* Every model contains fields and references. Fields are direct data-types, they
* allow storing things directly into them, while references are pointers to
* other models allowing you to store more complex data into them.
*
* @property IntegerField $_id This default primary key integer helps the system
* locating the records easily.
*
* @todo Add index support, so models can create indexes that are somewhat more complex
* @author César de la Cal <cesar@magic3w.com>
*/
class Schema
{
/**
* Contains a list of the fields that this model uses t ostore data.
* Fields are stored in a FILO way, so the earlier you register a field
* the further left will it be on a database table (if you look at it
* in table mode).
*
* @var Field[]
*/
private $fields;
+ private $indexes;
+
/**
* Contains a reference to the table this model is 'templating'. This
* means that the current model is attached to said table and offers to
* it information about the data that is stored to the DBMS and the format
* it should hold.
*
* @var Table
*/
private $table;
/**
* The name of the table that represents this schema on DBMS' side. This will
* be automatically generated from the class name and will be replacing the
* invalid inverted bar (\) with hyphens (-) that are not valid as a class name.
*
* @var string
*/
private $name;
/**
* Creates a new instance of the Model. This allows Spitfire to create
* and manage data accordingly to your wishes on a DB engine without
* requiring you to integrate with any concrete engine but writing code
* that SF will translate.
*
* @param string $name
* @param Table $table
*/
public final function __construct($name, Table$table = null) {
#Define the Model's table as the one just received
- $this->table = $table;
- $this->name = $name;
+ $this->table = $table;
+ $this->name = $name;
#Create a field called ID that automatically identifies records
$this->_id = new IntegerField(true);
+ $this->_id->setAutoIncrement(true);
- #Define _id as primary key and auto_increment
- $this->_id->setPrimary(true)->setAutoIncrement(true);
+ #Create a default index for the primary key
+ $pk = new Index([$this->_id]);
+ $pk->setPrimary(true);
+
+ #Create the index collection
+ $this->indexes = new Collection([$pk]);
}
/**
* Imports a set of fields. This allows to back them up in case they're
* needed. Please note that the parent setting for them will be rewritten.
*
* @param Field[] $fields
*/
public function setFields($fields) {
#Loop through the fields to import them
foreach($fields as $field) {
$this->{$field->getName()} = $field; #This triggers the setter
}
}
/**
* Returns a logical field for this model. "Logical field" refers to fields
* that can also contain complex datatypes aka References.
*
* You can use the Field::getPhysical() method to retrieve the physical fields
* the application uses to interact with the DBMS.
*
* @param string $name
* @return Field|null
*/
public function getField($name) {
if (isset($this->fields[$name])) { return $this->fields[$name]; }
else { return null; }
}
/**
* Returns the whole list of fields this model contains. This are logical fields
* and therefore can contain data that is too complex to be stored directly
* by a DB Engine, the table object is in charge of providing a list of
* DB Friendly fields.
*
* @return Field[]
*/
public function getFields() {
return $this->fields;
}
/**
* Returns the 'name' of the model. The name of a model is obtained by
* removing the Model part of tit's class name. It's best practice to
* avoid the usage of this function for anything rather than logging.
*
* This function has a special use case, it also defines the name of the
* future table. By changing this you change the table this model uses
* on DBMS, this is particularly useful when creating multiple models
* that refer to a single dataset like 'People' and 'Adults'.
*
* @staticvar string $name
* @return string
*/
public final function getName() {
return $this->name;
}
/**
* Returns the tablename spitfire considers best for this Model. This
* value is calculated by using the Model's name and replacing any
* <b>\</b> with hyphens to make the name database friendly.
*
* Hyphens are the only chars that DBMS tend to accept that class names
* do not. So this way we avoid any colissions in names that could be
* coincidentally similar.
*
* @return string
*/
public function getTableName() {
return str_replace('\\', '-', $this->getName());
}
/**
* Returns the table the Schema represents. The schema is the logical representation
* of a Table. While the Schema will manage logical fields that the programmer
* can directly write data to, the Table will take that data and translate it
* so the database engine can use it.
*
* @return Table
*/
public function getTable() {
return $this->table;
}
/**
* Sets the table this schema manages. This connection is used to determine
* what DBMS table it should address and to make correct data conversion.
*
* @param Table $table
*/
public function setTable($table) {
$this->table = $table;
}
/**
* Extending this function on models allows you to add restrictions to a
* query when this is made for this model. This way you can generate a
* behavior similar to a view where the records are always limited by
* the restriction set in this function.
*
* This is especially useful when fake deleting records from the database.
* You use a flag to indicate a record is deleted and use this function
* to hide any records that have that flag.
*
* @todo Move to the model. It makes no sense having it here
* @param Query $query The query that is being prepared to be executed.
* @return type
*/
public function getBaseRestrictions(Query$query) {
//Do nothing, this is meant for overriding
}
/**
* Returns a list of fields which compound the primary key of this model.
* The primary key is a set of records that identify a unique record.
*
* @return Field[]
*/
public function getPrimary() {
#Fetch the field list
$fields = $this->getFields();
#Drop the fields which aren't primary
foreach ($fields as $name => $field) {
if (!$field->isPrimary()) { unset($fields[$name]); }
}
#Return the cleared array
return $fields;
}
/**
* The getters and setters for this class allow us to create fields with
* a simplified syntax and access them just like they were properties
* of the object. Please note that some experts recommend avoiding magic
* methods for performance reasons. In this case you can use the field()
* method.
*
* @param string $name
* @param Field $value
*/
public function __set($name, $value) {
if ($value instanceof Field) {
$value->setName($name);
$value->setModel($this);
$this->fields[$name] = $value;
}
}
/**
* The getters and setters for this class allow us to create fields with
* a simplified syntax and access them just like they were properties
* of the object. Please note that some experts recommend avoiding magic
* methods for performance reasons. In this case you can use the field()
* method.
*
* @param string $name
* @throws PrivateException
* @return Field
*/
public function __get($name) {
if (isset($this->fields[$name])) { return $this->fields[$name]; }
else { throw new PrivateException('Schema: No field ' . $name . ' found'); }
}
/**
* Removes a field from the Schema. This is a somewhat rare method, since you
* should avoid it's usage in production environments and you should REALLY
* know what you're doing before using it.
*
* @param string $name
* @throws PrivateException
*/
public function __unset($name) {
- if (isset($this->fields[$name])) { unset($this->fields[$name]); }
+ if (isset($this->fields[$name])) {
+ $f = $this->fields[$name];
+ unset($this->fields[$name]);
+
+ #Find an index that may contain the field and remove it too
+ $this->indexes = $this->indexes->filter(function ($e) use ($f) {
+ return $e->contains($f);
+ });
+ }
else { throw new PrivateException('Schema: Could not delete. No field ' . $name . ' found'); }
}
}
diff --git a/storage/database/LayoutInterface.php b/storage/database/drivers/mysqlpdo/Index.php
similarity index 52%
copy from storage/database/LayoutInterface.php
copy to storage/database/drivers/mysqlpdo/Index.php
index 4a1309f..f654a85 100644
--- a/storage/database/LayoutInterface.php
+++ b/storage/database/drivers/mysqlpdo/Index.php
@@ -1,73 +1,53 @@
-<?php namespace spitfire\storage\database;
+<?php namespace spitfire\storage\database\drivers\mysqlpdo;
+
+use spitfire\storage\database\Field;
+use spitfire\storage\database\IndexInterface;
/*
* The MIT License
*
* Copyright 2017 César de la Cal Bretschneider <cesar@magic3w.com>.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-/**
- * The layout is basically a list of columns + indexes that makes up the schema
- * of a relation in a relational database.
- *
- * A driver can implement this interface to provide common operations on it's
- * tables for spitfire to run.
- */
-interface LayoutInterface
+class Index implements IndexInterface
{
- /**
- * Returns the name the DBMS should use to name this table. The implementing
- * class should respect user configuration including db_table_prefix
- *
- * @return string
- */
- function getTableName() : string;
-
- /**
- *
- * @param string $name
- * @return Field
- */
- function getField($name) : Field;
-
- /**
- *
- * @return Field[] The columns in this database table
- */
- function getFields();
-
- /**
- *
- * @return IndexInterface[] The indexes in this layout
- */
- function getIndexes();
+ public function __construct($fields) {
+ ;
+ }
-
- /**
- * Creates a table on the DBMS that is capable of holding the Model's data
- * appropriately. This will try to normalize the data as far as possible to
- * create consistent databases.
- */
- function create();
- function repair();
- function destroy();
+ public function getFields(): Field {
+
+ }
+
+ public function getName(): string {
+
+ }
+
+ public function isPrimary(): bool {
+
+ }
+
+ public function isUnique(): bool {
+
+ }
+
}
\ No newline at end of file
diff --git a/storage/database/drivers/mysqlpdo/Layout.php b/storage/database/drivers/mysqlpdo/Layout.php
index 8235a69..d0a0b84 100644
--- a/storage/database/drivers/mysqlpdo/Layout.php
+++ b/storage/database/drivers/mysqlpdo/Layout.php
@@ -1,145 +1,145 @@
<?php namespace spitfire\storage\database\drivers\mysqlpdo;
use spitfire\core\Environment;
use spitfire\exceptions\PrivateException;
use spitfire\storage\database\Field;
use spitfire\storage\database\LayoutInterface;
use spitfire\storage\database\Table;
/*
* The MIT License
*
* Copyright 2017 César de la Cal Bretschneider <cesar@magic3w.com>.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
class Layout implements LayoutInterface
{
/**
* The table that the system uses to connect layout and relation.
*
* @var Table
*/
private $table;
/**
* The prefixed name of the table. The prefix is defined by the environment
* and allows to have several environments on the same database.
*
* @var string
*/
private $tablename;
/**
* List of the physical fields this table handles. This array is just a
* shortcut to avoid looping through model-fields everytime a query is
* performed.
*
* @var Field[]
*/
private $fields;
/**
* An array of indexes that this table defines to manage it's queries and
* data.
*
* @var \spitfire\storage\database\IndexInterface[]
*/
private $indexes;
/**
*
* @param Table $table
*/
public function __construct(Table$table) {
#Assume the table
$this->table = $table;
#Get the physical table name. This will use the prefix to allow multiple instances of the DB
$this->tablename = Environment::get('db_table_prefix') . $table->getSchema()->getTableName();
#Create the physical fields
$fields = $this->table->getSchema()->getFields();
$columns = Array();
foreach ($fields as $field) {
$physical = $field->getPhysical();
while ($phys = array_shift($physical)) { $columns[$phys->getName()] = $phys; }
}
$this->fields = $columns;
}
public function create() {
}
public function destroy() {
}
/**
* Fetch the fields of the table the database works with. If the programmer
* has defined a custom set of fields to work with, this function will
* return the overriden fields.
*
* @return Field[] The fields this table handles.
*/
public function getFields() {
return $this->fields;
}
- public function getField($name) {
+ public function getField($name) : Field {
#If the data we get is already a DBField check it belongs to this table
if ($name instanceof Field) {
if ($name->getTable() === $this->table) { return $name; }
else { throw new PrivateException('Field ' . $name . ' does not belong to ' . $this); }
}
#Otherwise search for it in the fields list
if (isset($this->fields[(string)$name])) { return $this->fields[(string)$name]; }
#The field could not be found in the Database
throw new PrivateException('Field ' . $name . ' does not exist in ' . $this);
}
-
+
public function getIndexes() {
//TODO Implement
}
- public function getTableName() {
+ public function getTableName() : string {
}
public function repair() {
}
/**
* Returns the name of a table as DB Object reference (with quotes).
*
* @return string The name of the table escaped and ready for use inside
* of a query.
*/
public function __toString() {
return "`{$this->tablename}`";
}
}
\ No newline at end of file

File Metadata

Mime Type
text/x-diff
Expires
Apr 13 2021, 2:58 PM (9 w, 5 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2029
Default Alt Text
(40 KB)

Event Timeline