pyrsmk / olive
使用相同的 API 处理多种数据库类型
Requires
- php: >=5.3.0
README
此库现已过时。其继任者是 Olive2,它是 PDO 的小型包装。
Olive 是一个数据库库,旨在使用一个简单的 API 处理多个数据库。它适用于不需要高度优化的小型到中型项目,因为 API 只支持常见的任务。
此项目是一个概念验证,应按原样使用。
即使您可以使用相同的项目和相同的数据结构在不同的数据库类型(如 MySQL 和 MongoDB)之间切换,我也不鼓励任何人这样做。每个数据库系统都有其优点和缺点,您应该明智地选择满足您需求的内容。此外,API 上的某些方法可能是贪婪的,例如 MongoDB 中的 join()
会为每个连接执行一个额外的请求以检索数据。
请注意,MongoDB 是无关系的,不应与关系数据结构一起使用。
安装
composer require pyrsmk/olive
功能
- CRUD 操作
- 支持 AND/OR 操作符的查询
- 选择字段
- 创建别名
- 连接
- 排序
- 限制和跳过结果
- 支持正则表达式
- 命名空间
- 简单的 ORM/ODM 支持
创建数据库连接
创建数据库连接在所有数据库适配器中工作方式相同,它需要一个数据库名称作为第一个参数,一个选项数组作为第二个参数。以下是每个数据库对象的可用选项
- Olive\4d
- Olive\Cubrid
- Olive\Firebird
- Olive\Freetds
- Olive\Ibm
- Olive\Informix
- Olive\Mariadb(只是
Olive\Mysql
的别名) - Olive\Mongodb
- Olive\Mssql(Microsoft SQL Server)
- Olive\Mysql
- Olive\Odbc
- Olive\Oracle
- Olive\Postgresql
- Olive\Sqlite
- Olive\Sqlsrv(MS SQL Server 和 SQL Azure)
- Olive\Sybase
MongoDB
// Create a connection to a database on localhost using default 27017 port $olive=new Olive\Mongodb('my_database', array( 'username' => 'root', 'password' => 'blahblahblah' )); // Create a connection to mongodb.example.com:67095 (you can easily add more hosts if you want) $olive=new Olive\Mongodb('my_database', array( 'username' => 'root', 'password' => 'blahblahblah', 'hosts' => array( 'mongodb.example.com' => 67095 ) ));
MySQL
// Create a connection on localhost ('host' option is optional) $olive=new Olive\Mysql('my_database', array( 'username' => 'root', 'password' => 'blahblahblah', 'host' => 'localhost' ));
SQLite
// Create a simple connection with SQLite3 $olive=new Olive\Sqlite('path/to/database.db'); // Create a connection with SQLite2 $olive=new Olive\Sqlite('path/to/database.db', array( 'sqlite2' => true ));
获取数据容器
数据容器是表或集合的抽象类,例如。数据容器是创建查询的入口点。我们可以通过简单的调用来检索它们
$olive = new Olive\MariaDB('my_database', $options); // Get the users table $container = $olive->users;
如果您的表有一个奇怪的名字,您仍然可以使用来获取它
$container = $olive['some_weird#table;name'];
我们强烈建议您在应用程序中使用命名空间,以防止数据库被随机表污染,并避免不同应用程序/网站之间的不兼容性问题。
$container = $olive->my_app_users;
在Olive中,您可以为了简化指定一个全局命名空间。
// Set the global namespace $olive->setNamespace('my_app_'); // Get the global namespace $olive->getNamespace();
如果您需要移除全局命名空间
$olive->setNamespace(null);
CRUD操作
insert()
、update()
、save()
和 remove()
用于基本的CRUD操作。这些方法可以接受一个额外的参数:一个用于驱动器的选项数组。请参阅相关的PHP文档页面(PDO或MongoDB章节)以获取更多信息。
// Insert data $new_id = $olive->people->insert(array( 'firstname' => 'John', 'lastname' => 'Doe', 'age' => 52 )); // Update data $olive->people ->search('_id', 'is', $new_id) ->update(array( 'firstname' => 'John', 'lastname' => 'Doe', 'age' => 52 )); // Save data $olive->people->save(array( '_id' => 123, 'firstname' => 'John', 'lastname' => 'Doe', 'age' => 52 )); // Remove data $olive->people ->search('_id', 'is', $new_id) ->remove();
查询
搜索与获取
如您所见,搜索使用简单的语法来处理条件运算符。这些运算符是:
is
:等于运算符is not
:不等于运算符less
:字段值小于指定的值greater
:字段值大于指定的值in
:验证字段值是否在指定的数组中not in
:验证字段值是否不在指定的数组中like
:使用LIKE SQL语法将字段与模式匹配not like
:使用LIKE SQL语法验证字段是否不匹配提供的模式match
:使用正则表达式将字符串与模式匹配not match
:使用正则表达式验证字段是否不匹配提供的模式
让我们看看我们是如何获取结果的。
$ids = array(14, 51, 20, 18); // Search for articles with an ID that is not in the $ids array $olive->articles ->search('_id', 'not in', $ids) ->fetch(); // Get all articles $olive->articles ->search() ->fetch();
fetch()
方法检索所有结果。但还有其他方法,如 fetchOne()
获取结果中的第一行,以及 fetchFirst()
获取第一行的第一个字段。让我们看看一个具体的例子。
// Get the title of the article with the 72 ID $title=$olive->articles ->search('_id', 'is', 72) ->select('title') ->fetchFirst();
还有直接在单个调用中搜索和检索的方法。
// Get articles written by '@pyrsmk' $olive->articles->find('author_id', 'is', '@pyrsmk'); // Get one article $olive->articles->findOne('_id', 'is', 10); // Get the first field of the requested article $olive->articles->findFirst('_id', 'is', 10);
但请注意,findFirst()
存在于API一致性。因为我们没有选择任何字段,所以所有字段都被返回,第一个字段通常是行的ID。
当然,您可以在单个请求中指定多个搜索。每个搜索都会使用 AND
运算符附加到请求中。
// Let's get admins less older than 50 yo $title=$olive->members ->search('group', 'is', 'admins') ->search('age', 'less', '50') ->fetch();
让我们进一步了解搜索是如何工作的。实际上,对 search()
的每次调用都会使用 AND
运算符连接。但有时我们需要在查询中添加一个 OR
子句。这可以通过调用 orSearch()
方法获得。
$olive->articles->search('author_id', 'is', '@pyrsmk') ->orSearch('author_id', 'is', '@dreamysource') ->orSearch('author_id', 'is', '@4lbl');
所有 orSearch()
子句都将附加到之前的搜索中。
关于这个主题的最后一句话:调用 search()
返回一个新的 Query
对象。可能发生的情况是,例如,您需要在一个循环中做,并在每个周期中添加一个搜索。在这种情况下,您需要在调用 search()
之前获取一个新的查询。
$query = $olive->my_table ->query(); foreach($data as $name => $value) { $query->search($name, 'is', $value); } $results = $query->fetch();
选择字段与设置别名
// Get title, text, author and date fields $article = $olive->articles ->search('_id', 'is', 72) ->select('title') ->select('text') ->select('author') ->select('date') ->fetchOne(); // The second parameter of select() is the alias $item = $olive->items ->search('iditem', 'is', 72) ->select('iditem', '_id') ->select('text', 'french') ->select('title', 'h1') ->fetchOne();
在SQL数据库中,您可能需要在查询中为多个表设置别名以避免冲突。
$results = $olive->categories ->search('root.idparent', 'is', $id) ->from('categories', 'root') ->from('categories', 'subcategories') ->join('root.idparent', 'subcategories.idcat') ->join('subcategories.idcat', 'items.idcat') ->select('subcategories.idcat', '_id') ->select('items.title') ->join('items.idimg', 'images.idimg') ->select('images.uriimg', 'image') ->fetch();
连接
// Get articles from two weeks ago, with the author name $olive->articles ->search('date', 'greater', time() - 1209600) ->join('articles.author_id', 'members.id') ->select('title') ->select('text') ->select('date') ->select('members.name', 'author') ->fetch();
排序
sort()
方法接受字段和排序方向作为参数。方向是 asc
或 desc
。
$olive->articles ->search() ->sort('date', 'desc') ->fetch();
限制
// Get the 10 newest articles $olive->articles ->search() ->sort('date', 'desc') ->limit(10) ->fetch();
跳过
在使用limit()
进行分页时,跳过结果非常有用。
// Get articles for the page 3 $olive->articles ->search() ->sort('date', 'desc') ->limit(10) ->skip(20) ->fetch();
计数
为了方便使用,您可以直接计数搜索应返回的结果数量。
$olive->articles ->search('date', 'greater', time() - 1209600) ->count();
创建模型
为了简化模型并获得一个优雅的对象化API,您可以扩展Olive\Model
。构造函数接受一个Olive
对象作为参数,并期望$singular
、$plural
、$data_container
和$primary_key
类属性被正确定义。假设我们有一个要映射的users
表,以下是我们的定义方式
class MyUsersModel extends Olive\Model{ // 'singular' and 'plural' properties are used in calls (see the method below) protected $singular='user'; protected $plural='users'; // Define the data container name (AKA table or collection name) protected $data_container='users'; // Define the primary key name protected $primary_key='_id'; }
这就是我们需要的简单模型。但您通常需要特定的查询来优化事物。您只需通过向您的类中添加一个新方法来实现,比如getThoseFuckingWeirdResults()
。
以下是您可以原生的调用方法的详尽列表(将singular
和plural
参数替换为您在类中定义的参数)
<singular>Exists($id)
: 验证提供的id是否存在(例如:userExists(123)
)<singular>Exists($search)
: 验证提供的搜索是否存在(例如:userExists(array('email'=>'account@email.com'))
)<singular>ExistsBy<SearchField>($value)
: 通过验证其中一个字段来验证元素是否存在(例如:userExistsByEmail('account@email.com')
)<plural>Exist($search)
: 验证多个元素是否存在(例如:usersExist(array('name'=>'Thomas'))
)<plural>ExistBy<SearchField>($value)
: 通过与字段值比较来验证多个元素是否存在(例如:usersExistByName('Thomas')
)count<Plural>()
: 计数结果(例如:countUsers()
)count<Plural>($search)
: 计数结果(例如:countUsers('name','is','Thomas')
)count<Plural>By<SearchField>($value)
: 通过搜索一个字段来计数结果(例如:countUsersByName('Thomas')
)insert<Singular>($data)
: 插入数据(例如:insertUser($data)
)insert<Plural>($data)
: 插入多行(例如:insertUsers($data)
)add<Singular>($data)
:insert<Singular>
的别名add<Plural>($data)
:insert<Plural>
的别名get<Singular>($id, $fields)
: 通过id获取一行(例如:getUser(72)
)get<Singular>($search, $fields)
: 通过特定的搜索获取一行(例如:getUser(array('email'=>'account@email.com'))
)get<Singular><Field>($id)
: 通过id获取一个字段(例如:getUserEmail(72)
)get<Singular><Field>($search)
: 通过特定的搜索获取一个字段(例如:getUserEmail(array('name' => 'Thomas'))
)get<Singular>By<SearchField>($value, $fields)
: 通过一个字段获取一行(例如:getUserByName('Thomas')
)get<Singular><Field>By<SearchField>($value)
: 通过在另一个字段上的搜索获取一个字段(例如:getUserEmailByName('Thomas')
)get<Plural>($search, $fields)
: 搜索多行(例如:getUsers(array('status' => 'admin'))
)get<Plural><Field>($search)
: 搜索多行但检索一个字段(例如:getUsersEmail(array('status' => 'admin'))
)get<Plural>By<SearchField>($value, $fields)
: 通过一个特定的字段搜索多行(例如:getUsersByStatus('admin')
)get<Plural><Field>By<SearchField>($value)
: 通过一个特定的字段搜索多行,并为每一行返回一个字段(例如:getUsersEmailByStatus('admin')
)update<Singular>($id, $data)
: 更新特定ID的元素(例如:updateUser(72, $data)
)update<Singular>($search, $data)
: 通过搜索更新元素(例如:updateUser(array('_id' => 72), $data)
)update<Singular><Field>($id, $value)
: 更新元素的一个特定字段(例如:updateUserName(72,'Pierre')
)update<Singular><Field>($search, $value)
: 更新元素的特定字段(例如:updateUserName(array('_id' => 72), 'Pierre')
)update<Singular>By<SearchField>($value, $data)
: 通过搜索字段更新元素(例如:updateUserById(72, $data)
)update<Singular><Field>By<SearchField>($search_value, $field_value)
: 通过搜索另一个字段更新元素的字段(例如:updateUserNameById(72, 'Pierre')
)update<Plural>($search, $data)
: 更新多个元素(例如:updateUsers(array('name' => 'Pierre'), $data)
)update<Plural><Field>($search, $value)
: 更新多个元素的字段(例如:updateUsersName(array('name' => 'Pierre'), 'Jacques')
)update<Plural>By<SearchField>($value, $data)
: 通过搜索字段更新多个元素(例如:updateUsersByName('Pierre', $data)
)update<Plural><Field>By<SearchField>($search_value, $field_value)
: 通过搜索另一个字段更新多个元素的字段(例如:updateUsersNameByName('Pierre', 'Jacques')
)save<Singular>($data)
: 保存数据(例如:saveUser($data)
)set<Singular>($data)
:save<Singular>
的别名remove<Singular>($id)
: 删除元素(例如:removeUser(72)
)remove<Singular>($search)
: 通过搜索删除元素(例如:removeUser(array('_id' => 72))
)remove<Singular>By<SearchField>($value)
: 通过搜索字段删除元素(例如:removeUserByEmail('example@mail.com')
)remove<Plural>($search)
: 删除多个元素(例如:removeUsers(array('name' => 'Pierre'))
)remove<Plural>By<SearchField>($value)
: 通过搜索字段删除多个元素(例如:removeUsersByName('Pierre')
)delete<Singular>($id)
:remove<Singular>
的别名delete<Singular>($search)
:remove<Singular>
的别名delete<Singular>By<SearchField>($value)
:remove<Singular>By<SearchField>
的别名delete<Plural>($search)
:remove<Plural>
的别名delete<Plural>By<SearchField>($value)
:remove<Plural>By<SearchField>
的别名
我们应该查看API中定义的所有这些变量。其中大部分不言自明,但不是 $search
或 $fields
。$search
参数是一个关联数组,列出了用于查询的匹配值的字段
// Remove any user with their email and name fields set to 'pwet@example.com' and 'Thomas' respectively $userModel->removeUsers(array( 'email' => 'pwet@example.com', 'name' => 'Thomas' ));
$fields
参数也是一个关联数组,列出了要检索的字段并映射别名
// Get an user with the following fields : '_id', 'email', 'date' (alias of 'user_creation') and 'text' (alias of 'profile_text') $userModel->getUser($id,array( '_id', 'email', 'user_creation' => 'date' 'profile_text' => 'text' ));
高级使用
// Get table/collection names $names = $olive->getDataContainerNames(); // Get database object (like PDO, MongoClient, ...) $driver = $olive->getDriver(); // Verify adapter support if(Olive\Mysql::isSupported()) { // MySQL is currently supported in the PHP environment }
最后注意事项
- 您可能希望将
_id
作为默认主键用于您的表,因为它是在 MongoDB 中使用的键,因此您可以在使用不同数据库的应用程序中跨应用程序使用它 - 在 MongoDB 中,所有
_id
主键都是ObjectId
的实例,在 Olive 中,我们自动将 id 字符串化并在需要时创建对象:简而言之,您根本不需要担心ObjectId
许可证
Olive 在 MIT 许可证 下发布。