既然是讲数据库的,那首先自然是连接数据库了。
这里举两个例子,mysql数据库:
$db=new DB\SQL( 'mysql:host=localhost;port=3306;dbname=mysqldb', 'admin', 'p455w0rD');
sqlite数据库:
$db=new DB\SQL('sqlite:/absolute/path/to/your/database.sqlite'));
数据库查询:
很简单的,fatfree的数据库是不用配置的,很多东西都是内置的,当你需要改再去改就行,我们首先学习查询,就一步:
$f3->set('result',$db->exec('SELECT brandName FROM wherever'));
这样就行了,select 和 from是数据库语言,我们这就把数据库中符合条件的变量放到了result数组里面了。
然后我们用之前学的repeat调用试试看:
<repeat group="{{ @result }}" value="{{ @item }}"> <span>{{ @item.brandName }}</span></repeat>
最后再echo一下就可以了。注意一下,这里其实是个二维数组,结构大概是result -> brandName -> value。可以用之前学过的嵌入repeat,但是这里既然brandName只有一个,那么就用 . 符号就可以解决问题了。
深造:
如果要执行一组命令怎么办?两种方法:
$db->exec( array( 'DELETE FROM diet WHERE food="cola"', 'INSERT INTO diet (food) VALUES ("carrot")', 'SELECT * FROM diet' ));
还有
$db->begin();$db->exec('DELETE FROM diet WHERE food="cola"');$db->exec('INSERT INTO diet (food) VALUES ("carrot")');$db->exec('SELECT * FROM diet');$db->commit();
当然,如果指令出错的话,调用就会回卷,我们可以通过调用:
echo $db->log();
来查看指令的调用状况。
数据库安全:
为了方式用户恶意修改数据库,我们应该用下面这个方法代替上面的代码:
$db->exec( array( 'DELETE FROM diet WHERE food=:name', 'INSERT INTO diet (food) VALUES (?)', 'SELECT * FROM diet' ), array( array(':name'=>'cola'), array(1=>'carrot'), NULL ));
数据库的增删查改:
首先数据库文件的大概形式是这样的:
CREATE TABLE users ( userID VARCHAR(30), password VARCHAR(30), visits INT, PRIMARY KEY(userID) );
好,首先我们一步一步来,连接数据库
$db=new DB\SQL( 'mysql:host=localhost;port=3306;dbname=mysqldb', 'admin', 'wh4t3v3r');
然后
$user=new DB\SQL\Mapper($db,'users');$user->load(array('userID=?','tarzan'));
上面的这个$user是我们建立的一个‘users’属性的结构对象(也就是上面那个数据库文件的形式),里面是空的。
第二行就是把userID = tarzan 的给读出来的,附属的信息都一起读出来了。
数据库的插入修改:
继续上面的那个例子,如果我要修改这个类里面的访问量visits怎么办呢?
$user->visits++;$user->save();
这样就可以了。
如果要增加一个新的记录呢:
$user=new DB\SQL\Mapper($db,'users');// or $user=new DB\Mongo\Mapper($db,'users');// or $user=new DB\Jig\Mapper($db,'users');$user->userID='jane';$user->password=md5('secret');$user->visits=0;$user->save();
有没有发现我们一直都在用save函数,因为如果不save的话这里修改的值就会在脚本运行结束的时候恢复回去的。
fatfree框架是很自动的,如果这里增加的userID是已经存在的用户,这里的增加就会自动变成修改操作,毕竟我们文件里最重要的那句
PRIMARY KEY(userID)
不是白白加上去的,这句话既是监控key值的作用(知道到底是插入还是修改操作)又是映射变量的作用。
当然,还有一点要注意的,如果你要连续修改(插入)需要reset一下:
$user->reset();$user->userID='cheetah';$user->password=md5('unknown');$user->save();
原理说一下,因为如果是进行了操作的话,映射类里面就会包含这个最新的操作,我们这个save就是把映射类里面的操作commit到数据库里面,但是如果你要继续下一个操作就要重新clear一下映射类,所以我们需要reset一下。养成良好习惯就是每当操作前reset一下,操作后save一下。
删除用户:
$user=new DB\SQL\Mapper($db,'users');$user->load(array('userID=? AND password=?','cheetah','ch1mp'));$user->erase();
很简单,首先load该用户,然后erase就行了。用?是为了保护数据库不被看到。
POST变量与数据库传递:
POST跟GET一样是全局变量,都是被set过的,可以近似地看作一个tmp,不过POST是不对用户公开的,所以常用语数据库。
从POST读取到新变量user里面:
$f3->set('user',new DB\SQL\Mapper($db,'users'));$f3->get('user')->copyFrom('POST');$f3->get('user')->save();
把user里面的东西给POST:
$f3->set('user',new DB\SQL\Mapper($db,'users'));$f3->get('user')->load(array('userID=?','jane'));$f3->get('user')->copyTo('POST');
这里就是读取了jane的相关信息给了post。别忘了在php里面调用post是$_post,在fatfree里面就只要@POST就行。
继续,这里读取了jane的相关信息后,可以通过@POST.userID来调用信息。
例子:
<input type="text" name="userID" value="{{ @POST.userID }}">
这里的input类型是text文本输入,输入的信息存储到userID变量里,而后面那个value则是会在空格里显示的默认数据,就像我们登陆qq那样记录上一次登陆的用户名,用的就是上面这种方法。
条件筛选:
$user=new DB\SQL\Mapper($db,'users');$user->load('visits>3');// Rewritten as a parameterized query$user->load(array('visits>?',3));// For MongoDB users:// $user=new DB\Mongo\Mapper($db,'users');// $user->load(array('visits'=>array('$gt'=>3)));// If you prefer Jig:// $user=new DB\Jig\Mapper($db,'users');// $user->load('@visits>?',3);// Display the userID of the first record that matches the criteriaecho $user->userID;// Go to the next record that matches the same criteria$user->skip(); // Same as $user->skip(1);// Back to the first record$user->skip(-1);// Move three records forward$user->skip(3);
例子就说明得很清楚了,首先筛选出visits > 3 的user 然后再skip,其实skip就相当于是next,你要跳几步就skip几个,如果要回跳就skip(-1)。
可以用dry()函数来检验是否越界,如果在第一个skip(-1)或者在最后一个next,那么dry()都会返回一个true来表示已经越界了。
当然load函数还有一个特别的读取形式,类似于重载过一样:
$user->load( array('visits>?',3), array( 'order'=>'userID DESC' 'offset'=>5, 'limit'=>3 ));
这句话的意思就相当于数据库语言里的:
SELECT * FROM users WHERE visits>3 ORDER BY userID DESC LIMIT 3 OFFSET 5;
还有个更简单的写法:
$page=$user->paginate(2,5,array('visits>?',3));
那个2跟5就是从第2个开始数5个
虚领域:
首先给个表单:
CREATE TABLE products ( productID VARCHAR(30), description VARCHAR(255), supplierID VARCHAR(30), unitprice DECIMAL(10,2), quantity INT, PRIMARY KEY(productID) );
所谓的虚领域就是自己设定规则,举个例子:
$item=new DB\SQL\Mapper($db,'products');$item->totalprice='unitprice*quantity';$item->load(array('productID=:pid',':pid'=>'apple'));echo $item->totalprice;
这里设定的totalprice就是虚领域,默认等于unitprice * quantity 这个虚领域设定了之后其实就相当于里面的一个元素了,可以直接通过 -》 来调用。接着读取productID等于apple的元素然后返回这个虚领域也就是他们两个的乘积。
当然了,除了读取单个元素之外虚领域还可以在宏观上一些操作,例如取最大的数量:
$item->mostNumber='MAX(quantity)';$item->load();echo $item->mostNumber;
注意这里的load没有涉及任何变量,表示直接在全局里面load,然后就可以输出最大数字的元素了。
当然了,有时候你无法用一些现有的函数来表达你想要的东西,我们就可以通过数据库的语言来自己设立筛选条件:
$item->supplierName= 'SELECT name FROM suppliers '. 'WHERE products.supplierID=suppliers.supplierID';$item->load();echo $item->supplierName;
这里自己设定了筛选条件,就是把同名的找出来(一般如果有多个就默认第一个)
数据库查找:
数据库查找要用到find函数:
$frequentUsers=$user->find(array('visits>?',3),array('order'=>'userID'));
要注意,load跟find是完全不一样的,看名字就知道用途了,load是找出所有符合要求的然后我要对它进行操作或者运算,而find函数只要find到了就完事了,就只要看结果,不操作。而且还有一点不同,find因为工作是查找,所以会把找到的东西存起来,存到一个定义好的数组里面(后面会说)。也就是说哪些用于操作的skip,虚领域什么的都不能通过find实现。
我们来看看find函数的定义:
find( $criteria, array( 'group'=>'foo', 'order'=>'foo,bar', 'limit'=>5, 'offset'=>0 ));
第一个是基本条件,作为第一步筛选,然后下面的都是额外筛选的条件,放在一个数组里面。
$place=new DB\SQL\Mapper($db,'places');$list=$place->find('state="New York"');foreach ($list as $obj) echo $obj->city.', '.$obj->country;
这里假设定义好了一个城市的表,然后这里吧state = NEW York 的所有符合条件的映射都存到list数组里面了,然后下面通过foreach来调用查看结果。
接着上面的定义,如果我们不要筛选条件,要整个表搬走,可以用上cast()
$array=$place->cast();echo $array['city'].', '.$array['country'];
还有一个特别好用的函数count:
if (!$user->count(array('visits>?',10))) echo 'We need a better ad campaign!';
这里是如果连一个访问量大于10的用户都没有,那我们就确实需要投广告了。。。
还有可以通过select来做一些比find更复杂的筛选,看看select的定义:
select( 'foo, bar, MIN(baz) AS lowest', 'foo > ?', array( 'group'=>'foo, bar', 'order'=>'baz ASC', 'limit'=>5, 'offset'=>3 ));