Simplon-Postgresql Documentation
Simplon-Postgresql Documentation
Simplon/Postgres
1. Installing
2. Direct vs. PgSqlManager
3. Setup connection
4. Usage: Direct access
4.1. Query
4.2. Insert
4.3. Update
4.4. Replace
4.5. Delete
4.6. Execute
5. Usage: PgSqlManager
5.1. Query
5.2. Insert
5.3. Update
5.4. Replace
5.5. Delete
5.6. Execute
6. IN() Clause Handling
6.1. The issue
6.2. The solution
7. CRUD Helper
7.1. Intro
7.2. Requirements
7.3. Flexibility/Restrictions
7.4. Conclusion
7.5. Examples
7.6. Example Custom Vo
8. Exceptions
Dependecies
{
"require": {
"simplon/postgres": "*"
}
}
In constrast to the prior method the PgSqlManager uses a Builder Pattern to deal with the
database. What advantage does that offer? Well, in case that we want to do more things with our
query before sending it off we encapsule it as a Builder Pattern. From there on we could pass
it throughout our application to add more data or alike before sending the query finally off to the
database. Again, a quick example of how we would rewrite the above direct query:
$sqlBuilder
->setQuery('SELECT * FROM names WHERE name = :name')
->setConditions(array('name' => 'Peter'));
3. Setup connection
The library requires a config value object in order to instantiate a connection with Postgres. See
how it's done:
$config = array(
// required credentials
// optional
// standard setupPostgres
$dbConn = new \Simplon\Postgres\Postgres(
$config['host'],
$config['user'],
$config['password'],
$config['database']
);
\Simplon\Postgres\Postgres::__construct(
$host,
$user,
$password,
$database,
$fetchMode = \PDO::FETCH_ASSOC,
$charset = 'utf8',
array $options = array('port' => 3306, 'unixSocket' => '')
);
In case that you wanna use the PgSqlManager there is one piece missing:
FetchColumn
Returns a selected column from the first match. The example below returns id or false if
nothing was found.
// result
var_dump($result); // '1' || false
FetchColumnMany
Returns an array with the selected column from all matching datasets. In the example below an
array with all ids will be returned or false if nothing was found.
// result
var_dump($result); // ['1', '15', '30', ...] || false
FetchColumnManyCursor
Returns one matching dataset at a time. It is resource efficient and therefore handy when your
result has many data. In the example below you either iterate through the foreach loop in case
you have matchings or nothing will happen.
FetchRow
Returns all selected columns from a matched dataset. The example below returns id, age for the
matched dataset. If nothing got matched false will be returned.
FetchRowMany
Returns all selected columns from all matched dataset. The example below returns for each
matched dataset id, age. If nothing got matched false will be returned.
var_dump($result); // [ ['id' => '1', 'age' => '22'], ['id' => '15', 'age' =>
'40'], ... ] || false
FetchRowManyCursor
Same explanation as for FetchColumnManyCursor except that we receive all selected columns.
4.2. Insert
Single data
Inserting data into the database is pretty straight forward. Follow the example below:
$data = array(
'id' => false,
'name' => 'Peter',
'age' => 45,
);
var_dump($id); // 50 || bool
The result depends on the table. If the table holds an autoincrementing ID column you will
receive the ID count for the inserted data. If the table does not hold such a field you will receive
true for a successful insert. If anything went bogus you will receive false.
Many datasets
$data = array(
array(
'id' => false,
'name' => 'Peter',
'age' => 45,
),
array(
'id' => false,
'name' => 'Peter',
'age' => 16,
),
);
var_dump($id); // 50 || bool
The result depends on the table. If the table holds an autoincrementing ID column you will
receive the ID count for the inserted data. If the table does not hold such a field you will receive
true for a successful insert. If anything went bogus you will receive false.
4.3. Updating
Same as for insert statements accounts for updates. Its easy to understand. If the update
succeeded the response will be true. If something went wrong you will receive false.
$conds = array(
'id' => 50,
);
$data = array(
'name' => 'Peter',
'age' => 50,
);
Same as for insert statements accounts for updates. Its easy to understand. If the update
succeeded the response will be true. If something went wrong you will receive false.
$conds = array(
'id' => 50,
'name' => 'Peter',
);
$data = array(
'name' => 'Peter',
'age' => 50,
);
4.4. Replace
As Postgres states it: REPLACE works exactly like INSERT, except that if an old row in the table
has the same value as a new row for a PRIMARY KEY or a UNIQUE index, the old row is deleted
before the new row is inserted.
Replace a single datasets
As a result you will either receive the INSERT ID or false in case something went wrong.
$data = array(
'id' => 5,
'name' => 'Peter',
'age' => 16,
);
var_dump($result); // 1 || false
As a result you will either receive an array of INSERT IDs or false in case something went
wrong.
$data = array(
array(
'id' => 5,
'name' => 'Peter',
'age' => 16,
),
array(
'id' => 10,
'name' => 'John',
'age' => 22,
),
);
4.5. Delete
The following example demonstrates how to remove data. If the query succeeds we will receive
true else false.
$conds = array(
'id' => 50,
'name' => 'John',
);
4.6. Execute
This method is ment for calls which do not require any parameters such as TRUNCATE. If the call
succeeds you will receive true. If it fails an PostgresException will be thrown.
var_dump($result); // true
5. Usage: PgSqlManager
The following query examples will be a rewrite of the aforementioned direct access
examples. Remember: We need an instance of the PgSqlManager. Paragraph 3. Setup
connection shows how to get your hands on it.
5.1. Query
FetchColumn
Returns a selected column from the first match. In the example below id will be returned or
false if nothing was found.
$sqlBuilder
->setQuery('SELECT id FROM names WHERE name = :name')
->setConditions(array('name' => 'Peter'));
$result = $pgSqlManager->fetchColumn($sqlBuilder);
// result
var_dump($result); // '1' || false
FetchColumnMany
Returns an array with the selected column from all matching datasets. In the example below an
array with all ids will be returned or false if nothing was found.
$sqlBuilder
->setQuery('SELECT id FROM names WHERE name = :name')
->setConditions(array('name' => 'Peter'));
$result = $pgSqlManager->fetchColumnMany($sqlBuilder);
// result
var_dump($result); // ['1', '15', '30', ...] || false
FetchColumnManyCursor
Returns one matching dataset at a time. It is resource efficient and therefore handy when your
result has many data. In the example below you either iterate through the foreach loop in case
you have matchings or nothing will happen.
$sqlBuilder
->setQuery('SELECT id FROM names WHERE name = :name')
->setConditions(array('name' => 'Peter'));
FetchRow
Returns all selected columns from a matched dataset. The example below returns id, age for the
matched dataset. If nothing got matched false will be returned.
$sqlBuilder
->setQuery('SELECT id, age FROM names WHERE name = :name')
->setConditions(array('name' => 'Peter'));
$result = $pgSqlManager->fetchRow($sqlBuilder);
FetchRowMany
Returns all selected columns from all matched dataset. The example below returns for each
matched dataset id, age. If nothing got matched false will be returned.
$sqlBuilder
->setQuery('SELECT id, age FROM names WHERE name = :name')
->setConditions(array('name' => 'Peter'));
$result = $pgSqlManager->fetchRowMany($sqlBuilder);
var_dump($result); // [ ['id' => '1', 'age' => '22'], ['id' => '15', 'age' =>
'40'], ... ] || false
FetchRowManyCursor
Same explanation as for FetchColumnManyCursor except that we receive all selected columns.
$sqlBuilder
->setQuery('SELECT id, age FROM names WHERE name = :name')
->setConditions(array('name' => 'Peter'));
5.2. Insert
Single data
Inserting data into the database is pretty straight forward. Follow the example below:
$data = array(
'id' => false,
'name' => 'Peter',
'age' => 45,
);
$sqlBuilder
->setTableName('names')
->setData($data);
$id = $pgSqlManager->insert($sqlBuilder);
var_dump($id); // 50 || false
The result depends on the table. If the table holds an autoincrementing ID column you will
receive the ID count for the inserted data. If the table does not hold such a field you will receive
true for a successful insert. If anything went bogus you will receive false.
Many datasets
$data = array(
array(
'id' => false,
'name' => 'Peter',
'age' => 45,
),
array(
'id' => false,
'name' => 'Peter',
'age' => 16,
),
);
$result = $pgSqlManager->insert($sqlBuilder);
The result depends on the table. If the table holds an autoincrementing ID column you will
receive the ID count for the inserted data. If the table does not hold such a field you will receive
true for a successful insert. If anything went bogus you will receive false.
5.3. Update
Same as for insert statements accounts for updates. Its easy to understand. If the update
succeeded the response will be true. If something went wrong you will receive false.
$data = array(
'name' => 'Peter',
'age' => 50,
);
$sqlBuilder
->setTableName('names')
->setConditions(array('id' => 50))
->setData($data);
$result = $pgSqlManager->update($sqlBuilder);
Same as for insert statements accounts for updates. Its easy to understand. If the update
succeeded the response will be true. If something went wrong you will receive false.
$data = array(
'name' => 'Peter',
'age' => 50,
);
$sqlBuilder
->setTableName('names')
->setConditions(array('id' => 50))
->setConditionsQuery('id = :id OR name =: name')
->setData($data)
$result = $pgSqlManager->update($sqlBuilder);
5.4. Replace
As Postgres states it: REPLACE works exactly like INSERT, except that if an old row in the table
has the same value as a new row for a PRIMARY KEY or a UNIQUE index, the old row is deleted
before the new row is inserted.
As a result you will either receive the INSERT ID or false in case something went wrong.
$data = array(
'id' => 5,
'name' => 'Peter',
'age' => 16,
);
$sqlBuilder
->setTableName('names')
->setData($data);
$result = $pgSqlManager->replace($sqlBuilder);
var_dump($result); // 1 || false
Replace multiple datasets
As a result you will either receive an array of INSERT IDs or false in case something went
wrong.
$data = array(
array(
'id' => 5,
'name' => 'Peter',
'age' => 16,
),
array(
'id' => 10,
'name' => 'John',
'age' => 22,
),
);
$sqlBuilder
->setTableName('names')
->setData($data);
$result = $pgSqlManager->replaceMany($sqlBuilder);
5.5. Delete
The following example demonstrates how to remove data. If the query succeeds we will receive
true else false.
$sqlBuilder
->setTableName('names')
->setConditions(array('id' => 50));
$result = $pgSqlManager->delete($sqlBuilder);
The following example demonstrates how to remove data with a custom conditions query. If the
query succeeds we will receive true else false.
$result = $pgSqlManager->delete($sqlBuilder);
There is no way using an IN() clause via PDO. This functionality is simply not given. However,
you could do something like the following:
$ids = array(1,2,3,4,5);
$query = "SELECT * FROM users WHERE id IN (" . join(',', $ids) . ")";
Looks good at first sight - not sexy but probably does the job, right? Wrong. This approach only
works with INTEGERS and it does not ESCAPE the user's input - the reason why we use PDO in first
place.
Just for the record here is a string example which would not work:
The only way how this would work is by wrapping each value like the following: '"email"'.
Way too much work.
To take advantage of the built in IN() Clause with escaping and type handling do the
following:
// integers
$conds = array('ids' => array(1,2,3,4,5));
$query = "SELECT * FROM users WHERE id IN (:ids)";
// strings
$conds = array('emails' => array('[email protected]', '[email protected]'));
$query = "SELECT * FROM users WHERE email IN (:emails)";
7. CRUD Helper
7.1. Intro
CRUD stands for Create Read Update Delete and reflects the for basic functions for persisent
storage.
I found myself writing more and more CRUDs for all my object/database interactions simply for
the reason of having a SINGLE POINT OF ACCESS when I was interacting with these objects for
above mentioned functions. Eventually, it has sort of a touch of a database model but with more
flexibility. Also, we keep writing VALUE OBJECTS and by that we keep the red line for all our
code base.
Note: VALUE OBJECTS are actually MODELS while models are not value objects. The reason for
this is that a value object is vehicle for all sorts of data while models are only vehicles for
database data. At least that's what it should be.
7.2. Requirements/Restrictions
7.3. Flexibility
Set source: In case you have a table name which can't be easily pluralised (e.g.
person/people) you can set the source yourself via PgSqlCrudVo::$crudSource within
value object
Set custom read query: In case you need a custom query to get your object you can set
it when you instantiate the object new PgSqlCrudVo($query) or simply within your
__construct() { parent::construct($query); }.
Callbacks: You can implement two methods which will be called prior/after saving an
object: PgSqlCrudVo::crudBeforeSave($isCreateEvent) and
PgSqlCrudVo::crudAfterSave($isCreateEvent). The manager will pass you a
boolean to let you know what type of save process happens/happened. You could use this
e.g. to set automatically created_at and updated_at fields.
Set columns: If you have to either match property- and column name or only want a
selection of your properties make use of PgSqlCrudVo::crudColumns() within your
value object. It should return an array where the ARRAY KEY reflects the value object's
VARIABLE NAME and the ARRAY VALUE the COLUMN NAME. Example: array('createdAt'
=> 'created_at')
Ignore properties: Considering the prior point you could do the reverse and simply
IGNORE VARIABLES. For that implement PgSqlCrudVo::crudIgnore() which should
return an array of properties you would like to ignore.
No assumptions: There are no assumptions about primary keys or anything alike. You
set all conditions for reading, updating and/or deleting objects.
Casted values: Thanks to your value object which is always in between you and your
database you can cast all values - good bye STRING CASTED ONLY values.
7.4. Conclusion
That's all what is needed - at least for now. It's simple, explicit and flexible enough not to
restrict you in your requirements respectively your creativity.
7.5. Examples
Enough talk, bring it on! Alright, what is needed? Lets assume we have a database table called
users and a value object called UserVo. Note: the value object name has to be the singular of
the table's plural name.
... and here is our value object for the given table:
Now, lets do some CRUD, baby! For all processes we need an instance of our
PgSqlCrudManager:
/**
* construct it with an instance of your simplon/Postgres
*/
$PgSqlCrudManager = new \Simplon\Postgres\Crud\
PgSqlCrudManager($postgresInstance);
Create a user:
$userVo
->setName('Johnny Foobar')
->setEmail('[email protected]');
// print insert id
echo $userVo->getId(); // 1
Read a user:
// conditions: where id = 1
$conds = array('id' => 1);
// print name
echo $userVo->getName(); // Johnny Foobar
Update a user:
// conditions: where id = 1
$conds = array('id' => 1);
// update
/** @var UserVo $userVo */
$userVo = $PgSqlCrudManager->update($userVo, $conds);
// print name
echo $userVo->getName(); // Hansi Hinterseher
Delete a user:
// conditions: where id = 1
$conds = array('id' => 1);
/**
* UserVo::crudGetSource() is the name of the table
* based on the value object's name
*/
$PgSqlCrudManager->update(UserVo::crudGetSource(), $conds);
Setting a custom table name since the plural from person is not persons:
In case your column names are totally off there is a way to match them anyway against your
properties:
/**
* @return array
*/
public function crudColumns()
{
return array(
'id' => 'xx_id',
'name' => 'xx_name',
'email' => 'xx_email',
'createdAt' => 'xx_created_at',
'updatedAt' => 'xx_updated_at',
);
}
Sometimes there are some helper properties which are not part of your database entry. Here
is a way to ignore them:
/**
* @return array
*/
public function crudIgnore()
{
return array(
'isOffline',
);
}
8. Exceptions
For both access methods (direct, PgSqlManager) occuring exceptions will be wrapped by a
PostgresException. All essential exception information will be summarised as JSON within the
Exception Message.
License
Simplon/Postgres is freely distributable under the terms of the MIT license.
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.