inetz / simpleorm
This package is not auto-updated.
Last update: 2020-01-06 05:09:42 UTC
README
SimpleORM 类,用于简单对象关系映射 (适用于 PostgreSQL、MySQL 和 SQLite)
示例用法
class SimpleORM__DBHProvider extends SimpleORM {
protected function provide_dbh() { return get_global_dbh_from_wherever(); }
protected function provide_dbh_type() { return 'pg'; } // or 'mysql' or 'sqlite'
protected function include_prefix() { return ''; } // or '/path/to/lib';
}
class MyStudent extends SimpleORM__DBHProvider {
protected $table = 'student';
protected $primary_key = array( 'student_id' );
protected $column_sequences = array( 'student_id' => 'student_student_id_seq' );
protected $schema = array( 'student_id' => array(),
'name' => array( 'maxlength' => 25, 'required' => true ),
'email' => array( 'format' => 'email', 'maxlength' => 100, 'required' => true ),
'homepage_url' => array( 'maxlength' => 100, 'regex' => '/^http:\/\//' ),
'mentor_id' => array(),
'status' => array(),
'creation_date' => array(), # a database-side default will auto fill this in...
);
protected $relations = array(
'mentor' => array( 'relationship' => 'has_one',
'include' => 'model/Teacher.class.php', # A file to require_once(), (should be in include_path)
'class' => 'Teacher', # The class name
'columns' => 'mentor_id', # local cols to get the PKey for the new object (can be array if >1 col..)
),
'classes' => array( 'relationship' => 'has_many',
'include' => 'model/Class/Student.class.php', # A file to require_once(), (should be in include_path)
'class' => 'Class__Student', # The class name
'foreign_table' => 'class_student', # The table to SELECT FROM
'foreign_key_columns' => 'student_id', # The cols in the foreign table that correspond to Your PKey (can be array if >1 col..)
'foreign_table_pkey' => array('class_id','student_id'), # The primary key of that table (can be array if >1 col..)
'custom_where_clause' => "cancelled IS NOT NULL", # special condition (other than the FKey column join)
'order_by_clause' => 'course_name', # custom sorting (saves local sorting cost)
),
### Simple example if join table's columns match both other Pkeys
'teacher'=> array( 'relationship' => 'many_to_many',
'include' => 'model/Teacher.class.php', # A file to require_once(), (should be in include_path)
'class' => 'Teacher', # The class name
'foreign_table' => 'student', # The final table of the object we will be getting
'join_table' => 'student_teacher', # The in-between table that has both pKeys
'foreign_table_pkey' => 'teacher_id', # The pKey of the final table (note: can be THIS table's pKey)
'custom_where_clause' => "terminated = 0", # special condition (can use ft. (foreign table), and jt. (join_table), table aliases)
'order_by_clause' => 'name', # custom sorting (fields of both the join (jt.) and foreign table (ft.) are valid)
),
### Example if join table cols don't match their parent table's PKeys
'friends'=> array( 'relationship' => 'many_to_many',
'include' => 'model/MyStudent.class.php', # A file to require_once(), (should be in include_path)
'class' => 'MyStudent', # The class name (NOTE: can be THIS class)
'foreign_table' => 'student', # The final table of the object we will be getting
'join_table' => 'student_peer', # The in-between table that has both pKeys
'pkey_columns_in_join_table' => 'my_student_id' # (optional) if your PKey is named different in the join table
'foreign_table_pkey' => array('join_table_other_student_id' => 'student_id'), # The pKey of the final table (note: can be THIS table's pKey)
'join_table_fixed_values' => array('peer_type' => 'friend'), # OPTIONAL: Alwyas set (and assume to be set) these cols. Allows for multi-use of the same table
'order_by_clause' => 'name', # custom sorting (fields of both the join (jt.) and foreign table (ft.) are valid)
),
'enemies'=> array( 'relationship' => 'many_to_many',
'include' => 'model/MyStudent.class.php', # A file to require_once(), (should be in include_path)
'class' => 'MyStudent', # The class name (NOTE: can be THIS class)
'foreign_table' => 'student', # The final table of the object we will be getting
'join_table' => 'student_peer', # The in-between table that has both pKeys
'pkey_columns_in_join_table' => 'my_student_id' # (optional) if your PKey is named different in the join table
'foreign_table_pkey' => array('join_table_other_student_id' => 'student_id'), # The pKey of the final table (note: can be THIS table's pKey)
'change_status_instead_of_delete' => false, # OPTIONAL: Instead of delete, set "status" and "inactive_date" columns (requires you add these cols)
'join_table_fixed_values' => array('peer_type' => 'enemy'), # OPTIONAL: Alwyas set (and assume to be set) these cols. Allows for multi-use of the same table
'order_by_clause' => 'name', # custom sorting (fields of both the join (jt.) and foreign table (ft.) are valid)
),
);
public function expell($reason) {
foreach ($this->classes as $class_enrollment) {
### Set, locally saved in the object. Nothing sent to the database yet
### this is the same as calling ->set( array( 'cancelled' => true ) )
$class_enrollment->cancelled = true;
### Now, we save the change to the cancelled column to the database
### also, we could have used ->set_and_save( array( 'cancelled' => true ) ) to do this in one step...
$class_enrollment->save();
}
$this->set_and_save(array('status' => 'expelled'));
}
}
### Existing student
$newstud = new MyStudent(17);
echo $newstud->name;
echo $newstud->get('email');
echo $newstud->mentor->name;
### Inserts new row into database
$newstud = new MyStudent(); # object status = 'not_created'
$newstud->create(array('name' => 'Bob Dylan', 'email' => 'bob@dylan.com', mentor_id => 11)); # object status = 'active'
### After the insert, a SELECT is performed to get columns that were generated by DEFAULT values
echo $newstud->creation_date; # this value should have been filled by the database
### Update Many-to-Many relationship
### This method will INSERT and DELETE as necessary, leaving existing rows intact
$newstud->set_complete_relation('friends',array(18,79,20,728)); # add 4 friends
### Add a new Many-to-Many relation
$newstud->add_relation('enemies', 666); # add an enemy
### Accessing Has Many relations as object properties become arrays
### Accessing Has One relations as object properties become objects or null
echo $newstud->friends[0]->name; # Show my first friend's name (sorted by name)
### Make SURE that student ID=18 is NOT an enemy!
if ( $newstud->has_relation('enemies', 18) ) {
$newstud->remove_relation('enemies', 18);
}
### Expell and Delete
$newstud->expell('Was lazy.');
### Delete objects
$newstud->delete(); # object status = 'deleted'
该对象的实例表示数据库表中的一行。基本上,您创建自己的类来扩展此类,并给它一些关于表、列和其他表的关联(以及表示这些表的 SimpleORM 对象)的信息。Simple ORM 然后提供标准的 get、set、save、create 和 delete 方法来操作该行。
对象缓存
所有对象都存储在全局对象缓存中,这样同一对象行的多个实例就不会同时存在于内存中。如果允许,那么对一个对象的更改将不会反映在另一个对象上。对象缓存是通过简单的“对象转发”实现的:为数据库中给定行创建的第一个对象(例如,用户 Fred Jones)被创建,并是一个完全合格的对象,包含该对象的数据和关系缓存;第二次请求同一对象的新对象时,将查询缓存,并返回一个“转发”对象,该对象没有本地数据缓存(例如,第二个对象是“指向第一个 Fred Jones 对象的符号链接”)。当对“转发”对象调用任何方法时,该方法调用只是路由并重新调用实际完全合格的对象。
对象转发和自定义方法
因此,任何希望在扩展类中访问对象变量的方法,都应在方法代码的第一行包含此行:
if ( isset( $this->object_forward ) ) return $this->do_object_forward_method();
这只是为了快速检查对象是否是转发对象,而{@link do_object_forward_method()}方法会处理其余部分。
数据库兼容性
大多数主要功能已在 PostgreSQL、MySQL 和 SQLite 上进行了测试,并且只需对原始代码进行少量或无修改即可正常工作,这是因为数据库操作基于 PDO,PDO 执行了大部分数据库抽象。实际的 SQL 大多数情况下与大约 90% 的 SQL 解析器兼容,因此支持的数据引擎列表可能不止这三个。
数据库中至今为止的主要区别在于如何使用特定功能。例如,在 PostgreSQL 中,自动递增值使用命名序列完成,因此需要 '$column_sequences' 定义(关联数组,键 = 列名,值 = 序列名),但对于没有命名序列的引擎可以省略。这个功能本质上会覆盖 PDO 的行为。
对象的预缓存
通常 SimpleORM 在内存中已加载对象时,会很好地避免额外的查询。但是,在复杂的数据情况下,尤其是在你有一个主表和许多从表关系复杂的情况下,有一个预加载方法可以节省大量的额外循环和查询:pre_load_sub_relations()。传入的第一个参数是预加载的 has_many 关系名称。它返回该关系,就像内部 get_relation() 方法一样。然而,第二个参数是一个包含要搜索的 has_one 关系名称的数组。这是 SimpleORM 唯一一种知道如何或以某种方式导航其他 SimpleORM 对象的方法... 这些 has_one 关系可以属于 has_many 关系的目标,也可以属于它的匹配 has_one 的目标等。然而,它会避免将同一对象连接两次,因此在循环引用或创造性链接的情况下要小心...
实际上,这个方法就是形成一个大型的单一查询,试图将所有这些查询合并成一个大的查询,以便对所有需要的表执行 "SELECT *" 操作。它生成列别名以在输出中保持所有列的分离。注意:你 "has_many" 关系中的 "where_clause" 将用于这个新的巨型查询,也将有很多其他表被连接。如果其中一些连接的表与你的主表有相同的列名,那么你引用的某些列可能变得不明确。为了避免这种情况,最好在 "has_many" 关系的 "where_clause" 中使用完整的表名来前缀所有列名。
对象定义参考
这些是你在每个对象中定义的一些属性,以定义你的类代表哪个表、列等。
$table
此对象表示的表。必需。
$primary_key
此表的键。
列名的平面数组。必需。
$column_sequences
具有序列名称的列有哪些
一个关联数组,键是列名,值是对应的序列名。这仅适用于使用序列的数据库(如 PostgreSQL),并且当这些表具有使用它们的默认值的列时。如果没有提供这些值,对象在创建()(INSERT)调用后将无法获取任何自增值,如果这些列是主键的一部分,那么对象将无法使用。
对于 MySQL 和 SQLite 等其他数据库类型,在 create() 后能够自动获取自增值,而无需定义 $column_sequences。
$schema
定义列和简单的验证标准。必选。
这是一个关联数组,键是列名,每个值是关联数组,包含定义列名和验证设置的参数。验证标准由 do_validation() 函数定义。
可以使用验证函数 validate()、validate_column_value() 或 extract_and_validate() 来运行这些验证检查。
$relations
允许快速访问相关对象
关系是简单的方式,通过数据库中的外键直接链接到一个或多个相关对象。关系可以像访问列一样访问,但它们包含实际的 SimpleORM 对象。这使得可以轻松地深入到像这样的值
echo $student->classes[0]->class->instructor->name;
通过关系访问的外部对象需要在某个文件中设置它们自己的 SimpleORM 派生类。所有关系定义都包括 require_once() 的文件名和用于实例化对象的类名。
目前对象可以有 2 种关系类型:'has_one' 和 'has_many'。
'has_one' 类型用于您的表行具有数据库中另一个实体的 ID 或其他主键时。'has_one' 关系的定义只需命名具有外表 PKey 的列。在获取相关对象时,如果未找到,关系访问器返回 NULL。
'has_many' 类型用于数据库中存在具有您的对象主键的另一个表。这通常意味着您的对象“拥有”那个其他对象,但它始终意味着对于您的每个对象,都有 0 个或多个其他类型的对象。此类型的定义包括外键表名和用于实例化新对象必须选择的外键的主键。它还包括包含您的表主键的外键表中的列名。此关系的访问器将返回找到的所有对象的数组。
关系数据在第一次调用时被缓存,您必须手动调用 reset_state() 或 clear_relation_cache() 来获取新数据。
'has_many' 关系定义可以有一些自定义参数来调整执行的 SQL。'custom_where_clause' 可以向返回的对象列表添加特殊过滤器,例如仅获取 'active' 项目。'order_by_clause' 可以提供快速的数据库端排序。这直接放入 ORDER BY 子句中,因此您只能访问外键表中的列进行排序。这种服务器端排序比在 PHP 中手动排序对象要快得多。
方法参考
__construct( [$primary_key] )
每个对象代表数据库表中确切的一行。作为第一个参数传递的主键可以是单个值,也可以是多列复合主键的值。
不带主键调用构造函数将创建一个处于 'not_created' 状态的对象,这在调用 create() 之前作为一个初始阶段非常有用,create() 将插入行并使对象可用。在 'not_created' 状态下,大多数方法和属性访问将生成致命错误(E_USER_ERROR),包括:get()、set()、save()、get_relation()、delete()、pre_load_sub_relations() 以及列和关系的属性访问器。
第二个可选参数可以用来提前用数据填充对象。这使得对象可以跳过执行它自己的 select 查询以获取其行的数据,而是直接使用您提供的数据。该数组必须是一个关联数组,键名与对象模式定义中列的名称和大小写匹配,并且应包含行的所有列。
这主要在你刚刚查询出多行数据时很有用,然后你想为每行创建一些带有主键的对象。以下是一个例子,其中涉及的所有对象将只执行一次查询(假设你只是在读取)
require_once('models/Fruit.php');
$pdo = getDb();
// Need to select * so that ORM can avoid doing another select
$sql = 'SELECT * FROM fruit ORDER BY name';
$fruit_objs = array();
foreach ($pdo->query($sql) as $row) {
/// Passing the second parameter seeds the new object with data
$fruit_objs[] = new Fruit($row['fruit_id'], $row);
}
这也是对象缓存和检索的地方。一般来说,缓存模型使得所有使用主键实例化的对象实例都作为 Singleton 使用。缓存键是通过将对象的类、表和主键值连接成字符串来生成的。如果对象已被缓存,它将尽可能清除本地对象数据并设置 $object_forward。这将导致所有后续方法只是“重定向”到原始缓存对象。
如果传递的任何主键值都是 null,则不会执行对象缓存,因为这通常意味着对象行尚未创建。这也意味着对象的状态将是 'not_created'。一旦成功完成 create() 调用,状态将变为 'active',对象将被添加到缓存中。
exists()
这通过主键检查对象的状态,以查看它是否已安装并且实际上代表数据库中的一行。如果它尚未执行第一次数据检索 select,则此调用将触发该数据库查询。
reset_state()
快速重置所有缓存和状态
当您怀疑任何与SimpleORM无关的内容修改了对象后面的行,或者您的任何关系可能已被添加或删除时,请调用此函数。这将重置所有缓存数据和对象状态。
参见clear_relation_cache()
post_reset_state_handler()
由具有本地缓存的子类重写
dbh()
快速访问实际的PDO数据库句柄
get()
获取列值和/或关系
参数
- mixed $arg1 列或关系名称的数组,单个名称,或作为参数传递的多个名称(不是作为数组)例如 $my_user->get('name','email','birthday')
set()
获取列值和/或关系
参数
- array $to_set 要设置的列的关联数组
参见save()参见set_and_save()
unsaved_columns()
快速获取已通过set()设置但尚未使用save()保存的列的列表
set_and_save()
save()
获取所有本地设置的列,并发送数据库的UPDATE
pre_save_handler( $columns_to_save )
由子类重写,如触发器,如果返回false,则主操作退出(但不回滚!)
post_save_handler()
由子类重写,如触发器,如果返回false,则主操作退出(但不回滚!)
get_relation()
直接获取关系,也可以使用get()完成
has_relation()
仅针对多对多关系,检查两个对象是否相关
$relation - 关系名称 $pkey - 另一个对象的键值数组
add_relation()
仅针对多对多关系
$relation - 关系名称 $pkey - 另一个对象的键值数组
remove_relation()
仅针对多对多关系
$relation - 关系名称 $pkey - 另一个对象的键值数组
set_complete_relation()
仅针对多对多关系
$relation - 关系名称 $pkeys - 另外对象的键值数组
clear_relation_cache()
仅清除关系缓存,但不删除列数据
post_clear_relation_cache_handler()
由具有本地缓存的子类重写
create()
执行数据库插入
此方法使用您在关联数组中传递的列来构建INSERT查询。任何其他值都假定是可空的或具有数据库中的默认值。
由于默认值和数据库端触发器,它不会在本地数据缓存中保留您刚刚传递的数据。下次调用get()时,它将执行新的SELECT查询。
在插入后,为了获取新的主键,它可能使用$column_sequences来获取序列生成的值。
在成功插入后,还将对象添加到缓存中。
pre_create_handler()
由子类重写,如触发器,如果返回false,则主操作退出(但不回滚!)
post_create_handler()
由子类重写,如触发器,如果返回false,则主操作退出(但不回滚!)
delete()
执行数据库删除
同时从缓存中删除对象,并将状态设置为 '已删除'。在此之后,您无法对对象执行任何操作。
pre_delete_handler()
由子类重写,如触发器,如果返回false,则主操作退出(但不回滚!)
post_delete_handler()
由子类重写,如触发器,如果返回false,则主操作退出(但不回滚!)
pre_load_sub_relations()
类似于get_relation(),但预先在单个查询中加载子对象的(仅针对 "has_many" 关系)
这将返回与get_relation()相同的结果,但首先它会创建一个Monster查询来连接多个其他表,以便一次性预加载它们。注意:这将深入寻找多层嵌套的"has_one"关系。但它只会预加载"has_one"关系(使用OUTER JOIN)。
注意:在您的"has_many"关系中的"where_clause"将被用于这个新的Monster查询,该查询也将连接很多其他表。如果这些连接的表中有一些列与您的主表有相同的列名,那么您在"where_clause"中引用的一些列可能成为模糊引用。为了避免这种情况,最好在"has_many"关系的"where_clause"中用全表名作为别名前缀来前缀所有列名。
extract()
只从表单中提取给定的列
返回3个参数
- 一个只包含通过验证的值的关联数组。一些值可能会被清除,如请求时去除空格。使用此数组稍后调用set()。
- 布尔值,如果所有验证都通过则为true或false
- 一个关联数组,其中错误是值为一个数值数组,其风格类似于do_validation()
- validate($form_to_validate, $col_name_1, $col_name_2, $col_name_3, ...)
- validate($form_to_validate, $array_of_column_names)
- validate($form_to_validate, $array_of_column_names, $col_name_prefix)
$col_name_prefix在表单的关联键名有前缀时使用,并且错误关联数组也需要使用该前缀。这在两个或多个元素同时在同一个Web界面中同时编辑时很常见,否则不同的实体的字段可能会发生冲突和混合(例如,在一个表单中同时编辑一个班级和一个讲师,它们都有'name'字段,您可以将所有班级字段前缀为'class_',所有讲师字段前缀为'inst_'。注意,前缀不应包含在列名列表中。
validate()
接收一个关联数组,验证所有参数,并返回有效值、状态和错误
返回3个参数
- 一个只包含通过验证的值的关联数组。一些值可能会被清除,如请求时去除空格。使用此数组稍后调用set()。
- 布尔值,如果所有验证都通过则为true或false
- 一个关联数组,其中错误是值为一个数值数组,其风格类似于do_validation()
- validate($form_to_validate, $col_name_1, $col_name_2, $col_name_3, ...)
- validate($form_to_validate, $array_of_column_names)
- validate($form_to_validate, $array_of_column_names, $col_name_prefix)
$col_name_prefix在表单的关联键名有前缀时使用,并且错误关联数组也需要使用该前缀。这在两个或多个元素同时在同一个Web界面中同时编辑时很常见,否则不同的实体的字段可能会发生冲突和混合(例如,在一个表单中同时编辑一个班级和一个讲师,它们都有'name'字段,您可以将所有班级字段前缀为'class_',所有讲师字段前缀为'inst_'。注意,前缀不应包含在列名列表中。
extract_and_validate()
extract()和validate()的组合函数
参数
- string $col 需要验证的列名。它使用这个来读取模式定义并获取标准
- mixed $value 要测试的值
调用方式与extract()或validate()完全相同,包括前缀功能。
validate_column_value()
单值验证
参数
- string $col 需要验证的列名。它使用这个来读取模式定义并获取标准
- mixed $value 要测试的值
全局函数
这些函数在全局范围内可用,并且大多数都是由SimpleORM的各种方法内部调用的。
form_extract()
只从表单中提取给定的列
返回3个参数
- 一个只包含通过验证的值的关联数组。一些值可能会被清除,如请求时去除空格。使用此数组稍后调用set()。
- 布尔值,如果所有验证都通过则为true或false
- 一个关联数组,其中错误是值为一个数值数组,其风格类似于do_validation()
- validate($form_to_validate, $col_name_1, $col_name_2, $col_name_3, ...)
- validate($form_to_validate, $array_of_column_names)
- validate($form_to_validate, $array_of_column_names, $col_name_prefix)
$col_name_prefix在表单的关联键名有前缀时使用,并且错误关联数组也需要使用该前缀。这在两个或多个元素同时在同一个Web界面中同时编辑时很常见,否则不同的实体的字段可能会发生冲突和混合(例如,在一个表单中同时编辑一个班级和一个讲师,它们都有'name'字段,您可以将所有班级字段前缀为'class_',所有讲师字段前缀为'inst_'。注意,前缀不应包含在列名列表中。
validate()
接收一个关联数组,验证所有参数,并返回有效值、状态和错误
返回3个参数
- 一个只包含通过验证的值的关联数组。一些值可能会被清除,如请求时去除空格。使用此数组稍后调用set()。
- 布尔值,如果所有验证都通过则为true或false
- 一个关联数组,其中错误是值为一个数值数组,其风格类似于do_validation()
- validate($form_to_validate, $col_name_1, $col_name_2, $col_name_3, ...)
- validate($form_to_validate, $array_of_column_names)
- validate($form_to_validate, $array_of_column_names, $col_name_prefix)
$col_name_prefix在表单的关联键名有前缀时使用,并且错误关联数组也需要使用该前缀。这在两个或多个元素同时在同一个Web界面中同时编辑时很常见,否则不同的实体的字段可能会发生冲突和混合(例如,在一个表单中同时编辑一个班级和一个讲师,它们都有'name'字段,您可以将所有班级字段前缀为'class_',所有讲师字段前缀为'inst_'。注意,前缀不应包含在列名列表中。
extract_and_validate()
extract()和validate()的组合函数
参数
- string $col 需要验证的列名。它使用这个来读取模式定义并获取标准
- mixed $value 要测试的值
调用方式与extract()或validate()完全相同,包括前缀功能。
validate_column_value()
单值验证
参数
- string $col 需要验证的列名。它使用这个来读取模式定义并获取标准
- mixed $value 要测试的值
do_validation()
参数
- string $col 要验证的数据或数据库列的名称(如果未传入 $valhash,则用作默认名称)
- string $value 要测试的值
- string $valhash 包含验证参数的数组
以下是一些清洗操作
- 默认情况下,除非传递了 'no_strip_ws' 参数,否则会从所有值中删除前后空白。
- 如果 'format' 参数为 'decimal',则将删除任何前导 '+' 字符。
- 如果 'format' 参数为 'bool',则将所有 (! isset()) 的值转换为实际的布尔值 false。
- 如果 'format' 参数为 'credit_card_number',则将删除所有空格和连字符 ('-')。
- 'not_empty_string' 参数将空字符串值转换为实际的 NULL 值。
- 首先检查 'required' 参数,如果存在但值不是 isset(),则添加错误并返回。如果没有值,并且 'required' 参数不存在,则返回成功并不进行进一步检查。
- 'maxlength' 和 'minlength' 对字符串长度进行检查
- 'regex' 和 'negative_regex' 参数检查值是否通过给定的正则表达式(或未通过)
- 'format' 参数测试各种类型,包括:'email'、'bool'、'decimal'、'integer'、'date'(UTC)、'datetime'(UTC)、'credit_card'(使用 LUHN10)、'ip'(v4)
- 'gt'、'ge'、'lt'、'le' 测试数值是否大于、小于等...
- 用英语表述的错误信息(在完成语言抽象并使用作为默认值之前使用)
- 更通用的错误代码(例如,'invalid_regex'),可用于语言抽象。
- 布尔值,true 或 false,表示验证是否通过
- (可能)清洗后的值
- 如果失败,则返回上述格式的数组,如果传入,则为空数组。
list($ok, $item_count, $error) = do_validation('item_count',$_REQUEST['item_count'], array( 'name' => "number of items", 'format' => 'integer', 'required' => true ));
if ( ! $ok ) $request->page->add_error('item_count', $error);
返回值:mixed
dbh_query_bind()
运行只读 SQL 查询并绑定参数
参数
- string $sql 要运行的 SQL 查询
- 混合 $params 参数可以传递一个绑定的参数数组,或者直接在 SQL 参数后传递绑定参数。
dbh_do_bind()
执行带有绑定参数的(可能为写访问)SQL 查询
参数
- string $sql 要运行的 SQL 查询
- 混合 $params 参数可以传递一个绑定的参数数组,或者直接在 SQL 参数后传递绑定参数。
扩展功能
get_where()
这是一个静态方法,允许使用绑定参数快速执行 SQL Select,并返回您选择的 ORM 对象的一个或多个。
语法示例
### Get a single object (e.g. by a unique key), this will return an object or NULL
MyStudent::get_where(array('email' => $_REQUEST['email'], "status != 'deleted'"), true);
### Get a list of students (limit to 50, sort by name), this will return an array with 0 or more objects
MyStudent::get_where(array('lname' => $_REQUEST['search_last_name'], "status != 'deleted'"), 50, 'fname ASC, lname ASC');
每个对象配置的需求
get_where() 静态方法需要在每个将使用它的类中定义。这是一个简短的存根,它仅调用父类的 get_where() 方法。
public static function get_where($where = null, $limit_or_only_one = false, $order_by = null) { return parent::get_where($where, $limit_or_only_one, $order_by); }
这是因为 PHP 不保留静态方法调用中的类。仅在您的基类中添加 get_where() 不会起作用。基类没有方法可以确定您调用的实际对象类。例如,MyStudent::get_where() 将仅显示为 SimpleORM__DBHProvider::get_where(),没有方法可以确定它。
包含此库
class SimpleORM__DBHProvider extends SimpleORM {
... Regular class definition ...
/// Simple static function to get object(s) with simple WHERE
public static function get_where($where = null, $limit_or_only_one = false, $order_by = null) {
/// Because we are STATIC, and most everything we need is NON-STATIC
/// we first need a backtrace lead to tell us WHICH object is even
/// our parent, and then we can create an empty parent Non-Static
/// object to get the few params we need...
$bt = debug_backtrace();
if ( $bt[1]['function'] != 'get_where' ) {
trigger_error("Use of get_where() when not set up! The hack for whetever object you are calling is not set up!<br/>\n
You need to add a get_where() stub to your object (the one you are referring to in ". $bt[0]['file'] ." on line ". $bt[0]['line'] ."), that looks like:<br/>\n".'
public static function get_where($where = null, $limit_or_only_one = false, $order_by = null) { return parent::get_where($where, $limit_or_only_one, $order_by);'."<br/>\n".'
' , E_USER_ERROR);
}
$parent_class = $bt[1]['class'];
/// Otherwise, just get the parent object and continue
$tmp_obj = new $parent_class ();
/// Assemble a generic SQL based on the table of this object
$values = array();
if( $where ) {
$where_ary = array(); foreach ($where as $col => $val) {
/// If the where condition is just a string (not an assocative COL = VALUE), then just add it..
if ( is_int($col) ) { $where_ary[] = $val; }
/// Otherwise, basic ( assocative COL = VALUE )
else { $where_ary[] = "$col = ?"; $values[] = $val; }
}
}
$sql = "SELECT *
FROM ". $tmp_obj->get_table() ."
WHERE ". ( $where_ary ? join(' AND ', $where_ary) : '1' ) ."
". ( ! is_null($order_by) ? ( "ORDER BY ". $order_by ) : '' ) ."
". ( ( $limit_or_only_one !== true && $limit_or_only_one ) ? ( "LIMIT " . $limit_or_only_one ) : '' ) ."
";
$sth = $tmp_obj->dbh_query_bind($sql, $values);
$data = $sth->fetchAll();
/// Get the objs
$objs = array();
foreach ( $data as $row ) {
$pk_values = array(); foreach( $tmp_obj->get_primary_key() as $pkey_col ) $pk_values[] = $row[ $pkey_col ];
$objs[] = new $parent_class ( $pk_values, $row );
}
/// If they only ask asking for one object, just guve them that, not the array
return ( ($limit_or_only_one === true || $limit_or_only_one === 1) ? ( empty( $objs ) ? null : $objs[0] ) : $objs );
}
}
class MyStudent extends SimpleORM__DBHProvider {
... Regular class definition ...
public static function get_where($where = null, $limit_or_only_one = false, $order_by = null) { return parent::get_where($where, $limit_or_only_one, $order_by); }
}