Spec-Zone.ru › Phalcon 3

ODM (Object-Document Mapper)

Помимо возможности отображения таблиц в реляционных базах данных, Phalcon может отображать документы из баз данных NoSQL. ODM предоставляет функциональность CRUD, события, валидации и другие службы.

Благодаря отсутствию SQL-запросов и планов, базы данных NoSQL могут показать реальное улучшение производительности при использовании подхода Phalcon. Кроме того, нет построения SQL, что снижает вероятность SQL-инъекций.

Поддерживаются следующие базы данных NoSQL:

Имя Описание
MongoDB MongoDB — масштабируемая, высокопроизводительная, открытая база данных NoSQL.

Создание моделей

Модель — это класс, который расширяет Phalcon\Mvc\Collection. Она должна быть размещена в каталоге models. Файл модели должен содержать один класс; имя класса должно быть в обозначении с использованием верблюжьего регистра (camel case):

use Phalcon\Mvc\Collection;

class Robots extends Collection
{

}
Если вы используете PHP 5.4/5.5, рекомендуется объявить каждый столбец, который является частью модели, чтобы сохранить память и уменьшить выделение памяти.

По умолчанию модель «Robots» будет относиться к коллекции «robots». Если вы хотите вручную указать другое имя для отображаемой коллекции, можно использовать метод setSource().

use Phalcon\Mvc\Collection;

class Robots extends Collection
{
    public function initialize()
    {
        $this->setSource("the_robots");
    }
}

Понимание преобразования документов в объекты

Каждый экземпляр модели представляет документ в коллекции. Вы можете легко получить доступ к данным коллекции, читая свойства объекта. Например, для коллекции «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 }
>

Модели в пространствах имен

Пространства имен могут использоваться для предотвращения конфликтов имен классов. В этом случае необходимо указать имя связанной коллекции с помощью метода setSource().

namespace Store\Toys;

use Phalcon\Mvc\Collection;

class Robots extends Collection
{
    public function initialize()
    {
        $this->setSource("robots");
    }
}

Вы можете найти определённый документ по его ID и затем напечатать его имя:

// Find record with _id = "5087358f2d42b8c3d15ec4e2"
$robot = Robots::findById("5087358f2d42b8c3d15ec4e2");

// Prints "Bender"
echo $robot->name;

После того, как запись находится в памяти, вы можете внести изменения в её данные и сохранить изменения:

$robot = Robots::findFirst(
    [
        [
            "name" => "Astro Boy",
        ]
    ]
);

$robot->name = "Voltron";

$robot->save();

Установка соединения

Соединения извлекаются из контейнера служб. По умолчанию Phalcon пытается найти соединение в службе под названием «mongo»:

// 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
);

Поиск документов

Так как Phalcon\Mvc\Collection опирается на расширение Mongo 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";
}

Вы также можете использовать метод findFirst() для получения только первой записи, соответствующей заданным критериям:

// 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";

Оба метода find() и findFirst() принимают ассоциативный массив, определяющий критерии поиска:

// 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,
        ],
    ]
);

Доступные параметры запроса:

Параметр Описание Пример
conditions Условия поиска для операции find. Используется для извлечения только тех записей, которые удовлетворяют заданному критерию. По умолчанию Phalcon_model предполагает, что первый параметр — это условия. "conditions" => array('$gt' => 1990)
fields Возвращает определённые столбцы вместо полных полей в коллекции. При использовании этого параметра возвращается неполный объект. "fields" => array('name' => true)
sort Используется для сортировки результата. Используйте одно или несколько полей в качестве каждого элемента массива, 1 означает сортировку по возрастанию, -1 — по убыванию. "sort" => array("name" => -1, "status" => 1)
limit Ограничивает результаты запроса определённым диапазоном результатов. "limit" => 10
skip Пропускает определённое количество результатов. "skip" => 50

Если у вас есть опыт работы с SQL-базами данных, вы можете ознакомиться с Справочником по отображению SQL в Mongo.

Агрегации

Модель может возвращать вычисления, используя фреймворк агрегации, предоставляемый Mongo. Агрегированные значения вычисляются без необходимости использования MapReduce. С этим параметром легко выполнять задачи, такие как суммирование или усреднение значений полей:

$data = Article::aggregate(
    [
        [
            "\$project" => [
                "category" => 1,
            ],
        ],
        [
            "\$group" => [
                "_id" => [
                    "category" => "\$category"
                ],
                "id"  => [
                    "\$max" => "\$_id",
                ],
            ],
        ],
    ]
);

Создание/обновление записей

Метод Phalcon\Mvc\Collection::save() позволяет создавать/обновлять документы в зависимости от того, существуют ли они уже в коллекции, связанной с моделью. Метод save() вызывается внутри методами create и update класса Phalcon\Mvc\Collection.

Также метод выполняет связанные валидаторы и события, которые определены в модели:

$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» автоматически обновляется объектом MongoId, созданным драйвером:

$robot->save();

echo "The generated id is: ", $robot->getId();

Сообщения валидации

Phalcon\Mvc\Collection имеет систему сообщений, которая предоставляет гибкий способ вывода или хранения сообщений валидации, сгенерированных во время процессов вставки/обновления.

Каждое сообщение представляет собой экземпляр класса Phalcon\Mvc\Model\Message. Набор сгенерированных сообщений можно получить с помощью метода getMessages(). Каждое сообщение предоставляет расширенную информацию, такую как имя поля, которое сгенерировало сообщение, или тип сообщения:

if ($robot->save() === false) {
    $messages = $robot->getMessages();

    foreach ($messages as $message) {
        echo "Message: ", $message->getMessage();
        echo "Field: ", $message->getField();
        echo "Type: ", $message->getType();
    }
}

События валидации и менеджер событий

Модели позволяют реализовать события, которые будут генерироваться при выполнении вставки или обновления. Они помогают определить бизнес-правила для определённой модели. Ниже приведены поддерживаемые события Phalcon\Mvc\Collection и их порядок выполнения:

Операция Имя Может остановить операцию? Описание
Вставка/Обновление beforeValidation ДА Выполняется до процесса валидации и окончательной вставки/обновления в базу данных
Вставка beforeValidationOnCreate ДА Выполняется до процесса валидации только при выполнении операции вставки.
Обновление beforeValidationOnUpdate ДА Выполняется до проверки полей на обязательность или внешние ключи при выполнении операции обновления.
Вставка/Обновление onValidationFails ДА (уже остановлено) Выполняется до процесса валидации только при выполнении операции вставки.
Вставка afterValidationOnCreate ДА Выполняется после процесса валидации при выполнении операции вставки.
Обновление afterValidationOnUpdate ДА Выполняется после процесса валидации при выполнении операции обновления.
Вставка/Обновление afterValidation ДА Выполняется после процесса валидации.
Вставка/Обновление beforeSave ДА Выполняется перед необходимой операцией над системой баз данных.
Обновление beforeUpdate ДА Выполняется перед необходимой операцией над системой баз данных только при выполнении операции обновления.
Вставка beforeCreate ДА Выполняется перед необходимой операцией над системой баз данных только при выполнении операции вставки.
Обновление afterUpdate НЕТ Выполняется после необходимой операции над системой баз данных только при выполнении операции обновления.
Вставка afterCreate НЕТ Выполняется после необходимой операции над системой баз данных только при выполнении операции вставки.
Вставка/Обновление afterSave НЕТ Выполняется после необходимой операции над системой баз данных.

Чтобы модель реагировала на событие, необходимо реализовать метод с тем же именем, что и событие:

use Phalcon\Mvc\Collection;

class Robots extends Collection
{
    public function beforeValidationOnCreate()
    {
        echo "This is executed before creating a Robot!";
    }
}

События могут быть полезны для присвоения значений перед выполнением операции, например:

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, это означает, что мы можем создавать обработчики, которые выполняются при срабатывании события.

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, то нам нужно назначить его менеджеру моделей:

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
);

Реализация бизнес-правила

При выполнении вставки, обновления или удаления модель проверяет, есть ли методы с именами событий, перечисленных в таблице выше.

Рекомендуется объявлять методы валидации защищёнными (protected), чтобы предотвратить раскрытие реализации бизнес-логики в общедоступном виде.

Следующий пример реализует событие, которое проверяет, чтобы год не был меньше 0 при обновлении или вставке:

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.

Проверка целостности данных

Phalcon\Mvc\Collection предоставляет несколько событий для проверки данных и реализации бизнес-правил. Специальное событие «validation» позволяет вызывать встроенные валидаторы над записью. Phalcon предоставляет несколько встроенных валидаторов, которые могут быть использованы на этом этапе валидации.

Следующий пример демонстрирует, как его использовать:

use Phalcon\Mvc\Collection;
use Phalcon\Validation;
use Phalcon\Validation\Validator\InclusionIn;
use Phalcon\Validation\Validator\Numericality;

class Robots extends Collection
{
    public function validation()
    {
        $validation = new Validation();

        $validation->add(
            "type",
            new InclusionIn(
                [
                    "message" => "Type must be: mechanical or virtual",
                    "domain" => [
                        "Mechanical",
                        "Virtual",
                    ],
                ]
            )
        );

        $validation->add(
            "price",
            new Numericality(
                [
                    "message" => "Price must be numeric"
                ]
            )
        );

        return $this->validate($validation);
    }
}

Приведённый выше пример выполняет валидацию с использованием встроенного валидатора «InclusionIn». Он проверяет значение поля «type» в списке доменов. Если значение не входит в метод, то валидатор завершится ошибкой и вернёт false.

Для получения дополнительной информации о валидаторах, обратитесь к документации по валидации.

Удаление записей

Метод Phalcon\Mvc\Collection::delete() позволяет удалить документ. Его можно использовать следующим образом:

$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!";
    }
}

Вы также можете удалить множество документов, пройдя по результатам с помощью цикла %%%CODE_BLOCK_52%%:

$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!";
    }
}

Доступны следующие события, позволяющие определять пользовательские бизнес-правила, которые могут выполняться при выполнении операции удаления:

Операция Имя Может остановить операцию? Описание
Удаление beforeDelete ДА Выполняется перед выполнением операции удаления
Удаление afterDelete НЕТ Выполняется после выполнения операции удаления

События при ошибке валидации

Доступен другой тип событий, когда процесс валидации данных обнаруживает какие-либо несоответствия:

Операция Имя Описание
Вставка или обновление notSave Вызывается, когда операция вставки/обновления завершается ошибкой по любой причине
Вставка, удаление или обновление onValidationFails Вызывается, когда любая операция манипулирования данными завершается ошибкой

Неявные ID по сравнению с пользовательскими первичными ключами

По умолчанию Phalcon\Mvc\Collection предполагает, что атрибут _id автоматически генерируется с помощью MongoIds. Если модель использует пользовательские первичные ключи, это поведение можно переопределить:

use Phalcon\Mvc\Collection;

class Robots extends Collection
{
    public function initialize()
    {
        $this->useImplicitObjectIds(false);
    }
}

Настройка нескольких баз данных

В Phalcon все модели могут принадлежать одной базе данных или иметь отдельную. Фактически, когда Phalcon\Mvc\Collection необходимо подключиться к базе данных, он запрашивает сервис «mongo» в контейнере сервисов приложения. Вы можете переопределить этот сервис, установив его в методе initialize:

// This service returns a mongo database at 192.168.1.100
$di->set(
    "mongo1",
    function () {
        $mongo = new MongoClient(
            "mongodb://scott:[email protected]"
        );

        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
);

Затем в методе initialize() мы определяем сервис подключения для модели:

use Phalcon\Mvc\Collection;

class Robots extends Collection
{
    public function initialize()
    {
        $this->setConnectionService("mongo1");
    }
}

Вставка сервисов в модели

Вам может потребоваться получить доступ к сервисам приложения внутри модели. Следующий пример объясняет, как это сделать:

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» из контейнера DI. Благодаря этому, нам не нужно выводить сообщения после каждого сохранения.

© 2011–2017 Phalcon Framework Team
Licensed under the Creative Commons Attribution License 3.0.
https://docs.phalconphp.com/en/latest/reference/odm.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API