在之前的博客中,我们定义了ORM的接口,以及决定了使用PDO去实现。最后我们提到会有一个Model类实现ModelInterface接口。

现在我们来实现这个接口,如下:

<?php
namespace sf\db; use PDO; /**
* Model is the base class for data models.
* @author Harry Sun <sunguangjun@126.com>
*/
class Model implements ModelInterface
{
/**
* Declares the name of the database table associated with this Model class.
* @return string the table name
*/
public static function tableName()
{
return get_called_class();
} /**
* Returns the primary key **name(s)** for this Model class.
* @return string[] the primary key name(s) for this Model class.
*/
public static function primaryKey()
{
return ['id'];
} /**
* Returns a single model instance by a primary key or an array of column values.
*
* ```php
* // find the first customer whose age is 30 and whose status is 1
* $customer = Customer::findOne(['age' => 30, 'status' => 1]);
* ```
*
* @param mixed $condition a set of column values
* @return static|null Model instance matching the condition, or null if nothing matches.
*/
public static function findOne($condition)
{ } /**
* Returns a list of models that match the specified primary key value(s) or a set of column values.
*
* ```php
* // find customers whose age is 30 and whose status is 1
* $customers = Customer::findAll(['age' => 30, 'status' => 1]);
* ```
*
* @param mixed $condition a set of column values
* @return array an array of Model instance, or an empty array if nothing matches.
*/
public static function findAll($condition)
{ } /**
* Updates models using the provided attribute values and conditions.
* For example, to change the status to be 2 for all customers whose status is 1:
*
* ~~~
* Customer::updateAll(['status' => 1], ['status' => '2']);
* ~~~
*
* @param array $attributes attribute values (name-value pairs) to be saved for the model.
* @param array $condition the condition that matches the models that should get updated.
* An empty condition will match all models.
* @return integer the number of rows updated
*/
public static function updateAll($condition, $attributes)
{ } /**
* Deletes models using the provided conditions.
* WARNING: If you do not specify any condition, this method will delete ALL rows in the table.
*
* For example, to delete all customers whose status is 3:
*
* ~~~
* Customer::deleteAll([status = 3]);
* ~~~
*
* @param array $condition the condition that matches the models that should get deleted.
* An empty condition will match all models.
* @return integer the number of rows deleted
*/
public static function deleteAll($condition)
{ } /**
* Inserts the model into the database using the attribute values of this record.
*
* Usage example:
*
* ```php
* $customer = new Customer;
* $customer->name = $name;
* $customer->email = $email;
* $customer->insert();
* ```
*
* @return boolean whether the model is inserted successfully.
*/
public function insert()
{ } /**
* Saves the changes to this model into the database.
*
* Usage example:
*
* ```php
* $customer = Customer::findOne(['id' => $id]);
* $customer->name = $name;
* $customer->email = $email;
* $customer->update();
* ```
*
* @return integer|boolean the number of rows affected.
* Note that it is possible that the number of rows affected is 0, even though the
* update execution is successful.
*/
public function update()
{ } /**
* Deletes the model from the database.
*
* @return integer|boolean the number of rows deleted.
* Note that it is possible that the number of rows deleted is 0, even though the deletion execution is successful.
*/
public function delete()
{ }
}

当然现在里面还没有写任何的实现,只是继承了ModelInterface接口。

现在我们先来实现一下findOne方法,在开始实现之前我们要想,我们所有的model都要基于PDO,所以我们应该在model中有一个PDO的实例。所以我们需要在Model类中添加如下变量和方法。

    /**
* @var $pdo PDO instance
*/
public static $pdo; /**
* Get pdo instance
* @return PDO
*/
public static function getDb()
{
if (empty(static::$pdo)) {
$host = 'localhost';
$database = 'sf';
$username = 'jun';
$password = 'jun';
static::$pdo = new PDO("mysql:host=$host;dbname=$database", $username, $password);
static::$pdo->exec("set names 'utf8'");
} return static::$pdo;
}

用static变量可以保证所有继承该Model的类用的都是同一个PDO实例,getDb方法实现了单例模式(其中的配置暂时hard在这里,在之后的博客里会抽出来),保证了一个请求中,使用getDb只会取到一个PDO实例。

下面我们来实现findOne方法,我们现在定义的findOne有很多局限,例如不支持or,不支持select部分字段,不支持表关联等等。我们之后会慢慢完善这一些内容。其实findOne的实现就是拼接sql语句去执行,直接来看下代码:

    public static function findOne($condition)
{
//  拼接默认的前半段sql语句
$sql = 'select * from ' . static::tableName() . ' where ';
// 取出condition中value作为参数
$params = array_values($condition);
$keys = [];
foreach ($condition as $key => $value) {
array_push($keys, "$key = ?");
}
// 拼接sql完成
$sql .= implode(' and ', $keys);
$stmt = static::getDb()->prepare($sql);
$rs = $stmt->execute($params); if ($rs) {
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (!empty($row)) {
// 创建相应model的实例
$model = new static();
foreach ($row as $rowKey => $rowValue) {
// 给model的属性赋值
$model->$rowKey = $rowValue;
}
return $model;
}
}
// 默认返回null
return null;
}

我们需要来验证一下我们的代码是正确的,先在MySQL中设置一下用户及权限,mock一下数据。

相应的SQL语句如下:

/*创建新用户*/
CREATE USER jun@localhost IDENTIFIED BY 'jun'; /*用户授权 授权jun用户拥有sf数据库的所有权限*/
GRANT ALL PRIVILEGES ON sf.* TO jun@'%' IDENTIFIED BY 'jun'; /*刷新授权*/
FLUSH PRIVILEGES; /*创建数据库*/
CREATE DATABASE IF NOT EXISTS `sf`; /*选择数据库*/
USE `sf`; /*创建表*/
CREATE TABLE IF NOT EXISTS `user` (
id INT(20) NOT NULL AUTO_INCREMENT,
name VARCHAR(50),
age INT(11),
PRIMARY KEY(id)
); /*插入测试数据*/
INSERT INTO `user` (name, age) VALUES('harry', 20), ('tony', 23), ('tom', 24);

然后再在models文件夹中创建一个User.php,代码如下:

<?php
namespace app\models; use sf\db\Model; /**
* User model
* @property integer $id
* @property string $name
* @property integer $age
*/
class User extends Model
{
public static function tableName()
{
return 'user';
}
}

最后只剩在Controller中使用findOne验证一下了。修改SiteController.php的actionTest方法如下:

    public function actionTest()
{
$user = User::findOne(['age' => 20, 'name' => 'harry']);
$data = [
'first' => 'awesome-php-zh_CN',
'second' => 'simple-framework',
'user' => $user
];
echo $this->toJson($data);
}

打出的如下结果:

{"first":"awesome-php-zh_CN","second":"simple-framework","user":{"id":"1","name":"harry","age":"20"}}

如果将findOne中的条件改成['age' => 20, 'name' => 'tom'],返回值就变为如下结果:

{"first":"awesome-php-zh_CN","second":"simple-framework","user":null}

好了,今天就先到这里。项目内容和博客内容也都会放到Github上,欢迎大家提建议。

code:https://github.com/CraryPrimitiveMan/simple-framework/tree/0.5

blog project:https://github.com/CraryPrimitiveMan/create-your-own-php-framework

构建自己的PHP框架--实现Model类(1)的更多相关文章

  1. 构建自己的PHP框架--实现Model类(3)

    在之前的博客中,我们实现并完善了Model类的findOne方法,下面我们来实现其中的其他方法. 先来看findAll方法,这个方法和findOne很相似. public static functio ...

  2. 构建自己的PHP框架--实现Model类(2)

    在上一篇博客中我们简单实现了findOne方法,但我们可以看到,还是有一些问题的,下面我们来修正一下这些问题. 首先是返回的数据中,数字被转换成了字符串.我们需要的是数字啊... PDO中有属性可以支 ...

  3. tp框架之Model类与命名空间

    1.获取系统常量信息 public function shuchu() { var_dump(get_defined_constants()); } 2.跨控制器或跨模块调用 function dia ...

  4. 为测试框架model类自动生成xml结果集

    问题:有大量类似于theProductId这样名字的字符串需要转换成the_product_id这种数据库column名的形式. 思路:见到(见)大写字母(缝)就插入(插)一个“_”字符(针)进去,最 ...

  5. J2EE进阶(七)利用SSH框架根据数据表建立model类

    J2EE进阶(七)利用SSH框架根据数据表建立model类 前言 在利用SSH框架进行项目开发时,若将数据库已经建好,并且数据表之间的依赖关系已经确定,可以利用Hibernate的反转功能进行mode ...

  6. laravel5.1框架model类查询实现

    laravel框架model类查询实现: User::where(['uid'=8])->get(); User类继承自Model类:Illuminate\Database\Eloquent\M ...

  7. yii框架之gii创建数据表相应的model类

    一.首先是在数据库中建立project须要的表: 二.然后,配置相应文件: 在project文件夹下yiiProject\protected\config\main.php.在50行定义了db应用组件 ...

  8. 如何构建Android MVVM 应用框架

    概述 说到Android MVVM,相信大家都会想到Google 2015年推出的DataBinding框架.然而两者的概念是不一样的,不能混为一谈.MVVM是一种架构模式,而DataBinding是 ...

  9. 构建自己的PHP框架--创建组件的机制

    在之前的博客中,我们完成了基本的Model类,但是大家应该还记得,我们创建数据库的pdo实例时,是hard好的配置,并且直接hard在Model类中. 代码如下: public static func ...

随机推荐

  1. css中单位px、pt、em和rem的区别

    国内的设计师大都喜欢用px,而国外的网站大都喜欢用em和rem,那么三者有什么区别,又各自有什么优劣呢? px :像素(Pixel).相对长度单位.像素px是相对于显示器屏幕分辨率而言的.(引自CSS ...

  2. 用SQL语句将数据表中的数据保存为JSON格式

    没有找到好的工具,只想到了拼字符串的方式,用   NVARCHAR(MAX)  可能有截断,不推荐使用,方法中使用了 FOR XML PATH('') 实现,有关其使用方法参考这里 表结构: SQL ...

  3. Python之路Day18-正则及Django

    一. 正则表达式 1.定义正则表达式 /.../  用于定义正则表达式 /.../g 表示全局匹配 /.../i 表示不区分大小写 /.../m 表示多行匹配JS正则匹配时本身就是支持多行,此处多行匹 ...

  4. 分析错误:socket accept failed too many open files

    步骤:1.--查看当前各个进程打开的文件句柄数,其结果的第一列表示句柄数,第二列表示进程号lsof -n|awk '{print $2}'|sort|uniq -c |sort -nr|more 2. ...

  5. 我的emacs配置

    我的emacs配置文件 ;; .emacs ;; ============================== Basic Configure START ====================== ...

  6. Android中Intent的用法总结

    Intent只在Android中特有,我把它比作一种运载工具,就像飞机一样,会把一些人带到某个地方,而且如果需要的话,还可以找到机上有哪些人员(数据),这就需要另外一些设备来支持(如:Bundle), ...

  7. 解决 PowerDesigner 错误 The generation has been cancelled…

    在Model Settings中按照如下图设置即可.

  8. Android动画

    [浅谈Android动画] 总共四种:Tween Animation变换动画.Frame Animation帧动画 Layout Animation布局动画.Property Animation 属性 ...

  9. QParserGenerator的文法文件介绍

    在沉默了数月之后,博主心血来潮想继续介绍QParserGenerator,在这里我们将不再继续介绍任何有关于LALR(1)的算法(那东西只会把你的脑子变成一团浆糊),让我们来看一下QParserGen ...

  10. jieba.NET与Lucene.Net的集成

    首先声明:我对Lucene.Net并不熟悉,但搜索确实是分词的一个重要应用,所以这里还是尝试将两者集成起来,也许对你有一参考. 看到了两个中文分词与Lucene.Net的集成项目:Lucene.Net ...