diff --git a/doc/book/zend.db.adapter.md b/doc/book/zend.db.adapter.md new file mode 100644 index 0000000000..075c498f8b --- /dev/null +++ b/doc/book/zend.db.adapter.md @@ -0,0 +1,389 @@ +# Zend\\Db\\Adapter + +The Adapter object is the most important sub-component of `Zend\Db`. It is responsible for adapting +any code written in or for Zend\\Db to the targeted php extensions and vendor databases. In doing +this, it creates an abstraction layer for the PHP extensions, which is called the "Driver" portion +of the `Zend\Db` adapter. It also creates a lightweight abstraction layer, called the "Platform" +portion of the adapter, for the various idiosyncrasies that each vendor-specific platform might have +in its SQL/RDBMS implementation. + +## Creating an Adapter - Quickstart + +Creating an adapter can simply be done by instantiating the `Zend\Db\Adapter\Adapter` class. The +most common use case, while not the most explicit, is to pass an array of configuration to the +`Adapter`. + +```php +$adapter = new Zend\Db\Adapter\Adapter($configArray); +``` + +This driver array is an abstraction for the extension level required parameters. Here is a table for +the key-value pairs that should be in configuration array. + +> ## Note +Other names will work as well. Effectively, if the PHP manual uses a particular naming, this naming +will be supported by our Driver. For example, dbname in most cases will also work for 'database'. +Another example is that in the case of Sqlsrv, UID will work in place of username. Which format you +chose is up to you, but the above table represents the official abstraction names. + +So, for example, a MySQL connection using ext/mysqli: + +```php +$adapter = new Zend\Db\Adapter\Adapter(array( + 'driver' => 'Mysqli', + 'database' => 'zend_db_example', + 'username' => 'developer', + 'password' => 'developer-password' +)); +``` + +Another example, of a Sqlite connection via PDO: + +```php +$adapter = new Zend\Db\Adapter\Adapter(array( + 'driver' => 'Pdo_Sqlite', + 'database' => 'path/to/sqlite.db' +)); +``` + +It is important to know that by using this style of adapter creation, the `Adapter` will attempt to +create any dependencies that were not explicitly provided. A Driver object will be created from the +configuration array provided in the constructor. A Platform object will be created based off the +type of Driver class that was instantiated. And lastly, a default ResultSet object is created and +utilized. Any of these objects can be injected, to do this, see the next section. + +The list of officially supported drivers: + +- `Mysqli`: The ext/mysqli driver +- `Pgsql`: The ext/pgsql driver +- `Sqlsrv`: The ext/sqlsrv driver (from Microsoft) +- `Pdo_Mysql`: MySQL through the PDO extension +- `Pdo_Sqlite`: SQLite though the PDO extension +- `Pdo_Pgsql`: PostgreSQL through the PDO extension + +## Creating an Adapter Using Dependency Injection + +The more expressive and explicit way of creating an adapter is by injecting all your dependencies up +front. `Zend\Db\Adapter\Adapter` uses constructor injection, and all required dependencies are +injected through the constructor, which has the following signature (in pseudo-code): + +```php +use Zend\Db\Adapter\Platform\PlatformInterface; +use Zend\Db\ResultSet\ResultSet; + +class Zend\Db\Adapter\Adapter { + public function __construct($driver, PlatformInterface $platform = null, ResultSet +$queryResultSetPrototype = null) +} +``` + +What can be injected: + +- $driver - an array of connection parameters (see above) or an instance of +`Zend\Db\Adapter\Driver\DriverInterface` +- $platform - (optional) an instance of `Zend\Db\Platform\PlatformInterface`, the default will be +created based off the driver implementation +- $queryResultSetPrototype - (optional) an instance of `Zend\Db\ResultSet\ResultSet`, to understand +this object's role, see the section below on querying through the adapter + +## Query Preparation Through Zend\\Db\\Adapter\\Adapter::query() + +By default, query() prefers that you use "preparation" as a means for processing SQL statements. +This generally means that you will supply a SQL statement with the values substituted by +placeholders, and then the parameters for those placeholders are supplied separately. An example of +this workflow with `Zend\Db\Adapter\Adapter` is: + +```php +$adapter->query('SELECT * FROM `artist` WHERE `id` = ?', array(5)); +``` + +The above example will go through the following steps: + +- create a new Statement object +- prepare an array into a ParameterContainer if necessary +- inject the ParameterContainer into the Statement object +- execute the Statement object, producing a Result object +- check the Result object to check if the supplied sql was a "query", or a result set producing +statement +- if it is a result set producing query, clone the ResultSet prototype, inject Result as datasource, +return it +- else, return the Result + +## Query Execution Through Zend\\Db\\Adapter\\Adapter::query() + +In some cases, you have to execute statements directly. The primary purpose for needing to execute +sql instead of prepare and execute a sql statement, might be because you are attempting to execute a +DDL statement (which in most extensions and vendor platforms), are un-preparable. An example of +executing: + +```php +$adapter->query('ALTER TABLE ADD INDEX(`foo_index`) ON (`foo_column`)', +Adapter::QUERY_MODE_EXECUTE); +``` + +The primary difference to notice is that you must provide the Adapter::QUERY\_MODE\_EXECUTE +(execute) as the second parameter. + +## Creating Statements + +While query() is highly useful for one-off and quick querying of a database through Adapter, it +generally makes more sense to create a statement and interact with it directly, so that you have +greater control over the prepare-then-execute workflow. To do this, Adapter gives you a routine +called createStatement() that allows you to create a Driver specific Statement to use so you can +manage your own prepare-then-execute workflow. + +```php +// with optional parameters to bind up-front +$statement = $adapter->createStatement($sql, $optionalParameters); +$result = $statement->execute(); +``` + +## Using the Driver Object + +The Driver object is the primary place where `Zend\Db\Adapter\Adapter` implements the connection +level abstraction making it possible to use all of ZendDb's interfaces via the various ext/mysqli, +ext/sqlsrv, PDO, and other PHP level drivers. To make this possible, each driver is composed of 3 +objects: + +- A connection: `Zend\Db\Adapter\Driver\ConnectionInterface` +- A statement: `Zend\Db\Adapter\Driver\StatementInterface` +- A result: `Zend\Db\Adapter\Driver\ResultInterface` + +Each of the built-in drivers practices "prototyping" as a means of creating objects when new +instances are requested. The workflow looks like this: + +- An adapter is created with a set of connection parameters +- The adapter chooses the proper driver to instantiate, for example `Zend\Db\Adapter\Driver\Mysqli` +- That driver class is instantiated +- If no connection, statement or result objects are injected, defaults are instantiated + +This driver is now ready to be called on when particular workflows are requested. Here is what the +Driver API looks like: + +```php +namespace Zend\Db\Adapter\Driver; + + interface DriverInterface + { + const PARAMETERIZATION_POSITIONAL = 'positional'; + const PARAMETERIZATION_NAMED = 'named'; + const NAME_FORMAT_CAMELCASE = 'camelCase'; + const NAME_FORMAT_NATURAL = 'natural'; + public function getDatabasePlatformName($nameFormat = self::NAME_FORMAT_CAMELCASE); + public function checkEnvironment(); + public function getConnection(); + public function createStatement($sqlOrResource = null); + public function createResult($resource); + public function getPrepareType(); + public function formatParameterName($name, $type = null); + public function getLastGeneratedValue(); + } +``` + +From this DriverInterface, you can + +- Determine the name of the platform this driver supports (useful for choosing the proper platform +object) +- Check that the environment can support this driver +- Return the Connection object +- Create a Statement object which is optionally seeded by an SQL statement (this will generally be a +clone of a prototypical statement object) +- Create a Result object which is optionally seeded by a statement resource (this will generally be +a clone of a prototypical result object) +- Format parameter names, important to distinguish the difference between the various ways +parameters are named between extensions +- Retrieve the overall last generated value (such as an auto-increment value) + +Statement objects generally look like this: + +```php +namespace Zend\Db\Adapter\Driver; + +interface StatementInterface extends StatementContainerInterface +{ + public function getResource(); + public function prepare($sql = null); + public function isPrepared(); + public function execute($parameters = null); + + /** Inherited from StatementContainerInterface */ + public function setSql($sql); + public function getSql(); + public function setParameterContainer(ParameterContainer $parameterContainer); + public function getParameterContainer(); +} +``` + +Result objects generally look like this: + +```php +namespace Zend\Db\Adapter\Driver; + +interface ResultInterface extends \Countable, \Iterator +{ + public function buffer(); + public function isQueryResult(); + public function getAffectedRows(); + public function getGeneratedValue(); + public function getResource(); + public function getFieldCount(); +} +``` + +## Using The Platform Object + +The Platform object provides an API to assist in crafting queries in a way that is specific to the +SQL implementation of a particular vendor. Nuances such as how identifiers or values are quoted, or +what the identifier separator character is are handled by this object. To get an idea of the +capabilities, the interface for a platform object looks like this: + +```php +namespace Zend\Db\Adapter\Platform; + +interface PlatformInterface +{ + public function getName(); + public function getQuoteIdentifierSymbol(); + public function quoteIdentifier($identifier); + public function quoteIdentifierChain($identiferChain) + public function getQuoteValueSymbol(); + public function quoteValue($value); + public function quoteValueList($valueList); + public function getIdentifierSeparator(); + public function quoteIdentifierInFragment($identifier, array $additionalSafeWords = array()); +} +``` + +While one can instantiate your own Platform object, generally speaking, it is easier to get the +proper Platform instance from the configured adapter (by default the Platform type will match the +underlying driver implementation): + +```php +$platform = $adapter->getPlatform(); +// or +$platform = $adapter->platform; // magic property access +``` + +The following is a couple of example of Platform usage: + +```php +``` + +> linenos +/*\* @var $adapter ZendDbAdapterAdapter*/ /*\* @var $platform ZendDbAdapterPlatformSql92*/ $platform += $adapter->getPlatform(); +// "first\_name" echo $platform->quoteIdentifier('first\_name'); +// " echo $platform->getQuoteIdentifierSymbol(); +// "schema"."mytable" echo $platform->quoteIdentifierChain(array('schema','mytable'))); +// ' echo $platform->getQuoteValueSymbol(); +// 'myvalue' echo $platform->quoteValue('myvalue'); +// 'value', 'Foo O\\'Bar' echo $platform->quoteValueList(array('value',"Foo O'Bar"))); +// . echo $platform->getIdentifierSeparator(); +// "foo" as "bar" echo $platform->quoteIdentifierInFragment('foo as bar'); +// additionally, with some safe words: // ("foo"."bar" = "boo"."baz") echo +$platform->quoteIdentifierInFragment('(foo.bar = boo.baz)', array('(', ')', '=')); + +## Using The Parameter Container + +The ParameterContainer object is a container for the various parameters that need to be passed into +a Statement object to fulfill all the various parameterized parts of the SQL statement. This object +implements the ArrayAccess interface. Below is the ParameterContainer API: + +```php +namespace Zend\Db\Adapter; + + class ParameterContainer implements \Iterator, \ArrayAccess, \Countable { + public function __construct(array $data = array()) + + /** methods to interact with values */ + public function offsetExists($name) + public function offsetGet($name) + public function offsetSetReference($name, $from) + public function offsetSet($name, $value, $errata = null) + public function offsetUnset($name) + + /** set values from array (will reset first) */ + public function setFromArray(Array $data) + + /** methods to interact with value errata */ + public function offsetSetErrata($name, $errata) + public function offsetGetErrata($name) + public function offsetHasErrata($name) + public function offsetUnsetErrata($name) + + /** errata only iterator */ + public function getErrataIterator() + + /** get array with named keys */ + public function getNamedArray() + + /** get array with int keys, ordered by position */ + public function getPositionalArray() + + /** iterator: */ + public function count() + public function current() + public function next() + public function key() + public function valid() + public function rewind() + + /** merge existing array of parameters with existing parameters */ + public function merge($parameters) + } +``` + +In addition to handling parameter names and values, the container will assist in tracking parameter +types for PHP type to SQL type handling. For example, it might be important that: + +```php +$container->offsetSet('limit', 5); +``` + +be bound as an integer. To achieve this, pass in the ParameterContainer::TYPE\_INTEGER constant as +the 3rd parameter: + +```php +$container->offsetSet('limit', 5, $container::TYPE_INTEGER); +``` + +This will ensure that if the underlying driver supports typing of bound parameters, that this +translated information will also be passed along to the actual php database driver. + +## Examples + +Creating a Driver and Vendor portable Query, Preparing and Iterating Result + +```php +$adapter = new Zend\Db\Adapter\Adapter($driverConfig); + +$qi = function($name) use ($adapter) { return $adapter->platform->quoteIdentifier($name); }; +$fp = function($name) use ($adapter) { return $adapter->driver->formatParameterName($name); }; + +$sql = 'UPDATE ' . $qi('artist') + . ' SET ' . $qi('name') . ' = ' . $fp('name') + . ' WHERE ' . $qi('id') . ' = ' . $fp('id'); + +/** @var $statement Zend\Db\Adapter\Driver\StatementInterface */ +$statement = $adapter->query($sql); + +$parameters = array( + 'name' => 'Updated Artist', + 'id' => 1 +); + +$statement->execute($parameters); + +// DATA INSERTED, NOW CHECK + +/* @var $statement Zend\Db\Adapter\DriverStatementInterface */ +$statement = $adapter->query('SELECT * FROM ' + . $qi('artist') + . ' WHERE id = ' . $fp('id')); + +/* @var $results Zend\Db\ResultSet\ResultSet */ +$results = $statement->execute(array('id' => 1)); + +$row = $results->current(); +$name = $row['name']; +``` diff --git a/doc/book/zend.db.metadata.md b/doc/book/zend.db.metadata.md new file mode 100644 index 0000000000..2052bf78b0 --- /dev/null +++ b/doc/book/zend.db.metadata.md @@ -0,0 +1,223 @@ +# Zend\\Db\\Metadata + +`Zend\Db\Metadata` is as sub-component of Zend\\Db that makes it possible to get metadata +information about tables, columns, constraints, triggers, and other information from a database in a +standardized way. The primary interface for the Metadata objects is: + +```php +interface MetadataInterface +{ + public function getSchemas(); + + public function getTableNames($schema = null, $includeViews = false); + public function getTables($schema = null, $includeViews = false); + public function getTable($tableName, $schema = null); + + public function getViewNames($schema = null); + public function getViews($schema = null); + public function getView($viewName, $schema = null); + + public function getColumnNames($table, $schema = null); + public function getColumns($table, $schema = null); + public function getColumn($columnName, $table, $schema = null); + + public function getConstraints($table, $schema = null); + public function getConstraint($constraintName, $table, $schema = null); + public function getConstraintKeys($constraint, $table, $schema = null); + + public function getTriggerNames($schema = null); + public function getTriggers($schema = null); + public function getTrigger($triggerName, $schema = null); +} +``` + +## Basic Usage + +Usage of `Zend\Db\Metadata` is very straight forward. The top level class +Zend\\Db\\Metadata\\Metadata will, given an adapter, choose the best strategy (based on the database +platform being used) for retrieving metadata. In most cases, information will come from querying the +INFORMATION\_SCHEMA tables generally accessible to all database connections about the currently +accessible schema. + +Metadata::get\*Names() methods will return an array of strings, while the other methods will return +specific value objects with the containing information. This is best demonstrated by the script +below. + +```php +$metadata = new Zend\Db\Metadata\Metadata($adapter); + +// get the table names +$tableNames = $metadata->getTableNames(); + +foreach ($tableNames as $tableName) { + echo 'In Table ' . $tableName . PHP_EOL; + + $table = $metadata->getTable($tableName); + + + echo ' With columns: ' . PHP_EOL; + foreach ($table->getColumns() as $column) { + echo ' ' . $column->getName() + . ' -> ' . $column->getDataType() + . PHP_EOL; + } + + echo PHP_EOL; + echo ' With constraints: ' . PHP_EOL; + + foreach ($metadata->getConstraints($tableName) as $constraint) { + /** @var $constraint Zend\Db\Metadata\Object\ConstraintObject */ + echo ' ' . $constraint->getName() + . ' -> ' . $constraint->getType() + . PHP_EOL; + if (!$constraint->hasColumns()) { + continue; + } + echo ' column: ' . implode(', ', $constraint->getColumns()); + if ($constraint->isForeignKey()) { + $fkCols = array(); + foreach ($constraint->getReferencedColumns() as $refColumn) { + $fkCols[] = $constraint->getReferencedTableName() . '.' . $refColumn; + } + echo ' => ' . implode(', ', $fkCols); + } + echo PHP_EOL; + + } + + echo '----' . PHP_EOL; +} +``` + +Metadata returns value objects that provide an interface to help developers better explore the +metadata. Below is the API for the various value objects: + +The TableObject: + +```php +class Zend\Db\Metadata\Object\TableObject +{ + public function __construct($name); + public function setColumns(array $columns); + public function getColumns(); + public function setConstraints($constraints); + public function getConstraints(); + public function setName($name); + public function getName(); +} +``` + +The ColumnObject: + +```php +class Zend\Db\Metadata\Object\ColumnObject { + public function __construct($name, $tableName, $schemaName = null); + public function setName($name); + public function getName(); + public function getTableName(); + public function setTableName($tableName); + public function setSchemaName($schemaName); + public function getSchemaName(); + public function getOrdinalPosition(); + public function setOrdinalPosition($ordinalPosition); + public function getColumnDefault(); + public function setColumnDefault($columnDefault); + public function getIsNullable(); + public function setIsNullable($isNullable); + public function isNullable(); + public function getDataType(); + public function setDataType($dataType); + public function getCharacterMaximumLength(); + public function setCharacterMaximumLength($characterMaximumLength); + public function getCharacterOctetLength(); + public function setCharacterOctetLength($characterOctetLength); + public function getNumericPrecision(); + public function setNumericPrecision($numericPrecision); + public function getNumericScale(); + public function setNumericScale($numericScale); + public function getNumericUnsigned(); + public function setNumericUnsigned($numericUnsigned); + public function isNumericUnsigned(); + public function getErratas(); + public function setErratas(array $erratas); + public function getErrata($errataName); + public function setErrata($errataName, $errataValue); +} +``` + +The ConstraintObject: + +```php +class Zend\Db\Metadata\Object\ConstraintObject +{ + public function __construct($name, $tableName, $schemaName = null); + public function setName($name); + public function getName(); + public function setSchemaName($schemaName); + public function getSchemaName(); + public function getTableName(); + public function setTableName($tableName); + public function setType($type); + public function getType(); + public function hasColumns(); + public function getColumns(); + public function setColumns(array $columns); + public function getReferencedTableSchema(); + public function setReferencedTableSchema($referencedTableSchema); + public function getReferencedTableName(); + public function setReferencedTableName($referencedTableName); + public function getReferencedColumns(); + public function setReferencedColumns(array $referencedColumns); + public function getMatchOption(); + public function setMatchOption($matchOption); + public function getUpdateRule(); + public function setUpdateRule($updateRule); + public function getDeleteRule(); + public function setDeleteRule($deleteRule); + public function getCheckClause(); + public function setCheckClause($checkClause); + public function isPrimaryKey(); + public function isUnique(); + public function isForeignKey(); + public function isCheck(); + +} +``` + +The TriggerObject: + +```php +class Zend\Db\Metadata\Object\TriggerObject +{ + public function getName(); + public function setName($name); + public function getEventManipulation(); + public function setEventManipulation($eventManipulation); + public function getEventObjectCatalog(); + public function setEventObjectCatalog($eventObjectCatalog); + public function getEventObjectSchema(); + public function setEventObjectSchema($eventObjectSchema); + public function getEventObjectTable(); + public function setEventObjectTable($eventObjectTable); + public function getActionOrder(); + public function setActionOrder($actionOrder); + public function getActionCondition(); + public function setActionCondition($actionCondition); + public function getActionStatement(); + public function setActionStatement($actionStatement); + public function getActionOrientation(); + public function setActionOrientation($actionOrientation); + public function getActionTiming(); + public function setActionTiming($actionTiming); + public function getActionReferenceOldTable(); + public function setActionReferenceOldTable($actionReferenceOldTable); + public function getActionReferenceNewTable(); + public function setActionReferenceNewTable($actionReferenceNewTable); + public function getActionReferenceOldRow(); + public function setActionReferenceOldRow($actionReferenceOldRow); + public function getActionReferenceNewRow(); + public function setActionReferenceNewRow($actionReferenceNewRow); + public function getCreated(); + public function setCreated($created); +} +``` diff --git a/doc/book/zend.db.result-set.md b/doc/book/zend.db.result-set.md new file mode 100644 index 0000000000..1cfa5bf602 --- /dev/null +++ b/doc/book/zend.db.result-set.md @@ -0,0 +1,117 @@ +# Zend\\Db\\ResultSet + +`Zend\Db\ResultSet` is a sub-component of Zend\\Db for abstracting the iteration of rowset producing +queries. While data sources for this can be anything that is iterable, generally a +`Zend\Db\Adapter\Driver\ResultInterface` based object is the primary source for retrieving data. + +`Zend\Db\ResultSet`'s must implement the `Zend\Db\ResultSet\ResultSetInterface` and all +sub-components of Zend\\Db that return a ResultSet as part of their API will assume an instance of a +`ResultSetInterface` should be returned. In most casts, the Prototype pattern will be used by +consuming object to clone a prototype of a ResultSet and return a specialized ResultSet with a +specific data source injected. The interface of ResultSetInterface looks like this: + +```php +interface ResultSetInterface extends \Traversable, \Countable +{ + public function initialize($dataSource); + public function getFieldCount(); +} +``` + +## Quickstart + +`Zend\Db\ResultSet\ResultSet` is the most basic form of a ResultSet object that will expose each row +as either an ArrayObject-like object or an array of row data. By default, `Zend\Db\Adapter\Adapter` +will use a prototypical `Zend\Db\ResultSet\ResultSet` object for iterating when using the +`Zend\Db\Adapter\Adapter::query()` method. + +The following is an example workflow similar to what one might find inside +`Zend\Db\Adapter\Adapter::query()`: + +```php +use Zend\Db\Adapter\Driver\ResultInterface; +use Zend\Db\ResultSet\ResultSet; + +$stmt = $driver->createStatement('SELECT * FROM users'); +$stmt->prepare(); +$result = $stmt->execute($parameters); + +if ($result instanceof ResultInterface && $result->isQueryResult()) { + $resultSet = new ResultSet; + $resultSet->initialize($result); + + foreach ($resultSet as $row) { + echo $row->my_column . PHP_EOL; + } +} +``` + +## Zend\\Db\\ResultSet\\ResultSet and Zend\\Db\\ResultSet\\AbstractResultSet + +For most purposes, either a instance of `Zend\Db\ResultSet\ResultSet` or a derivative of +`Zend\Db\ResultSet\AbstractResultSet` will be being used. The implementation of the +`AbstractResultSet` offers the following core functionality: + +```php +abstract class AbstractResultSet implements Iterator, ResultSetInterface +{ + public function initialize($dataSource) + public function getDataSource() + public function getFieldCount() + + /** Iterator */ + public function next() + public function key() + public function current() + public function valid() + public function rewind() + + /** countable */ + public function count() + + /** get rows as array */ + public function toArray() +} +``` + +## Zend\\Db\\ResultSet\\HydratingResultSet + +`Zend\Db\ResultSet\HydratingResultSet` is a more flexible `ResultSet` object that allows the +developer to choose an appropriate "hydration strategy" for getting row data into a target object. +While iterating over results, `HydratingResultSet` will take a prototype of a target object and +clone it once for each row. The `HydratingResultSet` will then hydrate that clone with the row data. + +In the example below, rows from the database will be iterated, and during iteration, +`HydratingRowSet` will use the Reflection based hydrator to inject the row data directly into the +protected members of the cloned UserEntity object: + +```php +use Zend\Db\Adapter\Driver\ResultInterface; +use Zend\Db\ResultSet\HydratingResultSet; +use Zend\Stdlib\Hydrator\Reflection as ReflectionHydrator; + +class UserEntity { + protected $first_name; + protected $last_name; + public function getFirstName() { return $this->first_name; } + public function getLastName() { return $this->last_name; } + public function setFirstName($first_name) { $this->first_name = $first_name; } + public function setLastName($last_name) { $this->last_name = $last_name; } +} + +$stmt = $driver->createStatement($sql); +$stmt->prepare($parameters); +$result = $stmt->execute(); + +if ($result instanceof ResultInterface && $result->isQueryResult()) { + $resultSet = new HydratingResultSet(new ReflectionHydrator, new UserEntity); + $resultSet->initialize($result); + + foreach ($resultSet as $user) { + echo $user->getFirstName() . ' ' . $user->getLastName() . PHP_EOL; + } +} +``` + +For more information, see the `Zend\Stdlib\Hydrator` documentation to get a better sense of the +different strategies that can be employed in order to populate a target object. diff --git a/doc/book/zend.db.row-gateway.md b/doc/book/zend.db.row-gateway.md new file mode 100644 index 0000000000..e63d956aa3 --- /dev/null +++ b/doc/book/zend.db.row-gateway.md @@ -0,0 +1,89 @@ +# Zend\\Db\\RowGateway + +`Zend\Db\RowGateway` is a sub-component of Zend\\Db that implements the Row Gateway pattern from +PoEAA. This effectively means that Row Gateway objects primarily model a row in a database, and have +methods such as save() and delete() that will help persist this row-as-an-object in the database +itself. Likewise, after a row from the database is retrieved, it can then be manipulated and +save()'d back to the database in the same position (row), or it can be delete()'d from the table. + +The interface for a Row Gateway object simply adds save() and delete() and this is the interface +that should be assumed when a component has a dependency that is expected to be an instance of a +RowGateway object: + +```php +interface RowGatewayInterface +{ + public function save(); + public function delete(); +} +``` + +## Quickstart + +While most of the time, RowGateway will be used in conjunction with other Zend\\Db\\ResultSet +producing objects, it is possible to use it standalone. To use it standalone, you simply need an +Adapter and a set of data to work with. The following use case demonstrates +Zend\\Db\\RowGateway\\RowGateway usage in its simplest form: + +```php +use Zend\Db\RowGateway\RowGateway; + +// query the database +$resultSet = $adapter->query('SELECT * FROM `user` WHERE `id` = ?', array(2)); + +// get array of data +$rowData = $resultSet->current()->getArrayCopy(); + +// row gateway +$rowGateway = new RowGateway('id', 'my_table', $adapter); +$rowGateway->populate($rowData, true); + +$rowGateway->first_name = 'New Name'; +$rowGateway->save(); + +// or delete this row: +$rowGateway->delete(); +``` + +The workflow described above is greatly simplified when RowGateway is used in conjunction with the +TableGateway feature. What this achieves is a Table Gateway object that when select()'ing from a +table, will produce a ResultSet that is then capable of producing valid Row Gateway objects. Its +usage looks like this: + +```php +use Zend\Db\TableGateway\Feature\RowGatewayFeature; +use Zend\Db\TableGateway\TableGateway; + +$table = new TableGateway('artist', $adapter, new RowGatewayFeature('id')); +$results = $table->select(array('id' => 2)); + +$artistRow = $results->current(); +$artistRow->name = 'New Name'; +$artistRow->save(); +``` + +## ActiveRecord Style Objects + +If you wish to have custom behaviour for your RowGateway objects (essentially making them behave +similarly to the ActiveRecord pattern), pass a prototype object implementing the +`RowGatewayInterface` to the `RowGatewayFeature` constructor instead of a primary key: + +```php +use Zend\Db\TableGateway\Feature\RowGatewayFeature; +use Zend\Db\TableGateway\TableGateway; +use Zend\Db\RowGateway\RowGatewayInterface; + +class Artist implements RowGatewayInterface +{ + protected $adapter; + + public function __construct($adapter) + { + $this->adapter = $adapter; + } + + // ... save() and delete() implementations +} + +$table = new TableGateway('artist', $adapter, new RowGatewayFeature(new Artist($adapter))); +``` diff --git a/doc/book/zend.db.sql.ddl.md b/doc/book/zend.db.sql.ddl.md new file mode 100644 index 0000000000..8a34b40109 --- /dev/null +++ b/doc/book/zend.db.sql.ddl.md @@ -0,0 +1,247 @@ +# Zend\\Db\\Sql\\Ddl + +`Zend\Db\Sql\Ddl` is a sub-component of `Zend\Db\Sql` that allows consumers to create statement +objects that will produce DDL (Data Definition Language) SQL statements. When combined with a +platform specific `Zend\Db\Sql\Sql` object, these DDL objects are capable of producing +platform-specific `CREATE TABLE` statements, with specialized data types, constraints, and indexes +for a database/schema. + +The following platforms have platform specializations for DDL: + +- MySQL +- All databases compatible with ANSI SQL92 + +## Creating Tables + +Like `Zend\Db\Sql` objects, each statement type is represented by a class. For example, `CREATE +TABLE` is modeled by a `CreateTable` object; this is likewise the same for `ALTER TABLE` (as +`AlterTable`), and `DROP TABLE` (as `DropTable`). These classes exist in the `Zend\Db\Sql\Ddl` +namespace. To initiate the building of a DDL statement, such as `CreateTable`, one needs to +instantiate the object. There are a couple of valid patterns for this: + +```php +use Zend\Db\Sql\Ddl; + +$table = new Ddl\CreateTable(); + +// or with table +$table = new Ddl\CreateTable('bar'); + +// optionally, as a temporary table +$table = new Ddl\CreateTable('bar', true); +``` + +You can also set the table after instantiation: + +```php +$table->setTable('bar'); +``` + +Currently, columns are added by creating a column object, described in the data type table in the +data type section below: + +```php +use Zend\Db\Sql\Ddl\Column; +$table->addColumn(new Column\Integer('id')); +$table->addColumn(new Column\Varchar('name', 255)); +``` + +Beyond adding columns to a table, constraints can also be added: + +```php +use Zend\Db\Sql\Ddl\Constraint; +$table->addConstraint(new Constraint\PrimaryKey('id')); +$table->addConstraint( + new Constraint\UniqueKey(['name', 'foo'], 'my_unique_key') +); +``` + +## Altering Tables + +Similarly to `CreateTable`, you may also instantiate `AlterTable`: + +```php +use Zend\Db\Sql\Ddl; + +$table = new Ddl\AlterTable(); + +// or with table +$table = new Ddl\AlterTable('bar'); + +// optionally, as a temporary table +$table = new Ddl\AlterTable('bar', true); +``` + +The primary difference between a `CreateTable` and `AlterTable` is that the `AlterTable` takes into +account that the table and its assets already exist. Therefore, while you still have `addColumn()` +and `addConstraint()`, you will also see the ability to change existing columns: + +```php +use Zend\Db\Sql\Ddl\Column; +$table->changeColumn('name', Column\Varchar('new_name', 50)); +``` + +You may also drop existing columns or constraints: + +```php +$table->dropColumn('foo'); +$table->dropConstraint('my_index'); +``` + +## Dropping Tables + +To drop a table, create a `DropTable` statement object: + +```php +$drop = new Ddl\DropTable('bar'); +``` + +## Executing DDL Statements + +After a DDL statement object has been created and configured, at some point you will want to execute +the statement. To do this, you will need two other objects: an `Adapter` instance, and a properly +seeded `Sql` instance. + +The workflow looks something like this, with `$ddl` being a `CreateTable`, `AlterTable`, or +`DropTable` instance: + +```php +use Zend\Db\Sql\Sql; + +// existence of $adapter is assumed +$sql = new Sql($adapter); + +$adapter->query( + $sql->getSqlStringForSqlObject($ddl), + $adapter::QUERY_MODE_EXECUTE +); +``` + +By passing the `$ddl` object through the `$sql` object's `getSqlStringForSqlObject()` method, we +ensure that any platform specific specializations/modifications are utilized to create a platform +specific SQL statement. + +Next, using the constant `Zend\Db\Adapter\Adapter::QUERY_MODE_EXECUTE` ensures that the SQL +statement is not prepared, as many DDL statements on a variety of platforms cannot be prepared, only +executed. + +## Currently Supported Data Types + +These types exist in the `Zend\Db\Sql\Ddl\Column` namespace. Data types must implement +`Zend\Db\Sql\Ddl\Column\ColumnInterface`. + +In alphabetical order: + + ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeArguments For Construction
BigInteger$name, $nullable = false, $default = null, array $options = +array()
Blob$name, $length, $nullable = false, $default = null, array $options = +array()
Boolean$name
Char$name, $length
Column (generic)$name = null
Date$name
Decimal$name, $precision, $scale = null
Float$name, $digits, $decimal (Note: this class is deprecated as of 2.4.0; +use Floating instead
Floating$name, $digits, $decimal
Integer$name, $nullable = false, $default = null, array $options = +array()
Time$name
Varchar$name, $length
+ +Each of the above types can be utilized in any place that accepts a `Column\ColumnInterface` +instance. Currently, this is primarily in `CreateTable::addColumn()` and `AlterTable`'s +`addColumn()` and `changeColumn()` methods. + +## Currently Supported Constraint Types + +These types exist in the `Zend\Db\Sql\Ddl\Constraint` namespace. Data types must implement +`Zend\Db\Sql\Ddl\Constraint\ConstraintInterface`. + +In alphabetical order: + + ++++ + + + + + + + + + + + + + + + + + + + + + + + + +
TypeArguments For Construction
Check$expression, $name
ForeignKey$name, $column, $referenceTable, $referenceColumn, $onDeleteRule = null, +$onUpdateRule = null
PrimaryKey$columns
UniqueKey$column, $name = null
+ +Each of the above types can be utilized in any place that accepts a `Column\ConstraintInterface` +instance. Currently, this is primarily in `CreateTable::addConstraint()` and +`AlterTable::addConstraint()`. diff --git a/doc/book/zend.db.sql.md b/doc/book/zend.db.sql.md new file mode 100644 index 0000000000..51b1a6d9d2 --- /dev/null +++ b/doc/book/zend.db.sql.md @@ -0,0 +1,621 @@ +# Zend\\Db\\Sql + +`Zend\Db\Sql` is a SQL abstraction layer for building platform specific SQL queries via an +object-oriented API. The end result of an `Zend\Db\Sql` object will be to either produce a Statement +and Parameter container that represents the target query, or a full string that can be directly +executed against the database platform. To achieve this, `Zend\Db\Sql` objects require a +`Zend\Db\Adapter\Adapter` object in order to produce the desired results. + +## Zend\\Db\\Sql\\Sql (Quickstart) + +As there are four primary tasks associated with interacting with a database (from the DML, or Data +Manipulation Language): selecting, inserting, updating and deleting. As such, there are four primary +objects that developers can interact or building queries, `Zend\Db\Sql\Select`, `Insert`, `Update` +and `Delete`. + +Since these four tasks are so closely related, and generally used together within the same +application, `Zend\Db\Sql\Sql` objects help you create them and produce the result you are +attempting to achieve. + +```php +use Zend\Db\Sql\Sql; +$sql = new Sql($adapter); +$select = $sql->select(); // @return Zend\Db\Sql\Select +$insert = $sql->insert(); // @return Zend\Db\Sql\Insert +$update = $sql->update(); // @return Zend\Db\Sql\Update +$delete = $sql->delete(); // @return Zend\Db\Sql\Delete +``` + +As a developer, you can now interact with these objects, as described in the sections below, to +specialize each query. Once they have been populated with values, they are ready to either be +prepared or executed. + +To prepare (using a Select object): + +```php +use Zend\Db\Sql\Sql; +$sql = new Sql($adapter); +$select = $sql->select(); +$select->from('foo'); +$select->where(array('id' => 2)); + +$statement = $sql->prepareStatementForSqlObject($select); +$results = $statement->execute(); +``` + +To execute (using a Select object) + +```php +use Zend\Db\Sql\Sql; +$sql = new Sql($adapter); +$select = $sql->select(); +$select->from('foo'); +$select->where(array('id' => 2)); + +$selectString = $sql->buildSqlString($select); +$results = $adapter->query($selectString, $adapter::QUERY_MODE_EXECUTE); +``` + +Zend\\Db\\Sql\\Sql objects can also be bound to a particular table so that in getting a select, +insert, update, or delete object, they are all primarily seeded with the same table when produced. + +```php +use Zend\Db\Sql\Sql; +$sql = new Sql($adapter, 'foo'); +$select = $sql->select(); +$select->where(array('id' => 2)); // $select already has the from('foo') applied +``` + +## Zend\\Db\\Sql's Select, Insert, Update and Delete + +Each of these objects implements the following (2) interfaces: + +```php +interface PreparableSqlInterface { + public function prepareStatement(Adapter $adapter, StatementInterface $statement); +} +interface SqlInterface { + public function getSqlString(PlatformInterface $adapterPlatform = null); +} +``` + +These are the functions you can call to either produce (a) a prepared statement, or (b) a string to +be executed. + +## Zend\\Db\\Sql\\Select + +`Zend\Db\Sql\Select` is an object who's primary function is to present a unified API for building +platform specific SQL SELECT queries. The class can be instantiated and consumed without +`Zend\Db\Sql\Sql`: + +```php +use Zend\Db\Sql\Select; +$select = new Select(); +// or, to produce a $select bound to a specific table +$select = new Select('foo'); +``` + +If a table is provided to the Select object, then from() cannot be called later to change the name +of the table. + +Once you have a valid Select object, the following API can be used to further specify various select +statement parts: + +```php +class Select extends AbstractSql implements SqlInterface, PreparableSqlInterface +{ + const JOIN_INNER = 'inner'; + const JOIN_OUTER = 'outer'; + const JOIN_LEFT = 'left'; + const JOIN_RIGHT = 'right'; + const SQL_STAR = '*'; + const ORDER_ASCENDING = 'ASC'; + const ORDER_DESCENDING = 'DESC'; + + public $where; // @param Where $where + + public function __construct($table = null); + public function from($table); + public function columns(array $columns, $prefixColumnsWithTable = true); + public function join($name, $on, $columns = self::SQL_STAR, $type = self::JOIN_INNER); + public function where($predicate, $combination = Predicate\PredicateSet::OP_AND); + public function group($group); + public function having($predicate, $combination = Predicate\PredicateSet::OP_AND); + public function order($order); + public function limit($limit); + public function offset($offset); +} +``` + +### from(): + +```php +// as a string: +$select->from('foo'); + +// as an array to specify an alias: +// produces SELECT "t".* FROM "table" AS "t" + +$select->from(array('t' => 'table')); + +// using a Sql\TableIdentifier: +// same output as above + +$select->from(new TableIdentifier(array('t' => 'table'))); +``` + +### columns(): + +```php +// as array of names +$select->columns(array('foo', 'bar')); + +// as an associative array with aliases as the keys: +// produces 'bar' AS 'foo', 'bax' AS 'baz' + +$select->columns(array('foo' => 'bar', 'baz' => 'bax')); +``` + +### join(): + +```php +$select->join( + 'foo', // table name + 'id = bar.id', // expression to join on (will be quoted by platform object before insertion), + array('bar', 'baz'), // (optional) list of columns, same requirements as columns() above + $select::JOIN_OUTER // (optional), one of inner, outer, left, right also represented by constants +in the API +); + +$select->from(array('f' => 'foo')) // base table + ->join(array('b' => 'bar'), // join table with alias + 'f.foo_id = b.foo_id'); // join expression +``` + +### where(), having(): + +The `Zend\Db\Sql\Select` object provides bit of flexibility as it regards to what kind of parameters +are acceptable when calling where() or having(). The method signature is listed as: + +```php +/** + * Create where clause + * + * @param Where|\Closure|string|array $predicate + * @param string $combination One of the OP_* constants from Predicate\PredicateSet + * @return Select + */ +public function where($predicate, $combination = Predicate\PredicateSet::OP_AND); +``` + +As you can see, there are a number of different ways to pass criteria to both having() and where(). + +If you provide a `Zend\Db\Sql\Where` object to where() or a `Zend\Db\Sql\Having` object to having(), +the internal objects for Select will be replaced completely. When the where/having() is processed, +this object will be iterated to produce the WHERE or HAVING section of the SELECT statement. + +If you provide a `Closure` to where() or having(), this function will be called with the Select's +`Where` object as the only parameter. So the following is possible: + +```php +$spec = function (Where $where) { + $where->like('username', 'ralph%'); +}; + +$select->where($spec); +``` + +If you provide a string, this string will be used to instantiate a +`Zend\Db\Sql\Predicate\Expression` object so that it's contents will be applied as is. This means +that there will be no quoting in the fragment provided. + +Consider the following code: + +```php +// SELECT "foo".* FROM "foo" WHERE x = 5 +$select->from('foo')->where('x = 5'); +``` + +If you provide an array who's values are keyed by an integer, the value can either be a string that +will be then used to build a `Predicate\Expression` or any object that implements +`Predicate\PredicateInterface`. These objects are pushed onto the Where stack with the $combination +provided. + +Consider the following code: + +```php +// SELECT "foo".* FROM "foo" WHERE x = 5 AND y = z +$select->from('foo')->where(array('x = 5', 'y = z')); +``` + +If you provide an array who's values are keyed with a string, these values will be handled in the +following: + +- PHP value nulls will be made into a `Predicate\IsNull` object +- PHP value array()s will be made into a `Predicate\In` object +- PHP value strings will be made into a `Predicate\Operator` object such that the string key will be +identifier, and the value will target value. + +Consider the following code: + +```php +// SELECT "foo".* FROM "foo" WHERE "c1" IS NULL AND "c2" IN (?, ?, ?) AND "c3" IS NOT NULL +$select->from('foo')->where(array( + 'c1' => null, + 'c2' => array(1, 2, 3), + new \Zend\Db\Sql\Predicate\IsNotNull('c3') +)); +``` + +### order(): + +```php +$select = new Select; +$select->order('id DESC'); // produces 'id' DESC + +$select = new Select; +$select->order('id DESC') + ->order('name ASC, age DESC'); // produces 'id' DESC, 'name' ASC, 'age' DESC + +$select = new Select; +$select->order(array('name ASC', 'age DESC')); // produces 'name' ASC, 'age' DESC +``` + +### limit() and offset(): + +```php +$select = new Select; +$select->limit(5); // always takes an integer/numeric +$select->offset(10); // similarly takes an integer/numeric +``` + +## Zend\\Db\\Sql\\Insert + +The Insert API: + +```php +class Insert implements SqlInterface, PreparableSqlInterface +{ + const VALUES_MERGE = 'merge'; + const VALUES_SET = 'set'; + + public function __construct($table = null); + public function into($table); + public function columns(array $columns); + public function values(array $values, $flag = self::VALUES_SET); +} +``` + +Similarly to Select objects, the table can be set at construction time or via into(). + +### columns(): + +```php +$insert->columns(array('foo', 'bar')); // set the valid columns +``` + +### values(): + +```php +// default behavior of values is to set the values +// successive calls will not preserve values from previous calls +$insert->values(array( + 'col_1' => 'value1', + 'col_2' => 'value2' +)); +``` + +```php +// merging values with previous calls +$insert->values(array('col_2' => 'value2'), $insert::VALUES_MERGE); +``` + +## Zend\\Db\\Sql\\Update + +```php +class Update +{ + const VALUES_MERGE = 'merge'; + const VALUES_SET = 'set'; + + public $where; // @param Where $where + public function __construct($table = null); + public function table($table); + public function set(array $values, $flag = self::VALUES_SET); + public function where($predicate, $combination = Predicate\PredicateSet::OP_AND); +} +``` + +### set(): + +```php +$update->set(array('foo' => 'bar', 'baz' => 'bax')); +``` + +### where(): + +See where section below. + +## Zend\\Db\\Sql\\Delete + +```php +class Delete +{ + public $where; // @param Where $where + public function __construct($table = null); + public function from($table); + public function where($predicate, $combination = Predicate\PredicateSet::OP_AND); +} +``` + +### where(): + +See where section below. + +## Zend\\Db\\Sql\\Where & Zend\\Db\\Sql\\Having + +In the following, we will talk about Where, Having is implies as being the same API. + +Effectively, Where and Having extend from the same base object, a Predicate (and PredicateSet). All +of the parts that make up a where or having that are and'ed or or'd together are called predicates. +The full set of predicates is called a PredicateSet. This object set generally contains the values +(and identifiers) separate from the fragment they belong to until the last possible moment when the +statement is either used to be prepared (parameteritized), or executed. In parameterization, the +parameters will be replaced with their proper placeholder (a named or positional parameter), and the +values stored inside a Adapter\\ParameterContainer. When executed, the values will be interpolated +into the fragments they belong to and properly quoted. + +It is important to know that in this API, a distinction is made between what elements are considered +identifiers (TYPE\_IDENTIFIER) and which of those is a value (TYPE\_VALUE). There is also a special +use case type for literal values (TYPE\_LITERAL). These are all exposed via the +`Zend\Db\Sql\ExpressionInterface` interface. + +> ## Note +In ZF 2.1, an actual `Literal` type was added. `Zend\Db\Sql` now makes the distinction that Literals +will not have any parameters that need interpolating whereas it is expected that `Expression` +objects *might* have parameters that need interpolating. In cases where there are parameters in an +`Expression`, `Zend\Db\Sql\AbstractSql` will do its best to identify placeholders when the +Expression is processed during statement creation. In short, if you don't have parameters, use +`Literal` objects. + +The Zend\\Db\\Sql\\Where (Predicate/PredicateSet) API: + +```php +// Where & Having: +class Predicate extends PredicateSet +{ + public $and; + public $or; + public $AND; + public $OR; + public $NEST; + public $UNNEST; + + public function nest(); + public function setUnnest(Predicate $predicate); + public function unnest(); + public function equalTo($left, $right, $leftType = self::TYPE_IDENTIFIER, $rightType = +self::TYPE_VALUE); + public function notEqualTo($left, $right, $leftType = self::TYPE_IDENTIFIER, $rightType = +self::TYPE_VALUE); + public function lessThan($left, $right, $leftType = self::TYPE_IDENTIFIER, $rightType = +self::TYPE_VALUE); + public function greaterThan($left, $right, $leftType = self::TYPE_IDENTIFIER, $rightType = +self::TYPE_VALUE); + public function lessThanOrEqualTo($left, $right, $leftType = self::TYPE_IDENTIFIER, $rightType += self::TYPE_VALUE); + public function greaterThanOrEqualTo($left, $right, $leftType = self::TYPE_IDENTIFIER, +$rightType = self::TYPE_VALUE); + public function like($identifier, $like); + public function literal($literal); + public function expression($expression, $parameter); + public function isNull($identifier); + public function isNotNull($identifier); + public function in($identifier, array $valueSet = array()); + public function between($identifier, $minValue, $maxValue); + + + // Inherited From PredicateSet + + public function addPredicate(PredicateInterface $predicate, $combination = null); + public function getPredicates(); + public function orPredicate(PredicateInterface $predicate); + public function andPredicate(PredicateInterface $predicate); + public function getExpressionData(); + public function count(); +} +``` + +Each method in the Where API will produce a corresponding Predicate object of a similarly named +type, described below, with the full API of the object: + +### equalTo(), lessThan(), greaterThan(), lessThanOrEqualTo(), greaterThanOrEqualTo(): + +```php +$where->equalTo('id', 5); + +// same as the following workflow +$where->addPredicate( + new Predicate\Operator($left, Operator::OPERATOR_EQUAL_TO, $right, $leftType, $rightType) +); + +class Operator implements PredicateInterface +{ + const OPERATOR_EQUAL_TO = '='; + const OP_EQ = '='; + const OPERATOR_NOT_EQUAL_TO = '!='; + const OP_NE = '!='; + const OPERATOR_LESS_THAN = '<'; + const OP_LT = '<'; + const OPERATOR_LESS_THAN_OR_EQUAL_TO = '<='; + const OP_LTE = '<='; + const OPERATOR_GREATER_THAN = '>'; + const OP_GT = '>'; + const OPERATOR_GREATER_THAN_OR_EQUAL_TO = '>='; + const OP_GTE = '>='; + + public function __construct($left = null, $operator = self::OPERATOR_EQUAL_TO, $right = null, +$leftType = self::TYPE_IDENTIFIER, $rightType = self::TYPE_VALUE); + public function setLeft($left); + public function getLeft(); + public function setLeftType($type); + public function getLeftType(); + public function setOperator($operator); + public function getOperator(); + public function setRight($value); + public function getRight(); + public function setRightType($type); + public function getRightType(); + public function getExpressionData(); +} +``` + +### like($identifier, $like): + +```php +$where->like($identifier, $like): + +// same as +$where->addPredicate( + new Predicate\Like($identifier, $like) +); + +// full API + +class Like implements PredicateInterface +{ + public function __construct($identifier = null, $like = null); + public function setIdentifier($identifier); + public function getIdentifier(); + public function setLike($like); + public function getLike(); +} +``` + +### literal($literal); + +```php +$where->literal($literal); + +// same as +$where->addPredicate( + new Predicate\Literal($literal) +); + +// full API +class Literal implements ExpressionInterface, PredicateInterface +{ + const PLACEHOLDER = '?'; + public function __construct($literal = ''); + public function setLiteral($literal); + public function getLiteral(); +} +``` + +### expression($expression, $parameter); + +```php +$where->expression($expression, $parameter); + +// same as +$where->addPredicate( + new Predicate\Expression($expression, $parameter) +); + +// full API +class Expression implements ExpressionInterface, PredicateInterface +{ + const PLACEHOLDER = '?'; + public function __construct($expression = null, $valueParameter = null /*[, $valueParameter, ... +]*/); + public function setExpression($expression); + public function getExpression(); + public function setParameters($parameters); + public function getParameters(); + public function setTypes(array $types); + public function getTypes(); +} +``` + +### isNull($identifier); + +```php +$where->isNull($identifier); + +// same as +$where->addPredicate( + new Predicate\IsNull($identifier) +); + +// full API +class IsNull implements PredicateInterface +{ + public function __construct($identifier = null); + public function setIdentifier($identifier); + public function getIdentifier(); +} +``` + +### isNotNull($identifier); + +```php +$where->isNotNull($identifier); + +// same as +$where->addPredicate( + new Predicate\IsNotNull($identifier) +); + +// full API +class IsNotNull implements PredicateInterface +{ + public function __construct($identifier = null); + public function setIdentifier($identifier); + public function getIdentifier(); +} +``` + +### in($identifier, array $valueSet = array()); + +```php +$where->in($identifier, array $valueSet = array()); + +// same as +$where->addPredicate( + new Predicate\In($identifier, $valueSet) +); + +// full API +class In implements PredicateInterface +{ + public function __construct($identifier = null, array $valueSet = array()); + public function setIdentifier($identifier); + public function getIdentifier(); + public function setValueSet(array $valueSet); + public function getValueSet(); +} +``` + +### between($identifier, $minValue, $maxValue); + +```php +$where->between($identifier, $minValue, $maxValue); + +// same as +$where->addPredicate( + new Predicate\Between($identifier, $minValue, $maxValue) +); + +// full API +class Between implements PredicateInterface +{ + public function __construct($identifier = null, $minValue = null, $maxValue = null); + public function setIdentifier($identifier); + public function getIdentifier(); + public function setMinValue($minValue); + public function getMinValue(); + public function setMaxValue($maxValue); + public function getMaxValue(); + public function setSpecification($specification); +} +``` diff --git a/doc/book/zend.db.table-gateway.md b/doc/book/zend.db.table-gateway.md new file mode 100644 index 0000000000..ef7751fe97 --- /dev/null +++ b/doc/book/zend.db.table-gateway.md @@ -0,0 +1,179 @@ +# Zend\\Db\\TableGateway + +The Table Gateway object is intended to provide an object that represents a table in a database, and +the methods of this object mirror the most common operations on a database table. In code, the +interface for such an object looks like this: + +```php +interface Zend\Db\TableGateway\TableGatewayInterface +{ + public function getTable(); + public function select($where = null); + public function insert($set); + public function update($set, $where = null); + public function delete($where); +} +``` + +There are two primary implementations of the `TableGatewayInterface` that are of the most useful: +`AbstractTableGateway` and `TableGateway`. The `AbstractTableGateway` is an abstract basic +implementation that provides functionality for `select()`, `insert()`, `update()`, `delete()`, as +well as an additional API for doing these same kinds of tasks with explicit SQL objects. These +methods are `selectWith()`, `insertWith()`, `updateWith()` and `deleteWith()`. In addition, +AbstractTableGateway also implements a "Feature" API, that allows for expanding the behaviors of the +base `TableGateway` implementation without having to extend the class with this new functionality. +The `TableGateway` concrete implementation simply adds a sensible constructor to the +`AbstractTableGateway` class so that out-of-the-box, `TableGateway` does not need to be extended in +order to be consumed and utilized to its fullest. + +## Basic Usage + +The quickest way to get up and running with Zend\\Db\\TableGateway is to configure and utilize the +concrete implementation of the `TableGateway`. The API of the concrete `TableGateway` is: + +```php +class TableGateway extends AbstractTableGateway +{ + public $lastInsertValue; + public $table; + public $adapter; + + public function __construct($table, Adapter $adapter, $features = null, ResultSet +$resultSetPrototype = null, Sql $sql = null) + + /** Inherited from AbstractTableGateway */ + + public function isInitialized(); + public function initialize(); + public function getTable(); + public function getAdapter(); + public function getColumns(); + public function getFeatureSet(); + public function getResultSetPrototype(); + public function getSql(); + public function select($where = null); + public function selectWith(Select $select); + public function insert($set); + public function insertWith(Insert $insert); + public function update($set, $where = null); + public function updateWith(Update $update); + public function delete($where); + public function deleteWith(Delete $delete); + public function getLastInsertValue(); +} +``` + +The concrete `TableGateway` object practices constructor injection for getting dependencies and +options into the instance. The table name and an instance of an Adapter are all that is needed to +setup a working `TableGateway` object. + +Out of the box, this implementation makes no assumptions about table structure or metadata, and when +`select()` is executed, a simple ResultSet object with the populated Adapter's Result (the +datasource) will be returned and ready for iteration. + +```php +use Zend\Db\TableGateway\TableGateway; +$projectTable = new TableGateway('project', $adapter); +$rowset = $projectTable->select(array('type' => 'PHP')); + +echo 'Projects of type PHP: '; +foreach ($rowset as $projectRow) { + echo $projectRow['name'] . PHP_EOL; +} + +// or, when expecting a single row: +$artistTable = new TableGateway('artist', $adapter); +$rowset = $artistTable->select(array('id' => 2)); +$artistRow = $rowset->current(); + +var_dump($artistRow); +``` + +The `select()` method takes the same arguments as `Zend\Db\Sql\Select::where()` with the addition of +also being able to accept a closure, which in turn, will be passed the current Select object that is +being used to build the SELECT query. The following usage is possible: + +```php +use Zend\Db\TableGateway\TableGateway; +use Zend\Db\Sql\Select; +$artistTable = new TableGateway('artist', $adapter); + +// search for at most 2 artists who's name starts with Brit, ascending +$rowset = $artistTable->select(function (Select $select) { + $select->where->like('name', 'Brit%'); + $select->order('name ASC')->limit(2); +}); +``` + +## TableGateway Features + +The Features API allows for extending the functionality of the base `TableGateway` object without +having to polymorphically extend the base class. This allows for a wider array of possible mixing +and matching of features to achieve a particular behavior that needs to be attained to make the base +implementation of `TableGateway` useful for a particular problem. + +With the `TableGateway` object, features should be injected though the constructor. The constructor +can take Features in 3 different forms: as a single feature object, as a FeatureSet object, or as an +array of Feature objects. + +There are a number of features built-in and shipped with Zend\\Db: + +- GlobalAdapterFeature: the ability to use a global/static adapter without needing to inject it into +a `TableGateway` instance. This is more useful when you are extending the `AbstractTableGateway` +implementation: + +```php +use Zend\Db\TableGateway\AbstractTableGateway; +use Zend\Db\TableGateway\Feature; + +class MyTableGateway extends AbstractTableGateway +{ + public function __construct() + { + $this->table = 'my_table'; + $this->featureSet = new Feature\FeatureSet(); + $this->featureSet->addFeature(new Feature\GlobalAdapterFeature()); + $this->initialize(); + } +} + +// elsewhere in code, in a bootstrap +Zend\Db\TableGateway\Feature\GlobalAdapterFeature::setStaticAdapter($adapter); + +// in a controller, or model somewhere +$table = new MyTableGateway(); // adapter is statically loaded +``` + +- MasterSlaveFeature: the ability to use a master adapter for insert(), update(), and delete() while +using a slave adapter for all select() operations. + +```php +$table = new TableGateway('artist', $adapter, new Feature\MasterSlaveFeature($slaveAdapter)); +``` + +- MetadataFeature: the ability populate `TableGateway` with column information from a Metadata +object. It will also store the primary key information in case RowGatewayFeature needs to consume +this information. + +```php +$table = new TableGateway('artist', $adapter, new Feature\MetadataFeature()); +``` + +- EventFeature: the ability utilize a `TableGateway` object with Zend\\EventManager and to be able +to subscribe to various events in a `TableGateway` lifecycle. + +```php +$table = new TableGateway('artist', $adapter, new Feature\EventFeature($eventManagerInstance)); +``` + +- RowGatewayFeature: the ability for `select()` to return a ResultSet object that upon iteration +will return a `RowGateway` object for each row. + +```php +$table = new TableGateway('artist', $adapter, new Feature\RowGatewayFeature('id')); +$results = $table->select(array('id' => 2)); + +$artistRow = $results->current(); +$artistRow->name = 'New Name'; +$artistRow->save(); +``` diff --git a/doc/bookdown.json b/doc/bookdown.json new file mode 100644 index 0000000000..3a60f4f9d1 --- /dev/null +++ b/doc/bookdown.json @@ -0,0 +1,13 @@ +{ + "title": "Zend\\Db", + "target": "html/", + "content": [ + "book/zend.db.adapter.md", + "book/zend.db.result-set.md", + "book/zend.db.sql.md", + "book/zend.db.sql.ddl.md", + "book/zend.db.table-gateway.md", + "book/zend.db.row-gateway.md", + "book/zend.db.metadata.md" + ] +} \ No newline at end of file