pyrsmk/olive

此包已被废弃且不再维护。作者建议使用 pyrsmk/olive2 包。

使用相同的 API 处理多种数据库类型

0.26.7 2016-05-26 17:48 UTC

This package is auto-updated.

Last update: 2022-02-01 12:45:21 UTC


README

此库现已过时。其继任者是 Olive2,它是 PDO 的小型包装。

Olive 是一个数据库库,旨在使用一个简单的 API 处理多个数据库。它适用于不需要高度优化的小型到中型项目,因为 API 只支持常见的任务。

此项目是一个概念验证,应按原样使用。

即使您可以使用相同的项目和相同的数据结构在不同的数据库类型(如 MySQL 和 MongoDB)之间切换,我也不鼓励任何人这样做。每个数据库系统都有其优点和缺点,您应该明智地选择满足您需求的内容。此外,API 上的某些方法可能是贪婪的,例如 MongoDB 中的 join() 会为每个连接执行一个额外的请求以检索数据。

请注意,MongoDB 是无关系的,不应与关系数据结构一起使用

安装

composer require pyrsmk/olive

功能

  • CRUD 操作
  • 支持 AND/OR 操作符的查询
  • 选择字段
  • 创建别名
  • 连接
  • 排序
  • 限制和跳过结果
  • 支持正则表达式
  • 命名空间
  • 简单的 ORM/ODM 支持

创建数据库连接

创建数据库连接在所有数据库适配器中工作方式相同,它需要一个数据库名称作为第一个参数,一个选项数组作为第二个参数。以下是每个数据库对象的可用选项

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() 方法接受字段和排序方向作为参数。方向是 ascdesc

$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()

以下是您可以原生的调用方法的详尽列表(将singularplural参数替换为您在类中定义的参数)

  • <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 许可证 下发布。