PHPackages                             tivins/database - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. tivins/database

ActiveLibrary[Database &amp; ORM](/categories/database)

tivins/database
===============

A fluent, lightweight, and efficient PDO wrapper.

v1.0.0(3y ago)42071MITPHPPHP ^8.1CI failing

Since Dec 24Pushed 8mo ago1 watchersCompare

[ Source](https://github.com/tivins/database)[ Packagist](https://packagist.org/packages/tivins/database)[ RSS](/packages/tivins-database/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (3)Dependencies (2)Versions (4)Used By (0)

A PDO Wrapper
=============

[](#a-pdo-wrapper)

A secure, fluent, lightweight, and efficient PDO wrapper.

Helps protect against SQL injections.

---

[![Travis CI](https://camo.githubusercontent.com/7aeb76d234dbfb586ad75bb2f29c529d737ec8f5162ef5397c72f28cdc99968d/68747470733a2f2f6170702e7472617669732d63692e636f6d2f746976696e732f64617461626173652e7376673f6272616e63683d6d61696e)](https://app.travis-ci.com/tivins/database)[![Github CI](https://github.com/tivins/database/actions/workflows/php.yml/badge.svg)](https://github.com/tivins/database/actions/workflows/php.yml)[![Coverage Status](https://camo.githubusercontent.com/18647e142856002215378c1f1cbcb99bb0e671e946d89404dca56a5f4b6230fd/68747470733a2f2f636f766572616c6c732e696f2f7265706f732f6769746875622f746976696e732f64617461626173652f62616467652e7376673f6272616e63683d6d61696e)](https://coveralls.io/github/tivins/database?branch=main)

Install
-------

[](#install)

### Requirements

[](#requirements)

- [PHP](https://php.net) &gt;= 8.1
- [PDO extension](https://www.php.net/pdo)
- Optional development dependencies:
    - [PHPUnit](https://github.com/sebastianbergmann/phpunit/)

See [composer.json](/composer.json).

### Installation with Composer

[](#installation-with-composer)

```
composer require tivins/database
```

Quick example
-------------

[](#quick-example)

```
use Tivins\Database\Database;
use Tivins\Database\Connectors\MySQLConnector;

require 'vendor/autoload.php';

$db = new Database(new MySQLConnector('dbname', 'user', 'password', 'localhost'));

$posts = $db->select('books', 'b')
    ->leftJoin('users', 'u', 'b.author_id = u.id')
    ->addFields('b')
    ->addField('u', 'name', 'author_name')
    ->condition('b.year', 2010)
    ->execute()
    ->fetchAll();
```

Table of Contents
-----------------

[](#table-of-contents)

- [Usage](#usage)
    - [Connectors](#connectors)
    - [Queries](#using-queries)
        - [Select](#select-query)
        - [Insert](#insert-query)
        - [Update](#update-query)
        - [Create](#create-query)
        - [Delete](#delete-query)
        - [Extended Queries](#extended-queries)
            - [SelectInsert](#select-insert-query)
            - [Merge](#merge-query)
    - [Conditions and Expressions](#conditions)
        - [Nested conditions](#nested-conditions)
        - [Expressions](#expressions)
            - [Predefined expressions](#predefined-expressions)
            - [Insert expressions](#insert-expressions)
        - [Having](#having)
    - [Common Operations](#common-operations)
        - [Order by](#order-by)
        - [Range/Limit](#rangelimit)
    - [Transactions](#transactions)
    - [Error handling](#error-handling)
- [Development](#development)
    - [Unit tests](#unit-tests)

Usage
-----

[](#usage)

### Connectors

[](#connectors)

Creating a [`Database`](/src/Database.php) instance requires a valid [`Connector`](/src/Connectors/Connector.php) instance.

```
// MySQL
$connector = new MySQLConnector('dbname', 'user', 'password');
// SQLite
$connector = new SQLiteConnector('path/to/file');
// Native (PDO object)
$connector = new NativeConnector($existingPDOHandler);
```

Or

```
$db = new Database(new MySQLConnector(
    dbname:   'my_database',
    user:     'my_user',
    password: 'my_encrypted_password',
    host:     'localhost',
    port:     3306,
));
```

Then create a Database instance with the created connector:

```
$database = new Database($connector);
```

A `ConnectionException` can be thrown when the `new Database()` constructor attempts to connect using the given Connector.

Using Queries
-------------

[](#using-queries)

Both approaches below are valid:

```
// From database object
$query = $db->select('users', 'u');
// From new object
$query = new SelectQuery($db, 'users', 'u');
```

### Select Query

[](#select-query)

**Basic Usage**

```
$data = $db->select('books', 'b')
    ->addFields('b')
    ->condition('b.reserved', 0)
    ->execute()
    ->fetchAll();
```

**Joins**

You can also use `innerJoin` and `leftJoin`.

```
$db->select('books', 'b')
    ->addFields('b', ['id', 'title'])
    ->leftJoin('users', 'u', 'u.id = b.owner')
    ->addField('u', 'name', 'owner_name')
    ->condition('b.reserved', 1)
    ->execute()
    ->fetchAll();
```

**Expressions**

```
$db->select('books', 'b')
    ->addField('b', 'title')
    ->addExpression('concat(title, ?)', 'some_field', time())
    ->condition('b.reserved', 0)
    ->execute()
    ->fetchAll();
```

**Group By**

```
$tagsQuery = $db->select('tags', 't')
    ->innerJoin('book_tags', 'bt', 'bt.tag_id = t.id')
    ->addFields('t')
    ->addExpression('count(bt.book_id)', 'books_count')
    ->groupBy('t.id')
    ->orderBy('t.name', 'asc');
```

**Condition Expressions**

```
$db->select('books', 'b')
    ->addFields('b')
    ->conditionExpression('concat(b.id, "-", ?) = b.reference', $someValue)
    ->execute();
```

#### Range/Limit

[](#rangelimit)

```
$query->limit(10);          // implicit start from 0
$query->limitFrom(0, 10);   // explicit start from 0
$query->limitFrom(100, 50); // will fetch 50 rows from 100th row
```

#### Order By

[](#order-by)

`orderBy()` **adds** a new order statement to the query. It can be called multiple times.

```
$query->orderBy('field', 'desc');
```

Multiple times. In the following example, the results will be sorted by `post_type`, then by `date`:

```
$query->orderBy('post_type', 'desc')
      ->orderBy('date', 'asc');
```

### Insert Query

[](#insert-query)

```
$db->insert('book')
    ->fields([
        'title' => 'Book title',
        'author' => 'John Doe',
    ])
    ->execute();
```

#### Multiple inserts

[](#multiple-inserts)

```
$db->insert('book')
    ->multipleFields([
        ['title' => 'Book title', 'author' => 'John Doe'],
        ['title' => 'Another book title', 'author' => 'John Doe Jr'],
    ])
    ->execute();
```

Or,

```
$db->insert('book')
    ->multipleFields([
        ['Book title', 'John Doe'],
        ['Another book title', 'John Doe Jr'],
    ], ['title', 'author'])
    ->execute();
```

`execute()` will insert two rows into the `book` table.

 See the build result- Query ```
    insert into `book` (`title`,`author`) values (?,?), (?,?);
    ```
- Parameters ```
    ["Book title","John Doe","Another book title","John Doe Jr"]
    ```

#### Insert expressions

[](#insert-expressions)

Expressions can be used inside the array passed to the `fields()` method.

```
$db->insert('geom')
    ->fields([
        'name'     => $name,
        'position' => new InsertExpression('POINT(?,?)', $x, $y)
    ])
    ->execute();
```

`execute()` will insert one row into the `geom` table.

 See the build result- Query ```
    insert into `geom` (`name`, `position`) values (?, POINT(?,?))
    ```
- Parameters ```
    [$name, $x, $y]
    ```

`InsertExpression` is also allowed with a [MergeQuery](#merge-query).

### Update Query

[](#update-query)

```
$db->update('book')
    ->fields(['reserved' => 1])
    ->condition('id', 123)
    ->execute();
```

### Merge Query

[](#merge-query)

```
$db->merge('book')
    ->keys(['ean' => '123456'])
    ->fields(['title' => 'Book title', 'author' => 'John Doe'])
    ->execute();
```

### Delete Query

[](#delete-query)

Performs a `DELETE` query on the given table. All methods from [`Conditions`](/src/Conditions.php) can be used on a [`DeleteQuery`](/src/DeleteQuery.php) object.

```
$db->delete('book')
    ->whereIn('id', [3, 4, 5])
    ->execute();
```

### Create Query

[](#create-query)

Performs a `CREATE TABLE` query on the current database.

```
$query = $db->create('sample')
    ->addAutoIncrement(name: 'id')
    ->addInteger('counter', 0, unsigned: true, nullable: false)
    ->addInteger('null_val', null, nullable: false)
    ->addJSON('json_field')
    ->execute();
```

Field types:

- **Integers**

    ```
    $query->addPointer('id_user'); // Shortcut to Not-null Unsigned Integer
    ```
- **UnitEnum or BackedEnum**

    ```
    Enum Fruits { case Apple; case Banana; }
    $query->addEnum('fruits', Fruits::cases());
    ```
- **Standard Enum**

    ```
    $query->addStdEnum('fruits', ['apple','banana'], 'apple');
    ```

### Select-Insert Query

[](#select-insert-query)

Performs a SELECT, then an INSERT if not found.

```
$qry = $db->selectInsert('users')->matching(['name' => 'test', 'state' => 1]);
$qry->fetch()->id; // 1
$qry->getProcessedOperation(); // MergeOperation::INSERT

$qry = $db->selectInsert('users')->matching(['name' => 'test', 'state' => 1]);
$qry->fetch()->id; // 1
$qry->getProcessedOperation(); // MergeOperation::SELECT
```

By default, the array given in `matching()` is used to insert the new record.

You can define the fields for the INSERT query:

```
$matches = ['email' => 'user@example.com'];
$obj = $db->selectInsert('users')
    ->matching($matches)
    ->fields($matches + ['name' => 'user', 'created' => time()])
    ->fetch();
```

Expressions
-----------

[](#expressions)

You can use `SelectQuery::addExpression()` to add an expression to the selected fields.

**Signature:** `->addExpression(string $expression, string $alias, array $args)`

```
$query = $db->select('books', 'b')
    ->addExpression('concat(title, ?)', 'some_field', time())
    ->execute();
```

### Predefined Expressions

[](#predefined-expressions)

**Count (`addCount()`)**

```
$total = $db->select('table', 't')
    ->addCount('*')
    ->execute()
    ->fetchField();
```

Conditions
----------

[](#conditions)

Some examples:

```
->condition('field', 2);      // e.g.: where field = 2
->condition('field', 2, '>'); // e.g.: where field > 2
->condition('field', 2, '')
            ->like('title', '%php%')
    )
    ->execute();
```

The following is equivalent:

```
$db->select('book', 'b')
    ->fields('b', ['id', 'title', 'author'])
    ->condition(
        (new Conditions(Conditions::MODE_OR))
            ->condition('id', 3, '>')
            ->like('title', '%php%')
    )
    ->execute();
```

Having
------

[](#having)

```
$db->select('maps_polygons', 'p')
    // ...
    ->having($db->and()->isNotNull('geom'))
    ->execute();
    // ...
```

Transactions
------------

[](#transactions)

```
use Tivins\Database{ Database, DatabaseException, MySQLConnector };

function makeSomething(Database $db)
{
    $db->transaction();
    try {
        // do some stuff
    } catch (DatabaseException $exception) {
        $db->rollback();
        // log exception...
    }
}
```

Full Example
------------

[](#full-example)

See [FullTest.php](/tests/FullTest.php)

Error handling
--------------

[](#error-handling)

There are three main exceptions thrown by the Database class:

- **[ConnectionException](/src/Exceptions/ConnectionException.php)** - raised by the Database constructor if a connection cannot be established
- **[DatabaseException](/src/Exceptions/DatabaseException.php)** - thrown when a PDO exception is raised from query execution
- **[ConditionException](/src/Exceptions/ConditionException.php)** - raised when a given operator is not allowed

All of these exceptions have explicit messages (essentially from PDO).

**Short usage example:**

```
try {
    $this->db = new Database($connector);
} catch (ConnectionException $exception) {
    $this->logErrorInternally($exception->getMessage());
    $this->displayError("Cannot connect to the database.");
}
```

```
try {
    $this->db->insert('users')
        ->fields([
            'name' => 'DuplicateName',
        ])
        ->execute();
} catch (DatabaseException $exception) {
    $this->logErrorInternally($exception->getMessage());
    $this->displayError("Cannot create the user.");
}
```

Unit Tests
----------

[](#unit-tests)

Create a test database and grant permissions to a user on it. Add a `phpunit.xml` file at the root of the repository.

```
-- This is a quick-start example
CREATE DATABASE test_db;
CREATE USER test_user@localhost IDENTIFIED BY 'test_passwd';
GRANT ALL ON test_db.* TO test_user@localhost;
FLUSH PRIVILEGES;
```

```

```

Then run the unit tests:

```
vendor/bin/phpunit tests/
```

To include coverage testing, use:

```
mkdir -p build/logs
vendor/bin/phpunit tests/ --coverage-clover build/logs/cover.xml
```

License
-------

[](#license)

This project is released under the MIT License. See the bundled [LICENSE](https://github.com/tivins/database/blob/master/LICENSE) file for details.

In addition, if you are using the `--dev` mode, some parts of the project have their own licenses attached (either in the source files or in a `LICENSE` file next to them).

Statistics
----------

[](#statistics)

[![Download Status](https://camo.githubusercontent.com/c5cb9255d58a615c8575e54047bae40f9f98af25e562bfd61093b938ab4d989d/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f646d2f746976696e732f64617461626173652e737667)](https://packagist.org/packages/tivins/database/stats)

###  Health Score

35

—

LowBetter than 80% of packages

Maintenance43

Moderate activity, may be stable

Popularity16

Limited adoption so far

Community10

Small or concentrated contributor base

Maturity60

Established project with proven stability

 Bus Factor1

Top contributor holds 99.5% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~195 days

Total

3

Last Release

1216d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/13aae62b30274178dfd8d18ce4b6bee1a7d7bcbc9db17c2d22b2be5c7b51edc4?d=identicon)[Tivins](/maintainers/Tivins)

---

Top Contributors

[![tivins](https://avatars.githubusercontent.com/u/479448?v=4)](https://github.com/tivins "tivins (183 commits)")[![edunea](https://avatars.githubusercontent.com/u/123240159?v=4)](https://github.com/edunea "edunea (1 commits)")

---

Tags

databasefluentmysqlpdophpprepared-statementsqueryquery-buildersqlsqlinject-defensesqlinjectionsqlitephpdatabasepdo

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/tivins-database/health.svg)

```
[![Health](https://phpackages.com/badges/tivins-database/health.svg)](https://phpackages.com/packages/tivins-database)
```

###  Alternatives

[ifsnop/mysqldump-php

PHP version of mysqldump cli that comes with MySQL

1.3k5.5M69](/packages/ifsnop-mysqldump-php)[clouddueling/mysqldump-php

PHP version of mysqldump cli that comes with MySQL

1.3k22.9k](/packages/clouddueling-mysqldump-php)[popphp/pop-db

Pop Db Component for Pop PHP Framework

1814.6k11](/packages/popphp-pop-db)[riverside/php-orm

PHP ORM micro-library and query builder

111.2k](/packages/riverside-php-orm)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
