PHPackages                             simple-crud/simple-crud - 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. simple-crud/simple-crud

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

simple-crud/simple-crud
=======================

Create/Read/Update/Delete easily

v7.5.3(4y ago)2396.3k56[5 issues](https://github.com/oscarotero/simple-crud/issues)3MITPHPPHP &gt;=7.2

Since Sep 23Pushed 3y ago14 watchersCompare

[ Source](https://github.com/oscarotero/simple-crud)[ Packagist](https://packagist.org/packages/simple-crud/simple-crud)[ Docs](https://github.com/oscarotero/simple-crud)[ RSS](/packages/simple-crud-simple-crud/feed)WikiDiscussions master Synced 2mo ago

READMEChangelog (10)Dependencies (7)Versions (95)Used By (3)

SimpleCrud
==========

[](#simplecrud)

[![Build Status](https://camo.githubusercontent.com/77c88a9c846785e7197606e01c815f7dc9d5c60dc1ae4ff4fc9bbae249a66789/68747470733a2f2f7472617669732d63692e6f72672f6f736361726f7465726f2f73696d706c652d637275642e706e673f6272616e63683d6d6173746572)](https://travis-ci.org/oscarotero/simple-crud)[![Scrutinizer Code Quality](https://camo.githubusercontent.com/45b08c74183eebebd417ecb320e48e5cfe41567dcd5807fad96dcb357826c3f3/68747470733a2f2f7363727574696e697a65722d63692e636f6d2f672f6f736361726f7465726f2f73696d706c652d637275642f6261646765732f7175616c6974792d73636f72652e706e673f623d6d6173746572)](https://scrutinizer-ci.com/g/oscarotero/simple-crud/?branch=master)

PHP library to (Create, Read, Update, Delete) in Mysql/Sqlite databases with zero configuration and some magic.

Naming conventions:
-------------------

[](#naming-conventions)

This library relies in some conventions to avoid configuration.

- Table names SHOULD be in [singular](http://stackoverflow.com/a/5841297) and **camelCase**
- Fields names SHOULD be in **singular** and **camelCase**
- The primary key of all tables MUST be `id`.
- Foreign keys MUST be `[tableName]_id`. For example, `post` table uses `post_id` as foreign key.
- Associative tables MUST use an underscore joining the two tables in alphabetic order. For example, the relationship between `post` and `tag` is `post_tag` but `post` and `category` is `category_post`.

Installation
------------

[](#installation)

This package is installable and autoloadable via Composer as [simple-crud/simple-crud](https://packagist.org/packages/simple-crud/simple-crud).

```
$ composer require simple-crud/simple-crud

```

Classes
-------

[](#classes)

SimpleCrud has the following classes:

- **Database:** Manage the database connection. Uses internally [Atlas.PDO](https://github.com/atlasphp/Atlas.PDO)
- **Query:** Creates the database queries. SimpleCrud is tested with MySQL and SQLite but due uses [Atlas.Query](https://github.com/atlasphp/Atlas.Query) internally, in theory Postgres and Microsoft SQL should be supported too.
- **Table:** Manages a database table
- **Field:** Manages a database field. Used to format and validate values
- **Row:** To store and modify a row
- **RowCollection:** Is a collection of rows

Usage example
-------------

[](#usage-example)

Let's say we have the following database scheme:

```
CREATE TABLE "post" (
    `id`    INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
    `title` TEXT,
    `category_id` INTEGER,
    `type`  TEXT,

    FOREIGN KEY(`category_id`) REFERENCES category(id)
);

CREATE TABLE `category` (
    `id`    INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
    `name`  TEXT
);

CREATE TABLE `tag` (
    `id`    INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
    `name`  TEXT
);

CREATE TABLE `post_tag` (
    `id`    INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
    `tag_id`   INTEGER NOT NULL,
    `post_id`  INTEGER NOT NULL,

    FOREIGN KEY(`tag_id`) REFERENCES tag(id),
    FOREIGN KEY(`post_id`) REFERENCES post(id)
);
```

To start, create an instance of `SimpleCrud\Database` passing the `PDO` connection.

```
use SimpleCrud\Database;

$pdo = new PDO($dsn, $username, $password);

$db = new Database($pdo);

//To get any table, use magic properties, they will be instantiated on demand:
$post = $db->post;
```

SimpleCrud load the database scheme and detects automatically all relationships between the tables using the naming conventions described above. For example the table "post" has a field called "category\_id", so SimpleCrud knows that each post has one category.

**Note:** In production environment, you may want to cache the scheme in order to avoid execute these queries and improve the performance. You can do it in this way:

```
use SimpleCrud\Scheme\Cache;
use SimpleCrud\Scheme\Mysql;

if ($cache->has('db_scheme')) {
    $array = $cache->get('db_scheme');
    $scheme = new Cache($array);
} else {
    $scheme = new Mysql($pdo);
    $cache->save('db_scheme', $scheme->toArray());
}

$db = new Database($pdo, $scheme);
```

Using the library
-----------------

[](#using-the-library)

### Basic CRUD:

[](#basic-crud)

You can interact directly with the tables to insert/update/delete/select data:

Use `ArrayAccess` interface to access to the data using the `id`:

```
//Get the post id = 3;
$post = $db->post[3];

//Check if a row exists
if (isset($db->post[3])) {
    echo 'exists';
}

//Delete a post
unset($db->post[3]);

//Update a post
$db->post[3] = [
    'title' => 'Hello world'
];

//Insert a new post
$db->post[] = [
    'title' => 'Hello world 2'
];

//Tables implements the Countable interface
$totalPost = count($db->post);
```

### Select by other fields

[](#select-by-other-fields)

If you want to select a row by other key than `id`, just use the method `get`:

```
$post = $db->post->get(['slug' => 'post-slug']);
```

### Select or create

[](#select-or-create)

Sometimes, you want to get a row or create it if it does not exist. You can do it easily with `getOrCreate` method:

```
$post = $db->post->getOrCreate(['slug' => 'post-slug']);
```

### Rows

[](#rows)

A `Row` object represents a database row and is used to read and modify its data:

```
//get a row by id
$post = $db->post[34];

//Get/modify fields values
echo $post->title;

$post->title = 'New title';

//Update the row into database
$post->save();

//Remove the row in the database
$post->delete();

//Create a new row
$newPost = $db->post->create(['title' => 'The title']);

//Insert the row in the database
$newPost->save();
```

### Queries

[](#queries)

A `Query` object represents a database query. SimpleCrud uses magic methods to create queries. For example `$db->post->select()` returns a new instance of a `Select` query in the tabe `post`. Other examples: `$db->comment->update()`, `$db->category->delete()`, etc... Each query has modifiers like `orderBy()`, `limit()`:

```
//Create an UPDATE query with the table post
$updateQuery = $db->post->update(['title' => 'New title']);

//Add conditions, limit, etc
$updateQuery
    ->where('id = ', 23)
    ->limit(1);

//get the query as string
echo $updateQuery; //UPDATE `post` ...

//execute the query and returns a PDOStatement with the result
$PDOStatement = $updateQuery();
```

The method `get()` executes the query and returns the processed result of the query. For example, with `insert()` returns the id of the new row:

```
//insert a new post
$id = $db->post
    ->insert([
        'title' => 'My first post',
        'text' => 'This is the text of the post'
    ])
    ->get();

//Delete a post
$db->post
    ->delete()
    ->where('id = ', 23)
    ->get();

//Count all posts
$total = $db->post
    ->selectAggregate('COUNT')
    ->get();
//note: this is the same like count($db->post)

//Sum the ids of all posts
$total = $db->post
    ->selectAggregate('SUM', 'id')
    ->get();
```

`select()->get()` returns an instance of `RowCollection` with the result:

```
$posts = $db->post
    ->select()
    ->where('id > ', 10)
    ->orderBy('id ASC')
    ->limit(100)
    ->get();

foreach ($posts as $post) {
    echo $post->title;
}
```

If you only need the first row, use the modifier `one()`:

```
$post = $db->post
    ->select()
    ->one()
    ->where('id = ', 23)
    ->get();

echo $post->title;
```

`select()` has some interesting modifiers like `relatedWith()` to add automatically the `WHERE` clauses needed to select data related with other row or rowCollection:

```
//Get the post id = 23
$post = $db->post[23];

//Select the category related with this post
$category = $db->category
    ->select()
    ->relatedWith($post)
    ->one()
    ->get();
```

### Query API:

[](#query-api)

Queries use [Atlas.Query](http://atlasphp.io/cassini/query/) library to build the final queries, so you can see the documentation for all available options.

#### Select / SelectAggregate

[](#select--selectaggregate)

FunctionDescription`one`Select 1 result.`relatedWith(Row / RowCollection / Table $relation)`To select rows related with other rows or tables (relation added in `WHERE`).`joinRelation(Table $table)`To add a related table as `LEFT JOIN`.`getPageInfo()`Returns the info of the pagination.`from`[Atlas.Query Select()](http://atlasphp.io/cassini/query/select.html)`columns`[Atlas.Query Select()](http://atlasphp.io/cassini/query/select.html)`join`[Atlas.Query Select()](http://atlasphp.io/cassini/query/select.html)`catJoin`[Atlas.Query Select()](http://atlasphp.io/cassini/query/select.html)`groupBy`[Atlas.Query Select()](http://atlasphp.io/cassini/query/select.html)`having`[Atlas.Query Select()](http://atlasphp.io/cassini/query/select.html)`orHaving`[Atlas.Query Select()](http://atlasphp.io/cassini/query/select.html)`orderBy`[Atlas.Query Select()](http://atlasphp.io/cassini/query/select.html)`catHaving`[Atlas.Query Select()](http://atlasphp.io/cassini/query/select.html)`where`[Atlas.Query Select()](http://atlasphp.io/cassini/query/select.html)`whereSprintf`[Atlas.Query Select()](http://atlasphp.io/cassini/query/select.html)`catWhere`[Atlas.Query Select()](http://atlasphp.io/cassini/query/select.html)`orWhere`[Atlas.Query Select()](http://atlasphp.io/cassini/query/select.html)`orWhereSprintf`[Atlas.Query Select()](http://atlasphp.io/cassini/query/select.html)`whereEquals`[Atlas.Query Select()](http://atlasphp.io/cassini/query/select.html)`limit`[Atlas.Query Select()](http://atlasphp.io/cassini/query/select.html)`offset`[Atlas.Query Select()](http://atlasphp.io/cassini/query/select.html)`distinct`[Atlas.Query Select()](http://atlasphp.io/cassini/query/select.html)`forUpdate`[Atlas.Query Select()](http://atlasphp.io/cassini/query/select.html)`setFlag`[Atlas.Query Select()](http://atlasphp.io/cassini/query/select.html)`bindValue`[Atlas.Query Select()](http://atlasphp.io/cassini/query/select.html)#### Update

[](#update)

FunctionDescription`relatedWith(Row / RowCollection / Table $relation)`To update rows related with other rows or tables (relation added in `WHERE`).`set`[Atlas.Query Update()](http://atlasphp.io/cassini/query/update.html)`setFlag`[Atlas.Query Update()](http://atlasphp.io/cassini/query/update.html)`where`[Atlas.Query Update()](http://atlasphp.io/cassini/query/update.html)`orWhere`[Atlas.Query Update()](http://atlasphp.io/cassini/query/update.html)`catWhere`[Atlas.Query Update()](http://atlasphp.io/cassini/query/update.html)`orderBy`[Atlas.Query Update()](http://atlasphp.io/cassini/query/update.html)`limit`[Atlas.Query Update()](http://atlasphp.io/cassini/query/update.html)`offset`[Atlas.Query Update()](http://atlasphp.io/cassini/query/update.html)#### Insert

[](#insert)

FunctionDescription`orIgnore()`To ignore silently the insertion on duplicated keys, instead throw an exception.`set`[Atlas.Query Insert()](http://atlasphp.io/cassini/query/insert.html)`setFlag`[Atlas.Query Insert()](http://atlasphp.io/cassini/query/insert.html)#### Delete

[](#delete)

FunctionDescription`relatedWith(Row / RowCollection / Table $relation)`To delete rows related with other rows or tables (relation added in `WHERE`).`setFlag`[Atlas.Query Delete()](http://atlasphp.io/cassini/query/delete.html)`where`[Atlas.Query Delete()](http://atlasphp.io/cassini/query/delete.html)`orWhere`[Atlas.Query Delete()](http://atlasphp.io/cassini/query/delete.html)`catWhere`[Atlas.Query Delete()](http://atlasphp.io/cassini/query/delete.html)`orderBy`[Atlas.Query Delete()](http://atlasphp.io/cassini/query/delete.html)`limit`[Atlas.Query Delete()](http://atlasphp.io/cassini/query/delete.html)`offset`[Atlas.Query Delete()](http://atlasphp.io/cassini/query/delete.html)### Lazy loads

[](#lazy-loads)

Both `Row` and `RowCollection` can load automatically other related rows. Just use a property named as related table. For example:

```
//Get the category id=34
$category = $db->category[34];

//Load the posts of this category
$posts = $category->post;

//This is equivalent to:
$posts = $db->post
    ->select()
    ->relatedWith($category)
    ->get();

//But the result is cached so the database query is executed only the first time
$posts = $category->post;
```

This allows make things like this:

```
$titles = $db->post[34]->tag->post->title;

//Get the post id=34
//Get the tags of the post
//Then the posts related with these tags
//And finally, the titles of all these posts
```

Use magic methods to get a `Select` query returning related rows:

```
$category = $db->category[34];

//Magic property: Returns all posts of this category:
$posts = $category->post;

//Magic method: Returns the query instead the result
$posts = $category->post()
    ->where('pubdate > ', date('Y-m-d'))
    ->limit(10)
    ->get();
```

### Solving the n+1 problem

[](#solving-the-n1-problem)

The [n+1 problem](http://stackoverflow.com/questions/97197/what-is-the-n1-selects-issue) can be solved in the following way:

```
//Get some posts
$posts = $db->post
    ->select()
    ->get();

//preload all categories
$posts->category;

//now you can iterate with the posts
foreach ($posts as $post) {
    echo $post->category;
}
```

You can perform the select by yourself to include modifiers:

```
//Get some posts
$posts = $db->post
    ->select()
    ->get();

//Select the categories but ordered alphabetically descendent
$categories = $posts->category()
    ->orderBy('name DESC')
    ->get();

//Save the result in the cache and link the categories with each post
$posts->link($categories);

//now you can iterate with the posts
foreach ($posts as $post) {
    echo $post->category;
}
```

For many-to-many relations, you need to do one more step:

```
//Get some posts
$posts = $db->post
    ->select()
    ->get();

//Select the post_tag relations
$tagRelations = $posts->post_tag()->get();

//And now the tags of these relations
$tags = $tagRelations->tag()
    ->orderBy('name DESC')
    ->get();

//Link the tags with posts using the relations
$posts->link($tags, $tagRelations);

//now you can iterate with the posts
foreach ($posts as $post) {
    echo $post->tag;
}
```

### Relate and unrelate data

[](#relate-and-unrelate-data)

To save related rows in the database, you need to do this:

```
//Get a comment
$comment = $db->comment[5];

//Get a post
$post = $db->post[34];

//Relate
$post->relate($comment);

//Unrelate
$post->unrelate($comment);

//Unrelate all comments of the post
$post->unrelateAll($db->comment);
```

### Pagination

[](#pagination)

The `select` query has a special modifier to paginate the results:

```
$query = $db->post->select()
    ->page(1)
    ->perPage(50);

$posts = $query->get();

//To get the page info:
$pagination = $query->getPageInfo();

echo $pagination['totalRows']; //125
echo $pagination['totalPages']; //3
echo $pagination['currentPage']; //1
echo $pagination['previousPage']; //NULL
echo $pagination['nextPage']; //2
```

### Events

[](#events)

SimpleCrud uses [PSR-14 Event Dispatcher](https://www.php-fig.org/psr/psr-14/) to dispatch events. The events are attached to tables allowing to validate data, modify queries, etc.

```
use SimpleCrud\Events\BeforeSaveRow;
use SimpleCrud\Events\CreateSelectQuery;

//Get the event dispatcher
$dispatcher = $db->post->getEventDispatcher();

//Assign the BeforeSaveRow event listener
$dispatcher->listen(BeforeSaveRow::class, function (BeforeSaveRow $event) {
    $row = $event->getRow();

    if (!$row->createdAt) {
        $row->createdAt = new Datetime();
    }
});

//Assign a CreateSelectQuery
$dispatcher->listen(CreateSelectQuery::class, function (CreateSelectQuery $event) {
    $query = $event->getQuery();

    //Add automatically a where clause in all selects
    $query->where('active = true');
});

//Create a new post
$post = $db->post->create(['title' => 'Hello world']);

//Save the post, so BeforeSaveRow event is triggered
$post->save();

$post->createdAt; //This field was filled and saved

//Select a post, so CreateSelectQuery is triggered and only active posts are selected
$posts = $db->post->select()->get();
```

You can provide your own event dispatcher:

```
$myDispatcher = new Psr14EventDispatcher();

$db->post->setEventDispatcher($myDispatcher);
```

The available Events are:

- `SimpleCrud\Events\BeforeSaveRow`: Executed before save a row using `$row->save()`.
- `SimpleCrud\Events\BeforeCreateRow`: Executed before create a new row with `$table->create()`.
- `SimpleCrud\Events\CreateDeleteQuery`: Executed on create a DELETE query with `$table->delete()`.
- `SimpleCrud\Events\CreateInsertQuery`: Executed on create a INSERT query with `$table->insert()`.
- `SimpleCrud\Events\CreateSelectQuery`: Executed on create a SELECT query with `$table->select()`.
- `SimpleCrud\Events\CreateUpdateQuery`: Executed on create a UPDATE query with `$table->update()`.

### Fields

[](#fields)

The purpose of the `SimpleCrud\Fields` classes is to convert the data from/to the database for its usage. For example, in Mysql the format used to store datetime values is "Y-m-d H:i:s", so the class `SimpleCrud\Fields\Datetime` converts any string or `Datetime` instance to this format, and when you select this value, you get a Datetime instance. The available fields are:

- Boolean: To manage boolean values
- Date: To manage date values. Converts the database values to a `Datetime`
- Datetime: To manage datetime values. Converts the database values to a `Datetime`
- Decimal: Converts values to float numbers or NULL
- Field: It's the default field and doesn't transform the value
- Integer: Converts values to integers or NULL
- Json: To store json structures.
- Serializable: To store arrays or any other serializable data structure.
- Set: Manages multiple values. For example: \['red', 'blue', 'green'\] will be stored as "red,blue,green" in database.
- Point: Manages geometry points [more info](https://dev.mysql.com/doc/refman/5.7/en/gis-class-point.html)
- Other advanced fields can be found here:

The Field classes are asigned automatically according with the field type in the database. There are also "special names" that have specific types asigned:

- Integer format will be asigned to any field named `id` or ending by `_id`.
- Datetime format will be asigned to any field named `pubdate` or ending by `At` (for example: `createdAt`, `updatedAt` etc).
- Boolean format will be asigned to any field named `active` or starting by `is` or `has` (for example: `isActived`, `hasContent`, etc)

Example:

```
$post = $db->post->create([
    'title' => 'My post',
    'text' => 'My post text',
    'createdAt' => new Datetime('now'),
    'isActive' => true
]);

$post->save();

//Use magic properties to get the Field instance
$titleField = $db->post->title;
```

### Configuration

[](#configuration)

You may want to store some database configuration, for example the default language or base path where the assets are stored. To do that, there are the `getConfig` and `setConfig` methods:

```
$db->setConfig('name', 'value');

echo $db->getConfig('name'); //value
```

### Localizable fields

[](#localizable-fields)

If you need to save values in multiple languages, just have to create a field for each language using the language as suffix. For example, to save the title in english (en) and galician (gl), just create the fields `title_en` and `title_gl`.

Then, you have to configure the current language using the `SimpleCrud::ATTR_LOCALE` attribute:

```
//Set the current language as "en"
$db->setConfig(SimpleCrud::CONFIG_LOCALE, 'en');

//Select a post
$post = $db->post[23];

//Get the title in the current language
echo $post->title; //Returns the value of title_en

//You can access to any languages using the full name:
echo $post->title_en;
echo $post->title_gl;

//And assign a diferent value to the current language
$post->title = 'New title in english';
```

Debugging
---------

[](#debugging)

`SimpleCrud` use internally [Atlas.PDO](http://atlasphp.io/cassini/pdo/) to manage the connection and perform the queries in the database. You can see the documentation for more details.

```
$db->getConnection()->logQueries(true);

//-- Run queries --//

$queries = $db->getConnection()->getQueries();
```

Customization
-------------

[](#customization)

You can use your own custom classes for tables, rows and row collections:

### Custom Tables

[](#custom-tables)

Use `setTableClasses` to assign custom classes to table:

```
$db = new SimpleCrud\Database($pdo);

$db->setTableClasses([
    'post' => CustomPost::class,
    'comment' => CustomComment::class,
]);

$db->post; //Returns an instance of CustomPost
```

### FieldFactory

[](#fieldfactory)

To create field instances, SimpleCrud use the `SimpleCrud\Field\FieldFactory` factory class that you can customize or even replace with your own factory:

```
use SimpleCrud\Fields\FieldFactory;
use SimpleCrud\Fields\Boolean;

$db = new SimpleCrud\Database($pdo);

//Create a factory for your custom field
$factory = new FieldFactory(
    Year::class,          //Your custom field class name
    ['integer'],          //All fields of type integer will use this class
    ['year', '/$year/'],  //All fields named "year" or matching this regex will use this class
    ['min' => 2000],      //Default config
);

$db->setFieldFactory($factory);

//Modify a existing field
$db->getFieldFactory(Boolean::class)->addNames('enabled');

//Use it:
$db->post->fields['year']; //returns an instance of Year
$db->post->fields['enabled']; //returns an instance of SimpleCrud\Fields\Boolean
```

Creating your Rows and RowCollections
-------------------------------------

[](#creating-your-rows-and-rowcollections)

To define the Rows and RowCollections classes used in a specific table, first create a custom table and use `ROW_CLASS` and `ROWCOLLECTION_CLASS` protected constants to set the class.

```
namespace MyModels;

use SimpleCrud\Table;

class Post extends Table
{
    protected const ROW_CLASS = PostRow::class;
    protected const ROWCOLLECTION_CLASS = PostRowCollection::class;

    protected function init()
    {
        //Insert code to be executed after the instantion
    }

    public function selectLatest()
    {
        return $this->select()
            ->orderBy('createdAt DESC')
            ->limit(10);
    }
}
```

Now configure the database to use this class for the table `post`:

```
$db = new SimpleCrud\Database($pdo);
$db->setTableClasses([
    'post' => MyModels\Post::class,
]);

$latests = $db->post->selectLatest()->get(); //Returns an instance of MyModels\PostRowCollection

foreach ($latests as $post) {
    //Instances of MyModels\PostRow
}
```

###  Health Score

43

—

FairBetter than 91% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity39

Limited adoption so far

Community26

Small or concentrated contributor base

Maturity74

Established project with proven stability

 Bus Factor1

Top contributor holds 98% 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 ~33 days

Recently: every ~93 days

Total

94

Last Release

1501d ago

Major Versions

v2.1.1 → v3.02015-03-14

v3.1 → v4.0.02015-07-28

v4.1.0 → v5.0.02015-09-08

v5.4.5 → v6.0.02016-02-26

6.x-dev → v7.0.02019-04-03

PHP version history (2 changes)v0.1.0PHP &gt;=5.4

v7.0.0PHP &gt;=7.2

### Community

Maintainers

![](https://www.gravatar.com/avatar/e99df56b617f4af8cf0556a51a0ca20c7420104920c57d7e9eab093f92bb744f?d=identicon)[oscarotero](/maintainers/oscarotero)

---

Top Contributors

[![oscarotero](https://avatars.githubusercontent.com/u/377873?v=4)](https://github.com/oscarotero "oscarotero (436 commits)")[![andrenam](https://avatars.githubusercontent.com/u/2058657?v=4)](https://github.com/andrenam "andrenam (4 commits)")[![filisko](https://avatars.githubusercontent.com/u/8798694?v=4)](https://github.com/filisko "filisko (4 commits)")[![szepeviktor](https://avatars.githubusercontent.com/u/952007?v=4)](https://github.com/szepeviktor "szepeviktor (1 commits)")

---

Tags

cruddatabasemysqlormsqlitemysqlsqlitecrud

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/simple-crud-simple-crud/health.svg)

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

###  Alternatives

[doctrine/dbal

Powerful PHP database abstraction layer (DBAL) with many features for database schema introspection and management.

9.7k578.4M5.6k](/packages/doctrine-dbal)[scienta/doctrine-json-functions

A set of extensions to Doctrine that add support for json query functions.

58723.9M36](/packages/scienta-doctrine-json-functions)[nette/database

💾 Nette Database: layer with a familiar PDO-like API but much more powerful. Building queries, advanced joins, drivers for MySQL, PostgreSQL, SQLite, MS SQL Server and Oracle.

5656.7M234](/packages/nette-database)[ezsql/ezsql

Advance database access library. Make interacting with a database ridiculously easy. An universal interchangeable CRUD system.

86946.7k](/packages/ezsql-ezsql)[jv2222/ezsql

Advance database access library. Make interacting with a database ridiculously easy. An universal interchangeable CRUD system.

87311.3k2](/packages/jv2222-ezsql)[atlas/query

Object-oriented query builders and performers for MySQL, Postgres, SQLite, and SQLServer.

41249.0k7](/packages/atlas-query)

PHPackages © 2026

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