Обзор

Модель Phalcon\Mvc\Model - это M в MVC. Это класс, который соединяет бизнес-объекты и таблицы базы данных, чтобы создать постоянную модель домена, где логика и данные объединены в одно целое. Это реализация объектно-реляционного отображения (ORM).

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

Phalcon\Mvc\Model - это первый ORM, написанный на языках Zephir/C для PHP, дающий разработчикам высокую производительность при взаимодействии с базами данных, а также Простой в использовании.

Примечание: модели предназначены для работы с базой данных на высоком уровне абстракции. Если вам необходимо работать с базами данных на более низком уровне, ознакомьтесь с документацией компонента Phalcon\Db.

<?php

namespace MyApp\Models;

use Phalcon\Mvc\Model;

class Invoices extends Model
{

}
<?php

use MyApp\Models\Invoices;

$invoice = new Invoices();

$invoice->inv_cst_id      = 1;
$invoice->inv_status_flag = 1;
$invoice->inv_title       = 'Invoice for ACME Inc.';
$invoice->inv_total       = 100;
$invoice->inv_created_at  = '2019-12-25 01:02:03';

$result = $invoice->save();

if (false === $result) {
    
    echo 'Error saving Invoice: ';

    $messages = $invoice->getMessages();

    foreach ($messages as $message) {
        echo $message . PHP_EOL;
    }
} else {

    echo 'Record Saved';

}

Примечание: для получения информации о том, как создать модель, пожалуйста, проверьте раздел создание моделей

Константы

КонстантаЗначение
DIRTY_STATE_DETACHED 2
DIRTY_STATE_PERSISTENT 0
DIRTY_STATE_TRANSIENT 1
OP_CREATE 1
OP_DELETE 3
OP_NONE 0
OP_UPDATE 2
TRANSACTION_INDEX ‘transaction’

Методы

final public function __construct(
    mixed $data = null, 
    DiInterface $container = null,
    ManagerInterface $modelsManager = null
)

Создает объект модели. Метод принимает массив данных, которые используются для заполнения объекта с помощью внутреннего использования assign. При желании вы можете пройти контейнеру и объектные модели менеджера. Если они не будут переданы, будут использоваться значения по умолчанию.

public function __call(string $method, array $arguments): mixed

Обрабатывает вызовы методов, когда метод не реализован. Создает исключение Phalcon\Mvc\Model\Exception, если метод не существует

public static function __callStatic(
    string $method, 
    array $arguments
): mixed

Обрабатывает вызовы методов, когда статический метод не реализован. Выбрасывает Phalcon\Mvc\Model\Exception, если метод не существует

public function __get(string $property)

Магический метод получения связанных записей с использованием псевдонима отношения в качестве свойства

public function __isset(string $property): bool

Магический метод проверки, является ли свойство допустимым отношением

public function __set(string $property, mixed $value)

Магический метод присвоения значений модели

public function addBehavior(
    BehaviorInterface $behavior
): void

Настройка поведения в модели

<?php

namespace MyApp\Models;

use Phalcon\Mvc\Model;
use Phalcon\Mvc\Model\Behavior\Timestampable;

class Invoices extends Model
{
    public function initialize()
    {
        $this->addBehavior(
            new Timestampable(
                [
                    'onCreate' => [
                        'field'  => 'inv_created_at',
                        'format' => 'Y-m-d H:i:s',
                    ],
                ]
            )
        );
    }
}
public function appendMessage(
    MessageInterface $message
): ModelInterface

Добавляет настраиваемое сообщение о процессе проверки

<?php

namespace MyApp\Models;

use Phalcon\Mvc\Model;
use Phalcon\Messages\Message as Message;

class Invoices extends Model
{
    public function beforeSave()
    {
        if (0 === $this->inv_status_flag) {
            $message = new Message(
                'Sorry, an invoice cannot be unpaid'
            );

            $this->appendMessage($message);
        }
    }
}
public function assign(
    mixed $data, 
    array $whiteList = null, 
    array $dataColumnMap = null
): ModelInterface

Присваивает модели данные. Параметр data может быть массивом или строкой базы данных. Белый список whitelist - это массив свойств модели, которые будут обновлены в процессе назначения. Пропущенные свойства не будут приняты, даже если они включены в массив или строку базы данных; тем не менее, если один из них требуется моделью, данные не будут сохранены, и модель выдаст ошибку. dataColumnMap -это массив, который отображает столбцы из данных в фактическую модель. Это помогает, когда требуется сопоставить входные данные из массива, такого как $_POST, с полями в базе данных.

Назначение значений модели из массива

<?php

$invoice->assign(
    [
        'inv_cst_id'      => 1,
        'inv_status_flag' => 1,
        'inv_title'       => 'Invoice for ACME Inc.',
        'inv_total'       => 100,
        'inv_created_at'  => '2019-12-25 01:02:03',
    ]
);

assign с помощью строки базы данных. - Требуется карта столбцов

<?php

$invoice->assign(
    $row,
    null,
    [
        'inv_cst_id'      => 'customerId',
        'inv_status_flag' => 'status',
        'inv_title'       => 'title',
        'inv_total'       => 'total',
    ]
);

Обновите только поля inv_status_flag, inv_title, inv_total.

<?php

$invoice->assign(
    $_POST,
    [
        'inv_status_flag',
        'inv_title',
        'inv_total',
    ]
);

По умолчанию assign будет использовать сеттеры, если они существуют, вы можете отключить его, используя ini_set для прямого использования свойств

ini_set('phalcon.orm.disable_assign_setters', true);
<?php

$invoice->assign(
    $_POST,
    null,
    [
        'inv_status_flag',
        'inv_title',
        'inv_total',
    ]
);
public static function average(
    mixed $parameters = null
): float

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

<?php

use MyApp\Models\Invoices;

$average = Invoices::average(
    [
        'column' => 'inv_total',
    ]
);

echo 'AVG: ', $average, PHP_EOL;

$average = Invoices::average(
    [
        'inv_cst_id = 1',
        'column' => 'inv_total',
    ]
);

echo 'AVG [Customer: 1] ', $average, PHP_EOL;
public static function cloneResult(
    ModelInterface $base, 
    array $data, 
    int $dirtyState = 0
): ModelInterface

Присваивает модели значения из массива, возвращая новую модель

<?php

use MyApp\Models\Invoices;

$invoice = Invoices::cloneResult(
     new Invoices(),
    [
        'inv_cst_id'      => 1,
        'inv_status_flag' => 0,
        'inv_title'       => 'Invoice for ACME Inc. #2',
        'inv_total'       => 400,
        'inv_created_at'  => '2019-12-25 01:02:03',
    ]
 );
public static function cloneResultMap(
    mixed $base, 
    array $data, 
    array $columnMap, 
    int $dirtyState = 0, 
    bool $keepSnapshots = null
): ModelInterface

Присваивает модели значения из массива, возвращая новую модель, используя карту столбцов.

<?php

use MyApp\Models\Invoices;

$invoice = Invoices::cloneResultMap(
     new Invoices(),
     [
        'customerId' => 1,
        'status'     => 0,
        'title'      => 'Invoice for ACME Inc. #2',
        'total'      => 400,
        'created'    => '2019-12-25 01:02:03',
     ]
);
<?php

use MyApp\Models\Invoices;

$invoice = Invoices::cloneResultMap(
     new Invoices(),
     [
        'customerId' => 1,
        'status'     => 0,
        'title'      => 'Invoice for ACME Inc. #2',
        'total'      => 400,
        'created'    => '2019-12-25 01:02:03',
     ]
);
public static function cloneResultMapHydrate(
    array $data, 
    array $columnMap, 
    int $hydrationMode
): mixed

Возвращает гидратированный результат на основе данных и карты столбцов

public static function count(
    mixed $parameters = null
): int

Возвращает количество записей, соответствующих указанным условиям.

<?php

use MyApp\Models\Invoices;

$average = Invoices::count();

echo 'COUNT: ', $average, PHP_EOL;

$average = Invoices::count(
    'inv_cst_id = 1'
);

echo 'COUNT [Customer: 1] ', $average, PHP_EOL;
public function create(): bool

Вставляет модель в базу данных. Если запись существует в базе данных, функция create() выдаст исключение. Он возвратит true в случае успеха, false -в противном случае.

<?php

use MyApp\Models\Invoices;

$invoice = new Invoices();
$invoice->assign(
    [
        'inv_cst_id'      => 1,
        'inv_status_flag' => 1,
        'inv_title'       => 'Invoice for ACME Inc.',
        'inv_total'       => 100,
        'inv_created_at'  => '2019-12-25 01:02:03',
    ]
);

$result = $invoice->create();
public function delete(): bool

Удаляет экземпляр модели. Возврат true в случае успеха или false в противном случае.

<?php

use MyApp\Models\Invoices;

$invoice = Invoices::findFirst('inv_id = 4');
$result  = $invoice->delete();

$invoices = Invoices::find(
    [
        'conditions' => 'inv_cst_id = :cst_id:',
        'bind'       => [
            'cst_id' => 1,
        ]
    ]
);

foreach ($invoices as $invoice) {
    $invoice->delete();
}
public function dump(): array

Возвращает простое представление объекта, которое можно использовать с var_dump()

<?php

use MyApp\Models\Invoices;

$invoice = Invoices::findFirst('inv_id = 4');

var_dump(
    $invoice->dump()
);
public static function find(
    mixed $parameters = null
): ResultsetInterface

Запрос набора записей, соответствующих указанным условиям. функция find() является достаточно гибким, чтобы принимать различные параметры для поиска требуемых данных. Дополнительные сведения см. в разделе Поиск записей.

<?php

use MyApp\Models\Invoices;

$invoice = Invoices::find();
public static function findFirst(
    mixed $parameters = null
): ModelInterface | null

Запрос первой записи, соответствующей указанным условиям. Если запись не найдена, она возвращает результирующий набор или значение null.

ПРИМЕЧАНИЕ: findFirst() больше не возвращает значение false, если записи не найдены.

<?php

use MyApp\Models\Invoices;

$invoice = Invoices::findFirst();
public function fireEvent(string $eventName): bool

Инициирует событие, неявно вызывает поведение, и прослушиватели в диспетчере событий уведомляются

public function fireEventCancel(string $eventName): bool

Инициирует событие, неявно вызывает поведение, и прослушиватели в диспетчере событий уведомляются. Этот метод останавливается, если один из обратных вызовов/прослушивателей возвращает значение false

public function getChangedFields(): array

Возвращает список измененных значений.

<?php

use MyApp\Models\Invoices;

$invoice = Invoices::findFirst();

print_r(
    $invoice->getChangedFields()
); 
// []

$invoice->inv_total = 120;;

$invoice->getChangedFields();

print_r(
    $invoice->getChangedFields()
);
// ['inv_total']
public function getDirtyState(): int

Возвращает одну из констант DIRTY_STATE_*, указывающую, существует ли запись в базе данных или нет

public function getMessages(
    mixed $filter = null
): MessageInterface[]

Возвращает массив сообщений проверки

<?php

use MyApp\Models\Invoices;

$invoice = new Invoices();

$invoice->inv_cst_id      = 1;
$invoice->inv_status_flag = 1;
$invoice->inv_title       = 'Invoice for ACME Inc.';
$invoice->inv_total       = 100;
$invoice->inv_created_at  = '2019-12-25 01:02:03';

$result = $invoice->save();

if (false === $result) {
    
    echo 'Error saving Invoice: ';

    $messages = $invoice->getMessages();

    foreach ($messages as $message) {
        echo $message . PHP_EOL;
    }
} else {

    echo 'Record Saved';

}

ПРИМЕЧАНИЕ: save() больше не принимает параметры для установки данных. Вместо этого можно использовать assign .

public function getModelsManager(): ManagerInterface

Возвращает диспетчер моделей, связанный с экземпляром сущности

public function getModelsMetaData(): MetaDataInterface

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

public function getOperationMade(): int

Возвращает тип последней операции, выполняемой ORM. Возвращает одну из констант класса OP_*

public function getOldSnapshotData(): array

Возвращает внутренние старые данные моментального снимка

final public function getReadConnection(): AdapterInterface

Получение соединения, используемого для чтения данных для модели

final public function getReadConnectionService(): string

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

public function getRelated(
    string $alias, 
    mixed $arguments = null
): Phalcon\Mvc\Model\Resultset\Simple | null

Возвращает связанные записи, основанные на определенных уравнениях. Если отношение "один к одному" и записи не найдены, возвращается значение null

ПРИМЕЧАНИЕ: getRelated() больше не возвращает значение false, если запись не найдена в связи "один к одному".

<?php

use MyApp\Models\Customers;

$customer = Customers::findFirst('cst_id = 1');
$invoices = $customer->getRelated('invoices');
public function isRelationshipLoaded(
    string $relationshipAlias
): bool

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

<?php

use MyApp\Models\Customers;

$customer = Customers::findFirst('cst_id = 1');
$invoices = $customer->isRelationshipLoaded('invoices'); // false

$invoices = $customer->getRelated('invoices');
$invoices = $customer->isRelationshipLoaded('invoices'); // true
final public function getSchema(): string

Возвращает имя схемы, в которой находится сопоставленная таблица

public function getSnapshotData(): array

Возвращает внутренние данные моментального снимка

final public function getSource(): string

Возвращает имя таблицы, сопоставленной в модели

public function getUpdatedFields(): array

Возвращает список обновленных значений.

<?php

use MyApp\Models\Invoices;

$invoice = Invoices::findFirst();

print_r(
    $invoice->getChangedFields()
); 
// []

$invoice->inv_total = 120;;

$invoice->getChangedFields();

print_r(
    $invoice->getChangedFields()
);
// ['inv_total']

$invoice->save();

print_r(
    $invoice->getChangedFields()
);
// []

print_r(
    $invoice->getUpdatedFields()
);
// ['inv_total']
final public function getWriteConnection(): AdapterInterface

Получение соединения, используемого для записи данных в модель

final public function getWriteConnectionService(): string

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

public function hasChanged(
    string | array $fieldName = null, 
    bool $allFields = false
): bool

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

<?php

use MyApp\Models\Invoices;

$invoice = new Invoices();

$invoice->inv_cst_id      = 1;
$invoice->inv_status_flag = 1;
$invoice->inv_title       = 'Invoice for ACME Inc.';
$invoice->inv_total       = 100;
$invoice->inv_created_at  = '2019-12-25 01:02:03';

$result = $invoice->create();

$invoice->inv_total = 120;

$hasChanged = $invoice->hasChanged('inv_title');
// false
$hasChanged = $invoice->hasChanged(
    [
        'inv_total',
    ]
);
// true
$hasChanged = $invoice->hasChanged(
    [
        'inv_title', 
        'inv_total'
    ], 
    true
);
// false
public function hasSnapshotData(): bool

Проверка наличия у объекта внутренних данных моментального снимка

public function hasUpdated(
    string | array $fieldName = null, 
    bool $allFields = false
): bool

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

public function jsonSerialize(): array

Сериализация объекта для json_encode

echo json_encode($invoice);
public static function maximum(
    mixed $parameters = null
): mixed

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

<?php

use MyApp\Models\Invoices;

$id = Invoices::maximum(
    [
        'column' => 'inv_id',
    ]
);

echo 'MAX: ', $id, PHP_EOL;

$max = Invoices::maximum(
    [
        'inv_cst_id = 1',
        'column' => 'inv_total',
    ]
);

echo 'MAX [Customer: 1] ', $max, PHP_EOL;
public static function minimum(
    mixed parameters = null
): mixed 

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

<?php

use MyApp\Models\Invoices;

$id = Invoices::minimum(
    [
        'column' => 'inv_id',
    ]
);

echo 'MIN: ', $id, PHP_EOL;

$max = Invoices::minimum(
    [
        'inv_cst_id = 1',
        'column' => 'inv_total',
    ]
);

echo 'MIN [Customer: 1] ', $max, PHP_EOL;
public static function query(
    DiInterface $container = null
): CriteriaInterface

Создание критериев для конкретной модели

public function readAttribute(
    string $attribute
): mixed | null

Считывает значение атрибута по его имени

echo $invoice->readAttribute('inv_title');
public function refresh(): ModelInterface

Обновление атрибутов модели, повторно запрашивающих запись из базы данных

public function save(): bool

Вставка или обновление экземпляра модели. Возвращает true при успехе или false в противном случае.

<?php

use MyApp\Models\Invoices;

$invoice = new Invoices();

$invoice->inv_cst_id      = 1;
$invoice->inv_status_flag = 1;
$invoice->inv_title       = 'Invoice for ACME Inc.';
$invoice->inv_total       = 100;
$invoice->inv_created_at  = '2019-12-25 01:02:03';

$result = $invoice->save();

$invoice = Invoices::findFirst('inv_id = 100');

$invoice->inv_total = 120;

$invoice->save();

ПРИМЕЧАНИЕ: save() больше не принимает параметры для установки данных. Вместо этого можно использовать assign.

public function serialize(): string

Сериализует объект, игнорируя соединения, службы, связанные объекты или статические свойства

public function unserialize(mixed $data)

Несериализует объект из сериализованной строки

final public function setConnectionService(
    string $connectionService
): void

Задает имя службы подключения DependateInjection

public function setDirtyState(
    int $dirtyState
): ModelInterface | bool

Устанавливает грязное состояние объекта с помощью одной из констант DIRTY_STATE_*

public function setEventsManager(
    EventsManagerInterface $eventsManager
)

Задает имя службы подключения DependateInjection, используемое для чтения данных

public function setOldSnapshotData(
    array $data, 
    array $columnMap = null
)

Устанавливает старые данные моментального снимка записи. Этот метод используется внутри для настройки старых данных моментального снимка, когда модель была настроена на сохранение данных моментального снимка

public function setSnapshotData(
    array $data, 
    array $columnMap = null
): void

Устанавливает данные моментального снимка записи. Этот метод используется внутри для настройки данных моментального снимка, когда модель была настроена на сохранение данных моментального снимка

public function setTransaction(
    TransactionInterface $transaction
): ModelInterface

Задает транзакцию, связанную с экземпляром модели

<?php

use MyApp\Models\Customers;
use MyApp\Models\Invoices;
use Phalcon\Mvc\Model\Transaction\Manager;
use Phalcon\Mvc\Model\Transaction\Failed;

try {
    $txManager   = new Manager();
    $transaction = $txManager->get();
    
    $customer = new Customers();
    $customer->setTransaction($transaction);
    $customer->cst_name_last  = 'Vader';
    $customer->cst_name_first = 'Darth';

    if (false === $customer->save()) {
        $transaction->rollback('Cannot save Customer');
    }

    $invoice = new Invoices();
    $invoice->setTransaction($transaction);

    $invoice->inv_cst_id      = $customer->cst_id;
    $invoice->inv_status_flag = 1;
    $invoice->inv_title       = 'Invoice for ACME Inc.';
    $invoice->inv_total       = 100;
    $invoice->inv_created_at  = '2019-12-25 01:02:03';

    if (false === $invoice->save()) {
        $transaction->rollback('Cannot save record');
    }

    $transaction->commit();
} catch (Failed $ex) {
    echo 'ERROR: ', $ex->getMessage();
}
public static function setup(
    array $options
): void

Включает/отключает параметры в ORM, такие как события, переименование столбцов и т.д.

final public function setWriteConnectionService(
    string $connectionService
): void

Задает имя службы подключения DependateInjection, используемое для записи данных

public function skipOperation(bool $skip): void
```php
Skips the current operation forcing a success state

```php
public static function sum(
    array $parameters = null
): float

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

<?php

use MyApp\Models\Invoices;

$total = Invoices::sum(
    [
        'column' => 'inv_total',
    ]
);

echo 'SUM: ', $total, PHP_EOL;

$total = Invoices::sum(
    [
        'inv_cst_id = 1',
        'column' => 'inv_total',
    ]
);

echo 'SUM [Customer: 1] ', $total, PHP_EOL;
public function toArray(
    array $columns = null
): array

Возвращает экземпляр в виде представления массива. Принимает массив с именами столбцов для включения в результат

<?php

use MyApp\Models\Invoices;

$invoice = Invoices::findFirst('inv_id = 4');

print_r(
    $invoice->toArray()
);

//  [
//      'inv_id'          => 4,
//      'inv_cst_id'      = $customer->cst_id,
//      'inv_status_flag' = 1,
//      'inv_title'       = 'Invoice for ACME Inc.',
//      'inv_total'       = 100,
//      'inv_created_at'  = '2019-12-25 01:02:03',
//  ]

print_r(
    $invoice->toArray(
        [
            'inv_status_flag',
            'inv_title',
            'inv_total',
        ]
    )
);

//  [
//      'inv_status_flag' = 1,
//      'inv_title'       = 'Invoice for ACME Inc.',
//      'inv_total'       = 100,
//  ]
public function update(): bool

Обновление экземпляра модели. Если экземпляр не существует в сохраняемости, будет создано исключение. Возврат true при успехе или false в противном случае.

<?php

use MyApp\Models\Invoices;

$invoice = Invoices::findFirst('inv_id = 4');

$invoice->inv_total = 120;

$invoice->update();
public function writeAttribute(
    string $attribute, 
    mixed $value
): void

Записывает значение атрибута по его имени

$invoice->writeAttribute('inv_total', 120);
protected function allowEmptyStringValues(
    array $attributes
): void

Задает список атрибутов, которые должны быть пропущены из созданной инструкции UPDATE

<?php 

use Phalcon\Mvc\Model;

class Invoices extends Model
{
    public function initialize()
    {
        $this->allowEmptyStringValues(
            [
                'inv_created_at',
            ]
        );
    }
}
protected function belongsTo(
    string | array $fields, 
    string $referenceModel, 
    string | array $referencedFields, 
    array options = null
): Relation

Настройка обратного отношения 1-1 или n-1 между двумя моделями

<?php 

namespace MyApp\Models;

use Phalcon\Mvc\Model;

class InvoicesXProducts extends Model
{
    public function initialize()
    {
        $this->belongsTo(
            'ixp_inv_id',
            Invoices::class,
            'inv_id'
        );
    }
}
protected function hasMany(
    string | array $fields, 
    string $referenceModel, 
    string | array $referencedFields, 
    array options = null
): Relation

Настройка отношения 1-n между двумя моделями

<?php 

namespace MyApp\Models;

use Phalcon\Mvc\Model;

class Customers extends Model
{
    public function initialize()
    {
        $this->hasMany(
            'cst_id',
            Invoices::class,
            'inv_cst_id'
        );
    }
}
protected function hasManyToMany(
    string | array $fields,
    string $intermediateModel, 
    string | array $intermediateFields,
    string | array $intermediateReferencedFields,
    string $referenceModel, 
    string | array $referencedFields,
    array $options = null
): Relation

Настройка отношения n-n между двумя моделями через промежуточное отношение

<?php 

namespace MyApp\Models;

use Phalcon\Mvc\Model;

class Invoices extends Model
{
    public function initialize()
    {
        $this->hasManyToMany(
            'inv_id',
            InvoicesXProducts::class,
            'ixp_inv_id',
            'ixp_prd_id',
            Products::class,
            'prd_id'
        );
    }
}
protected function hasOne(
    string | array $fields, 
    string $referenceModel, 
    string | array $referencedFields, 
    array options = null
): Relation

Настройка отношения 1-1 между двумя моделями

<?php 

namespace MyApp\Models;

use Phalcon\Mvc\Model;

class Invoices extends Model
{
    public function initialize()
    {
        $this->hasOne(
            'inv_cst_id',
            Customers::class,
            'cst_id'
        );
    }
}
protected function keepSnapshots(
    bool $keepSnapshot
): void

Устанавливает, должна ли модель сохранять исходный снимок записи в памяти

<?php

namespace MyApp\Models;

use Phalcon\Mvc\Model;

class Invoices extends Model
{
    public function initialize()
    {
        $this->keepSnapshots(true);
    }
}
final protected function setSchema(
    string $schema
): ModelInterface

Задает имя схемы, в которой находится сопоставленная таблица

final protected function setSource(
    string $source
): ModelInterface

Задает имя таблицы, с которой должна быть сопоставлена модель

protected function skipAttributes(array $attributes)

Задает список атрибутов, которые должны быть пропущены из созданной инструкции INSERT/UPDATE

<?php

namespace MyApp\Models;

use Phalcon\Mvc\Model;

class Invoices extends Model
{
    public function initialize()
    {
        $this->skipAttributes(
            [
                'inv_created_at',
            ]
        );
    }
}
protected function skipAttributesOnCreate(
    array $attributes
): void

Задает список атрибутов, которые должны быть пропущены из созданной инструкции INSERT

<?php

namespace MyApp\Models;

use Phalcon\Mvc\Model;

class Invoices extends Model
{
    public function initialize()
    {
        $this->skipAttributesOnCreate(
            [
                'inv_created_at',
            ]
        );
    }
}
protected function skipAttributesOnUpdate(
    array $attributes
): void

Задает список атрибутов, которые должны быть пропущены из созданной инструкции UPDATE

<?php

namespace MyApp\Models;

use Phalcon\Mvc\Model;

class Invoices extends Model
{
    public function initialize()
    {
        $this->skipAttributesOnUpdate(
            [
                'inv_modified_at',
            ]
        );
    }
}
protected function useDynamicUpdate(
    bool dynamicUpdate
): void

Задает, должна ли модель использовать динамическое обновление вместо обновления всех полей

<?php

namespace MyApp\Models;

use Phalcon\Mvc\Model;

class Invoices extends Model
{
    public function initialize()
    {
        $this->useDynamicUpdate(true);
    }
}
protected function validate(
    ValidationInterface $validator
): bool

Выполняет средства проверки при каждом вызове проверки

<?php

namespace MyApp\Models;

use Phalcon\Mvc\Model;
use Phalcon\Validation;
use Phalcon\Validation\Validator\ExclusionIn;

class Invoices extends Model
{
    public function validation()
    {
        $validator = new Validation();

        $validator->add(
            'inv_status_flag',
            new ExclusionIn(
                [
                    'domain' => [
                        0,
                        1,
                    ],
                ]
            )
        );

        return $this->validate($validator);
    }
}
public function validationHasFailed(): bool

Проверка того, что процесс проверки создал какие-либо сообщения

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

Модель-это класс, который расширяется от Phalcon\Mvc\Model. Его имя класса должно быть в случае верблюда нотация:

<?php

namespace MyApp\Models;

use Phalcon\Mvc\Model;

class Invoices extends Model
{

}

По умолчанию модель MyApp\Models\Invoices будет сопоставлена с таблицей invoices. Если вы хотите вручную указать другое имя для сопоставленной таблицы, вы можете использовать методsetSource():

<?php

namespace MyApp\Models;

use Phalcon\Mvc\Model;

class Invoices extends Model
{
    public function initialize()
    {
        $this->setSource('co_invoices');
    }
}

Теперь модель Invoices сопоставлена с таблицей co_invoices. Метод initialize() помогает настроить эту модель с помощью пользовательского поведения, т.е. другой таблицы.

Метод initialize() вызывается только один раз во время запроса. Этот метод предназначен для выполнения инициализации, которая применяется ко всем экземплярам модели, созданной в приложении. Если требуется выполнить задачи инициализации для каждого созданного экземпляра, можно использовать метод onConstruct():

<?php

namespace MyApp\Models;

use Phalcon\Mvc\Model;

class Invoices extends Model
{
    public function onConstruct()
    {
        // ...
    }
}

Свойства против сеттеров/геттеров

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

<?php

namespace MyApp\Models;

use Phalcon\Mvc\Model;

class Invoices extends Model
{
    public $inv_id;
    public $inv_cst_id;
    public $inv_status_flag;
    public $inv_title;
    public $inv_total;
    public $inv_created_at;
}

Другая реализация заключается в использовании функций getter и setter, которые управляют тем, какие свойства являются общедоступными для этой модели.

Преимущество использования геттеров и сеттеров заключается в том, что разработчик может выполнять преобразования и проверки достоверности значений, заданных или извлеченных для модели, что невозможно при использовании общих свойств.

Кроме того, геттеры и сеттеры позволяют вносить будущие изменения без изменения интерфейса класса модели. Таким образом, если имя поля изменяется, единственное необходимое изменение будет в частной собственности модели, на которую ссылается соответствующий геттер/сеттер, и нигде больше в коде.

<?php

namespace MyApp\Models;

use InvalidArgumentException;
use Phalcon\Mvc\Model;

class Invoices extends Model
{
    private $inv_id;
    private $inv_cst_id;
    private $inv_status_flag;
    private $inv_title;
    private $inv_total;
    private $inv_created_at;

    public function getId(): int
    {
        return (int) $this->inv_id;
    }

    public function getCustomerId(): int
    {
        return (int) $this->inv_cst_id;
    }
    
    public function getStatus(): int
    {
        return (int) $this->inv_status_flag;
    }
    
    public function getTitle(): string
    {
        return (string) $this->inv_title;
    }
    
    public function getTotal(): float
    {
        return (float) $this->inv_total;
    }
    
    public function getCreatedAt(): string
    {
        return (string) $this->inv_created_at;
    }

    public function setCustomerId(int $customerId): Invoices
    {
        $this->inv_cst_id = $customerId;
        
        return $this;
    }
    
    public function setStatus(int $status): Invoices
    {
        $this->inv_status_flag = $status;
        
        return $this;
    }
    
    public function setTitle(string $title): Invoices
    {
        $this->inv_title = $title;
        
        return $this;
    }
    
    public function setTotal(float $total): Invoices
    {
        if ($total < 0) {
            throw new InvalidArgumentException(
                'Incorrect total'
            );
        }

        $this->inv_total = $total;
        
        return $this;
    }
    
    public function setCreatedAt(string $date): Invoices
    {
        $this->inv_created_at = $date;
    
        return $this;
    }
}

Публичные свойства обеспечивают меньшую сложность в разработке. Однако геттеры / сеттеры могут значительно повысить тестируемость, расширяемость и ремонтопригодность приложений. Вам нужно будет решить, какая стратегия лучше для вас в зависимости от потребностей приложения. ORM совместим с обеими схемами определения свойств.

ПРИМЕЧАНИЕ: подчеркивания в именах свойств могут быть проблематичными при использовании геттеров и сеттеров.

Если вы используете подчеркивания в именах свойств, вы все равно должны использовать Camel case в объявлениях getter/setter для использования с магическими методами. (например, $model->getPropertyName вместо $model->getProperty_name, $model->findByPropertyName вместо $model->findByProperty_name, и т. д.).

ОРМ ожидает регистр имен и знаков подчеркивания, как правило, удалены. Поэтому рекомендуется называть свойства так, как показано в документации. Вы можете использовать карту столбцов (как описано выше), чтобы обеспечить правильное сопоставление ваших свойств с их аналогами в базе данных.

Записи В Объекты

Каждый экземпляр модели представляет собой строку в таблице. Вы можете легко получить доступ к записанным данным, прочитав свойства объекта. Например, для таблицы ' co_customers’ с записями:

mysql> select * from co_customers;
+--------+---------------+----------------+
| cst_id | cst_name_last | cst_name_first |
+--------+---------------+----------------+
|      1 | Vader         | Darth          |
|      2 | Skywalker     | Like           |
|      3 | Skywalker     | Leia           |
+--------+---------------+----------------+
3 rows in set (0.00 sec)

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

<?php

use MyApp\Models\Customers;

// cst_id = 3
$customer = Customers::findFirst(3);

// 'Leia'
echo $customer->cst_name_first;

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

<?php

use MyApp\Models\Customers;

// cst_id = 3
$customer = Customers::findFirst(3);

$customer->cst_name_last = 'Princess';

$customer->save();

Как вы можете видеть, нет необходимости использовать необработанные инструкции SQL. Phalcon\Mvc\Model обеспечивает высокую абстракцию баз данных для веб-приложений, упрощая операции с базами данных.

Поиск Записей

Phalcon\Mvc\Model также предлагает несколько методов для запроса записей.

find

Метод возвращает набор записей Phalcon\Mvc\Model\ResultsetPhalcon\Mvc\Model\Resultset\Complex или Phalcon\Mvc\Model\Resultset\Simple , даже если возвращаемый результат является только одной записью.

Метод принимает различные параметры для извлечения данных:

<?php

use MyApp\Models\Customers;

$invoice = Invoices::findFirst('inv_id = 3');

Вы также можете передать строку с предложением WHERE. В приведенном выше примере мы получаем одну и ту же запись, поручив ОРМ, чтобы дать нам записать с inv_cst_id = 3

Наиболее гибкий синтаксис заключается в передаче массива с различными параметрами:

<?php

use MyApp\Models\Customers;

$invoice = Invoices::findFirst(
    [
        'inv_id = 3',
    ]
);

Первый параметр массива (без ключа) обрабатывается так же, как и приведенный выше пример (передача строки). Массив принимает дополнительные параметры, которые предлагают дополнительные опции для настройки операции поиска.

findFirst

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

<?php

use MyApp\Models\Invoices;

$invoice = Invoices::findFirst();

Вызов findFirst без параметра вернет первую запись, которую найдет ORM. Обычно это первая запись в таблице.

<?php

use MyApp\Models\Invoices;

// cst_id = 3
$invoice = Invoices::findFirst(3);

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

<?php

use MyApp\Models\Invoices;

$invoice = Invoices::findFirst('inv_id = 3');

Вы также можете передать строку с предложением WHERE . В приведенном выше примере мы получаем одну и ту же запись, поручив ОРМ, чтобы дать нам записать с inv_cst_id = 3

Примечание: если первичный ключ таблицы не числовой, используйте условие. Смотрите примеры ниже.

$uuid = '5741bfd7-6870-40b7-adf6-cbacb515b9a9';
$invoice = Invoices::findFirst([
    'uuid = ?0',
    'bind' => [$uuid],
]);

// OR

$uuid = '5741bfd7-6870-40b7-adf6-cbacb515b9a9';
$invoice = Invoices::findFirst([
    'uuid = :primary:',
    'bind' => ['primary' => $uuid],
]);

ПРИМЕЧАНИЕ: Если в ваших условиях не используются связанные параметры, PHQL создает новый план внутри компании, поэтому потребляет больше памяти. Рекомендуется использовать привязанные параметры!

<?php


use MyApp\Models\Invoices;

$invoice = Invoices::findFirst('uuid = "5741bfd7-6870-40b7-adf6-cbacb515b9a9"');
<?php


use MyApp\Models\Invoices;

$invoice = Invoices::findFirst('uuid = "5741bfd7-6870-40b7-adf6-cbacb515b9a9"');

Параметры

ПРИМЕЧАНИЕ: настоятельно рекомендуется использовать синтаксис массива с условиями и привязкой, чтобы защитить себя от SQL-инъекций, особенно когда критерий исходит из пользовательского ввода. Дополнительную информацию смотрите в разделе Параметры привязки.

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

<?php

use MyApp\Models\Invoices;

$invoices = Invoices::find(
    [
        'inv_cst_id = 3',
        'order' => 'inv_total desc'
    ]
);

Вы можете (и должны) использовать элементы массива conditions и bind , которые связывают параметры с параметрами запроса. Использование этой реализации гарантирует, что ваши параметры привязаны и, таким образом, уменьшает возможность SQL-инъекций:

<?php

use MyApp\Models\Invoices;

$invoices = Invoices::find(
    [
        'conditions' => 'inv_cst_id = :cst_id:',
        'bind'       => [
            'cst_id' => 3,
        ],
        'order'      => 'inv_total desc',
    ]
);

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

bind

BIND используется вместе с conditions, заменяя заполнители и экранируя значения, таким образом повышая безопасность

<?php

use MyApp\Models\Invoices;

$invoice = Invoices::findFirst(
    [
        'conditions' => 'inv_id = :inv_id:',
        'bind'       => [
            'inv_id' => 3,
        ],
    ]
);

bindTypes

При привязке параметров этот параметр можно использовать для определения дополнительного приведения к связанным параметрам, что еще больше повышает безопасность запроса.

<?php

use MyApp\Models\Invoices;
use Phalcon\Mvc\Model\Column;

$invoice = Invoices::findFirst(
    [
        'conditions' => 'inv_id = :inv_id:',
        'bind'       => [
            'inv_id' => 3,
        ],
        'bindTypes'  => [
            Column::BIND_PARAM_INT,
        ],
    ]
);

cache

Кэширование результирующего набора, уменьшая непрерывный доступ к реляционной системе.

<?php

use MyApp\Models\Invoices;

$invoices = Invoices::find(
    [
        'conditions' => 'inv_cst_id = :cst_id:',
        'bind'       => [
            'cst_id' => 3,
        ],
        'cache'      => [
            'key'      => 'customer.3',
            'lifetime' => 84600,
        ],
        'order'      => 'inv_total desc',
    ]
);

columns

Возвращает определенные столбцы в модели.

ПРИМЕЧАНИЕ: При использовании этого параметра возвращается неполный объект, поэтому нельзя вызывать такие методы, как update(), getRelated() и т.д.

<?php

use MyApp\Models\Invoices;

$invoices = Invoices::find(
    [
        'columns'    => [
            'inv_id',
            'total' => 'inv_total'
        ],
        'conditions' => 'inv_cst_id = :cst_id:',
        'bind'       => [
            'cst_id' => 3,
        ],
    ]
);
<?php

use MyApp\Models\Invoices;

$invoices = Invoices::find(
    [
        'columns'    => [
            'inv_id',
            'total' => 'inv_total'
        ],
        'conditions' => 'inv_cst_id = :cst_id:',
        'bind'       => [
            'cst_id' => 3,
        ],
    ]
);

Массив столбцов может возвращать столбцы непосредственно, если для одного из элементов массива задано только значение. Однако если указать ключ, он будет использоваться в качестве псевдонима для этого поля. В вышеприведенном примере cst_name_first называется first.

conditions

Условия поиска для операции поиска. Используется для извлечения только тех записей, которые соответствуют указанному критерию. По умолчанию Phalcon\Mvc\Model предполагает, что первым параметром являются условия.

<?php

use MyApp\Models\Invoices;

$invoices = Invoices::find(
    [
        'conditions' => 'inv_cst_id = :cst_id:',
        'bind'       => [
            'cst_id' => 3,
        ],
    ]
);

for_update

С помощью этого параметра Phalcon\Mvc\Model считывает последние доступные данные, устанавливая монопольные блокировки для каждой считываемой строки

<?php

use MyApp\Models\Invoices;

$invoices = Invoices::find(
    [
        'conditions' => 'inv_cst_id = :cst_id:',
        'bind'       => [
            'cst_id' => 3,
        ],
        'for_update' => true,
    ]
);

group

Позволяет собирать данные по нескольким записям и группировать результаты по одному или нескольким столбцам 'group' => 'name, status'

<?php

use MyApp\Models\Invoices;

$invoices = Invoices::find(
    [
        'conditions' => 'inv_cst_id = :cst_id:',
        'bind'       => [
            'cst_id' => 3,
        ],
        'group'      => 'inv_status_flag',
    ]
);

hydration

Задает стратегию гидратации для представления каждой возвращенной записи в результате

<?php

use MyApp\Models\Invoices;
use Phalcon\Mvc\Model\Resultset;

$invoices = Invoices::find(
    [
        'conditions' => 'inv_cst_id = :cst_id:',
        'bind'       => [
            'cst_id' => 3,
        ],
        'hydration' => Resultset::HYDRATE_OBJECTS,
    ]
);

limit

Ограничить результаты запроса определенным диапазоном

<?php

use MyApp\Models\Invoices;

$invoices = Invoices::find(
    [
        'conditions' => 'inv_cst_id = :cst_id:',
        'bind'       => [
            'cst_id' => 3,
        ],
        'limit'      => 10,
    ]
);

offset

Смещение результатов запроса на определенную сумму

<?php

use MyApp\Models\Invoices;

$invoices = Invoices::find(
    [
        'conditions' => 'inv_cst_id = :cst_id:',
        'bind'       => [
            'cst_id' => 3,
        ],
        'limit'      => 10,
        'offset'     => 100,
    ]
);

order

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

<?php

use MyApp\Models\Invoices;

$invoices = Invoices::find(
    [
        'conditions' => 'inv_cst_id = :cst_id:',
        'bind'       => [
            'cst_id' => 3,
        ],
        'order'      => 'inv_status_flag, inv_total desc',
    ]
);

shared_lock

С помощью этого параметра Phalcon\Mvc\Model считывает последние доступные данные, устанавливая общие блокировки для каждой считываемой строки

<?php

use MyApp\Models\Invoices;

$invoices = Invoices::find(
    [
        'conditions'  => 'inv_cst_id = :cst_id:',
        'bind'        => [
            'cst_id' => 3,
        ],
        'shared_lock' => true,
    ]
);

query

При желании также доступен способ создания запросов объектно-ориентированным способом, вместо использования массива параметров:

<?php

use MyApp\Models\Invoices;

$invoices = Invoices::query()
    ->where('inv_cst_id = :cst_id:')
    ->andWhere('inv_total > :total:')
    ->bind(
        [
            'cst_id' => 3,
            'total'  => 1000,
        ]
    )
    ->orderBy('inv_status_flag, inv_total desc')
    ->execute()
;

Статический метод query() возвращает объект Phalcon\Mvc\Model\Criteria, дружественный к IDE auto complete.

Все запросы внутренне обрабатываются как запросы PHQL. PHQL-это высокоуровневый, объектно-ориентированный и SQL-подобный язык. Этот язык предлагает больше возможностей для выполнения запросов, таких как объединение других моделей, групповые записи, агрегации и т. д.

findBy*

Можно использовать метод findBy<property-name>(). Этот метод расширяется на вышеупомянутом методе find(). Он позволяет быстро выполнить выбор записей из таблицы, используя имя свойства в самом методе и передавая ему параметр, содержащий данные, которые нужно искать в этом столбце.

Для следующей модели:

<?php

namespace MyApp\Models;

use Phalcon\Mvc\Model;

class Invoices extends Model
{
    public $inv_cst_id;
    public $inv_id;
    public $inv_status_flag;
    public $inv_title;
    public $inv_created_at;
}

У нас есть свойства inv_cst_id, inv_id, inv_status_flag, inv_title, inv_created_at. Если мы хотим найти все счета с inv_total = 100 , мы можем использовать:

 

<?php

use MyApp\Models\Invoices;

$invoices = Invoices::find(
    [
        'conditions'  => 'inv_total = :total:',
        'bind'        => [
            'total' => 100,
        ],
    ]
);

но мы также можем использовать:

<?php

use MyApp\Models\Invoices;

$invoices = Invoices::findByInvTotal(100);

ПРИМЕЧАНИЕ: имена свойств изменяются на camel case, если они имеют подчеркивания. inv_total становится InvTotal

Вы также можете передавать параметры в массиве в качестве второго параметра. Эти параметры совпадают с теми, которые вы можете передать в методе find.

<?php

use MyApp\Models\Invoices;

$invoices = Invoices::findByInvTotal(
    100,
    [
        'order' => `inv_cst_id, inv_created_at`
    ]
);

findFirstBy*

Наконец, можно использовать метод findFirstBy<property-name>() . Этот метод расширяется на вышеупомянутом методе findFirst() . Он позволяет быстро выполнить выбор из таблицы, используя имя свойства в самом методе и передавая ему параметр, содержащий данные, которые нужно искать в этом столбце.

Для следующей модели:

<?php

namespace MyApp\Models;

use Phalcon\Mvc\Model;

class Guestbook extends Model
{
    public $id;
    public $email;
    public $name;
    public $text;
}

У нас есть свойства id, email, name и text. Если мы хотим найти запись гостевой книги для Darth Vader , мы можем:

<?php

use MyApp\Models\Guestbook;

$guest = Guestbook::findFirst(
    [
        'conditions'  => 'name = :name:',
        'bind'        => [
            'name' => 'Darth Vader',
        ],
    ]
);

Но мы также можем использовать:

<?php

use MyApp\Models\Guestbook;

$name  = 'Darth Vader';
$guest = Guestbook::findFirstByName($name);

ПРИМЕЧАНИЕ: Обратите внимание, что мы использовали Name в вызове метода и передали ему переменную $name содержащую имя, которое мы ищем в нашей таблице. Обратите внимание, что при поиске соответствия с нашим запросом все остальные свойства также доступны нам.

Модель Результирующих Наборов

В то время как findFirst() возвращает непосредственно экземпляр вызываемого класса (когда есть данные, которые должны быть возвращены), метод find() возвращает Phalcon\Mvc\Model\Resultset\Simple. Это объект, который инкапсулирует все функциональные возможности результирующего набора, такие как поиск, обход, подсчет и т. д.

Эти объекты более мощные, чем стандартные массивы. Одна из самых больших особенностей Phalcon\Mvc\Model\Resultset заключается в том, что в любой момент времени в памяти остается только одна запись. Это очень помогает в управлении памятью, особенно при работе с большими объемами данных.

Некоторые примеры обхода результирующих наборов:

<?php

use MyApp\Models\Invoices;

$invoices = Invoices::find();

// инструкция foreach
foreach ($invoices as $invoice) {
    echo $invoice->inv_title, PHP_EOL;
}

// пока
$invoices->rewind();
while ($invoices->valid()) {
    $invoice = $invoices->current();

    echo $invoice->inv_title, PHP_EOL;

    $invoices->next();
}

// считать
echo count($invoices);
echo $invoices->count();

// искать
$invoices->seek(2);
$invoice = $invoices->current();

// массив
$invoice = $invoices[5];

// array - isset
if (true === isset($invoices[3])) {
   $invoice = $invoices[3];
}

// Первый
$invoice = $invoices->getFirst();

// Последний
$invoice = $invoices->getLast();

Результирующие наборы Phalcon эмулируют прокручиваемые курсоры. Вы можете получить любую строку, просто обратившись к ее позиции или отыскав внутренний указатель на определенную позицию.

ПРИМЕЧАНИЕ: некоторые системы баз данных не поддерживают прокручиваемые курсоры. Это заставляет Phalcon повторно выполнить запрос, чтобы перемотать курсор в начало и получить запись в требуемой позиции. Аналогично, если результирующий набор был пройден несколько раз, запрос должен быть выполнен столько же раз.

Сохранение больших результатов запросов в памяти потребит много ресурсов. Однако можно указать Phalcon извлекать данные в порциях строк, тем самым уменьшая необходимость повторного выполнения запроса во многих случаях. Для этого можно задать значение настройки orm.resultset_prefetch_records. Это можно сделать либо в php.ini или в методе модели setup(). Более подробную информацию об этом можно найти в разделе функции.

Обратите внимание, что результирующие наборы могут быть сериализованы и сохранены в бэкэнде кэша. Phalcon\Cache может помочь с этой задачей. Однако сериализация данных приводит к тому, что Phalcon\Mvc\Model извлекает все данные из базы данных в массиве, что потребляет больше памяти во время этого процесса.

<?php

use MyApp\Models\Invoices;

$invoices = Invoices::find();

file_put_contents(
    'invoices.cache',
    serialize($invoices)
);

$invoices = unserialize(
    file_get_contents('invoices.cache')
);

foreach ($invoices as $invoice) {
    echo $invoice->inv_title;
}

Пользовательские Результирующие Наборы

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

При использовании пользовательских наборов результатов это больше не требуется. Пользовательский результирующий набор инкапсулирует функциональные возможности, которые в противном случае были бы в модели, и может быть повторно использован другими моделями, таким образом сохраняя код DRY. Таким образом, метод find() больше не возвращает значение по умолчанию Phalcon\Mvc\Model\Resultset, а настраивается. Phalcon позволяет сделать это с помощью getResultsetClass() в модели.

Сначала необходимо определить класс результирующего набора:

<?php

namespace MyApp\Mvc\Model\Resultset;

use \Phalcon\Mvc\Model\Resultset\Simple;

class Custom extends Simple
{
    public function calculate() {
        // ....
    }
}

В модели мы задаем класс в getResultsetClass() следующим образом:

<?php

namespace Phalcon\Test\Models\Statistics;

use MyApp\Mvc\Model\Resultset\Custom;
use Phalcon\Mvc\Model;

class Invoices extends Model
{
    public function initialize()
    {
        $this->setSource('co_invoices');
    }
    
    public function getResultsetClass()
    {
        return Custom::class;
    }
}

и, наконец, в вашем коде будет что-то вроде этого:

<?php

use MyApp\Models\Invoices;

$invoices = Invoices::find(
    [
        'conditions'  => 'inv_cst_id = :cst_id:',
        'bind'        => [
            'cst_id' => 3,
        ],
    ]
);

$calculated = $invoices->calculate();

Фильтрация Результирующих Наборов

Наиболее эффективным способом фильтрации данных является установка некоторых критериев поиска, базы данных будут использовать индексы, установленные на таблицах, чтобы быстрее возвращать данные. Phalcon дополнительно позволяет фильтровать данные с помощью PHP:

<?php

$invoices = Invoices::find();

$invoices = $invoices->filter(
    function ($invoice) {
        if (1 === $invoice->inv_status_flag) {
            return $invoice;
        }
    }
);

Приведенный выше пример возвращает только оплаченные счета из нашей таблицы (inv_status_flag = 1);

Обязательные Параметры

вязанные параметры также поддерживаются в Phalcon\Mvc\Model. Рекомендуется использовать эту методологию, чтобы исключить возможность того, что ваш код будет подвержен атакам SQL-инъекций. Поддерживаются как строковые, так и целочисленные заполнители.

Примечание:при использовании целочисленных заполнителей вы должны префиксировать их ? (?0, ?1). При использовании заполнителей строк необходимо заключить строку в : (:name:, :total:).

Примеры:

<?php

use MyApp\Models\Invoices;

$invoices = Invoices::find(
    [
        'conditions'  => 'inv_title LIKE :title: AND ' .
                         'inv_total > :total:',
        'bind'        => [
            'title' => '%ACME%',
            'total' => 1000,
        ],
    ]
);

$invoices = Invoices::find(
    [
        'conditions'  => 'inv_title LIKE ?0 = ?0 AND ' .
                         'inv_total > ?1',
        'bind'        => [
            0 => '%ACME%',
            1 => 1000,
        ],
    ]
);

$invoices = Invoices::find(
    [
        'conditions'  => 'inv_title = ?0 AND ' .
                         'inv_total > :total:',
        'bind'        => [
            0       => '%ACME%',
            'total' => 1000,
        ],
    ]
);

троки автоматически экранируются с помощью PDO. Эта функция учитывает кодировку соединения, поэтому рекомендуется определить правильную кодировку в параметрах соединения или в конфигурации базы данных, так как неправильная кодировка будет производить нежелательные эффекты при хранении или извлечении данных.

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

<?php

use MyApp\Models\Invoices;
use Phalcon\Db\Column;

$parameters = [
    'title' => '%ACME%',
    'total' => 1000,
];

$types = [
    'title' => Column::BIND_PARAM_STR,
    'total' => Column::BIND_PARAM_INT,
];

$invoices = Invoices::find(
    [
        'conditions'  => 'inv_title LIKE :title: AND ' .
                         'inv_total > :total:',
        'bind'        => $parameters,
        'bindTypes'   => $types,
    ]
);

ПРИМЕЧАНИЕ: Поскольку типом привязки по умолчанию является Phalcon\Db\Column::BIND_PARAM_STR, нет необходимости указывать параметр bindTypes, если все столбцы являются строками

Можно также связать массивы в параметрах, особенно при использовании ключевого слова IN SQL.

ПРИМЕЧАНИЕ: Вы должны использовать нулевой массив для массивов без пропущенных элементов

<?php

use MyApp\Models\Invoices;

$customerIds = [1, 3, 4]; // $array: [[0] => 1, [1] => 2, [2] => 4]

$invoices = Invoices::find(
    [
        'conditions'  => 'inv_cst_id IN ({customerId:array})',
        'bind'        => [
            'customerId' => $customerIds,
        ],
    ]
);

unset($customerIds[1]);  // $array: [[0] => 1, [2] => 4]

$customerIds = array_values($customerIds);  // $array: [[0] => 1, [1] => 4]

$invoices = Invoices::find(
    [
        'conditions'  => 'inv_cst_id IN ({customerId:array})',
        'bind'        => [
            'customerId' => $customerIds,
        ],
    ]
);

 

Примечание: Связанные параметры доступны для всех методов запроса, таких как find() и findFirst() , а также для таких методов вычисления, как count(), sum(), average() и т.д.

Если вы используете finders , например  find(), findFirst(), и т. д., вы можете ввести связанные параметры при использовании синтаксиса строки для первого параметра вместо использования элемента массива условий conditions . Также при использовании findFirstBy* параметры привязываются автоматически.

<?php

use MyApp\Models\Invoices;

$invoices = Invoices::find(
    'inv_total > ?0',
    'bind'        => [
        1000,
    ]
);

$invoices = Invoices::findByInvTotal(1000);

До/После Извлечения

Есть случаи, когда нам нужно манипулировать данными после того, как они были извлечены из базы данных, чтобы наша модель содержала то, что нам нужно на прикладном уровне. Как видно из документа события, модели действуют как слушатели, поэтому мы можем реализовать некоторые события в качестве методов в модели.

Такие методы включают в себя beforeSave, afterSave и afterFetch , как показано в нашем примере ниже. Метод afterFetch будет выполняться сразу после заполнения модели данными из базы данных. Мы можем использовать этот метод для изменения или преобразования данных в модели.

<?php

namespace MyApp\Models;

use Phalcon\Mvc\Model;

class Invoices extends Model
{
    public $inv_id;
    public $inv_cst_id;
    public $inv_status_flag;
    public $inv_total;
    public $status;

    public function beforeSave()
    {
        $this->status = join(',', $this->status);
    }

    public function afterFetch()
    {
        $this->status = explode(',', $this->status);
    }
    
    public function afterSave()
    {
        $this->status = explode(',', $this->status);
    }
}

In the example above, we get a comma-separated string from the database and explode it to an array so that it can be used from our application. After that, you can add or remove elements in the array; before the model saves it, implode will be called to store the array as a string in the database.

Если вы используете getters / setters вместо / или вместе с public properties, вы можете инициализировать поле, как только оно будет доступно:

<?php

namespace MyApp\Models;

use Phalcon\Mvc\Model;

class Invoices extends Model
{
    public $inv_id;
    public $inv_cst_id;
    public $inv_status_flag;
    public $inv_total;
    public $status;

    public function getStatus()
    {
        return explode(',', $this->status);
    }
}

Вычисления

Вычисления (или агрегации) являются помощниками для часто используемых функций систем базы данных, таких как COUNT, SUM, MAX, MIN или AVGPhalcon\Mvc\Model позволяет использовать эти функции непосредственно из открытых методов.

COUNT

<?php

$rowcount = Invoices::count();

// inv_cst_id = 3
$rowcount = Invoices::count(
    [
        'inv_cst_id = ?0',
        'bind'        => [
            3,
        ],
    ]
);

Для группирования результатов также можно использовать параметр group . Результаты подсчета отображаются в свойстве rowcount каждого объекта в возвращенной коллекции.

<?php

$group = Invoices::count(
    [
        'group' => 'inv_cst_id',
    ]
);
foreach ($group as $row) {
   echo 'Count: ', $row->rowcount, ' - Customer: ', $row->inv_cst_id;
}

$group = Invoices::count(
    [
        'group' => 'inv_cst_id',
        'order' => 'rowcount',
    ]
);

SUM

<?php

$total = Invoices::sum(
    [
        'column' => 'inv_total',
    ]
);

$total = Invoices::sum(
    [
        'column'     => 'total',
        'conditions' => 'inv_cst_id = ?0',
        'bind'       => [
            3
        ]
    ]
);

Можно также сгруппировать результаты. Результаты подсчета отображаются в свойстве sumatory каждого объекта в возвращенной коллекции.

<?php

$group = Invoices::sum(
    [
        'column' => 'inv_total',
        'group'  => 'inv_cst_id',
    ]
);

foreach ($group as $row) {
   echo 'Customer: ', $row->inv_cst_id, ' - Total: ', $row->sumatory;
}

$group = Invoices::sum(
    [
        'column' => 'inv_total',
        'group'  => 'inv_cst_id',
        'order'  => 'sumatory DESC',
    ]
);

AVERAGE

<?php

$average = Invoices::average(
    [
        'column' => 'inv_total',
    ]
);

$average = Invoices::average(
    [
        'column'     => 'inv_total',
        'conditions' => 'inv_status_flag = ?0',
        'bind'       => [
            0
        ]
    ]
);

MAX - MIN

<?php

$max = Invoices::maximum(
    [
        'column' => 'inv_total',
    ]
);

$max = Invoices::maximum(
    [
        'column'     => 'inv_total',
        'conditions' => 'inv_status_flag = ?0',
        'bind'       => [
            0
        ],
    ]
);

$min = Invoices::minimum(
    [
        'column' => 'inv_total',
    ]
);

$min = Invoices::minimum(
    [
        'column'     => 'inv_total',
        'conditions' => 'inv_status_flag = ?0',
        'bind'       => [
            0
        ],
    ]
);

Создание-Обновление

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

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

<?php

use MyApp\Models\Invoices;

$invoice = new Invoices();

$invoice->inv_cst_id      = 1;
$invoice->inv_status_flag = 1;
$invoice->inv_title       = 'Invoice for ACME Inc.';
$invoice->inv_total       = 100;
$invoice->inv_created_at  = '2019-12-25 01:02:03';

$result = $invoice->save();

if (false === $result) {
    
    echo 'Error saving Invoice: ';

    $messages = $invoice->getMessages();

    foreach ($messages as $message) {
        echo $message . PHP_EOL;
    }
} else {

    echo 'Record Saved';
}

Вы также можете использовать метод assign() и передать массив элементов field => value , чтобы избежать назначения каждого столбца вручную. Phalcon\Mvc\Model будет проверять, есть ли задатчики, реализованные для столбцов, передаваемых в массив, отдавая им приоритет, вместо того, чтобы назначать непосредственно значения атрибутов:

<?php

use MyApp\Models\Invoices;

$invoice = new Invoices();

$invoice->assign(
    [
        'inv_cst_id'      => 1,
        'inv_status_flag' => 1,
        'inv_title'       => 'Invoice for ACME Inc.',
        'inv_total'       => 100,
        'inv_created_at'  => '2019-12-25 01:02:03',
    ]
);

$result = $invoice->save();

Значения, присвоенные непосредственно или через массив атрибутов, экранируются / очищаются в соответствии с типом данных соответствующего атрибута. Таким образом, вы можете передать небезопасный массив, не беспокоясь о возможных SQL-инъекциях:

<?php

use MyApp\Models\Invoices;

$invoice = new Invoices();

$invoice->assign($_POST);

$result = $invoice->save();

ПРИМЕЧАНИЕ: Без мер предосторожности массовое назначение может позволить злоумышленникам установить значение любого столбца базы данных. Используйте эту функцию, только если требуется разрешить пользователю вставлять/обновлять каждый столбец в модели, даже если эти поля не находятся в отправленной форме.

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

<?php

use MyApp\Models\Invoices;

$invoice = new Invoices();

$invoice->assign(
    $_POST,
    [
        'inv_cst_id',
        'inv_status_flag',
        'inv_title',
        'inv_total',
    ]
);

$result = $invoice->save();

ПРИМЕЧАНИЕ: в действительно загруженных приложениях вы можете использовать create или update для соответствующих операций. Используя эти два метода вместо save, мы гарантируем, что данные будут сохранены или нет в базе данных, так как они вызывают исключения На create , если запись уже существует, и на update , если запись не существует.

<?php

use MyApp\Models\Invoices;

$invoice = new Invoices();

$invoice->inv_id          = 1234;
$invoice->inv_cst_id      = 1;
$invoice->inv_status_flag = 1;
$invoice->inv_title       = 'Invoice for ACME Inc.';
$invoice->inv_total       = 100;
$invoice->inv_created_at  = '2019-12-25 01:02:03';

$result = $invoice->update();

if (false === $result) {
    
    echo 'Error saving Invoice: ';

    $messages = $invoice->getMessages();

    foreach ($messages as $message) {
        echo $message . PHP_EOL;
    }
} else {

    echo 'Record Updated';

}

Методы create и update также принимают массив значений в качестве параметра.

Удаление

Метод delete() позволяет удалить запись. Он возвращает логическое значение, означающее успех или неудачу

<?php

use MyApp\Models\Invoices;

$invoice = Invoices::findFirst(
    [
        'conditions' => 'inv_id = :id:',
        'bind'       => [
            'id' => 4,
        ]
    ]
);

if (false !== $invoice) {
    if (false === $invoice->delete()) {
        $messages = $invoice->getMessages();
    
        foreach ($messages as $message) {
            echo $message . PHP_EOL;
        }
    } else {

        echo 'Record Deleted';
    }
}

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

<?php

use MyApp\Models\Invoices;

$invoice = Invoices::find(
    [
        'conditions' => 'inv_cst_id = :id:',
        'bind'       => [
            'id' => 3,
        ]
    ]
);

foreach ($invoices as $invoice) {
    if (false === $invoice->delete()) {
        $messages = $invoice->getMessages();
    
        foreach ($messages as $message) {
            echo $message . PHP_EOL;
        }
    } else {

        echo 'Record Deleted';
    }
}

ПРИМЕЧАНИЕ: смотрите раздел транзакции о том, как можно удалить все записи в цикле с помощью одной операции

Режимы Гидратации

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

Однако бывают случаи, когда вам нужно будет получить данные в режиме только для чтения, например, в случае простого просмотра данных. В этих случаях полезно изменить способ возврата записей, чтобы сэкономить ресурсы и повысить производительность. Стратегия, используемая для представления этих объектов, возвращаемых в результирующем наборе, называется гидратацией  hydration.

Phalcon предлагает три способа гидратации данных:

  • Массивы: Phalcon\Mvc\Model\Resultset::HYDRATE_ARRAYS
  • Объекты: Phalcon\Mvc\Model\Resultset::HYDRATE_OBJECTS
  • Записи: Phalcon\Mvc\Model\Resultset::HYDRATE_RECORDS

Режим гидратации по умолчанию-возвращает записи (HYDRATE_RECORDS). Мы можем легко изменить режим гидратации, чтобы получить массивы или объекты обратно. Изменение режима гидратации на что-либо другое, кроме HYDRATE_RECORDS , вернет обратно объекты (или массивы), которые не имеют связи с базой данных, т. е. мы не сможем выполнять какие-либо операции с этими объектами, такие как save(), create(), delete() и т. д.

<?php

use MyApp\Models\Invoices;
use Phalcon\Mvc\Model\Resultset;

$invoices = Invoices::findFirst(
    [
        'conditions' => 'inv_id = :id:',
        'bind'       => [
            'id' => 4,
        ]
    ]
);

// Array
$invoices->setHydrateMode(
    Resultset::HYDRATE_ARRAYS
);

foreach ($invoices as $invoice) {
    echo $invoice['inv_total'], PHP_EOL;
}

// \stdClass
$invoices->setHydrateMode(
    Resultset::HYDRATE_OBJECTS
);

foreach ($invoices as $invoice) {
    echo $invoice->inv_total, PHP_EOL;
}

// Invoices
$invoices->setHydrateMode(
    Resultset::HYDRATE_RECORDS
);

foreach ($invoices as $invoice) {
    echo $invoice->inv_total, PHP_EOL;
}

Режим гидратации также может быть передан в качестве параметра find, findFirst, findFirstBy* и т. д.:

<?php

use MyApp\Models\Invoices;
use Phalcon\Mvc\Model\Resultset;

$invoices = Invoices::findFirst(
    [
        'hydration'  => Resultset::HYDRATE_ARRAYS,
        'conditions' => 'inv_id = :id:',
        'bind'       => [
            'id' => 4,
        ],
    ]
);

foreach ($invoices as $invoice) {
    echo $invoice['inv_total'], PHP_EOL;
}

Префиксы таблиц

Если все таблицы должны иметь определенный префикс и без задания источника во всех моделях, можно использовать Phalcon\Mvc\Model\Manager и метод setModelPrefix():

<?php

namespace MyApp\Models;

use Phalcon\Mvc\Model\Manager;
use Phalcon\Mvc\Model;

class Invoices extends Model
{

}

$manager = new Manager();

$manager->setModelPrefix('co_');

$invoices = new Invoices(null, null, $manager);

echo $invoices->getSource(); // возвратит co_invoices

Столбцы идентификации

Некоторые модели могут иметь столбцы идентификаторов. Эти столбцы обычно являются первичным ключом сопоставленной таблицы. Phalcon\Mvc\Model может распознать столбец идентификаторов, пропускающий его в сгенерированных инструкциях INSERT SQL, чтобы позволить системе базы данных правильно создать новое значение для этого поля. После создания новой записи поле идентификатора всегда регистрируется со значением, сгенерированным для него в системе базы данных:

<?php

$invoice->save();

echo $invoice->inv_id; // 4

Phalcon\Mvc\Model пытается распознать столбец идентификаторов из каждой таблицы. Однако в зависимости от системы базы данных эти столбцы могут быть последовательными столбцами, например, в случае PostgreSQL илиauto_increment столбцами в случае MySQL.

PostgreSQL использует последовательности для автоматического генерирования числовых значений первичного ключа. Phalcon пытается получить сгенерированное значение из последовательности table_field_seq , например: co_invoices_id_seq. Если имя последовательности отличается, вы всегда можете использовать метод getSequenceName() в модели, указывая Phalcon последовательность, которую он должен использовать для первичного ключа:

<?php

namespace MyApp\Models;

use Phalcon\Mvc\Model;

class Invoices extends Model
{
    public function getSequenceName()
    {
        return 'invoices_sequence_name';
    }
}

Пропуск Колонок

 

В зависимости от того, как вы реализуете бизнес-правила или правила модели в своей базе данных, некоторые поля вполне могут быть проигнорированы в операциях с базой данных. Например, если у нас есть inv_created_date в нашей модели, мы можем поручить системе базы данных ввести в нее текущую метку времени:

CREATE TABLE co_invoices (
    // ...
    inv_created_at datetime DEFAULT CURRENT_TIMESTAMP
)

Приведенный выше код (для MySQL) предписывает СУБД назначить текущую метку времени в поле inv_created_at при создании записи. Поэтому мы можем опустить это поле при создании записи. Точно так же мы можем игнорировать некоторые поля при обновлении записей.

Для решения этой задачи мы можем использовать skipAttributes (для любой операции), skipAttributesOnCreate (создать) или skipAttributesOnUpdate (обновить)

Чтобы сообщить Phalcon\Mvc\Model, что всегда пропускает некоторые поля при создании и / или обновлении записей, чтобы делегировать системе базы данных назначение значений триггером или по умолчанию:

<?php

namespace MyApp\Models;

use Phalcon\Mvc\Model;

class Invoices extends Model
{
    public function initialize()
    {
        $this->skipAttributes(
            [
                'inv_total',
                'inv_created_at',
            ]
        );

        $this->skipAttributesOnCreate(
            [
                'inv_created_at',
            ]
        );

        $this->skipAttributesOnUpdate(
            [
                'inv_modified_at',
            ]
        );
    }
}

Если вы хотите задать значения по умолчанию в свойствах модели (например, inv_created_at) , вы можете использовать Phalcon\Db\RawValue:

<?php

use MyApp\Models\Invoices;
use Phalcon\Db\RawValue;

$invoice = new Invoices();
$invoice->inv_id          = 1234;
$invoice->inv_cst_id      = 1;
$invoice->inv_status_flag = 1;
$invoice->inv_title       = 'Invoice for ACME Inc.';
$invoice->inv_total       = 100;
$invoice->inv_created_at  = new RawValue('default');

$invoice->create();

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

<?php

namespace MyApp\Models;

use Phalcon\Mvc\Model;
use Phalcon\Db\RawValue;

class Invoices extends Model
{
    public function beforeCreate()
    {
        $this->inv_created_at = new RawValue('default');
    }
}

ПРИМЕЧАНИЕ: никогда не используйте Phalcon\Db\RawValue для назначения внешних данных (например, ввода пользователем) или переменных данных. Значение этих полей игнорируется при привязке параметров к запросу. Таким образом, он может быть использован для атак SQL-инъекций.

Динамические обновления

Инструкции SQL UPDATE по умолчанию создаются с каждым столбцом, определенным в модели (полное обновление SQL для всех полей). Можно изменить определенные модели для выполнения динамических обновлений, в этом случае только измененные поля используются для создания последней инструкции SQL.

В некоторых случаях это может повысить производительность за счет уменьшения трафика между приложением и сервером базы данных, особенно когда целевая таблица имеет поля blob/text:

<?php

namespace MyApp\Models;

use Phalcon\Mvc\Model;

class Invoices extends Model
{
    public function initialize()
    {
        $this->useDynamicUpdate(true);
    }
}

Сопоставление столбцов

ORM поддерживает независимую карту столбцов, которая позволяет разработчику использовать различные имена столбцов в модели по сравнению с именами в таблице. Phalcon распознает новые имена столбцов и переименует их соответственно, чтобы они соответствовали соответствующим столбцам в базе данных. Это отличная возможность, когда нужно переименовать поля в базе данных, не беспокоясь обо всех запросах в коде.

<?php

namespace MyApp\Models;

use Phalcon\Mvc\Model;

class Invoices extends Model
{
    public $inv_id;
    public $inv_cst_id;
    public $inv_status_flag;
    public $inv_title;
    public $inv_total;
    public $inv_created_at;

    public function columnMap()
    {
        return [
            'inv_id'          => 'id',
            'inv_cst_id'      => 'customerId',
            'inv_status_flag' => 'status',
            'inv_title'       => 'title',
            'inv_total'       => 'total',
            'inv_created_at'  => 'createdAt',
        ];
    }
}

ПРИМЕЧАНИЕ: В массиве, определенном в карте столбцов, ключи являются фактическими именами полей в базе данных, а значения - виртуальными полями, которые мы можем использовать в вашем коде

Теперь мы можем использовать эти виртуальные поля (или карту столбцов) в вашем коде:

<?php

use MyApp\Models\Invoices;

$invoice = Invoices::findFirst(
    [
        'conditions' => 'inv_id = :id:',
        'bind'       => [
            'id' => 4,
        ]
    ]
);

echo $invoice->customerId, PHP_EOL,
     $invoice->total, PHP_EOL,
     $invoice->createdAt, PHP_EOL;

$invoices = Invoices::find(
    [
        'order' => 'createdAt DESC',
    ]
);

foreach ($invoices as $invoice) {
    echo $invoice->customerId, PHP_EOL,
         $invoice->total, PHP_EOL,
         $invoice->createdAt, PHP_EOL;
}

$invoice = new Invoices();

$invoice->customerId = 1;
$invoice->status     = 1;
$invoice->title      = 'Invoice for ACME Inc.';
$invoice->total      = 100;
$invoice->createdAt  = '2019-12-25 01:02:03';

$invoice->save();

Соображения

При переименовании столбцов учитывайте следующее:

  • Ссылки на атрибуты в отношениях/валидаторах должны использовать виртуальные имена
  • Ссылка на реальные имена столбцов приведет к исключению со стороны ORM

Независимая карта столбцов позволяет вам:

  • Пишите приложения, используя свои собственные соглашения
  • Исключите из кода префиксы/суффиксы поставщиков
  • Изменение имен столбцов без изменения кода приложения

Запись Моментальных Снимков

Определенные модели могут быть настроены для сохранения моментального снимка записи, когда они запрашиваются. Вы можете использовать эту функцию для реализации аудита или просто для того, чтобы узнать, какие поля были изменены в модели по сравнению с данными в базе данных.

<?php

namespace MyApp\Models;

use Phalcon\Mvc\Model;

class Invoices extends Model
{
    public function initialize()
    {
        $this->keepSnapshots(true);
    }
}

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

<?php

use MyApp\Models\Invoices;

$invoice = Invoices::findFirst();

$invoice->inv_total = 120;

var_dump($invoice->getChangedFields()); // ['inv_total']

var_dump($invoice->hasChanged('inv_total')); // true

var_dump($invoice->hasChanged('inv_cst_id')); // false

Моментальные снимки обновляются при создании/обновлении модели. Используя hasUpdated() и getUpdatedFields() может использоваться, чтобы проверить, если поля были обновлены после создания/сохранения/обновления, но это может вызвать проблемы для вашего приложения, если вы выполните getChangedFields() в afterUpdate(), afterSave() или afterCreate().

Вы можете отключить эту функцию с помощью:

<?php

Phalcon\Mvc\Model::setup(
    [
        'updateSnapshotOnSave' => false,
    ]
);

или, если вы предпочитаете установить это в вашем php.ini

phalcon.orm.update_snapshot_on_save = 0

Использование этой функции будет иметь следующий эффект:

<?php

namespace MyApp\Models;

use Phalcon\Mvc\Model;

class Invoices extends Model
{
    public $inv_id;
    public $inv_cst_id;
    public $inv_status_flag;
    public $inv_title;
    public $inv_total;
    public $inv_created_at;

    public function initialize()
    {
        $this->keepSnapshots(true);
    }
}

$invoice = new Invoices();

$invoice->inv_id          = 1234;
$invoice->inv_cst_id      = 1;
$invoice->inv_status_flag = 1;
$invoice->inv_title       = 'Invoice for ACME Inc.';
$invoice->inv_total       = 100;
$invoice->inv_created_at  = '2019-12-25 01:02:03';

$invoice->create();

var_dump(
    $invoice->getChangedFields() // []
);

$invoice->inv_total = 120;

var_dump(
    $invoice->getChangedFields() // ['inv_total']
);

$invoice->update();

var_dump(
    $invoice->getChangedFields() // []
);

getUpdatedFields() будет корректно возвращать обновленные поля или, как упоминалось выше, вы можете вернуться к предыдущему поведению, установив соответствующее значение ini.

События

Как упоминалось ранее, Phalcon\Mvc\Model действует как слушатель событий. Поэтому все события, которые прослушивает модель, могут быть реализованы как методы в самой модели. Вы можете проверить события документа для получения дополнительной информации.

Поддерживаемые события::

  • afterCreate
  • afterDelete
  • afterFetch
  • afterSave
  • afterUpdate
  • afterValidation
  • afterValidationOnCreate
  • afterValidationOnUpdate
  • beforeDelete
  • beforeCreate
  • beforeSave
  • beforeUpdate
  • beforeValidation
  • beforeValidationOnCreate
  • beforeValidationOnUpdate
  • notDeleted
  • notSaved
  • onValidationFails
  • prepareSave
  • validation
<?php

namespace MyApp\Models;

use Phalcon\Mvc\Model;
use Phalcon\Messages\Message as Message;

class Invoices extends Model
{
    public function beforeSave()
    {
        if (0 === $this->inv_status_flag) {
            $message = new Message(
                'Sorry, an invoice cannot be unpaid'
            );

            $this->appendMessage($message);
        }
    }
}

Транзакции

Транзакции необходимы для обеспечения целостности данных, когда нам нужно вставить или обновить данные в нескольких таблицах во время одной и той же операции. Phalcon\Mvc\Model предоставляет метод setTransaction , который позволяет привязать каждую модель к активной транзакции.

<?php

use MyApp\Models\Customers;
use MyApp\Models\Invoices;
use Phalcon\Mvc\Model\Transaction\Manager;
use Phalcon\Mvc\Model\Transaction\Failed;

try {
    $txManager   = new Manager();
    $transaction = $txManager->get();
    
    $customer = new Customers();
    $customer->setTransaction($transaction);
    $customer->cst_name_last  = 'Vader';
    $customer->cst_name_first = 'Darth';

    if (false === $customer->save()) {
        $transaction->rollback('Cannot save Customer');
    }

    $invoice = new Invoices();
    $invoice->setTransaction($transaction);

    $invoice->inv_cst_id      = $customer->cst_id;
    $invoice->inv_status_flag = 1;
    $invoice->inv_title       = 'Invoice for ACME Inc.';
    $invoice->inv_total       = 100;
    $invoice->inv_created_at  = '2019-12-25 01:02:03';

    if (false === $invoice->save()) {
        $transaction->rollback('Cannot save record');
    }

    $transaction->commit();
} catch (Failed $ex) {
    echo 'ERROR: ', $ex->getMessage();
}

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

<?php

use MyApp\Models\Customers;
use MyApp\Models\Invoices;
use Phalcon\Mvc\Model\Transaction\Manager;
use Phalcon\Mvc\Model\Transaction\Failed;

try {
    $txManager   = new Manager();
    $transaction = $txManager->get();
    
    $customer = new Customers();
    $customer->setTransaction($transaction);
    $customer->cst_name_last  = 'Vader';
    $customer->cst_name_first = 'Darth';

    if (false === $customer->save()) {
        $transaction->rollback('Cannot save Customer');
    }
    
    $average = Invoices::average(
        [
            Model::TRANSACTION_INDEX => $transaction,
            'column'     => 'inv_total',
            'conditions' => 'inv_cst_id = :customerId:',
            'bind'       => [
                'customerId' => 3,
            ],
        ]
    );

    $invoice = new Invoices();
    $invoice->setTransaction($transaction);

    $invoice->inv_cst_id      = $customer->cst_id;
    $invoice->inv_status_flag = 1;
    $invoice->inv_title       = 'Invoice for ACME Inc.';
    $invoice->inv_total       = 100 + $average;
    $invoice->inv_created_at  = '2019-12-25 01:02:03';

    if (false === $invoice->save()) {
        $transaction->rollback('Cannot save record');
    }

    $transaction->commit();
} catch (Failed $ex) {
    echo 'ERROR: ', $ex->getMessage();
}

Изменение Схемы

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

<?php

namespace MyApp\Models;

use Phalcon\Mvc\Model;

class Invoices extends Model
{
    public function initialize()
    {
        $this->setSchema('invoices');
    }
}

Несколько баз данных

Модели Phalcon по умолчанию подключаются к тому же подключению к базе данных (службеdb ), которое было определено в контейнере внедрения зависимостей. Однако может потребоваться подключить определенные модели к различным соединениям, которые могут быть соединениями с различными базами данных.

Мы можем определить, какая модель подключается к какой базе данных в методе initialize каждой модели:

<?php

use Phalcon\Di\FactoryDefault;
use Phalcon\Db\Adapter\Pdo\Mysql;
use Phalcon\Db\Adapter\Pdo\PostgreSQL;

$container = new FactoryDefault();

// MySQL
$container->set(
    'dbMysql',
    function () {
        return new Mysql(
            [
                'host'     => 'localhost',
                'username' => 'root',
                'password' => 'secret',
                'dbname'   => 'tutorial',
            ]
        );
    },
    true
);

// PostgreSQL
$container->set(
    'dbPostgres',
    function () {
        return new PostgreSQL(
            [
                'host'     => 'localhost',
                'username' => 'postgres',
                'password' => '',
                'dbname'   => 'tutorial',
            ]
        );
    }
);

и в методе initialize():

<?php

namespace MyApp\Models;

use Phalcon\Mvc\Model;

class Invoices extends Model
{
    public function initialize()
    {
        $this->setConnectionService('dbPostgres');
    }
}

Дополнительная гибкость доступна в отношении подключений к базе данных. Вы можете указать другое соединение для операций чтения и другое для операций записи. Это особенно полезно, когда у вас есть базы данных памяти, которые можно использовать для операций чтения, и другие, более мощные базы данных, которые используются для операций записи.

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

<?php

use Phalcon\Di\FactoryDefault;
use Phalcon\Db\Adapter\Pdo\Mysql;
use Phalcon\Db\Adapter\Pdo\PostgreSQL;

$container = new FactoryDefault();

// MySQL - read
$container->set(
    'mysqlRead',
    function () {
        return new Mysql(
            [
                'host'     => '10.0.4.100',
                'username' => 'root',
                'password' => 'secret',
                'dbname'   => 'tutorial',
            ]
        );
    },
    true
);

// MySQL - write
$container->set(
    'mysqlWrite',
    function () {
        return new Mysql(
            [
                'host'     => '10.0.4.200',
                'username' => 'root',
                'password' => 'secret',
                'dbname'   => 'tutorial',
            ]
        );
    },
    true
);

и в методе initialize():

<?php

namespace MyApp\Models;

use Phalcon\Mvc\Model;

class Invoices extends Model
{
    public function initialize()
    {
        $this->setReadConnectionService('mysqlRead');

        $this->setWriteConnectionService('mysqlWrite');
    }
}

ORM также предоставляет функции горизонтального сегментирования, позволяя реализовать выбор сегментов в соответствии с условиями запроса:

<?php

namespace MyApp\Models;

use Phalcon\Mvc\Model;

class Invoices extends Model
{
    /**
     * Dynamically selects a shard
     *
     * @param array $intermediate
     * @param array $bindParams
     * @param array $bindTypes
     *
     * @return Phalcon\Db\Adapter\AdapterInterface
     */
    public function selectReadConnection(
        $intermediate, 
        $bindParams, 
        $bindTypes
    ) {
        if (true === isset($intermediate['where'])) {
            $conditions = $intermediate['where'];

            if ($conditions['left']['name'] === 'id') {
                $id = $conditions['right']['value'];

                if ($id > 0 && $id < 10000) {
                    return $this->getDI()->get('dbShard1');
                }

                if ($id > 10000) {
                    return $this->getDI()->get('dbShard2');
                }
            }
        }

        return $this->getDI()->get('dbShard0');
    }
}

В приведенном выше примере мы проверяем массив $intermediate , который является массивом, построенным внутренне в Phalcon, предлагая промежуточное представление запроса. Мы проверяем, есть ли у нас какие-либо условия where. Если нет, мы просто используем осколок dbShard0 по умолчанию.

Если условия были определены, мы проверяем, есть ли у нас id в качестве поля в условиях, и получаем его значение. Если id находится между 0 и 100000 , то мы используем dbShard1, альтернативно dbShard2.

Метод selectReadConnection() вызывается каждый раз, когда нам нужно получить данные из базы данных, и возвращает правильное соединение, которое будет использоваться.

Инъекция Зависимостей

Phalcon\Mvc\Model плотно привязан к контейнеру DI. Контейнер можно извлечь с помощью метода getDI. Таким образом, Вы имеете доступ ко всем сервисам, зарегистрированным в контейнере DI.

В следующем примере показано, как можно распечатать любые сообщения, созданные неудачной операцией сохранения в модели, и отобразить эти сообщения в программе flash messenger. Для этого мы используем событие notSaved:

<?php

namespace MyApp\Models;

use Phalcon\Mvc\Model;

class Invoices extends Model
{
    public function notSaved()
    {
        $flash    = $this->getDI()->getFlash();
        $messages = $this->getMessages();

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

Особенности Модели

У ORM есть несколько вариантов, которые управляют определенным поведением глобально. Вы можете включить или отключить эти функции, добавив определенные строки в свой php.ini файл или используйте статический метод setup для модели. Вы можете включить или отключить эти функции временно или навсегда.

phalcon.orm.column_renaming = false
phalcon.orm.events          = false

или с помощью модели:

<?php

use Phalcon\Mvc\Model;

Model::setup(
    [
        'columnRenaming' => false,
        'events'         => false,
    ]
);

Доступные варианты::

ВариантПо умолчаниюОписание
caseInsensitiveColumnMap false Нечувствительность к регистру сопоставление столбцов
castLastInsertIdToInt false Приводит lastInsertId к целому числу
castOnHydrate false Автоматическое приведение к оригинальным типам при гидратации
columnRenaming true Переименование столбцов
disableAssignSetters false Отключить сеттеров
enableImplicitJoins true Включить неявные соединения
events true Обратные вызовы, крючки и уведомления о событиях от всех моделей
exceptionOnFailedMetaDataSave false Создать исключение при неудачном сохранении метаданных
exceptionOnFailedSave false Создать исключение при неудачном save()
ignoreUnknownColumns false Игнорировать неизвестные столбцы в модели
lateStateBinding false Поздняя привязка состояния метода Phalcon\Mvc\Model::cloneResultMap()
notNullValidations true Автоматическая проверка имеющихся столбцов, отличных от null
phqlLiterals true Литералы в синтаксическом анализаторе PHQL
prefetchRecords 0 Количество записей для предварительной выборки при получении данных из ORM
updateSnapshotOnSave true Обновить моментальные снимки при save()
virtualForeignKeys true Виртуальные внешние ключи

ini опции:

; phalcon..orm.ast_cache = null
; phalcon..orm.cache_level = 3
; phalcon..orm.case_insensitive_column_map = false
; phalcon..orm.cast_last_insert_id_to_int = false
; phalcon..orm.cast_on_hydrate = false
; phalcon..orm.column_renaming = true
; phalcon..orm.disable_assign_setters = false
; phalcon..orm.enable_implicit_joins = true
; phalcon..orm.enable_literals = true
; phalcon..orm.events = true
; phalcon..orm.exception_on_failed_metadata_save = true
; phalcon..orm.exception_on_failed_save = false
; phalcon..orm.ignore_unknown_columns = false
; phalcon..orm.late_state_binding = false
; phalcon..orm.not_null_validations = true
; phalcon.orm.parser_cache = null,
; phalcon..orm.resultset_prefetch_records = 0
; phalcon..orm.unique_cache_id = 3
; phalcon..orm.update_snapshot_on_save = true
; phalcon..orm.virtual_foreign_keys = true

ПРИМЕЧАНИЕ Phalcon\Mvc\Model::assign() (который также используется при создании, обновлении/сохранении модели) всегда использует задатчики, если они существуют, когда передаются аргументы данных, даже если это необходимо или необходимо. Это добавит дополнительные накладные расходы в приложение. Вы можете изменить это поведение, добавив phalcon.orm.disable_assign_setters = 1 к вашему ini-файлу, он будет просто использовать $this->property = value.

Отдельный Компонент

Вы можете использовать Phalcon\Mvc\Model самостоятельно, выполнив необходимую настройку самостоятельно, если хотите. Приведенный ниже пример демонстрирует, как этого можно достичь.

<?php

use Phalcon\Di;
use Phalcon\Mvc\Model;
use Phalcon\Mvc\Model\Manager;
use Phalcon\Db\Adapter\Pdo\Sqlite;
use Phalcon\Mvc\Model\Metadata\Memory;

$container = new Di();

$container->set(
    'db',
    new Sqlite(
        [
            'dbname' => 'sample.db',
        ]
    )
);

$container->set(
    'modelsManager',
    new Manager()
);

$container->set(
    'modelsMetadata',
    new Memory()
);


class Invoices extends Model
{

}

echo Invoices::count();