Spec-Zone.ru › Phalcon 2

ODM (Объектно-документный маппер)

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

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

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

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

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

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

class Robots extends \Phalcon\Mvc\Collection
{

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

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

class Robots extends \Phalcon\Mvc\Collection
{
    public function getSource()
    {
        return "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 }
>

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

Пространства имён можно использовать для избежания конфликтов имён классов. В этом случае необходимо указать имя связанной коллекции, используя getSource:

namespace Store\Toys;

class Robots extends \Phalcon\Mvc\Collection
{

    public function getSource()
    {
        return "robots";
    }

}

Вы можете найти определённый документ по его идентификатору и затем вывести его имя:

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

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

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

$robot = Robots::findFirst(array(
    array('name' => 'Astroy 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(array(
    array("type" => "mechanical")
));
echo "There are ", count($robots), "\n";

// Get and print mechanical robots ordered by name upward
$robots = Robots::find(array(
    array("type" => "mechanical"),
    "sort" => array("name" => 1)
));

foreach ($robots as $robot) {
    echo $robot->name, "\n";
}

// Get first 100 mechanical robots ordered by name
$robots = Robots::find(array(
    array("type" => "mechanical"),
    "sort" => array("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(array(
    array("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(array(
    "conditions" => array(
        "type" => "mechanical",
        "year" => "1999"
    )
));

// All virtual robots ordered by name downward
$robots = Robots::find(array(
    "conditions" => array("type" => "virtual"),
    "sort"       => array("name" => -1)
));

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

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

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

Агрегации

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

$data = Article::aggregate(array(
    array(
        '$project' => array('category' => 1)
    ),
    array(
        '$group' => array(
            '_id' => array('category' => '$category'),
            'id' => array('$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";
    foreach ($robot->getMessages() 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) {
    foreach ($robot->getMessages() as $message) {
        echo "Message: ", $message->getMessage();
        echo "Field: ", $message->getField();
        echo "Type: ", $message->getType();
    }
}

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

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

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

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

class Robots extends \Phalcon\Mvc\Collection
{

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

}

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

class Products extends \Phalcon\Mvc\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, что означает, что мы можем создавать слушателей, которые запускаются при возникновении события.

$eventsManager = new Phalcon\Events\Manager();

//Attach an anonymous function as a listener for "model" events
$eventsManager->attach('collection', function($event, $robot) {
    if ($event->getType() == 'beforeSave') {
        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, то нам нужно назначить его менеджеру моделей:

//Registering the collectionManager service
$di->set('collectionManager', function() {

    $eventsManager = new Phalcon\Events\Manager();

    // Attach an anonymous function as a listener for "model" events
    $eventsManager->attach('collection', function($event, $model) {
        if (get_class($model) == 'Robots') {
            if ($event->getType() == 'beforeSave') {
                if ($model->name == 'Scooby Doo') {
                    echo "Scooby Doo isn't a robot!";
                    return false;
                }
            }
        }
        return true;
    });

    // Setting a default EventsManager
    $modelsManager = new Phalcon\Mvc\Collection\Manager();
    $modelsManager->setEventsManager($eventsManager);
    return $modelsManager;

}, true);

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

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

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

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

class Robots extends \Phalcon\Mvc\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\Model\Validator\InclusionIn,
    Phalcon\Mvc\Model\Validator\Numericality;

class Robots extends \Phalcon\Mvc\Collection
{

    public function validation()
    {

        $this->validate(new InclusionIn(
            array(
                "field"  => "type",
                "message" => "Type must be: mechanical or virtual",
                "domain" => array("Mechanical", "Virtual")
            )
        ));

        $this->validate(new Numericality(
            array(
                "field"  => "price",
                "message" => "Price must be numeric"
            )
        ));

        return $this->validationHasFailed() != true;
    }

}

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

Имя Описание Пример
Email Проверяет, соответствует ли поле формату корректного адреса электронной почты Пример
ExclusionIn Проверяет, что значение не входит в список возможных значений Пример
InclusionIn Проверяет, что значение входит в список возможных значений Пример
Numericality Проверяет, соответствует ли поле числовому формату Пример
Regex Проверяет, соответствует ли значение поля заданному регулярному выражению Пример
StringLength Проверяет длину строки Пример

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

class UrlValidator extends \Phalcon\Mvc\Collection\Validator
{

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

}

Добавление валидатора к модели:

class Customers extends \Phalcon\Mvc\Collection
{

    public function validation()
    {
        $this->validate(new UrlValidator(array(
            "field"  => "url",
        )));
        if ($this->validationHasFailed() == true) {
            return false;
        }
    }

}

Идея создания валидаторов состоит в том, чтобы сделать их многократно используемыми в нескольких моделях. Валидатор также может быть таким простым, как:

class Robots extends \Phalcon\Mvc\Collection
{

    public function validation()
    {
        if ($this->type == "Old") {
            $message = new Phalcon\Mvc\Model\Message(
                "Sorry, old robots are not allowed anymore",
                "type",
                "MyType"
            );
            $this->appendMessage($message);
            return false;
        }
        return true;
    }

}

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

Метод 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";
        foreach ($robot->getMessages() as $message) {
            echo $message, "\n";
        }
    } else {
        echo "The robot was deleted successfully!";
    }
}

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

$robots = Robots::find(array(
    array("type" => "mechanical")
));
foreach ($robots as $robot) {
    if ($robot->delete() == false) {
        echo "Sorry, we can't delete the robot right now: \n";
        foreach ($robot->getMessages() as $message) {
            echo $message, "\n";
        }
    } else {
        echo "The robot was deleted successfully!";
    }
}

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

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

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

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

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

Неявные идентификаторы против пользовательских первичных ключей

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

class Robots extends Phalcon\Mvc\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, мы определяем сервис соединения для модели:

class Robots extends \Phalcon\Mvc\Collection
{
    public function initialize()
    {
        $this->setConnectionService('mongo1');
    }

}

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

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

class Robots extends \Phalcon\Mvc\Collection
{

    public function notSave()
    {
        // Obtain the flash service from the DI container
        $flash = $this->getDI()->getShared('flash');

        // Show validation messages
        foreach ($this->getMessages() as $message){
            $flash->error((string) $message);
        }
    }

}

Событие «notSave» срабатывает всякий раз, когда действие «создание» или «обновление» завершается неудачно. Мы отображаем сообщения об ошибках валидации, получая сервис «flash» из контейнера DI. Делая это, нам не нужно выводить сообщения после каждого сохранения.

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

Spec-Zone.ru

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