內容目录
上一个主题< 缓存对象关系映射(Caching in the ORM) 下一个主题 |
对象文档映射 ODM (Object-Document Mapper)¶除了可以 映射关系数据库的表 之外,Phalcon还可以使用NoSQL数据库如MongoDB等。Phalcon中的ODM具有可以非常容易的实现如下功能:CRUD,事件,验证等。 因为NoSQL数据库中无sql查询及计划等操作故可以提高数据操作的性能。再者,由于无SQL语句创建的操作故可以减少SQL注入的危险。 当前Phalcon中支持的NosSQL数据库如下:
创建模型(Creating Models)¶NoSQL中的模型类扩展自 Phalcon\Mvc\Collection.模型必须要放入模型文件夹中而且每个模型文件必须只能有一个模型类; 模型类名应该为小驼峰法书写: <?php
use Phalcon\Mvc\Collection;
class Robots extends Collection
{
}
<?php
use Phalcon\Mvc\Collection;
class Robots extends Collection
{
public function initialize()
{
$this->setSource("the_robots");
}
}
理解文档对象(Understanding Documents To Objects)¶每个模型的实例和数据库表中的一个文档(记录)相对应。我们可以非常容易的通过读取对象属性来访问表格的数据。例如访问robots表格: $ mongo test
MongoDB shell version: 1.8.2
connecting to: test
> db.robots.find()
{ "_id" : ObjectId("508735512d42b8c3d15ec4e1"), "name" : "Astro Boy", "year" : 1952,
"type" : "mechanical" }
{ "_id" : ObjectId("5087358f2d42b8c3d15ec4e2"), "name" : "Bender", "year" : 1999,
"type" : "mechanical" }
{ "_id" : ObjectId("508735d32d42b8c3d15ec4e3"), "name" : "Wall-E", "year" : 2008 }
>
模型中使用命名空间(Models in Namespaces)¶我们在这里可以使用命名空间来避免类名冲突。这个例子中我们使用:code:`setSource()`方法来标明要使用的数据库表: <?php
namespace Store\Toys;
use Phalcon\Mvc\Collection;
class Robots extends Collection
{
public function initialize()
{
$this->setSource("robots");
}
}
我们可以通过对象的ID查找到对象然后打印出其名字: <?php
// Find record with _id = "5087358f2d42b8c3d15ec4e2"
$robot = Robots::findById("5087358f2d42b8c3d15ec4e2");
// Prints "Bender"
echo $robot->name;
一旦记录被加载到内存中,我们就可以对这些数据进行修改了,修改之后还可以保存: <?php
$robot = Robots::findFirst(
[
[
"name" => "Astro Boy",
]
]
);
$robot->name = "Voltron";
$robot->save();
设置连接(Setting a Connection)¶这里的MongoDB服务是从服务容器中取得的。默认,Phalcon会使mongo作服务名: <?php
// Simple database connection to localhost
$di->set(
"mongo",
function () {
$mongo = new MongoClient();
return $mongo->selectDB("store");
},
true
);
// Connecting to a domain socket, falling back to localhost connection
$di->set(
"mongo",
function () {
$mongo = new MongoClient(
"mongodb:///tmp/mongodb-27017.sock,localhost:27017"
);
return $mongo->selectDB("store");
},
true
);
查找文档(Finding Documents)¶Phalcon\Mvc\Collection 依赖于Mongo的PHP扩展,这样我们就可以直接从数据库中查询出文档记录然后Phalcon会 透明的(我们无需关心过程和方法)为我们转换为模型的实例。 Phalcon\Mvc\Collection <?php
// How many robots are there?
$robots = Robots::find();
echo "There are ", count($robots), "\n";
// How many mechanical robots are there?
$robots = Robots::find(
[
[
"type" => "mechanical",
]
]
);
echo "There are ", count($robots), "\n";
// Get and print mechanical robots ordered by name upward
$robots = Robots::find(
[
[
"type" => "mechanical",
],
"sort" => [
"name" => 1,
],
]
);
foreach ($robots as $robot) {
echo $robot->name, "\n";
}
// Get first 100 mechanical robots ordered by name
$robots = Robots::find(
[
[
"type" => "mechanical",
],
"sort" => [
"name" => 1,
],
"limit" => 100,
]
);
foreach ($robots as $robot) {
echo $robot->name, "\n";
}
这里我们可以使用 <?php
// What's the first robot in robots collection?
$robot = Robots::findFirst();
echo "The robot name is ", $robot->name, "\n";
// What's the first mechanical robot in robots collection?
$robot = Robots::findFirst(
[
[
"type" => "mechanical",
]
]
);
echo "The first mechanical robot name is ", $robot->name, "\n";
<?php
// First robot where type = "mechanical" and year = "1999"
$robot = Robots::findFirst(
[
"conditions" => [
"type" => "mechanical",
"year" => "1999",
],
]
);
// All virtual robots ordered by name downward
$robots = Robots::find(
[
"conditions" => [
"type" => "virtual",
],
"sort" => [
"name" => -1,
],
]
);
可用的查询选项:
如果你有使用sql(关系)数据库的经验,你也许想查看二者的映射表格 SQL to Mongo Mapping Chart . 聚合(Aggregations)¶我们可以使用Mongo提供的方法使用Mongo模型返回聚合结果。聚合结果不是使用MapReduce来计算的。基于此,我们可以非常容易的取得聚合值,比如总计或平均值等: <?php
$data = Article::aggregate(
[
[
"\$project" => [
"category" => 1,
],
],
[
"\$group" => [
"_id" => [
"category" => "\$category"
],
"id" => [
"\$max" => "\$_id",
],
],
],
]
);
创建和更新记录(Creating Updating/Records)¶
当然这个方法内部也会调用我们在模型中定义的验证方法或事件等: <?php
$robot = new Robots();
$robot->type = "mechanical";
$robot->name = "Astro Boy";
$robot->year = 1952;
if ($robot->save() === false) {
echo "Umh, We can't store robots right now: \n";
$messages = $robot->getMessages();
foreach ($messages as $message) {
echo $message, "\n";
}
} else {
echo "Great, a new robot was saved successfully!";
}
“_id”属性会被Mongo驱动自动的随MongId_而更新。 <?php
$robot->save();
echo "The generated id is: ", $robot->getId();
验证信息(Validation Messages)¶Phalcon\Mvc\Collection 提供了一个信息子系统,使用此系统开发者可以非常容易的实现在数据处理中的验证信息的显示及保存。 每条信息即是一个 Phalcon\Mvc\Model\Message 类的对象实例。我们使用getMessages来取得此信息。每条信息中包含了 如哪个字段产生的消息,或是消息类型等信息: <?php
if ($robot->save() === false) {
$messages = $robot->getMessages();
foreach ($messages as $message) {
echo "Message: ", $message->getMessage();
echo "Field: ", $message->getField();
echo "Type: ", $message->getType();
}
}
验证事件和事件管理(Validation Events and Events Manager)¶在模型类的数据操作过程中可以产生一些事件。我们可以在这些事件中定义一些业务规则。下面是 Phalcon\Mvc\Collection 所支持的事件及其执行顺序:
为了响应一个事件,我们需在模型中实现同名方法: <?php
use Phalcon\Mvc\Collection;
class Robots extends Collection
{
public function beforeValidationOnCreate()
{
echo "This is executed before creating a Robot!";
}
}
在执行操作之前先在指定的事件中设置值有时是非常有用的: <?php
use Phalcon\Mvc\Collection;
class Products extends Collection
{
public function beforeCreate()
{
// Set the creation date
$this->created_at = date("Y-m-d H:i:s");
}
public function beforeUpdate()
{
// Set the modification date
$this->modified_in = date("Y-m-d H:i:s");
}
}
另外,这个组件也可以和 Phalcon\Events\Manager 进行集成,这就意味着我们可以通过事件触发创建监听器。 <?php
use Phalcon\Events\Event;
use Phalcon\Events\Manager as EventsManager;
$eventsManager = new EventsManager();
// Attach an anonymous function as a listener for "model" events
$eventsManager->attach(
"collection:beforeSave",
function (Event $event, $robot) {
if ($robot->name === "Scooby Doo") {
echo "Scooby Doo isn't a robot!";
return false;
}
return true;
}
);
$robot = new Robots();
$robot->setEventsManager($eventsManager);
$robot->name = "Scooby Doo";
$robot->year = 1969;
$robot->save();
上面的例子中EventsManager仅在对象和监听器(匿名函数)之间扮演了一个桥接器的角色。如果我们想在创建应用时使用同一个EventsManager,我们需要把这个EventsManager对象设置到 collectionManager服务中: <?php
use Phalcon\Events\Event;
use Phalcon\Events\Manager as EventsManager;
use Phalcon\Mvc\Collection\Manager as CollectionManager;
// Registering the collectionManager service
$di->set(
"collectionManager",
function () {
$eventsManager = new EventsManager();
// Attach an anonymous function as a listener for "model" events
$eventsManager->attach(
"collection:beforeSave",
function (Event $event, $model) {
if (get_class($model) === "Robots") {
if ($model->name === "Scooby Doo") {
echo "Scooby Doo isn't a robot!";
return false;
}
}
return true;
}
);
// Setting a default EventsManager
$modelsManager = new CollectionManager();
$modelsManager->setEventsManager($eventsManager);
return $modelsManager;
},
true
);
实现业务规则(Implementing a Business Rule)¶当插入或更新删除等执行时,模型会检查上面表格中列出的方法是否存在。 我们建议定义模型里的验证方法以避免业务逻辑暴露出来。 下面的例子中实现了在保存或更新时对年份的验证,年份不能小于0年: <?php
use Phalcon\Mvc\Collection;
class Robots extends Collection
{
public function beforeSave()
{
if ($this->year < 0) {
echo "Year cannot be smaller than zero!";
return false;
}
}
}
在响应某些事件时返回了false则会停止当前的操作。 如果事实响应未返回任何值, Phalcon\Mvc\Collection 会假定返回了true值。 验证数据完整性(Validating Data Integrity)¶Phalcon\Mvc\Collection 提供了若干个事件用于验证数据和实现业务逻辑。特定的事件中我们可以调用内建的验证器, Phalcon提供了一些验证器可以用在此阶段的验证上。 下面的例子中展示了如何使用: <?php
use Phalcon\Mvc\Collection;
use Phalcon\Mvc\Model\Validator\InclusionIn;
use Phalcon\Mvc\Model\Validator\Numericality;
class Robots extends Collection
{
public function validation()
{
$this->validate(
new InclusionIn(
[
"field" => "type",
"message" => "Type must be: mechanical or virtual",
"domain" => [
"Mechanical",
"Virtual",
],
]
)
);
$this->validate(
new Numericality(
[
"field" => "price",
"message" => "Price must be numeric",
]
)
);
return $this->validationHasFailed() !== true;
}
}
上面的例子使用了内建的”InclusionIn”验证器。这个验证器检查了字段的类型是否在指定的范围内。如果值不在范围内即验证失败会返回false. 下面支持的内验证器:
除了内建的验证器外,我们还可以创建自己的验证器: <?php
use Phalcon\Mvc\Model\Validator as CollectionValidator;
class UrlValidator extends CollectionValidator
{
public function validate($model)
{
$field = $this->getOption("field");
$value = $model->$field;
$filtered = filter_var($value, FILTER_VALIDATE_URL);
if (!$filtered) {
$this->appendMessage(
"The URL is invalid",
$field,
"UrlValidator"
);
return false;
}
return true;
}
}
添加验证器到模型: <?php
use Phalcon\Mvc\Collection;
class Customers extends Collection
{
public function validation()
{
$this->validate(
new UrlValidator(
[
"field" => "url",
]
)
);
if ($this->validationHasFailed() === true) {
return false;
}
}
}
创建验证器的目的即是使之在多个模型间重复利用以实现代码重用。验证器可简单如下: <?php
use Phalcon\Mvc\Collection;
use Phalcon\Mvc\Model\Message as ModelMessage;
class Robots extends Collection
{
public function validation()
{
if ($this->type === "Old") {
$message = new ModelMessage(
"Sorry, old robots are not allowed anymore",
"type",
"MyType"
);
$this->appendMessage($message);
return false;
}
return true;
}
}
删除记录(Deleting Records)¶
<?php
$robot = Robots::findFirst();
if ($robot !== false) {
if ($robot->delete() === false) {
echo "Sorry, we can't delete the robot right now: \n";
$messages = $robot->getMessages();
foreach ($messages as $message) {
echo $message, "\n";
}
} else {
echo "The robot was deleted successfully!";
}
}
也可以使用遍历的方式删除多个条目的数据: <?php
$robots = Robots::find(
[
[
"type" => "mechanical",
]
]
);
foreach ($robots as $robot) {
if ($robot->delete() === false) {
echo "Sorry, we can't delete the robot right now: \n";
$messages = $robot->getMessages();
foreach ($messages as $message) {
echo $message, "\n";
}
} else {
echo "The robot was deleted successfully!";
}
}
当删除操作执行时我们可以执行如下事件,以实现定制业务逻辑的目的:
验证失败事件(Validation Failed Events)¶验证失败时依据不同的情形下列事件会触发:
固有 Id 和 用户主键(Implicit Ids vs. User Primary Keys)¶默认 Phalcon\Mvc\Collection 会使用MongoIds_来产生 <?php
use Phalcon\Mvc\Collection;
class Robots extends Collection
{
public function initialize()
{
$this->useImplicitObjectIds(false);
}
}
设置多个数据库(Setting multiple databases)¶Phalcon中,所有的模型可以只属于一个数据库或是多个数据库。事实上当 Phalcon\Mvc\Collection 试图连接数据库时 Phalcon会从DI中取名为mongo的服务。当然我们可在模型的initialize方法中进行连接设置: <?php
// This service returns a mongo database at 192.168.1.100
$di->set(
"mongo1",
function () {
$mongo = new MongoClient(
"mongodb://scott:nekhen@192.168.1.100"
);
return $mongo->selectDB("management");
},
true
);
// This service returns a mongo database at localhost
$di->set(
"mongo2",
function () {
$mongo = new MongoClient(
"mongodb://localhost"
);
return $mongo->selectDB("invoicing");
},
true
);
然后在初始化方法,我们定义了模型的连接: <?php
use Phalcon\Mvc\Collection;
class Robots extends Collection
{
public function initialize()
{
$this->setConnectionService("mongo1");
}
}
注入服务到模型(Injecting services into Models)¶我们可能需要在模型内使用应用的服务,下面的例子中展示了如何去做: <?php
use Phalcon\Mvc\Collection;
class Robots extends Collection
{
public function notSave()
{
// Obtain the flash service from the DI container
$flash = $this->getDI()->getShared("flash");
$messages = $this->getMessages();
// Show validation messages
foreach ($messages as $message) {
$flash->error(
(string) $message
);
}
}
}
notSave事件在创建和更新失败时触发。我们使用flash服务来处理验证信息。如此做我们无需在每次保存后打印消息出来。 |