目次前のトピックへ次のトピックへこのページ |
ODM (Object-Document Mapper)¶In addition to its ability to map tables in relational databases, Phalcon can map documents from NoSQL databases. The ODM offers a CRUD functionality, events, validations among other services. Due to the absence of SQL queries and planners, NoSQL databases can see real improvements in performance using the Phalcon approach. Additionally, there are no SQL building reducing the possibility of SQL injections. The following NoSQL databases are supported:
モデルの作成¶A model is a class that extends from Phalcon\Mvc\Collection. It must be placed in the models directory. A model file must contain a single class; its class name should be in camel case notation: <?php
use Phalcon\Mvc\Collection;
class Robots extends Collection
{
}
By default model “Robots” will refer to the collection “robots”. If you want to manually specify another name for the mapping collection,
you can use the <?php
use Phalcon\Mvc\Collection;
class Robots extends Collection
{
public function initialize()
{
$this->setSource("the_robots");
}
}
Understanding Documents To Objects¶Every instance of a model represents a document in the collection. You can easily access collection data by reading object properties. For example, for a collection “robots” with the documents: $ 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 }
>
名前空間内のモデル¶Namespaces can be used to avoid class name collision. In this case it is necessary to indicate the name of the related collection using the <?php
namespace Store\Toys;
use Phalcon\Mvc\Collection;
class Robots extends Collection
{
public function initialize()
{
$this->setSource("robots");
}
}
You could find a certain document by its ID and then print its name: <?php
// Find record with _id = "5087358f2d42b8c3d15ec4e2"
$robot = Robots::findById("5087358f2d42b8c3d15ec4e2");
// Prints "Bender"
echo $robot->name;
Once the record is in memory, you can make modifications to its data and then save changes: <?php
$robot = Robots::findFirst(
[
[
"name" => "Astro Boy",
]
]
);
$robot->name = "Voltron";
$robot->save();
接続設定¶Connections are retrieved from the services container. By default, Phalcon tries to find the connection in a service called “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
);
ドキュメントの検索¶As Phalcon\Mvc\Collection relies on the Mongo PHP extension you have the same facilities to query documents and convert them transparently to model instances: <?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";
}
You could also use the <?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";
Both <?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,
],
]
);
The available query options are:
If you have experience with SQL databases, you may want to check the SQL to Mongo Mapping Chart. 集計¶A model can return calculations using aggregation framework provided by Mongo. The aggregated values are calculate without having to use MapReduce. With this option is easy perform tasks such as totaling or averaging field values: <?php
$data = Article::aggregate(
[
[
"\$project" => [
"category" => 1,
],
],
[
"\$group" => [
"_id" => [
"category" => "\$category"
],
"id" => [
"\$max" => "\$_id",
],
],
],
]
);
レコードの作成、更新¶The Also the method executes associated validators and events that are defined in the model: <?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!";
}
The “_id” property is automatically updated with the MongoId object created by the driver: <?php
$robot->save();
echo "The generated id is: ", $robot->getId();
バリデーション・メッセージ¶Phalcon\Mvc\Collection has a messaging subsystem that provides a flexible way to output or store the validation messages generated during the insert/update processes. Each message consists of an instance of the class Phalcon\Mvc\Model\Message. The set of messages generated can be retrieved with the method getMessages(). Each message provides extended information like the field name that generated the message or the message type: <?php
if ($robot->save() === false) {
$messages = $robot->getMessages();
foreach ($messages as $message) {
echo "Message: ", $message->getMessage();
echo "Field: ", $message->getField();
echo "Type: ", $message->getType();
}
}
バリデーション・イベントとイベント・マネージャ¶Models allow you to implement events that will be thrown when performing an insert or update. They help define business rules for a certain model. The following are the events supported by Phalcon\Mvc\Collection and their order of execution:
To make a model to react to an event, we must to implement a method with the same name of the event: <?php
use Phalcon\Mvc\Collection;
class Robots extends Collection
{
public function beforeValidationOnCreate()
{
echo "This is executed before creating a Robot!";
}
}
Events can be useful to assign values before performing an operation, for example: <?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");
}
}
Additionally, this component is integrated with Phalcon\Events\Manager, this means we can create listeners that run when an event is triggered. <?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();
In the example given above the EventsManager only acted as a bridge between an object and a listener (the anonymous function). If we want all objects created in our application use the same EventsManager, then we need to assign this to the Models Manager: <?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
);
ビジネス・ルールの実装¶When an insert, update or delete is executed, the model verifies if there are any methods with the names of the events listed in the table above. We recommend that validation methods are declared protected to prevent that business logic implementation from being exposed publicly. The following example implements an event that validates the year cannot be smaller than 0 on update or insert: <?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;
}
}
}
Some events return false as an indication to stop the current operation. If an event doesn’t return anything, Phalcon\Mvc\Collection will assume a true value. データ整合性の検証¶Phalcon\Mvc\Collection provides several events to validate data and implement business rules. The special “validation” event allows us to call built-in validators over the record. Phalcon exposes a few built-in validators that can be used at this stage of validation. The following example shows how to use it: <?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;
}
}
The example given above performs a validation using the built-in validator “InclusionIn”. It checks the value of the field “type” in a domain list. If the value is not included in the method, then the validator will fail and return false. The following built-in validators are available:
In addition to the built-in validators, you can create your own validators: <?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;
}
}
Adding the validator to a model: <?php
use Phalcon\Mvc\Collection;
class Customers extends Collection
{
public function validation()
{
$this->validate(
new UrlValidator(
[
"field" => "url",
]
)
);
if ($this->validationHasFailed() === true) {
return false;
}
}
}
The idea of creating validators is to make them reusable across several models. A validator can also be as simple as: <?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;
}
}
レコードの削除¶The <?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!";
}
}
You can also delete many documents by traversing a resultset with a <?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!";
}
}
The following events are available to define custom business rules that can be executed when a delete operation is performed:
バリデーション失敗のイベント¶Another type of events is available when the data validation process finds any inconsistency:
暗黙のID VS. ユーザー指定プライマリキー¶By default Phalcon\Mvc\Collection assumes that the <?php
use Phalcon\Mvc\Collection;
class Robots extends Collection
{
public function initialize()
{
$this->useImplicitObjectIds(false);
}
}
複数のデータベースの設定¶In Phalcon, all models can belong to the same database connection or have an individual one. Actually, when Phalcon\Mvc\Collection needs to connect to the database it requests the “mongo” service in the application’s services container. You can overwrite this service setting it in the initialize method: <?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
);
Then, in the <?php
use Phalcon\Mvc\Collection;
class Robots extends Collection
{
public function initialize()
{
$this->setConnectionService("mongo1");
}
}
モデルにサービスを注入する¶You may be required to access the application services within a model, the following example explains how to do that: <?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
);
}
}
}
The “notSave” event is triggered whenever a “creating” or “updating” action fails. We’re flashing the validation messages obtaining the “flash” service from the DI container. By doing this, we don’t have to print messages after each saving. |