Page MenuHomePhabricator

No OneTemporary

diff --git a/storage/database/DB.php b/storage/database/DB.php
index 9fb9db8..ccf6b11 100644
--- a/storage/database/DB.php
+++ b/storage/database/DB.php
@@ -1,178 +1,189 @@
<?php namespace spitfire\storage\database;
use BadMethodCallException;
use spitfire\cache\MemoryCache;
use spitfire\core\Environment;
use spitfire\exceptions\PrivateException;
use spitfire\io\CharsetEncoder;
+use spitfire\storage\database\restrictionmaker\RestrictionMaker;
/**
* This class creates a "bridge" beetwen the classes that use it and the actual
* driver.
*
* @author César de la Cal <cesar@magic3w.com>
*/
abstract class DB
{
const MYSQL_PDO_DRIVER = 'mysqlPDO';
private $settings;
private $tableCache;
private $encoder;
+ private $restrictionMaker;
/**
* Creates an instance of DBInterface. If options are set it will import
* them. Otherwise it will try to read them from the current environment.
*
* @param Settings $settings Name of the database driver to be used. You can
* choose one of the DBInterface::DRIVER_? consts.
*/
public function __construct(Settings$settings) {
$this->settings = $settings;
$this->tableCache = new TablePool($this);
$this->encoder = new CharsetEncoder(Environment::get('system_encoding'), $settings->getEncoding());
+ $this->restrictionMaker = new RestrictionMaker();
}
/**
* The encoder will allow the application to encode / decode database that
* is directed towards or comes from the database.
*
* @return CharsetEncoder
*/
public function getEncoder() {
return $this->encoder;
}
/**
* Gets the connection settings for this connection.
*
* @return Settings
*/
public function getSettings() {
return $this->settings;
}
/**
* Attempts to repair schema inconsistencies. These method is not meant
* to be called by the user but aims to provide an endpoint the driver
* can use when running into trouble.
*
* This method does not actually repair broken databases but broken schemas,
* if your database is broken or data on it corrupt you need to use the
* DBMS specific tools to repair it.
*
* Repairs the list of tables/models <b>currently</b> loaded into the db.
* If a model hasn't been accessed during execution it won't be listed
* here.
*
* Please note, that this function is used only for maintenance and repair
* works on tables. Meaning that it is not relevant if <b>all</b> tables
* were imported.
*/
public function repair() {
$tables = $this->tableCache->getCache()->getAll();
foreach ($tables as $table) {
$table->getLayout()->repair();
}
}
/**
* Returns a table adapter for the database table with said name to allow
* querying and data-manipulation..
*
* @param string|Schema $tablename Name of the table that should be used.
* If you pass a model to this function it will automatically
* read the name from the model and use it to find the
* table.
*
* @throws PrivateException If the table could not be found
* @return Table The database table adapter
*/
public function table($tablename) {
#If the parameter is a Model, we get it's name
if ($tablename instanceof Schema) {
return $this->tableCache->set($tablename->getName(), new Table($this, $tablename));
}
#We just tested if it's a Schema, let's see if it's a string
if (!is_string($tablename)) {
throw new BadMethodCallException('DB::table requires Schema or string as argument');
}
#Check if the table can be found in the table cache
return $this->tableCache->get(strtolower($tablename));
}
/**
* Returns our table cache. This allows an application that uses Spitfire (or
* it's own core) to check whether a table is already cached or inject it's
* own tables.
*
* @return MemoryCache
*/
public function getTableCache() {
return $this->tableCache;
}
+
+ /**
+ *
+ * @return RestrictionMaker
+ */
+ public function getRestrictionMaker() {
+ return $this->restrictionMaker;
+ }
/**
* Allows short-hand access to tables by using: $db->tablename
*
* @param string $table Name of the table
* @return Table
*/
public function __get($table) {
#Otherwise we try to get the table with this name
return $this->table($table);
}
/**
* Returns the handle used by the system to connect to the database.
* Depending on the driver this can be any type of content. It should
* only be used by applications with special needs.
*
* @abstract
* @return mixed The connector used by the system to communicate with
* the database server. The data-type of the return value depends on
* the driver used by the system.
*/
abstract public function getConnection();
/**
* Allows the application to create the database needed to store the tables
* and therefore data for the application. Some DBMS like SQLite won't support
* multiple databases - so this may not do anything.
*
* @abstract
* @return bool Returns whether the operation could be completed successfully
*/
abstract public function create();
/**
* Destroys the database and all of it's contents. Drivers may not allow
* this method to be called unless they're being operated in debug mode or
* a similar mode.
*
* @abstract
* @throws PrivateException If the driver rejected the operation
* @return bool Whether the operation could be completed
*/
abstract public function destroy();
/**
* In modern spitfire drivers, all the object creation for a database is handled
* by the object factories. This factories allow the system to create any object
* they need: Queries, Tables, Fields...
*
* This removes the need to have some driver specific objects just for the
* sake of providing a certain type. This way all SQL drivers can share some
* standard components while replacing the ones they specifically need.
*
* @return ObjectFactoryInterface
*/
abstract public function getObjectFactory();
}
diff --git a/storage/database/RestrictionGroup.php b/storage/database/RestrictionGroup.php
index 901296d..2d11742 100644
--- a/storage/database/RestrictionGroup.php
+++ b/storage/database/RestrictionGroup.php
@@ -1,301 +1,287 @@
<?php namespace spitfire\storage\database;
use Exception;
use InvalidArgumentException;
use Reference;
use spitfire\core\Collection;
use spitfire\exceptions\PrivateException;
/**
* A restriction group contains a set of restrictions (or restriction groups)
* that can be used by the database to generate more complex queries.
*
* This groups can be different of two different types, they can be 'OR' or 'AND',
* changing the behavior of the group by making it more or less restrictive. This
* OR and AND types are known from most DBMS.
*/
abstract class RestrictionGroup extends Collection
{
const TYPE_OR = 'OR';
const TYPE_AND = 'AND';
private $parent;
private $type = self::TYPE_OR;
public function __construct(RestrictionGroup$parent = null, $restrictions = Array() ) {
$this->parent = $parent;
parent::__construct($restrictions);
}
/**
*
* @deprecated since version 0.1-dev 20170720
* @param type $r
*/
public function removeRestriction($r) {
parent::remove($r);
}
/**
*
* @deprecated since version 0.1-dev 20170720
* @param type $restriction
*/
public function putRestriction($restriction) {
parent::push($restriction);
}
/**
*
* @deprecated since version 0.1-dev 20170720
* @param type $restrictions
*/
public function setRestrictions($restrictions) {
parent::reset();
parent::add($restrictions);
}
/**
* Adds a restriction to the current query. Restraining the data a field
* in it can contain.
*
* @todo This method does not accept logical fields as parameters
* @see http://www.spitfirephp.com/wiki/index.php/Method:spitfire/storage/database/Query::addRestriction
*
* @deprecated since version 0.1-dev 20170923
* @param string $fieldname
* @param mixed $value
* @param string $operator
* @return RestrictionGroup
* @throws PrivateException
*/
public function addRestriction($fieldname, $value, $operator = '=') {
return $this->where($fieldname, $operator, $value);
}
/**
* Adds a restriction to the current query. Restraining the data a field
* in it can contain.
*
* @todo This method does not accept logical fields as parameters
* @see http://www.spitfirephp.com/wiki/index.php/Method:spitfire/storage/database/Query::addRestriction
*
* @param string $fieldname
* @param mixed $value
* @param string $_
* @return RestrictionGroup
* @throws PrivateException
*/
public function where($fieldname, $value, $_ = null) {
$params = func_num_args();
+ $rm = $this->getQuery()->getTable()->getDb()->getRestrictionMaker();
+ /*
+ * Depending on how the parameters are provided, where will appropriately
+ * shuffle them to make them look correctly.
+ */
if ($params === 3) { list($operator, $value) = [$value, $_]; }
else { $operator = '='; }
- try {
- #If the name of the field passed is a physical field we just use it to
- #get a queryField
- $field = $fieldname instanceof QueryField? $fieldname : $this->getQuery()->getTable()->getField($fieldname);
- $restriction = $this->getQuery()->restrictionInstance($this->getQuery()->queryFieldInstance($field), $value, $operator);
- }
- catch (Exception $e) {
- #Otherwise we create a complex restriction for a logical field.
- $field = $this->getQuery()->getTable()->getSchema()->getField($fieldname);
-
- if ($fieldname instanceof Reference && $fieldname->getTarget() === $this->getQuery()->getTable()->getSchema())
- { $field = $fieldname; }
-
- #If the fieldname was not null, but the field is null - it means that the system could not find the field and is kicking back
- if ($field === null && $fieldname!== null) { throw new PrivateException('No field ' . is_object($fieldname)? get_class($fieldname) : $fieldname, 1602231949); }
-
- $restriction = $this->getQuery()->compositeRestrictionInstance($field, $value, $operator);
- }
-
- parent::push($restriction);
+ parent::push($rm->make($this, $fieldname, $operator, $value));
return $this;
}
/**
*
* @deprecated since version 0.1-dev 20170720
* @param type $restrictions
*/
public function getRestrictions() {
return parent::toArray();
}
public function importRestrictions(RestrictionGroup$query) {
$restrictions = $query->getRestrictions();
foreach($restrictions as $r) {
$copy = clone $r;
$copy->setParent($this);
$this->putRestriction($copy);
}
}
/**
*
* @deprecated since version 0.1-dev 20170720
* @param type $index
*/
public function getRestriction($index) {
return parent::offsetGet($index);
}
/**
*
* @deprecated since version 0.1-dev 20170720
* @return type
*/
public function getConnectingRestrictions() {
trigger_error('Method RestrictionGroup::getConnectingRestrictions() is deprecated', E_USER_DEPRECATED);
$_ret = Array();
foreach ($this->toArray() as $r) { $_ret = array_merge($_ret, $r->getConnectingRestrictions());}
return $_ret;
}
/**
*
* @deprecated since version 0.1-dev 20171110
*/
public function filterCompositeRestrictions() {
$restrictions = $this->toArray();
foreach ($restrictions as $r) {
if ($r instanceof CompositeRestriction) { $this->removeRestriction($r); }
if ($r instanceof RestrictionGroup) { $r->filterCompositeRestrictions(); }
}
}
/**
*
* @deprecated since version 0.1-dev 20171110
*/
public function filterSimpleRestrictions() {
$restrictions = $this->toArray();
foreach ($restrictions as $r) {
if ($r instanceof Restriction) { $this->removeRestriction($r); }
if ($r instanceof RestrictionGroup) { $r->filterSimpleRestrictions(); }
}
}
/**
* Removes empty groups from the group. This is important since otherwise a
* query generator will most likely generate malformed SQL for this query.
*/
public function filterEmptyGroups() {
$restrictions = $this->toArray();
foreach ($restrictions as $r) {
if ($r instanceof RestrictionGroup) { $r->filterEmptyGroups(); }
if ($r instanceof RestrictionGroup && $r->isEmpty()) { $this->remove($r); }
}
}
/**
* @param string $type
* @return RestrictionGroup
*/
public function group($type = self::TYPE_OR) {
#Create the group and set the type we need
$group = $this->getQuery()->restrictionGroupInstance($this);
$group->setType($type);
#Add it to our restriction list
return $this->push($group);
}
public function endGroup() {
return $this->parent;
}
public function setQuery(Query$query) {
$this->parent = $query;
foreach ($this->restrictions as $restriction) { $restriction->setQuery($query);}
}
public function setParent(RestrictionGroup$query) {
$this->parent = $query;
foreach ($this->restrictions as $restriction) { $restriction->setParent($query);}
}
public function getParent() {
return $this->parent;
}
/**
* As opposed to the getParent method, the getQuery method will ensure that
* the return is a query.
*
* This allows the application to quickly get information about the query even
* if the restrictions are inside of several layers of restriction groups.
*
* @return Query
*/
public function getQuery() {
return $this->parent->getQuery();
}
public function setType($type) {
if ($type === self::TYPE_AND || $type === self::TYPE_OR) {
$this->type = $type;
return $this;
}
else {
throw new InvalidArgumentException("Restriction groups can only be of type AND or OR");
}
}
public function getType() {
return $this->type;
}
/**
* This is the equivalent of makeExecutionPlan on the root query for any subquery.
* Since subqueries are logical root queries and can be executed just like
* normal ones they require an equivalent method that is named differently.
*
* It retrieves all the subqueries that are needed to be executed on a relational
* DB before the main query.
*
* We could have used a single method with a flag, but this way seems cleaner
* and more hassle free than otherwise.
*
* @return Query[]
*/
public function getPhysicalSubqueries() {
$_ret = Array();
foreach ($this->getRestrictions() as $r) {
$_ret = array_merge($_ret, $r->getPhysicalSubqueries());
}
return $_ret;
}
/**
* When cloning a restriction group we need to ensure that the new restrictions
* are assigned to the parent, and not some other object.
*
* TODO: This would be potentially much simpler if the collection provided a
* walk method that would allow to modify the elements from within.
*/
public function __clone() {
$restrictions = $this->getRestrictions();
foreach ($restrictions as &$r) {
$r = clone $r;
$r->setParent($this);
}
$this->reset()->add($restrictions);
}
abstract public function __toString();
}
diff --git a/storage/database/Table.php b/storage/database/Table.php
index 9cc02ad..5cc3d35 100644
--- a/storage/database/Table.php
+++ b/storage/database/Table.php
@@ -1,298 +1,304 @@
<?php namespace spitfire\storage\database;
use CoffeeBean;
use Model;
use spitfire\exceptions\PrivateException;
use spitfire\storage\database\Schema;
/**
* This class simulates a table belonging to a database. This way we can query
* and handle tables with 'compiler-friendly' code that will inform about errors.
*
* @author César de la Cal <cesar@magic3w.com>
*/
class Table
{
/**
* A reference to the database driver loaded. This allows the system to
* use several databases without the models colliding.
*
* @var DB
*/
protected $db;
/**
* The model this table uses as template to create itself on the DBMS. This is
* one of the key components to Spitfire's ORM as it allows the DB engine to
* create the tables automatically and to discover the data relations.
*
* @var Schema
*/
protected $schema;
/**
* Provides access to the table's layout (physical schema)
*
* @var LayoutInterface
*/
private $layout = false;
/**
* Provides access to the table's record operations. Basically, a relational
* table is composed of schema + relation (data).
*
* @var Relation
*/
private $relation;
/**
* Contains the bean this table uses to generate forms for itself. The bean
* contains additional data to make the data request more user friendly.
*
* @var CoffeeBean
*/
protected $bean;
/**
* Caches a list of fields that compound this table's primary key. The property
* is empty when the table is constructed and collects the primary key's fields
* once they are requested for the first time.
*
* @var \spitfire\storage\database\Index|null
*/
protected $primaryK;
/**
* Just like the primary key field, this property caches the field that contains
* the autonumeric field. This will usually be the ID that the DB refers to
* when working with the table.
*
* @var Field
*/
protected $autoIncrement;
/**
* Creates a new Database Table instance. The tablename will be used to find
* the right model for the table and will be stored prefixed to this object.
*
* @param DB $db
* @param string|Schema $schema
*
* @throws PrivateException
*/
public function __construct(DB$db, $schema) {
$this->db = $db;
$factory = $this->db->getObjectFactory();
if (!$schema instanceof Schema) {
throw new PrivateException('Table requires a Schema to be passed');
}
#Attach the schema to this table
$this->schema = $schema;
$this->schema->setTable($this);
#Create a database table layout (physical schema)
$this->layout = $factory->makeLayout($this);
#Create the relation
$this->relation = $factory->makeRelation($this);
}
/**
* 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 overridden fields.
*
* @return Field[] The fields this table handles.
*/
public function getFields() {
trigger_error('Deprecated function Table::getFields() called', E_USER_DEPRECATED);
return $this->layout->getFields();
}
+ /**
+ *
+ * @deprecated since version 0.1-dev 20171128
+ * @param type $name
+ * @return type
+ */
public function getField($name) {
trigger_error('Deprecated function Table::getField() called', E_USER_DEPRECATED);
return $this->layout->getField($name);
}
/**
* Returns the name of the table that is being used. The table name
* includes the database's prefix.
*
* @return string
*/
public function getTablename() {
trigger_error('Deprecated function Table::getTablename() called', E_USER_DEPRECATED);
return $this->layout->getTableName();
}
/**
* Returns the database the table belongs to.
* @return DB
*/
public function getDb() {
return $this->db;
}
/**
* Get's the table's primary key. This will always return an array
* containing the fields the Primary Key contains.
*
* @return IndexInterface
*/
public function getPrimaryKey() {
/*
* If the primary was already determined, we use the cached version.
*/
if ($this->primaryK) { return $this->primaryK; }
$indexes = $this->layout->getIndexes();
return $this->primaryK = $indexes->filter(function (IndexInterface$i) { return $i->isPrimary(); })->rewind();
}
public function getAutoIncrement() {
if ($this->autoIncrement) { return $this->autoIncrement; }
//Implicit else
$fields = $this->layout->getFields();
foreach($fields as $field) {
if ($field->getLogicalField()->isAutoIncrement()) { return $this->autoIncrement = $field; }
}
return null;
}
/**
* Looks for a record based on it's primary data. This can be one of the
* following:
* <ul>
* <li>A single basic data field like a string or a int</li>
* <li>A string separated by : to separate those fields (SF POST standard)</li>
* <li>An array with the data</li>
* </ul>
* This function is intended to be used to provide controllers with prebuilt
* models so they don't need to fetch it again.
*
* @todo Move to relation
*
* @param mixed $id
*
* @return Model
*/
public function getById($id) {
#If the data is a string separate by colons
if (!is_array($id)) { $id = explode(':', $id); }
#Create a query
$table = $this;
$primary = $table->getPrimaryKey();
$query = $table->getDb()->getObjectFactory()->queryInstance($this);
#Add the restrictions
while(count($primary))
{ $query->addRestriction (array_shift($primary), array_shift($id)); }
#Return the result
$_return = $query->fetch();
return $_return;
}
/**
*
* @deprecated since version 0.1-dev 20160902
* @return Schema
*/
public function getModel() {
return $this->schema;
}
/**
*
* @deprecated since version 0.1-dev 20170801
* @return Relation
*/
public function getCollection() {
return $this->relation;
}
/**
* Gives access to the relation, the table's component that manages the data
* that the table contains.
*
* @return Relation
*/
public function getRelation() {
return $this->relation;
}
/**
*
* @return LayoutInterface
*/
public function getLayout(): LayoutInterface {
return $this->layout;
}
/**
*
* @return Schema
*/
public function getSchema() {
return $this->schema;
}
/**
* Returns the bean this model uses to generate Forms to feed itself with data
* the returned value normally is a class that inherits from CoffeeBean.
*
* @deprecated since version 0.1-dev 20161220
* @return CoffeeBean
*/
public function getBean($name = null) {
if (!$name) { $beanName = $this->schema->getName() . 'Bean'; }
else { $beanName = $name . 'Bean'; }
$bean = new $beanName($this);
return $bean;
}
public function get($field, $value, $operator = '=') {
return $this->relation->get($field, $value, $operator);
}
public function getAll() {
return $this->relation->getAll();
}
public function newRecord($data = Array()) {
return $this->relation->newRecord($data);
}
/**
* If the table cannot handle the request it will pass it on to the db
* and add itself to the arguments list.
*
* @param string $name
* @param mixed $arguments
*
* @return mixed
*/
public function __call($name, $arguments) {
#We basically reject __call since it is a bad programming habit to rely on
#redirecting every call
trigger_error("Called Table::__call() requesting $name()", E_USER_DEPRECATED);
#Add the table to the arguments for the db
array_unshift($arguments, $this);
#Pass on
return call_user_func_array(Array($this->db, $name), $arguments);
}
}
diff --git a/storage/database/restrictionmaker/CompositeRemoteFieldWorker.php b/storage/database/restrictionmaker/CompositeRemoteFieldWorker.php
index ebeade4..f9444ec 100644
--- a/storage/database/restrictionmaker/CompositeRemoteFieldWorker.php
+++ b/storage/database/restrictionmaker/CompositeRemoteFieldWorker.php
@@ -1,72 +1,72 @@
<?php namespace spitfire\storage\database\restrictionmaker;
use ChildrenField;
use Reference;
use spitfire\storage\database\RestrictionGroup;
/*
* 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.
*/
/**
* This class creates restrictions in the event of the field provided being provided
* as a field from a different table that references a field on the query's local
* table.
*
* This is the exact same as querying by a child field, only that, in this case,
* the child was never defined on the host table.
*/
-class CompositeRemoteWorker implements WorkerInterface
+class CompositeRemoteFieldWorker implements WorkerInterface
{
/**
*
* @param RestrictionGroup $parent
* @param Reference $field
* @param string $operator
* @param mixed $value
*/
public function make(RestrictionGroup $parent, $field, $operator, $value) {
$query = $parent->getQuery();
$of = $query->getTable()->getDb()->getObjectFactory();
/*
* There's several very specific requisites for this component to work.
* First of all, it is required to
*/
if (!($field instanceof Reference && $field->getTable() !== $query->getTable() && $field->getTarget() === $query->getTable()->getSchema())) {
return false;
}
/*
* Create a child field that connects the tables appropriately. Generally
* speaking, we could be using the field directly. But it makes the whole
* ordeal simpler.
*/
$child = new ChildrenField($field->getSchema(), $field->getName());
$child->setSchema($query->getTable()->getSchema());
return $of->restrictionCompositeInstance($parent, $child, $value, $operator);
}
}
diff --git a/storage/database/restrictionmaker/CompositeWorker.php b/storage/database/restrictionmaker/CompositeWorker.php
index fbaf7dc..44f6fae 100644
--- a/storage/database/restrictionmaker/CompositeWorker.php
+++ b/storage/database/restrictionmaker/CompositeWorker.php
@@ -1,58 +1,58 @@
<?php namespace spitfire\storage\database\restrictionmaker;
use spitfire\storage\database\RestrictionGroup;
/*
* 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 CompositeWorker implements WorkerInterface
{
/**
*
* @param RestrictionGroup $parent
* @param string $field
* @param string $operator
* @param mixed $value
*/
public function make(RestrictionGroup$parent, $field, $operator, $value) {
/*
* Find the appropriate field for the maker to assemble a restriction. If
* this returns an empty value, then this maker can't assemble a restriction
*/
$logical = $parent->getQuery()->getTable()->getSchema()->getField($field);
/*
* If the field is null or the value is null, then this maker is not a match
* for the behavior needed.
*/
if ($logical === null || $value === null) {
return false;
}
- return $parent->getQuery()->compositeRestrictionInstance($field, $value, $operator);
+ return $parent->getQuery()->compositeRestrictionInstance($logical, $value, $operator);
}
}
diff --git a/storage/database/restrictionmaker/RestrictionMaker.php b/storage/database/restrictionmaker/RestrictionMaker.php
index f20f3a1..729e31a 100644
--- a/storage/database/restrictionmaker/RestrictionMaker.php
+++ b/storage/database/restrictionmaker/RestrictionMaker.php
@@ -1,78 +1,80 @@
<?php namespace spitfire\storage\database\restrictionmaker;
use spitfire\exceptions\PrivateException;
use spitfire\model\Field as Logical;
use spitfire\storage\database\Field;
use spitfire\storage\database\RestrictionGroup;
/*
* 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 restriction maker class allows Spitfire to generate restrictions according
* to several provided parameters.
*/
class RestrictionMaker
{
private $workers = [];
/**
* Instances the restriction maker, the maker will automatically initialize
* the appropriate workers.
*/
public function __construct() {
-
$this->workers[] = new SimpleRestrictionWorker();
+ $this->workers[] = new ValueNulledWorker();
+ $this->workers[] = new CompositeWorker();
$this->workers[] = new FieldNulledWorker();
+ $this->workers[] = new CompositeRemoteFieldWorker();
}
/**
* Loops over the workers defined to make a restriction for the scenario we
* provide.
*
* @param string|Logical|Field $field
* @param type $operator
* @param type $value
*/
public function make(RestrictionGroup$parent, $field, $operator, $value) {
/*
* Loop over the workers to create a restriction. In case of error, the
* workers will return a boolean false or null.
*/
foreach ($this->workers as $worker) {
$r = $worker->make($parent, $field, $operator, $value);
if ($r) { return $r; }
}
/*
* If nothing could be found, we report the issue back to the user. There's
* literally nothing we can do any more.
*/
$str = is_object($field)? get_class($field) : $field;
throw new PrivateException('Restriction for the field ' . $str . ' could not be assembled', 1711101051);
}
}
\ No newline at end of file
diff --git a/storage/database/restrictionmaker/SimpleRestrictionWorker.php b/storage/database/restrictionmaker/SimpleRestrictionWorker.php
index d0c3aa7..ac278f8 100644
--- a/storage/database/restrictionmaker/SimpleRestrictionWorker.php
+++ b/storage/database/restrictionmaker/SimpleRestrictionWorker.php
@@ -1,72 +1,72 @@
<?php namespace spitfire\storage\database\restrictionmaker;
use Exception;
use spitfire\storage\database\Query;
use spitfire\storage\database\QueryField;
use spitfire\storage\database\Restriction;
use spitfire\storage\database\RestrictionGroup;
/*
* 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 SimpleRestrictionWorker implements WorkerInterface
{
/**
*
* @param Query $parent
* @param QueryField|string $fieldname
* @param string $operator
* @param mixed $value
*
* @return boolean|Restriction
*/
public function make(RestrictionGroup$parent, $fieldname, $operator, $value) {
/*
* We need an object factory to create the appropriate elements for the
* restriction.
*/
$of = $parent->getQuery()->getTable()->getDb()->getObjectFactory();
/*
* If the name of the field passed is a physical field we just use it to
* get a queryField.
*/
try {
- $field = $fieldname instanceof QueryField? $fieldname : $parent->getQuery()->getTable()->getField($fieldname);
+ $field = $fieldname instanceof QueryField? $fieldname : $parent->getQuery()->getTable()->getLayout()->getField($fieldname);
return $of->restrictionInstance($parent, $of->queryFieldInstance($parent->getQuery(), $field), $value, $operator);
}
/*
* If the table threw an exception because there was no such field, we
* catch the exception and return false because we can't figure out how to
* construct the query.
*/
catch (Exception $ex) {
return false;
}
}
}
\ No newline at end of file
diff --git a/tests/storage/database/RestrictionGroupTest.php b/tests/storage/database/RestrictionGroupTest.php
index 9edb3d6..523ed42 100644
--- a/tests/storage/database/RestrictionGroupTest.php
+++ b/tests/storage/database/RestrictionGroupTest.php
@@ -1,38 +1,39 @@
<?php namespace tests\spitfire\storage\db;
use IntegerField;
use PHPUnit\Framework\TestCase;
use spitfire\storage\database\drivers\mysqlPDOField;
use spitfire\storage\database\drivers\MysqlPDOQuery;
use spitfire\storage\database\drivers\MysqlPDOQueryField;
use spitfire\storage\database\drivers\MysqlPDORestriction;
use spitfire\storage\database\drivers\MysqlPDORestrictionGroup;
-use spitfire\storage\database\drivers\MysqlPDOTable;
use spitfire\storage\database\Schema;
+use spitfire\storage\database\Table;
+use function db;
class RestrictionGroupTest extends TestCase
{
/**
* This test creates a
*/
public function testClone() {
- $table = new MysqlPDOTable(db(), new Schema('test'));
+ $table = new Table(db(), new Schema('test'));
$query = new MysqlPDOQuery($table);
$field = new mysqlPDOField(new IntegerField(), 'test');
$queryfield = new MysqlPDOQueryField($query, $field);
$groupa = new MysqlPDORestrictionGroup($query);
$groupa->putRestriction(new MysqlPDORestriction($groupa, $queryfield, 'A'));
$groupb = clone $groupa;
$this->assertEquals($groupa->getRestriction(0)->getParent() === $groupb->getRestriction(0)->getParent(),
false, 'The two restrictions from two cloned queries should have different parents');
$this->assertEquals($groupa->getRestriction(0)->getQuery() === $groupb->getRestriction(0)->getQuery(),
true, 'The two restrictions should share a common query');
}
}
\ No newline at end of file
diff --git a/tests/storage/database/drivers/mysqlpdo/QueryTest.php b/tests/storage/database/drivers/mysqlpdo/QueryTest.php
new file mode 100644
index 0000000..c4e0eb8
--- /dev/null
+++ b/tests/storage/database/drivers/mysqlpdo/QueryTest.php
@@ -0,0 +1,54 @@
+<?php namespace tests\spitfire\storage\database\drivers\mysqlpdo;
+
+use IntegerField;
+use PHPUnit\Framework\TestCase;
+use spitfire\exceptions\PrivateException;
+use spitfire\storage\database\drivers\mysqlpdo\Driver;
+use spitfire\storage\database\Schema;
+use spitfire\storage\database\Settings;
+use spitfire\storage\database\Table;
+use StringField;
+
+class QueryTest extends TestCase
+{
+
+ private $db;
+
+ /**
+ * The table we're testing.
+ *
+ * @var Table
+ */
+ private $table;
+ private $schema;
+
+ public function setUp() {
+ //Just in case Mr. Bergmann decides to add code to the setUp
+ parent::setUp();
+
+ try {
+ $this->db = new Driver(Settings::fromArray([]));
+ $this->db->create();
+
+ $this->schema = new Schema('test');
+
+ $this->schema->field1 = new IntegerField(true);
+ $this->schema->field2 = new StringField(255);
+
+ $this->table = new Table($this->db, $this->schema);
+ }
+ catch (PrivateException$e) {
+ $this->markTestSkipped('MySQL PDO driver is not available.');
+ }
+ }
+
+ public function tearDown() {
+ $this->db->destroy();
+ }
+
+ public function testQuery() {
+ $result = $this->table->get('field1', 1)->fetch();
+ $this->assertNotEquals(null, $result);
+ }
+
+}

File Metadata

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

Event Timeline