ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
#### Hydration Modes 水合模式 As mentioned previously, resultsets are collections of complete objects, this means that every returned result is an object representing a row in the database. These objects can be modified and saved again to persistence: 如前所述,resultset是完整对象的集合,这意味着每个返回的结果都是表示数据库中的一行的对象。这些对象可以被修改并保存为持久性: ~~~ <?php use Store\Toys\Robots; $robots = Robots::find(); // Manipulating a resultset of complete objects foreach ($robots as $robot) { $robot->year = 2000; $robot->save(); } ~~~ Sometimes records are obtained only to be presented to a user in read-only mode, in these cases it may be useful to change the way in which records are represented to facilitate their handling. The strategy used to represent objects returned in a resultset is called ‘hydration mode’: 有时,只有在只读模式下才能将记录显示给用户,在这种情况下,更改记录的表示方式以方便处理是很有用的。用于表示结果集返回的对象的策略称为“水合模式”: ~~~ <?php 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; } ~~~ Hydration mode can also be passed as a parameter of ‘find’: 水合模式也可以作为“find”的参数传递: ~~~ <?php 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; } ~~~ #### 自动生成标识列(Auto-generated identity columns) Some models may have identity columns. These columns usually are the primary key of the mapped table. Phalcon\Mvc\Model can recognize the identity column omitting it in the generated SQL INSERT, so the database system can generate an auto-generated value for it. Always after creating a record, the identity field will be registered with the value generated in the database system for it: 有些模型可能有标识列。这些列通常是映射表的主键。在生成的SQL INSERT中,可以识别出身份列的标识列,因此数据库系统可以为它生成自动生成的值。在创建记录之后,标识字段将以数据库系统中生成的值进行注册: ~~~ <?php $robot->save(); echo "The generated id is: ", $robot->id; ~~~ Phalcon\Mvc\Model is able to recognize the identity column. Depending on the database system, those columns may be serial columns like in PostgreSQL or auto_increment columns in the case of MySQL. 在这个问题上,Phalcon\Mvc\Model能够识别身份栏。根据数据库系统的不同,这些列可能是像PostgreSQL或MySQL这样的自动增量列的串行列。 PostgreSQL uses sequences to generate auto-numeric values, by default, Phalcon tries to obtain the generated value from the sequence “table_field_seq”, for example: robots_id_seq, if that sequence has a different name, the `getSequenceName()` method needs to be implemented: PostgreSQL使用序列来生成自动数字值,在默认情况下,Phalcon试图从序列“tablefieldseq”中获得生成的值,例如:如果这个序列有一个不同的名字,`getSequenceName()`方法需要实现: ~~~ <?php namespace Store\Toys; use Phalcon\Mvc\Model; class Robots extends Model { public function getSequenceName() { return "robots_sequence_name"; } } ~~~ #### 忽略指定列的数据(Skipping Columns) To tell Phalcon\Mvc\Model that always omits some fields in the creation and/or update of records in order to delegate the database system the assignation of the values by a trigger or a default: 为了告诉To tell Phalcon\Mvc\Model在创建和/或更新记录的时候,总是省略一些字段,以便将数据库系统委派给一个触发器或默认值 ~~~ <?php 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", ] ); } } ~~~ This will ignore globally these fields on each INSERT/UPDATE operation on the whole application. If you want to ignore different attributes on different INSERT/UPDATE operations, you can specify the second parameter (boolean) - true for replacement. Forcing a default value can be done in the following way: 在整个应用程序的每次插入/更新操作中,都会忽略全局这些字段。如果您想忽略不同的插入/更新操作的不同属性,可以指定第二个参数(boolean)-true用于替换。强制默认值可以按照以下方式进行: ~~~ <?php 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(); ~~~ A callback also can be used to create a conditional assignment of automatic default values: 还可以使用回调来创建自动默认值的条件赋值: ~~~ <?php 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"); } } } ~~~ * * * * * > Never use a Phalcon\Db\RawValue to assign external data (such as user input) or variable data. The value of these fields is ignored when binding parameters to the query. So it could be used to attack the application injecting SQL. >永远不要使用一个 Phalcon\Db\RawValue参数值来分配外部数据(例如用户输入)或变量数据。在将参数绑定到查询时,这些字段的值会被忽略。因此,它可以用来攻击应用程序注入SQL。 * * * * * #### 动态更新(Dynamic Update) SQL UPDATE statements are by default created with every column defined in the model (full all-field SQL update). You can change specific models to make dynamic updates, in this case, just the fields that had changed are used to create the final SQL statement. 默认情况下,SQL UPDATE语句是在模型中定义的每个列创建的(完整的字段SQL更新)。您可以更改特定的模型来进行动态更新,在本例中,只是使用了更改的字段来创建最终的SQL语句。 In some cases this could improve the performance by reducing the traffic between the application and the database server, this specially helps when the table has blob/text fields: 在某些情况下,这可以通过减少应用程序和数据库服务器之间的通信来提高性能,这在表有blob/text字段时特别有用。 ~~~ <?php namespace Store\Toys; use Phalcon\Mvc\Model; class Robots extends Model { public function initialize() { $this->useDynamicUpdate(true); } } ~~~ #### 独立的列映射(Independent Column Mapping) The ORM supports an independent column map, which allows the developer to use different column names in the model to the ones in the table. Phalcon will recognize the new column names and will rename them accordingly to match the respective columns in the database. This is a great feature when one needs to rename fields in the database without having to worry about all the queries in the code. A change in the column map in the model will take care of the rest. For example: ORM支持独立的列映射,这允许开发人员在模型中使用不同的列名。为了匹配数据库中相应的列,它将识别新的列名,并相应地对其进行重命名。当需要重命名数据库中的字段而不必担心代码中的所有查询时,这是一个很好的特性。模型中列映射的更改将处理其余部分。例如: ~~~ <?php 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", ]; } } ~~~ Then you can use the new names naturally in your code: 然后你可以在你的代码中自然地使用这些新名字: ~~~ <?php 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(); ~~~ Take into consideration the following the next when renaming your columns: 在重新命名您的列时,请考虑以下内容: * References to attributes in relationships/validators must use the new names 关系/验证器中的属性引用必须使用新名称 * Refer the real column names will result in an exception by the ORM 引用实际的列名将导致ORM出现异常 The independent column map allow you to: 独立的列映射允许您这样做: * Write applications using your own conventions 使用您自己的约定编写应用程序 * Eliminate vendor prefixes/suffixes in your code 在您的代码中消除供应商前缀/后缀 * Change column names without change your application code 更改列名,而不更改应用程序代码 #### 记录快照(Record Snapshots) Specific models could be set to maintain a record snapshot when they’re queried. You can use this feature to implement auditing or just to know what fields are changed according to the data queried from the persistence: 可以设置特定的模型,以便在查询时保持记录快照。您可以使用这个特性来实现审计,或者仅仅根据从持久性中查询的数据来了解哪些字段被更改了。 ~~~ <?php namespace Store\Toys; use Phalcon\Mvc\Model; class Robots extends Model { public function initialize() { $this->keepSnapshots(true); } } ~~~ When activating this feature the application consumes a bit more of memory to keep track of the original values obtained from the persistence. In models that have this feature activated you can check what fields changed: 当激活该特性时,应用程序会消耗更多的内存来跟踪从持久性中获得的原始值。在具有该特性的模型中,您可以检查哪些字段发生了变化: ~~~ <?php 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 ~~~ #### 设置模式(Pointing to a different schema) 如果一个模型映射到一个在非默认的schemas/数据库中的表,你可以通过 `setSchema()` 方法去定义它: ~~~ <?php namespace Store\Toys; use Phalcon\Mvc\Model; class Robots extends Model { public function initialize() { $this->setSchema("toys"); } } ~~~ #### 设置多个数据库(Setting multiple databases) 在Phalcon中,所有模型可以属于同一个数据库连接,也可以分属独立的数据库连接。实际上,当 Phalcon\Mvc\Model 需要连接数据库的时候,它在应用服务容器内请求”db”这个服务。 可以通过在` initialize()` 方法内重写这个服务的设置。 ~~~ <?php 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() `方法内,我们为这个模型定义数据库连接。 ~~~ <?php namespace Store\Toys; use Phalcon\Mvc\Model; class Robots extends Model { public function initialize() { $this->setConnectionService("dbPostgres"); } } ~~~ 另外Phalcon还提供了更多的灵活性,你可分别定义用来读取和写入的数据库连接。这对实现主从架构的数据库负载均衡非常有用。 (译者注:EvaEngine项目为使用Phalcon提供了更多的灵活性,推荐了解和使用) ~~~ <?php namespace Store\Toys; use Phalcon\Mvc\Model; class Robots extends Model { public function initialize() { $this->setReadConnectionService("dbSlave"); $this->setWriteConnectionService("dbMaster"); } } ~~~ 另外ORM还可以通过根据当前查询条件来实现一个 ‘shard’ 选择器,来实现水平切分的功能。 ~~~ <?php 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()` 方法用来选择正确的数据库连接,这个方法拦截任何新的查询操作: ~~~ <?php use Store\Toys\Robots; $robot = Robots::findFirst('id = 101'); ~~~ #### 注入服务到模型(Injecting services into Models) 你可能需要在模型中用到应用中注入的服务,下面的例子会教你如何去做: ~~~ <?php 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); } } } ~~~ 每当 “create” 或者 “update” 操作失败时会触发 “notSave” 事件。所以我们从DI中获取 “flash” 服务并推送确认消息。这样的话,我们不需要每次在save之后去打印信息。 #### 禁用或启用特性(Disabling/Enabling Features) In the ORM we have implemented a mechanism that allow you to enable/disable specific features or options globally on the fly. According to how you use the ORM you can disable that you aren’t using. These options can also be temporarily disabled if required: 在ORM中,我们已经实现了一种机制,允许您在全局范围内启用/禁用特定的特性或选项。根据您如何使用ORM,您可以禁用您不使用的ORM。如果需要,这些选项也可以临时禁用: ~~~ <?php use Phalcon\Mvc\Model; Model::setup( [ "events" => false, "columnRenaming" => false, ] ); ~~~ The available options are: 可用的选项是: | Option |Description 描述 | Default | | --- | --- | --- | | events | Enables/Disables callbacks, hooks and event notifications from all the models 从所有模型中启用/禁用回调、钩子和事件通知 | `true` | | columnRenaming | columnRenaming 修改字段名字 | `true` | | notNullValidations | The ORM automatically validate the not null columns present in the mapped table ORM 自动验证映射表中存在的not null列 | `true` | | virtualForeignKeys | Enables/Disables the virtual foreign keys 启用/禁用虚拟外键 | `true` | | phqlLiterals | Enables/Disables literals in the PHQL parser 在PHQL解析器中启用/禁用文本 | `true` | |lateStateBinding | Enables/Disables late state binding of the `Mvc\Model::cloneResultMap()` method 启用/禁用Mvc模型的后期绑定::cloneResultMap()的方法 | `false` | #### 独立的组件(Stand-Alone component) Using Phalcon\Mvc\Model in a stand-alone mode can be demonstrated below: 在一个独立的模式下使用 Phalcon\Mvc\Model 可以在下面演示: ~~~ <?php 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(); ~~~