Phalcon 3 - Working with Models (Advanced)

العمل مع النماذج (متقدم)




phalcon

وسائط الترطيب

كما ذكر سابقا ، resultsets هي مجموعات من كائنات كاملة ، وهذا يعني أن كل النتيجة المرتدة هي كائن يمثل صفًا في قاعدة البيانات. هذه الكائنات يمكن تعديلها وحفظها مرة أخرى إلى المثابرة:

use Store\Toys\Robots;

$robots = Robots::find();

// Manipulating a resultset of complete objects
foreach ($robots as $robot) {
    $robot->year = 2000;

    $robot->save();
}

في بعض الأحيان يتم الحصول على السجلات فقط ليتم عرضها على المستخدم في وضع القراءة فقط ، وفي هذه الحالات قد يكون من المفيد تغيير الطريقة التي يتم بها تمثيل السجلات لتسهيل التعامل معها. تسمى الاستراتيجية المستخدمة لتمثيل الكائنات التي يتم إرجاعها في مجموعة من النتائج باسم "وضع الترطيب":

use Phalcon\Mvc\Model\Resultset;
use Store\Toys\Robots;

$robots = Robots::find();

// Return every robot as an array
$robots->setHydrateMode(
    Resultset::HYDRATE_ARRAYS
);

foreach ($robots as $robot) {
    echo $robot["year"], PHP_EOL;
}

// Return every robot as a stdClass
$robots->setHydrateMode(
    Resultset::HYDRATE_OBJECTS
);

foreach ($robots as $robot) {
    echo $robot->year, PHP_EOL;
}

// Return every robot as a Robots instance
$robots->setHydrateMode(
    Resultset::HYDRATE_RECORDS
);

foreach ($robots as $robot) {
    echo $robot->year, PHP_EOL;
}

يمكن أيضًا تمرير وضع الترطيب كمعلمة لـ "find":

use Phalcon\Mvc\Model\Resultset;
use Store\Toys\Robots;

$robots = Robots::find(
    [
        "hydration" => Resultset::HYDRATE_ARRAYS,
    ]
);

foreach ($robots as $robot) {
    echo $robot["year"], PHP_EOL;
}

أعمدة الهوية التي تم إنشاؤها تلقائيًا

قد تحتوي بعض الطرازات على أعمدة هوية. هذه الأعمدة عادة ما تكون المفتاح الأساسي للجدول المعين. Phalcon\Mvc\Model التعرف على عمود الهوية الذي يحذفه في SQL INSERT الذي تم إنشاؤه ، بحيث يمكن لنظام قاعدة البيانات توليد قيمة تم إنشاؤها تلقائيًا لها. دائمًا بعد إنشاء سجل ، سيتم تسجيل حقل الهوية بالقيمة التي تم إنشاؤها في نظام قاعدة البيانات لها:

$robot->save();

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

Phalcon\Mvc\Model قادر على التعرف على عمود الهوية. اعتمادًا على نظام قاعدة البيانات ، قد تكون هذه الأعمدة عبارة عن أعمدة متسلسلة كما في أعمدة PostgreSQL أو auto_increment في حالة MySQL.

تستخدم PostgreSQL تسلسلًا لإنشاء قيم رقمية تلقائية ، بشكل افتراضي ، يحاول Phalcon الحصول على القيمة الناتجة من التسلسل "table_field_seq" ، على سبيل المثال: robots_id_seq ، إذا كان لهذا التسلسل اسم مختلف ، getSequenceName() تنفيذ الأسلوب getSequenceName() :

namespace Store\Toys;

use Phalcon\Mvc\Model;

class Robots extends Model
{
    public function getSequenceName()
    {
        return "robots_sequence_name";
    }
}

تخطي الأعمدة

لإخبار Phalcon\Mvc\Model الذي يحذف دائمًا بعض الحقول في إنشاء و / أو تحديث السجلات من أجل تفويض نظام قاعدة البيانات ، تعيين القيم بواسطة مشغل أو افتراضي:

namespace Store\Toys;

use Phalcon\Mvc\Model;

class Robots extends Model
{
    public function initialize()
    {
        // Skips fields/columns on both INSERT/UPDATE operations
        $this->skipAttributes(
            [
                "year",
                "price",
            ]
        );

        // Skips only when inserting
        $this->skipAttributesOnCreate(
            [
                "created_at",
            ]
        );

        // Skips only when updating
        $this->skipAttributesOnUpdate(
            [
                "modified_in",
            ]
        );
    }
}

سيؤدي هذا إلى تجاهل هذه الحقول على مستوى العالم في كل عملية INSERT / UPDATE على التطبيق بالكامل. إذا كنت ترغب في تجاهل سمات مختلفة على عمليات INSERT / UPDATE مختلفة ، يمكنك تحديد المعلمة الثانية (منطقية) - صحيح للاستبدال. يمكن إجراء قيمة افتراضية بالطريقة التالية:

use Store\Toys\Robots;

use Phalcon\Db\RawValue;

$robot = new Robots();

$robot->name       = "Bender";
$robot->year       = 1999;
$robot->created_at = new RawValue("default");

$robot->create();

يمكن أيضًا استخدام رد اتصال لإنشاء تعيين شرطي للقيم الافتراضية التلقائية:

namespace Store\Toys;

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

class Robots extends Model
{
    public function beforeCreate()
    {
        if ($this->price > 10000) {
            $this->type = new RawValue("default");
        }
    }
}
لا تستخدم Phalcon\Db\RawValue لتعيين بيانات خارجية (مثل إدخال المستخدم) أو بيانات متغيرة. يتم تجاهل قيمة هذه الحقول عند معلمات الربط إلى الاستعلام. لذلك يمكن استخدامه لمهاجمة تطبيق حقن SQL.

التحديث الديناميكي

يتم بشكل افتراضي إنشاء عبارات SQL UPDATE مع كل عمود محدد في الطراز (كامل تحديث SQL كل حقل). يمكنك تغيير نماذج محددة لإجراء تحديثات ديناميكية ، وفي هذه الحالة ، يتم استخدام الحقول التي تم تغييرها لإنشاء عبارة SQL النهائية.

في بعض الحالات ، قد يؤدي هذا إلى تحسين الأداء عن طريق تقليل عدد الزيارات بين التطبيق وخادم قاعدة البيانات ، وهذا يساعد بشكل خاص عندما يكون الجدول يحتوي على حقول blob / text:

namespace Store\Toys;

use Phalcon\Mvc\Model;

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

تعيين العمود المستقل

يدعم ORM خريطة عمود مستقلة ، والتي تسمح للمطور باستخدام أسماء أعمدة مختلفة في النموذج إلى تلك الموجودة في الجدول. سيتعرف Phalcon على أسماء الأعمدة الجديدة وسيعيد تسميتها وفقًا لذلك لتطابق الأعمدة المعنية في قاعدة البيانات. هذه ميزة رائعة عندما يحتاج المرء إلى إعادة تسمية الحقول في قاعدة البيانات دون الحاجة إلى القلق بشأن جميع الاستعلامات في التعليمات البرمجية. سيؤدي تغيير في خريطة الأعمدة في النموذج إلى الاعتناء بالباقي. فمثلا:

namespace Store\Toys;

use Phalcon\Mvc\Model;

class Robots extends Model
{
    public $code;

    public $theName;

    public $theType;

    public $theYear;

    public function columnMap()
    {
        // Keys are the real names in the table and
        // the values their names in the application
        return [
            "id"       => "code",
            "the_name" => "theName",
            "the_type" => "theType",
            "the_year" => "theYear",
        ];
    }
}

بعد ذلك ، يمكنك استخدام الأسماء الجديدة بشكل طبيعي في شفرتك:

use Store\Toys\Robots;

// Find a robot by its name
$robot = Robots::findFirst(
    "theName = 'Voltron'"
);

echo $robot->theName, "\n";

// Get robots ordered by type
$robot = Robots::find(
    [
        "order" => "theType DESC",
    ]
);

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

// Create a robot
$robot = new Robots();

$robot->code    = "10101";
$robot->theName = "Bender";
$robot->theType = "Industrial";
$robot->theYear = 2999;

$robot->save();

ضع في اعتبارك ما يلي عند إعادة تسمية الأعمدة:

  • يجب أن تستخدم المراجع إلى السمات في العلاقات / المدققين الأسماء الجديدة
  • تشير أسماء الأعمدة الحقيقية إلى استثناء من ORM

تسمح لك خريطة الأعمدة المستقلة بما يلي:

  • اكتب التطبيقات باستخدام الاتفاقيات الخاصة بك
  • قم بإزالة البادئات / لاحقات المورّد في التعليمات البرمجية
  • تغيير أسماء الأعمدة دون تغيير رمز التطبيق الخاص بك

تسجيل لقطات

يمكن تعيين نماذج محددة للحفاظ على لقطة قياسية عندما يتم الاستعلام عنها. يمكنك استخدام هذه الميزة لتنفيذ التدوين أو لمجرد معرفة الحقول التي تم تغييرها وفقًا للبيانات التي تم الاستعلام عنها من الثبات:

namespace Store\Toys;

use Phalcon\Mvc\Model;

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

عند تنشيط هذه الميزة ، يستهلك التطبيق ذاكرة أكثر قليلاً لتتبع القيم الأصلية التي تم الحصول عليها من المثابرة. في الموديلات التي تم تنشيط هذه الميزة ، يمكنك التحقق من الحقول التي تم تغييرها:

use Store\Toys\Robots;

// Get a record from the database
$robot = Robots::findFirst();

// Change a column
$robot->name = "Other name";

var_dump($robot->getChangedFields()); // ["name"]

var_dump($robot->hasChanged("name")); // true

var_dump($robot->hasChanged("type")); // false

مشيرا إلى مخطط مختلف

إذا تم تعيين نموذج لجدول موجود في مخططات / قواعد بيانات مختلفة عن الافتراضي. يمكنك استخدام الطريقة setSchema() لتعريف ما يلي:

namespace Store\Toys;

use Phalcon\Mvc\Model;

class Robots extends Model
{
    public function initialize()
    {
        $this->setSchema("toys");
    }
}

وضع قواعد بيانات متعددة

في Phalcon ، يمكن أن تنتمي كافة طرازات إلى نفس اتصال قاعدة البيانات أو أن يكون لديك اتصال فردي. في الواقع ، عندما يحتاج Phalcon\Mvc\Model للاتصال بقاعدة البيانات فإنه يطلب خدمة "db" في حاوية خدمات التطبيق. يمكنك استبدال هذه الخدمة بإعدادها في طريقة initialize() :

use Phalcon\Db\Adapter\Pdo\Mysql as MysqlPdo;
use Phalcon\Db\Adapter\Pdo\PostgreSQL as PostgreSQLPdo;

// This service returns a MySQL database
$di->set(
    "dbMysql",
    function () {
        return new MysqlPdo(
            [
                "host"     => "localhost",
                "username" => "root",
                "password" => "secret",
                "dbname"   => "invo",
            ]
        );
    }
);

// This service returns a PostgreSQL database
$di->set(
    "dbPostgres",
    function () {
        return new PostgreSQLPdo(
            [
                "host"     => "localhost",
                "username" => "postgres",
                "password" => "",
                "dbname"   => "invo",
            ]
        );
    }
);

ثم ، في طريقة initialize() ، نحدد خدمة الاتصال للنموذج:

namespace Store\Toys;

use Phalcon\Mvc\Model;

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

لكن فالكون يمنحك المزيد من المرونة ، يمكنك تحديد الاتصال الذي يجب استخدامه "للقراءة" و "الكتابة". هذا مفيد بشكل خاص لتحقيق التوازن بين الحمل وقواعد البيانات الخاصة بك لتنفيذ هندسة العبد-الرئيسي:

namespace Store\Toys;

use Phalcon\Mvc\Model;

class Robots extends Model
{
    public function initialize()
    {
        $this->setReadConnectionService("dbSlave");

        $this->setWriteConnectionService("dbMaster");
    }
}

يوفر المكتب أيضًا تسهيلات المشاركة الأفقية ، من خلال السماح لك بتطبيق اختيار "شارد" وفقًا لشروط الاستعلام الحالية:

namespace Store\Toys;

use Phalcon\Mvc\Model;

class Robots extends Model
{
    /**
     * Dynamically selects a shard
     *
     * @param array $intermediate
     * @param array $bindParams
     * @param array $bindTypes
     */
    public function selectReadConnection($intermediate, $bindParams, $bindTypes)
    {
        // Check if there is a 'where' clause in the select
        if (isset($intermediate["where"])) {
            $conditions = $intermediate["where"];

            // Choose the possible shard according to the conditions
            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");
                }
            }
        }

        // Use a default shard
        return $this->getDI()->get("dbShard0");
    }
}

يتم selectReadConnection() الأسلوب selectReadConnection() لاختيار الاتصال الصحيح ، تعترض هذه الطريقة أي استعلام جديد تم تنفيذه:

use Store\Toys\Robots;

$robot = Robots::findFirst('id = 101');

خدمات الحقن في النماذج

قد يُطلب منك الوصول إلى خدمات التطبيق داخل نموذج ، يوضح المثال التالي كيفية القيام بذلك:

namespace Store\Toys;

use Phalcon\Mvc\Model;

class Robots extends Model
{
    public function notSaved()
    {
        // Obtain the flash service from the DI container
        $flash = $this->getDI()->getFlash();

        $messages = $this->getMessages();

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

يتم تشغيل الحدث "notSaved" في كل مرة يفشل فيها إجراء "إنشاء" أو "تحديث". لذلك نحن بصدد رسائل التحقق من الصحة الحصول على خدمة "فلاش" من حاوية DI. من خلال القيام بذلك ، لن نضطر إلى طباعة الرسائل بعد كل حفظ.

تعطيل / تمكين الميزات

في ORM قمنا بتنفيذ آلية تسمح لك بتمكين / تعطيل ميزات أو خيارات معينة على مستوى العالم أثناء التنقل. وفقًا لكيفية استخدامك لـ ORM ، يمكنك تعطيل ذلك لا تستخدمه. يمكن أيضًا تعطيل هذه الخيارات مؤقتًا إذا لزم الأمر:

use Phalcon\Mvc\Model;

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

الخيارات المتاحة هي:

اختيار وصف افتراضي
أحداث لتمكين / تعطيل عمليات الرد والخطافات وإشعارات الأحداث من جميع الطرز true
columnRenaming لتمكين / تعطيل إعادة تسمية العمود true
notNullValidations يقوم ORM تلقائيًا بالتحقق من صحة الأعمدة الخالية الموجودة في الجدول المعين true
virtualForeignKeys لتمكين / تعطيل المفاتيح الخارجية الظاهرية true
phqlLiterals لتمكين / تعطيل القيم الحرفية في محلل PHQL true
lateStateBinding لتمكين / تعطيل ربط الحالة المتأخر لطريقة Mvc\Model::cloneResultMap() false

مكون مستقل

يمكن توضيح استخدام Phalcon\Mvc\Model في وضع مستقل أدناه:

use Phalcon\Di;
use Phalcon\Mvc\Model;
use Phalcon\Mvc\Model\Manager as ModelsManager;
use Phalcon\Db\Adapter\Pdo\Sqlite as Connection;
use Phalcon\Mvc\Model\Metadata\Memory as MetaData;

$di = new Di();

// Setup a connection
$di->set(
    "db",
    new Connection(
        [
            "dbname" => "sample.db",
        ]
    )
);

// Set a models manager
$di->set(
    "modelsManager",
    new ModelsManager()
);

// Use the memory meta-data adapter or other
$di->set(
    "modelsMetadata",
    new MetaData()
);

// Create a model
class Robots extends Model
{

}

// Use the model
echo Robots::count();