Custom row and selection classes for Nette Database
This library intends to have the same interface as Nette Database, but wraps Selection
and ActiveRow
objects.
This allows you to add custom methods (or even other services) into those objects. Query to each table
results in a table-specific "Selection" and "ActiveRow" objects. This is not an ORM.
- Install using Composer:
$ composer require filsedla/hyperrow
- Register an extension in your config file
extensions:
hyperrow: Filsedla\Hyperrow\Extension
The base interface for NDBT is Context
. Hyperrow wraps it in Hyperrow\Database
class. For start,
you can use it directly (Note: the extension does not register Database
service automatically.):
- Filsedla\Hyperrow\Database(@database.context, ...)
Then you can make a query as usual:
$userRow = $database->table('user')->where('email', $email)->fetch();
For this to work, it is necessary to create two classes. They can be empty:
class UserSelection extends Hyperrow\HyperSelection
{
}
class UserRow extends Hyperrow\HyperRow
{
}
Next, tell Hyperrow to use those classes providing their FQNs:
hyperrow:
classes:
selection:
mapping: Model\*Selection
row:
mapping: Model\*Row
The asterix will be automatically substituted for a table name.
Note: all configuration options have default values, so the last step may not be necessary if you match them.
Now, $userRow
is of type UserRow
, which allows us to write, for example:
if ($userRow->isValid()) ...
provided that we have added a method:
class UserRow extends Hyperrow\HyperRow
{
/**
* @return bool
*/
public function isValid()
{
return !$this->deleted;
}
}
In the basic example, you manually created custom selection and row classes. You could have subclassed
the Hyperrow\Database
class and add custom methods there as well.
However, Hyperrow can also generate methods and classes based on your database tables and columns. Hyperrow then expects you to use the following hierarchy to separate generated and manually created methods.
-
Hyperrow\Database
- library classProject\GeneratedDatabase
- contains generated methods for accessing tablesProject\Database
- created once manually, contains all other custom methods
-
Hyperrow\HyperRow
- library classProject\BaseRow
- created manually, a project base class for row classes from all tablesProject\*GeneratedRow
- generated class with methods for accessing columns, related and referenced rowsProject\*Row
- generated once, a row class for specific table with manually added methods
The same hierarchy applies for HyperSelection
. All 'Generated' classes are fully re-generated each
time the generator is run. You can specify their FQNs in config (wildcards will be substituted for
the table name they correspond to).
-
See the default config file and add configuration options to your project config into the Hyperrow extension section for fields whose default value does not fit you. The
dir
property and the wholeclasses
subtree is necessary. -
Create the two 'base' classes (make sure you match the configured FQNs from the 1st step):
class BaseSelection extends Hyperrow\HyperSelection
{
}
class BaseRow extends Hyperrow\HyperRow
{
}
You can later add methods shared between all table selection/row classes.
-
Set up and run the generator. See bootstrap.php or generate.php for two different methods. For a real project it is probably better to use a separate php file to start the generator.
-
Create your own
Database
class and register it as a service in your config file.
class Database extends GeneratedDatabase
{
}
Note: If you already have your row/selection classes from the basic example, make sure to change their
extends
to the 'generated' class, e.g. class UserRow extends UserGeneratedRow
.
- Test it by making a query using the new methods:
$database->user->withEmail('example@gmail.com')->fetch();
Your IDE should autocomplete and the call should succeed and return an UserRow
.
See the example subdirectory for a complete setup with the generator.
From v0.6 Hyperrow Database
supports nested transactions. They are implemented using
SAVEPOINT
SQL commands. If your database supports these, you can enable nested transactions
in config (note: default is off):
hyperrow:
nestedTransactions: on
The interface methods are the same as Nette Database (beginTransaction()
etc.)
Further, Database
has a useful method transaction()
that takes a (closure) callback.
Everything inside the provided callback is executed in a transaction.
$database->transaction(function () use ($variables) {
// Execute in transaction
});
See source Database.php for details.