使用模型(Working with Models) ====================================== 在应用程åºä¸ï¼Œæ¨¡åž‹æ˜¯ä»£è¡¨çš„æ˜¯ä¸€ç§æ•°æ®ä»¥åŠé€šè¿‡ä¸€äº›è§„åˆ™æ¥æ“作这些数æ®ï¼Œæ¨¡åž‹ä¸»è¦ç”¨äºŽé€šè¿‡ä¸€äº›è§„则使其与数æ®åº“表进行相互æ“作,在大多数情况下,æ¯ä¸ªæ•°æ®åº“表将对应到一个模型,整个应用程åºçš„业务逻辑都会集ä¸åœ¨æ¨¡åž‹ä¸ã€‚ :doc:`Phalcon\\Mvc\\Model <../api/Phalcon_Mvc_Model>` 是应用程åºä¸æ‰€æœ‰æ¨¡åž‹çš„基类,它ä¿è¯äº†æ•°æ®åº“的独立性,基本的CURDæ“作,高级的查询功能,多表关è”ç‰åŠŸèƒ½ã€‚ :doc:`Phalcon\\Mvc\\Model <../api/Phalcon_Mvc_Model>` æä¾›äº†SQLè¯å¥çš„动æ€è½¬åŒ–功能,é¿å…了直接使用SQLè¯å¥å¸¦æ¥çš„安全风险。 .. highlights:: Models是数æ®åº“的高级抽象层,如果您需è¦ä¸Žæ•°æ®åº“直接打交é“ï¼Œä½ å¯ä»¥æŸ¥çœ‹ :doc:`Phalcon\\Db <../api/Phalcon_Db>` 组件文档。 创建模型 --------------- 一个Model就是一个继承自 :doc:`Phalcon\\Mvc\\Model <../api/Phalcon_Mvc_Model>` 的类文件,它必须放到models文件夹目录下,一个Modelæ–‡ä»¶å¿…é¡»æ˜¯ä¸€ä¸ªç‹¬ç«‹çš„ç±»æ–‡ä»¶ï¼ŒåŒæ—¶å®ƒçš„命å采用驼蜂å¼çš„书写方法: .. code-block:: php <?php class Robots extends \Phalcon\Mvc\Model { } 上é¢çš„ä¾‹åæ˜¯ä¸€ä¸ª "Robots"æ¨¡åž‹ç±»ï¼Œéœ€è¦æ³¨æ„的是,类Robots继承自 :doc:`Phalcon\\Mvc\\Model <../api/Phalcon_Mvc_Model>`ã€‚å› ä¸ºç»§æ‰¿ï¼Œè¯¥æ¨¡åž‹æä¾›äº†å¤§é‡çš„功能,包括基本的数æ®åº“CRUDCreate, Read, Update, Destroy) æ“作,数æ®éªŒè¯ï¼Œå…ˆè¿›çš„æ£€ç´¢åŠŸèƒ½ï¼Œå¹¶ä¸”å¯ä»¥åŒæ—¶å…³è”多个模型。 .. highlights:: 推èä½ ä½¿ç”¨PHP5.4版本,这å¯ä»¥ä½¿å¾—模型ä¸çš„属性在ä¿å˜åˆ°å†…å˜æ—¶ï¼Œæ›´èŠ‚çœå†…å˜ã€‚ 默认情况下,模型"Robots"对应的是数æ®åº“表"robots"ï¼Œå¦‚æžœä½ æƒ³æ‰‹å·¥æŒ‡å®šæ˜ å°„åˆ°å…¶ä»–çš„æ•°æ®åº“è¡¨ï¼Œä½ å¯ä»¥ä½¿ç”¨ getSource() 方法: .. code-block:: php <?php class Robots extends \Phalcon\Mvc\Model { public function getSource() { return "the_robots"; } } æ¤æ—¶ï¼Œæ¨¡åž‹"Robots"æ˜ å°„åˆ°æ•°æ®åº“表"the_robots",initialize()方法有助于在模型ä¸å»ºç«‹è‡ªå®šä¹‰è¡Œä¸ºï¼Œå¦‚,ä¸åŒçš„æ•°æ®è¡¨ã€‚initialize()方法在请求期间åªè¢«è°ƒç”¨ä¸€æ¬¡ã€‚ 在模型ä¸ä½¿ç”¨å‘½å空间 -------------------- 命å空间å¯ä»¥ç”¨æ¥é¿å…ç±»å冲çªï¼Œåœ¨è¿™ç§æƒ…况下,使用getSource()æ–¹æ³•æ¥æŒ‡å®šæ•°æ®è¡¨å称是必è¦çš„: .. code-block:: php <?php namespace Store\Toys; class Robots extends \Phalcon\Mvc\Model { public function getSource() { return "robots"; } } Understanding Records To Objects -------------------------------- æ¯ä¸€ä¸ªæ¨¡åž‹å¯¹è±¡è¡¨ç¤ºæ•°æ®è¡¨ä¸çš„一行数æ®ï¼Œä½ å¯ä»¥è½»æ¾çš„通过读å–对象的属性æ¥è®¿é—®æ•°æ®ã€‚举个例å,数æ®è¡¨"robots"的记录如下: .. code-block:: bash mysql> select * from robots; +----+------------+------------+------+ | id | name | type | year | +----+------------+------------+------+ | 1 | Robotina | mechanical | 1972 | | 2 | Astro Boy | mechanical | 1952 | | 3 | Terminator | cyborg | 2029 | +----+------------+------------+------+ 3 rows in set (0.00 sec) ä½ å¯ä»¥é€šè¿‡æ•°æ®åº“ä¸»é”®æŸ¥æ‰¾æŸæ¡è®°å½•ï¼Œç„¶åŽæ‰“å°å‡ºå®ƒä»¬çš„åå—: .. code-block:: php <?php // Find record with id = 3 $robot = Robots::findFirst(3); // Prints "Terminator" echo $robot->name; 一旦记录被读å–到内å˜ä¸ï¼Œä½ å¯ä»¥ä¿®æ”¹å®ƒçš„æ•°æ®ï¼Œç„¶åŽä¿å˜æ›´æ”¹ï¼š .. code-block:: php <?php $robot = Robots::findFirst(3); $robot->name = "RoboCop"; $robot->save(); æ£å¦‚ä½ æ‰€çœ‹åˆ°çš„ï¼Œè¿™é‡Œæ²¡æœ‰ä½¿ç”¨åŽŸå§‹çš„SQLè¯å¥ã€‚:doc:`Phalcon\\Mvc\\Model <../api/Phalcon_Mvc_Model>` 为webåº”ç”¨ç¨‹åºæä¾›äº†é«˜åº¦çš„æ•°æ®åº“抽象。 查找记录 --------------- :doc:`Phalcon\\Mvc\\Model <../api/Phalcon_Mvc_Model>` 还æä¾›äº†å¤šç§æ–¹æ³•æ¥æŸ¥è¯¢æ•°æ®è®°å½•。下é¢çš„例åå°†ä¸ºä½ å±•ç¤ºå¦‚ä½•é€šè¿‡ModelæŸ¥è¯¢å•æ¡ä»¥åŠå¤šæ¡è®°å½•: .. code-block:: php <?php // How many robots are there? $robots = Robots::find(); echo "There are ", count($robots), "\n"; // How many mechanical robots are there? $robots = Robots::find("type = 'mechanical'"); echo "There are ", count($robots), "\n"; // Get and print virtual robots ordered by name $robots = Robots::find(array( "type = 'virtual'", "order" => "name" )); foreach ($robots as $robot) { echo $robot->name, "\n"; } // Get first 100 virtual robots ordered by name $robots = Robots::find(array( "type = 'virtual'", "order" => "name", "limit" => 100 )); foreach ($robots as $robot) { echo $robot->name, "\n"; } ä½ ä¹Ÿå¯ä»¥ä½¿ç”¨findFirst()方法æ¥èŽ·å–给定æ¡ä»¶ä¸‹çš„第一æ¡è®°å½•: .. code-block:: php <?php // What's the first robot in robots table? $robot = Robots::findFirst(); echo "The robot name is ", $robot->name, "\n"; // What's the first mechanical robot in robots table? $robot = Robots::findFirst("type = 'mechanical'"); echo "The first mechanical robot name is ", $robot->name, "\n"; // Get first virtual robot ordered by name $robot = Robots::findFirst(array("type = 'virtual'", "order" => "name")); echo "The first virtual robot name is ", $robot->name, "\n"; find()å’ŒfindFirst()è¿™ä¸¤ä¸ªæ–¹æ³•éƒ½æŽ¥æ”¶ä¸€ä¸ªå…³è”æ•°ç»„作为检索æ¡ä»¶ï¼š .. code-block:: php <?php $robot = Robots::findFirst( array( "type = 'virtual'", "order" => "name DESC", "limit" => 30 ) ); $robots = Robots::find( array( "conditions" => "type = ?1", "bind" => array(1 => "virtual") ) ); å¯ç”¨çš„æŸ¥è¯¢é€‰é¡¹åˆ—表: +-------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------+ | Parameter | Description | Example | +=============+==================================================================================================================================================================================================+=========================================================================+ | conditions | Search conditions for the find operation. Is used to extract only those records that fulfill a specified criterion. By default Phalcon\Mvc\Model assumes the first parameter are the conditions. | "conditions" => "name LIKE 'steve%'" | +-------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------+ | bind | Bind is used together with options, by replacing placeholders and escaping values thus increasing security | "bind" => array("status" => "A", "type" => "some-time") | +-------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------+ | bindTypes | When binding parameters, you can use this parameter to define additional casting to the bound parameters increasing even more the security | "bindTypes" => array(Column::BIND_TYPE_STR, Column::BIND_TYPE_INT) | +-------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------+ | order | Is used to sort the resultset. Use one or more fields separated by commas. | "order" => "name DESC, status" | +-------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------+ | limit | Limit the results of the query to results to certain range | "limit" => 10 | +-------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------+ | group | Allows to collect data across multiple records and group the results by one or more columns | "group" => "name, status" | +-------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------+ | for_update | With this option, :doc:`Phalcon\\Mvc\\Model <../api/Phalcon_Mvc_Model>` reads the latest available data, setting exclusive locks on each row it reads | "for_update" => true | +-------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------+ | shared_lock | With this option, :doc:`Phalcon\\Mvc\\Model <../api/Phalcon_Mvc_Model>` reads the latest available data, setting shared locks on each row it reads | "shared_lock" => true | +-------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------+ | cache | Cache the resulset, reducing the continuous access to the relational system | "cache" => array("lifetime" => 3600, "key" => "my-find-key") | +-------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------+ å¦‚æžœä½ æ„¿æ„ï¼Œä½ ä¹Ÿå¯ä»¥é€šè¿‡é¢å‘对象的方å¼åˆ›å»ºæŸ¥è¯¢ï¼Œè€Œä¸æ˜¯ä½¿ç”¨ä¸Šé¢è®²åˆ°çš„å…³è”æ•°ç»„的形å¼ï¼š .. code-block:: php <?php $robots = Robots::query() ->where("type = :type:") ->bind(array("type" => "mechanical")) ->order("name") ->execute(); 陿€æ–¹æ³• query()返回一个 :doc:`Phalcon\\Mvc\\Model\\Criteria <../api/Phalcon_Mvc_Model_Criteria>` çš„å®žä¾‹åŒ–å¯¹è±¡ï¼Œå› æ¤å®ƒå¯¹IDE自动æç¤ºåŠŸèƒ½éžå¸¸å‹å¥½ã€‚ æ‰€æœ‰çš„æŸ¥è¯¢éƒ½è¢«è¿›è¡Œå†…éƒ¨å¤„ç†æˆ :doc:`PHQL <phql>` 。PHQL是一个高层次的,é¢å‘对象的类SQLè¯è¨€ã€‚è¿™ç§è¯è¨€ä¸ºä½ æä¾›æ›´å¤šçš„功能æ¥è¿›è¡ŒæŸ¥è¯¢ï¼Œå¦‚ä¸Žå…¶ä»–æ¨¡åž‹å…³è”æŸ¥è¯¢ï¼Œå®šä¹‰åˆ†ç»„ï¼Œæ·»åŠ èšåˆç‰ã€‚ 模型数æ®é›†(Model Resultsets) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ findFirst()方法直接返回一个类的实例对象(查询有数æ®è¿”回的时候),find()方法则返回:doc:`Phalcon\\Mvc\\Model\\Resultset\\Simple <../api/Phalcon_Mvc_Model_Resultset_Simple>` 的一个实例对象,这个对象是一个å°è£…äº†æ‰€æœ‰åŠŸèƒ½çš„ç»“æžœé›†ï¼Œæ¯”å¦‚åƒæ•°æ®é历,寻找特定的数æ®è®°å½•,计数ç‰ç‰ã€‚ è¿™äº›å¯¹è±¡æ¯”æ ‡å‡†æ•°ç»„æ›´ä¸ºå¼ºå¤§ï¼Œæœ€å¤§çš„ä¼˜ç‚¹ä¹‹ä¸€æ˜¯ :doc:`Phalcon\\Mvc\\Model\\Resultset <../api/Phalcon_Mvc_Model_Resultset>` 在任何时候它在内å˜ä¸åªä¿å˜ä¸€æ¡è®°å½•,这æžå¤§çš„优化了内å˜ç®¡ç†ï¼Œç‰¹åˆ«æ˜¯å¤„ç†å¤§é‡æ•°æ®çš„æ—¶å€™ã€‚ .. code-block:: php <?php // Get all robots $robots = Robots::find(); // Traversing with a foreach foreach ($robots as $robot) { echo $robot->name, "\n"; } // Traversing with a while $robots->rewind(); while ($robots->valid()) { $robot = $robots->current(); echo $robot->name, "\n"; $robots->next(); } // Count the resultset echo count($robots); // Alternative way to count the resultset echo $robots->count(); // Move the internal cursor to the third robot $robots->seek(2); $robot = $robots->current() // Access a robot by its position in the resultset $robot = $robots[5]; // Check if there is a record in certain position if (isset($robots[3]) { $robot = $robots[3]; } // Get the first record in the resultset $robot = robots->getFirst(); // Get the last record $robot = robots->getLast(); Phalconæ•°æ®é›†æ¨¡æ‹Ÿæ¸¸æ ‡çš„æ–¹å¼ï¼Œä½ å¯ä»¥èŽ·å–ä»»æ„一行数æ®ï¼Œåªéœ€è¦é€šè¿‡è®¿é—®å…¶ä½ç½®ï¼Œæˆ–者通过移动内部指针到一个特定的ä½ç½®ã€‚éœ€è¦æ³¨æ„的是,一些数æ®åº“ç³»ç»Ÿå¹¶ä¸æ”¯æŒæ¸¸æ ‡ï¼Œè¿™å°†ä¼šå¯¼è‡´æ¯æ¬¡å¼ºåˆ¶é‡æ–°æ‰§è¡Œï¼Œæ¸¸æ ‡ç§»åŠ¨åˆ°å¤´éƒ¨ï¼Œå¹¶ä»Žå¤´åˆ°å°¾åŽ»æŸ¥è¯¢è¯·æ±‚ä½ç½®ã€‚åŒç†ï¼Œå¦‚果一个结果集é历多次,查询必须被执行相åŒçš„æ¬¡æ•°ã€‚ 大é‡çš„æŸ¥è¯¢ç»“æžœå˜å‚¨åœ¨å†…å˜ä¸ï¼Œä¼šæ¶ˆè€—大é‡çš„资æºã€‚resultsets are obtained from the database in chunks of 32 rows reducing the need for re-execute the request in several cases. 请注æ„,结果集å¯ä»¥è¢«åºåˆ—化åŽå˜å‚¨åˆ°ç¼“å˜ä¸ã€‚:doc:`Phalcon\\Cache <cache>` å¯ä»¥å¸®åŠ©å®Œæˆè¿™é¡¹ä»»åŠ¡ã€‚However, serializing data causes :doc:`Phalcon\\Mvc\\Model <../api/Phalcon_Mvc_Model>` to retrieve all the data from the database in an array, thus consuming more memory while this process takes place. .. code-block:: php <?php // Query all records from model parts $parts = Parts::find(); // Store the resultset into a file file_put_contents("cache.txt", serialize($parts)); // Get parts from file $parts = unserialize(file_get_contents("cache.txt")); // Traverse the parts foreach ($parts as $part) { echo $part->id; } 傿•°ç»‘定 ^^^^^^^^^^^^^^^^^^ 在 :doc:`Phalcon\\Mvc\\Model <../api/Phalcon_Mvc_Model>` åŒæ ·æ”¯æŒå‚数类型绑定。虽然会有比较å°çš„æ€§èƒ½æ¶ˆè€—,但我们推èä½ ä½¿ç”¨è¿™ç§æ–¹æ³•ï¼Œå› ä¸ºå®ƒä¼šæ¸…é™¤SQL注入攻击,å—ç¬¦ä¸²è¿‡æ»¤åŠæ•´å½¢æ•°æ®éªŒè¯ç‰ã€‚绑定绑定,å¯ä»¥é€šè¿‡å¦‚下方å¼å®žçŽ°ï¼š .. code-block:: php <?php // Query robots binding parameters with string placeholders $conditions = "name = :name: AND type = :type:"; //Parameters whose keys are the same as placeholders $parameters = array( "name" => "Robotina", "type" => "maid" ); //Perform the query $robots = Robots::find(array( $conditions, "bind" => $parameters )); // Query robots binding parameters with integer placeholders $conditions = "name = ?1 AND type = ?2"; $parameters = array(1 => "Robotina", 2 => "maid"); $robots = Robots::find(array( $conditions, "bind" => $parameters )); // Query robots binding parameters with both string and integer placeholders $conditions = "name = :name: AND type = ?1"; //Parameters whose keys are the same as placeholders $parameters = array( "name" => "Robotina", 1 => "maid" ); //Perform the query $robots = Robots::find(array( $conditions, "bind" => $parameters )); å½“ä½¿ç”¨æ•°å—æ—¶ï¼Œä½ å¯èƒ½éœ€è¦å®šä¹‰ä»–们为整形数å—。比如 1或2, åœ¨è¿™ç§æƒ…况下,有å¯èƒ½æ˜¯å—符串"1"或"2"ï¼Œè€Œä¸æ˜¯æ•°å—ï¼Œæ‰€ä»¥è¿™æ˜¯ä¸æ£ç¡®çš„。 在使用 PDO_ 的时候å—符串是被自动转义的,æ¤åŠŸèƒ½å’Œæ•°æ®åº“连接的å—符集有关,所以在进行数æ®åº“连接时,必须设置æ£ç¡®çš„è¿žæŽ¥å‚æ•°æˆ–者在数æ®åº“ä¸è®¾ç½®å¥½ï¼Œé”™è¯¯çš„å—符集会导致数æ®åœ¨å˜å‚¨è¯»å–æ—¶äº§ç”Ÿæ„æƒ³ä¸åˆ°çš„结果。 æ¤å¤–ï¼Œä½ è¿˜å¯ä»¥é€šè¿‡è®¾ç½®å‚æ•°"bindTypes"ï¼Œå®šä¹‰å‚æ•°çš„æ•°æ®ç±»åž‹ï¼š .. code-block:: php <?php //Bind parameters $parameters = array( "name" => "Robotina", "year" => 2008 ); //Casting Types $types = array( Phalcon\Db\Column::BIND_PARAM_STR, Phalcon\Db\Column::BIND_PARAM_INT ); // Query robots binding parameters with string placeholders $conditions = "name = :name: AND year = :year:"; $robots = Robots::find(array( $conditions, "bind" => $parameters, "bindTypes" => $types )); 傿•°ç»‘定å¯ä»¥ç”¨äºŽæ‰€æœ‰çš„æŸ¥è¯¢æ–¹æ³•上,比如find()å’ŒfindFirst()。当然也包括一些计算类的方法,如 count(),sum(),average()ç‰ã€‚ 模型之间的关系 ---------------------------- 共有四ç§ç±»åž‹çš„关系:一对一,一对多,多对一,多对多。关系å¯ä»¥æ˜¯å•å‘也å¯ä»¥æ˜¯åŒå‘的,并且æ¯ä¸ªå¯ä»¥æ˜¯ç®€å•çš„(一个一个的Model)æˆ–è€…æ›´å¤æ‚çš„(组åˆModel)。模型管ç†å™¨ç®¡ç†è¿™äº›å…³ç³»çš„外键约æŸï¼Œè¿™å°†æœ‰åŠ©äºŽå®šä¹‰å‚ç…§å®Œæ•´æ€§ä»¥åŠæ–¹ä¾¿å¿«æ·çš„è®¿é—®å…³è”æ•°æ®ã€‚é€šè¿‡å…³ç³»æ˜ å°„ï¼Œå¯ä»¥åœ¨ä¸€ä¸ªè®°å½•ä¸å¾ˆå®¹æ˜“的访问相关模型ä¸çš„æ•°æ®ã€‚ å•å‘关系 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unidirectional relations are those that are generated in relation to one another but not vice versa. åŒå‘关系 ^^^^^^^^^^^^^^^^^^^^^^^ The bidirectional relations build relationships in both models and each model defines the inverse relationship of the other. 定义关系 ^^^^^^^^^^^^^^^^^^^^^^ 在Phalconä¸ï¼Œå…³ç³»çš„定义必须在modelçš„initialize()方法ä¸è¿›è¡Œå®šä¹‰ï¼Œé€šè¿‡æ–¹æ³•belongsTo(),hasOne(), hasMany() 进行关è”å…³ç³»ï¼Œç”¨å½“å‰æ¨¡åž‹çš„属性关è”å…¶ä»–æ¨¡åž‹ã€‚è¿™å‡ ä¸ªæ–¹æ³•éƒ½éœ€è¦3ä¸ªå‚æ•°ï¼Œå³ï¼š 当剿¨¡åž‹å±žæ€§ï¼Œå…³è”模型åç§°ï¼Œå…³è”æ¨¡åž‹çš„属性。 +-----------+----------------------------+ | Method | Description | +===========+============================+ | hasMany | Defines a 1-n relationship | +-----------+----------------------------+ | hasOne | Defines a 1-1 relationship | +-----------+----------------------------+ | belongsTo | Defines a n-1 relationship | +-----------+----------------------------+ 下é¢çš„schema显示了三个数æ®è¡¨çš„å…³ç³»ï¼Œç”¨è¿™ä¸ªä½œä¸ºä¾‹åæœ‰åŠ©äºŽæˆ‘ä»¬æ›´å¥½çš„ç†è§£ï¼š .. code-block:: sql CREATE TABLE `robots` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(70) NOT NULL, `type` varchar(32) NOT NULL, `year` int(11) NOT NULL, PRIMARY KEY (`id`) ); CREATE TABLE `robots_parts` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `robots_id` int(10) NOT NULL, `parts_id` int(10) NOT NULL, `created_at` DATE NOT NULL, PRIMARY KEY (`id`), KEY `robots_id` (`robots_id`), KEY `parts_id` (`parts_id`) ); CREATE TABLE `parts` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(70) NOT NULL, PRIMARY KEY (`id`) ); * The model "Robots" has many "RobotsParts". * The model "Parts" has many "RobotsParts". * The model "RobotsParts" belongs to both "Robots" and "Parts" models as a one-to-many relation. 在模型ä¸ä»–ä»¬çš„å®žçŽ°æ–¹æ³•æ˜¯è¿™æ ·çš„ï¼š .. code-block:: php <?php class Robots extends \Phalcon\Mvc\Model { public function initialize() { $this->hasMany("id", "RobotsParts", "robots_id"); } } .. code-block:: php <?php class Parts extends \Phalcon\Mvc\Model { public function initialize() { $this->hasMany("id", "RobotsParts", "parts_id"); } } .. code-block:: php <?php class RobotsParts extends \Phalcon\Mvc\Model { public function initialize() { $this->belongsTo("robots_id", "Robots", "id"); $this->belongsTo("parts_id", "Parts", "id"); } } åœ¨æ˜ å°„å…³ç³»ä¸ï¼Œç¬¬ä¸€ä¸ªå‚æ•°æ˜¯å½“å‰æ¨¡åž‹çš„å±žæ€§ï¼Œç¬¬äºŒä¸ªå‚æ•°ä¸ºå…³è”模型的类åç§°ï¼Œç¬¬ä¸‰ä¸ªå‚æ•°ä¸ºå…³è”æ¨¡åž‹çš„å±žæ€§ã€‚ä½ ä¹Ÿå¯ä»¥åœ¨æ˜ 射关系ä¸ä½¿ç”¨æ•°ç»„定义多个属性。 Taking advantage of relationships ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 当明确定义了模型之间的关系åŽï¼Œå°±å¾ˆå®¹æ˜“通过查找到的记录找到相关模型的记录 .. code-block:: php <?php $robot = Robots::findFirst(2); foreach ($robot->getRobotsParts() as $robotPart) { echo $robotPart->getParts()->name, "\n"; } Phalconä½¿ç”¨é”æœ¯æ–¹æ³• __callæ¥èŽ·å¾—å…³è”æ¨¡åž‹çš„æ•°æ®ã€‚如果被调用的方法ä¸å«æœ‰"get"å‰è¾ï¼Œ:doc:`Phalcon\\Mvc\\Model <../api/Phalcon_Mvc_Model>` 将返回 findFirst()/find()的结果集。下é¢çš„ç¤ºä¾‹å±•ç¤ºäº†ä½¿ç”¨å’Œæœªä½¿ç”¨é”æœ¯æ–¹æ³•èŽ·å–æ•°æ®çš„区别: .. code-block:: php <?php $robot = Robots::findFirst(2); // Robots model has a 1-n (hasMany) // relationship to RobotsParts then $robotsParts = $robot->getRobotsParts(); // Only parts that match conditions $robotsParts = $robot->getRobotsParts("created_at = '2012-03-15'"); // Or using bound parameters $robotsParts = $robot->getRobotsParts(array( "created_at = :date:", "bind" => array("date" => "2012-03-15" ))); $robotPart = RobotsParts::findFirst(1); // RobotsParts model has a n-1 (belongsTo) // relationship to RobotsParts then $robot = $robotPart->getRobots(); Getting related records manually: .. code-block:: php <?php $robot = Robots::findFirst(2); // Robots model has a 1-n (hasMany) // relationship to RobotsParts then $robotsParts = RobotsParts::find("robots_id = '" . $robot->id . "'"); // Only parts that match conditions $robotsParts = RobotsParts::find( "robots_id = '" . $robot->id . "' AND created_at='2012-03-15'" ); $robotPart = RobotsParts::findFirst(1); // RobotsParts model has a n-1 (belongsTo) // relationship to RobotsParts then $robot = Robots::findFirst("id = '" . $robotPart->robots_id . "'"); å‰è¾"get"使用find()/findFirst()æ¥èŽ·å–å…³è”è®°å½•ã€‚å½“ç„¶ä½ ä¹Ÿå¯ä»¥"count"å‰è¾æ¥èŽ·å–记录的数é‡ï¼š .. code-block:: php <?php $robot = Robots::findFirst(2); echo "The robot have ", $robot->countRobotsParts(), " parts\n"; 虚拟外键 ^^^^^^^^^^^^^^^^^^^^ 默认情况下,关è”关系并ä¸å®šä¹‰å¤–键约æŸï¼Œä¹Ÿå°±æ˜¯è¯´ï¼Œå¦‚æžœä½ å°è¯•insert/updateæ•°æ®çš„è¯ï¼Œå°†ä¸ä¼šè¿›è¡Œå¤–键验è¯ï¼ŒPhalcon也ä¸ä¼šæç¤ºéªŒè¯ä¿¡æ¯ã€‚ä½ å¯ä»¥ä¿®æ”¹æ¤è¡Œä¸ºï¼Œå¢žåŠ ä¸€ä¸ªå‚æ•°å®šä¹‰è¿™ç§å…³ç³»ã€‚ RobotsPart模型å¯ä»¥è¿™æ ·ä¿®æ”¹ï¼Œä»¥å®žçްæ¤åŠŸèƒ½ï¼š .. code-block:: php <?php class RobotsParts extends \Phalcon\Mvc\Model { public function initialize() { $this->belongsTo("robots_id", "Robots", "id", array( "foreignKey" => true )); $this->belongsTo("parts_id", "Parts", "id", array( "foreignKey" => array( "message" => "The part_id does not exist on the parts model" ) )); } } å¦‚æžœä½ åœ¨belongsTo()ä¸è®¾ç½®äº†å¤–键约æŸï¼Œå®ƒå°†ä¼šéªŒè¯insert/updateçš„å€¼æ˜¯ä¸æ˜¯ä¸€ä¸ªæœ‰æ•ˆçš„å€¼ã€‚åŒæ ·åœ°ï¼Œå¦‚æžœä½ åœ¨hasMany()/hasOne()ä¸è®¾ç½®äº†å¤–键约æŸï¼Œå®ƒå°†ä¼šéªŒè¯è®°å½•是å¦å¯ä»¥åˆ 除。 .. code-block:: php <?php class Parts extends \Phalcon\Mvc\Model { public function initialize() { $this->hasMany("id", "RobotsParts", "parts_id", array( "foreignKey" => array( "message" => "The part cannot be deleted because other robots are using it" ) )); } } Generating Calculations ----------------------- æ•°é‡ç»Ÿè®¡æ˜¯æ•°æ®åº“ä¸å¸¸ç”¨çš„功能,如COUNT,SUM,MAX,MIN,AVG. :doc:`Phalcon\\Mvc\\Model <../api/Phalcon_Mvc_Model>` å¯ä»¥é€šè¿‡å…¬å¼€çš„æ–¹æ³•实现æ¤ç§åŠŸèƒ½ã€‚ Count examples: .. code-block:: php <?php // How many employees are? $rowcount = Employees::count(); // How many different areas are assigned to employees? $rowcount = Employees::count(array("distinct" => "area")); // How many employees are in the Testing area? $rowcount = Employees::count("area = 'Testing'"); //Count employees grouping results by their area $group = Employees::count(array("group" => "area")); foreach ($group as $row) { echo "There are ", $group->rowcount, " in ", $group->area; } // Count employees grouping by their area and ordering the result by count $group = Employees::count( array( "group" => "area", "order" => "rowcount" ) ); Sum examples: .. code-block:: php <?php // How much are the salaries of all employees? $total = Employees::sum(array("column" => "salary")); // How much are the salaries of all employees in the Sales area? $total = Employees::sum( array( "column" => "salary", "conditions" => "area = 'Sales'" ) ); // Generate a grouping of the salaries of each area $group = Employees::sum( array( "column" => "salary", "group" => "area" ) ); foreach ($group as $row) { echo "The sum of salaries of the ", $group->area, " is ", $group->sumatory; } // Generate a grouping of the salaries of each area ordering // salaries from higher to lower $group = Employees::sum( array( "column" => "salary", "group" => "area", "order" => "sumatory DESC" ) ); Average examples: .. code-block:: php <?php // What is the average salary for all employees? $average = Employees::average(array("column" => "salary")); // What is the average salary for the Sales's area employees? $average = Employees::average( array( "column" => "salary", "conditions" => "area = 'Sales'" ) ); Max/Min examples: .. code-block:: php <?php // What is the oldest age of all employees? $age = Employees::maximum(array("column" => "age")); // What is the oldest of employees from the Sales area? $age = Employees::maximum( array( "column" => "age", "conditions" => "area = 'Sales'" ) ); // What is the lowest salary of all employees? $salary = Employees::minimum(array("column" => "salary")); 缓å˜ç»“果集 ^^^^^^^^^^^^^^^^^^ 频ç¹è®¿é—®æ•°æ®åº“往往是WEBåº”ç”¨æ€§èƒ½æ–¹é¢æœ€å¸¸è§çš„ç“¶é¢ˆä¹‹ä¸€ã€‚è¿™æ˜¯ç”±äºŽå¤æ‚的连接过程,PHP必须在æ¯ä¸ªè¯·æ±‚都从数æ®åº“èŽ·å–æ•°æ®ã€‚一个较完善的技术架构是,将ä¸ç»å¸¸æ”¹å˜çš„结果集缓å˜åˆ°ç³»ç»Ÿä¸å¯ä»¥æ›´å¿«è®¿é—®çš„地方(通常是内å˜ï¼‰ã€‚ 当 :doc:`Phalcon\\Mvc\\Model <../api/Phalcon_Mvc_Model>` 需è¦ç¼“å˜ç»“果集时,它会ä¾èµ–于容器ä¸çš„"modelsCache"这个æœåŠ¡ã€‚ Phalconæä¾›äº†ä¸€ä¸ªç»„件缓å˜ä»»ä½•类型的数æ®ï¼Œæˆ‘们下é¢å°†ä»‹ç»å®ƒå¦‚何与模型一å—å·¥ä½œã€‚é¦–å…ˆï¼Œä½ éœ€è¦æŠŠå®ƒä½œä¸ºä¸€ä¸ªæœåŠ¡æ³¨å†Œåˆ°æœåС容噍ä¸ï¼š .. code-block:: php <?php //Set the models cache service $di->set('modelsCache', function(){ //Cache data for one day by default $frontCache = new Phalcon\Cache\Frontend\Data(array( "lifetime" => 86400 )); //Memcached connection settings $cache = new Phalcon\Cache\Backend\Memcached($frontCache, array( "host" => "localhost", "port" => "11211" )); return $cache; }); ä½ å¯ä»¥åˆ›å»ºå’Œè‡ªå®šä¹‰ç¼“å˜è§„则,然åŽä½œä¸ºä¸€ä¸ªåŒ¿å函数使用它们。一é‡ç¼“å˜è¢«æ£ç¡®è®¾ç½®ï¼Œå¯ä»¥æŒ‰å¦‚下方å¼ç¼“å˜ç»“果集: .. code-block:: php <?php // Get products without caching $products = Products::find(); // Just cache the resultset. The cache will expire in 1 hour (3600 seconds) $products = Products::find(array("cache" => true)); // Cache the resultset only for 5 minutes $products = Products::find(array("cache" => 300)); // Cache the resultset with a key pre-defined $products = Products::find(array("cache" => array("key" => "my-products-key"))); // Cache the resultset with a key pre-defined and for 2 minutes $products = Products::find( array( "cache" => array( "key" => "my-products-key", "lifetime" => 120 ) ) ); // Using a custom cache $products = Products::find(array("cache" => $myCache)); 默认情况下,:doc:`Phalcon\\Mvc\\Model <../api/Phalcon_Mvc_Model>` 将创建一个唯一的KEYæ¥ä¿å˜ç»“果集数æ®ï¼Œå®ƒä½¿ç”¨md5 hash内部SQLè¯å¥çš„æ–¹å¼æ¥ç”Ÿæˆå”¯ä¸€KEY,这将是éžå¸¸å®žç”¨çš„ï¼Œå› ä¸ºå®ƒä¼šäº§ç”Ÿä¸€ä¸ªæ–°çš„å”¯ä¸€çš„KEYå€¼ã€‚å¦‚æžœä½ æƒ³æ”¹å˜KEYå€¼ï¼Œä½ å¯ä»¥åƒä¸Šé¢çš„ç¤ºä¾‹ä¸€æ ·éšæ—¶ä½¿ç”¨key傿•°è¿›è¡ŒæŒ‡å®šï¼ŒgetLastKey()方法检索最åŽçš„缓å˜KEYå€¼ï¼Œè¿™æ ·å°±å¯ä»¥ä»Žç¼“å˜ä¸å®šä½å’Œæ£€ç´¢ç»“果集: .. code-block:: php <?php // Cache the resultset using an automatic key $products = Products::find(array("cache" => 3600)); // Get last generated key $automaticKey = $products->getCache()->getLastKey(); // Use resultset as normal foreach($products as $product){ //... } 缓å˜çš„KEY是通过 :doc:`Phalcon\\Mvc\\Model <../api/Phalcon_Mvc_Model>` 自动生æˆçš„,而且问题以"phc"为å‰è¾ï¼Œè¿™å°†æœ‰åŠ©äºŽè¯†åˆ«æ¤ç±»ç¼“å˜KEY是与 :doc:`Phalcon\\Mvc\\Model <../api/Phalcon_Mvc_Model>` 相关的: .. code-block:: php <?php // Set the cache to the models manager $cache = $di->getModelsCache(); // Get keys created by Phalcon\Mvc\Model foreach ($cache->queryKeys("phc") as $key) { echo $key, "\n"; } 请注æ„ï¼Œå¹¶éžæ‰€æœ‰çš„结果集都必须被缓å˜ã€‚å˜åŒ–éžå¸¸é¢‘ç¹çš„结果ä¸åº”该被缓å˜èµ·æ¥ï¼Œå› ä¸ºåœ¨è¿™ç§æƒ…å†µä¸‹ä»–ä»¬æ˜¯æ— æ•ˆçš„ï¼Œè€Œä¸”ä¼šå½±å“æ€§èƒ½ã€‚æ¤å¤–,ä¸ç»å¸¸æ›´æ”¹çš„大数æ®é›†å¯ä»¥è¢«ç¼“å˜ï¼Œä½†æ˜¯å¦ä¸€å®šéœ€è¦ç¼“å˜å¾—è¡¡é‡ä¸€ä¸‹ï¼Œä¸å¯¹æ€§èƒ½é€ æˆä¸€å®šçš„å½±å“,还是å¯ä»¥æŒ‰å—的。 åŒæ ·ï¼Œç¼“å˜ç³»ç»Ÿä¹Ÿå¯ä»¥åº”用于使用关è”关系生æˆçš„结果集: .. code-block:: php <?php // Query some post $post = Post::findFirst(); // Get comments related to a post, also cache it $comments = $post->getComments(array("cache" => true)); // Get comments related to a post, setting lifetime $comments = $post->getComments(array("cache" => true, "lifetime" => 3600)); 当获å–缓å˜ç»“æžœé›†å¤±è´¥æ—¶ï¼Œä½ å¯ä»¥ç®€å•的通过它的KEY值从缓å˜ç³»ç»Ÿä¸åˆ 除它。 Creating Updating/Records ------------------------- Phalcon\\Mvc\\Model::save() 方法å…è®¸ä½ åˆ›å»º/更新记录。save方法自动调用 :doc:`Phalcon\\Mvc\\Model <../api/Phalcon_Mvc_Model>` 内部的createå’Œupdate方法,如果想达到预期般的工作效果,æ£ç¡®å®šä¹‰å®žä½“主键是éžå¸¸å¿…须的,以确ä¿åˆ›å»ºå’Œæ›´æ–°è®°å½•æˆåŠŸã€‚ åŒæ—¶ï¼Œæ–¹æ³•的执行关è”到 validators,虚拟外键以åŠåœ¨æ¨¡åž‹ä¸å®šä¹‰çš„事件: .. code-block:: php <?php $robot = new Robots(); $robot->type = "mechanical"; $robot->name = "Astro Boy"; $robot->year = 1952; if ($robot->save() == false) { echo "Umh, We can't store robots right now: \n"; foreach ($robot->getMessages() as $message) { echo $message, "\n"; } } else { echo "Great, a new robot was saved successfully!"; } save方法还å¯ä»¥ç›´æŽ¥é€šè¿‡ä¼ 入一个数组的形å¼è¿›è¡Œä¿å˜æ•°æ®ï¼ŒPhalcon\\Mvc\\Model ä¼šè‡ªåŠ¨å®Œæˆæ•°ç»„和对象的绑定的,而ä¸éœ€è¦ç›´æŽ¥æŒ‡å®šå¯¹è±¡çš„属性值: .. code-block:: php <?php $robot = new Robots(); $robot->save(array( "type" => "mechanical", "name" => "Astro Boy", "year" => 1952 )); æ•°æ®ç›´æŽ¥èµ‹å€¼æˆ–通过数组绑定,这些数æ®éƒ½ä¼šæ ¹æ®ç›¸å…³çš„æ•°æ®ç±»åž‹è¢«escaped/sanitizedï¼Œæ‰€ä»¥ä½ å¯ä»¥ä¼ 递一个ä¸å®‰å…¨çš„æ•°ç»„,而ä¸å¿…担心å‘生SQL注入: .. code-block:: php <?php $robot = new Robots(); $robot->save($_POST); Create/Update with Certainty ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ å½“ä¸€ä¸ªåº”ç”¨ç¨‹åºæœ‰å¾ˆå¤šçš„竞争的时候,也许我们希望创建一个记录,但实际上是更新一个记录(想ä¸åˆ°è€å¤–也æžä½œå½ï¼Œå“ˆå“ˆï¼‰ã€‚如果我们使用Phalcon\\Mvc\\Model::save()ä¿å˜æ•°æ®åˆ°æ•°æ®åº“,首先我们得确定我们的记录是将被创建还是更新: .. code-block:: php <?php $robot = new Robots(); $robot->type = "mechanical"; $robot->name = "Astro Boy"; $robot->year = 1952; //This record only must be created if ($robot->create() == false) { echo "Umh, We can't store robots right now: \n"; foreach ($robot->getMessages() as $message) { echo $message, "\n"; } } else { echo "Great, a new robot was created successfully!"; } 方法"create"å’Œ"update"éƒ½æŽ¥å—æ•°ç»„ä½œä¸ºå‚æ•°. Auto-generated identity columns ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 有些模型å¯èƒ½æœ‰æ ‡è¯†åˆ—ã€‚è¿™äº›åˆ—é€šå¸¸æ˜¯æ˜ å°„æ•°æ®è¡¨çš„主键。 :doc:`Phalcon\\Mvc\\Model <../api/Phalcon_Mvc_Model>` å¯ä»¥è¯†åˆ«æ ‡è¯†åˆ—ï¼ŒåŒæ—¶ä¼šå¿½ç•¥å®ƒå†…部的SQL INSERT,所以数æ®åº“系统能够生æˆä¸€ä¸ªè‡ªåŠ¨ç”Ÿæˆçš„值。在创建一个记录åŽï¼Œæ ‡è¯†åˆ—总是会通过数æ®åº“系统产生一个值: .. code-block:: php <?php $robot->save(); echo "The generated id is: ", $robot->id; :doc:`Phalcon\\Mvc\\Model <../api/Phalcon_Mvc_Model>` èƒ½å¤Ÿè¯†åˆ«æ ‡è¯†åˆ—ã€‚æ ¹æ®ä¸åŒçš„æ•°æ®åº“系统,这些列å¯èƒ½æ˜¯ä¸²è¡Œåˆ—,例如PostgreSQL以åŠMYSQLçš„auto_increment列。 PostgreSQL使用åºåˆ—æ¥ç”Ÿæˆè‡ªåŠ¨çš„æ•°å€¼ï¼Œé»˜è®¤æƒ…å†µä¸‹ï¼ŒPhalcon试图多åºåˆ—table_field_seqæ¥èŽ·å¾—ç”Ÿæˆçš„值,例如:robots_id_seq,如果该åºåˆ—具有ä¸åŒçš„å称,"getSequenceName"æ–¹æ³•éœ€è¦æ˜Žç¡®æŒ‡å®šï¼š .. code-block:: php <?php class Robots extends \Phalcon\Mvc\Model { public function getSequenceName() { return "robots_sequence_name"; } } Validation Messages ^^^^^^^^^^^^^^^^^^^ :doc:`Phalcon\\Mvc\\Model <../api/Phalcon_Mvc_Model>` 有一个消æ¯ä¼ 递å系统,它æä¾›äº†ä¸€ä¸ªçµæ´»çš„输出方å¼ï¼Œæˆ–å˜å‚¨åœ¨insert/update过程ä¸çš„éªŒè¯æ¶ˆæ¯ã€‚ æ¯ä¸ªæ¶ˆæ¯éƒ½æ˜¯ç±» :doc:`Phalcon\\Mvc\\Model\\Message <../api/Phalcon_Mvc_Model_Message>` 的一个实例对象。生æˆçš„该组消æ¯å¯ä»¥é€šè¿‡getMessages()方法æ¥èŽ·å–。æ¯ä¸ªæ¶ˆæ¯éƒ½æä¾›äº†æ‰©å±•的信æ¯ï¼Œå¦‚å—æ®µåç§°ï¼ŒåŒæ—¶äº§ç”Ÿäº†æ¶ˆæ¯åŠæ¶ˆæ¯ç±»åž‹ï¼š .. code-block:: php <?php if ($robot->save() == false) { foreach ($robot->getMessages() as $message) { echo "Message: ", $message->getMessage(); echo "Field: ", $message->getField(); echo "Type: ", $message->getType(); } } :doc:`Phalcon\\Mvc\\Model <../api/Phalcon_Mvc_Model>` 也å¯ä»¥äº§ç”Ÿä»¥ä¸‹ç±»åž‹çš„éªŒè¯æ¶ˆæ¯ï¼š +---------------------+------------------------------------------------------------------------------------------------------------------------------------+ | Type | Description | +=====================+====================================================================================================================================+ | PresenceOf | Generated when a field with a non-null attribute on the database is trying to insert/update a null value | +---------------------+------------------------------------------------------------------------------------------------------------------------------------+ | ConstraintViolation | Generated when a field part of a virtual foreign key is trying to insert/update a value that doesn't exist in the referenced model | +---------------------+------------------------------------------------------------------------------------------------------------------------------------+ | InvalidValue | Generated when a validator failed because of an invalid value | +---------------------+------------------------------------------------------------------------------------------------------------------------------------+ 验è¯äº‹ä»¶åŠäº‹ä»¶ç®¡ç† ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 模型å…è®¸ä½ å®žçŽ°äº‹ä»¶ï¼Œå½“æ‰§è¡Œinsertå’Œupdateçš„æ—¶å€™ï¼Œè¿™äº›äº‹ä»¶å°†è¢«æŠ›å‡ºã€‚ä»–ä»¬å¸®åŠ©ä½ å®šä¹‰ä¸šåŠ¡è§„åˆ™ã€‚ä»¥ä¸‹æ˜¯ :doc:`Phalcon\\Mvc\\Model <../api/Phalcon_Mvc_Model>` 支æŒçš„事件以åŠä»–们的执行顺åºï¼š +--------------------+--------------------------+-----------------------+---------------------------------------------------------------------------------------------------------------------+ | Operation | Name | Can stop operation? | Explanation | +====================+==========================+=======================+=====================================================================================================================+ | Inserting/Updating | beforeValidation | YES | Is executed before the fields are validated for not nulls or foreign keys | +--------------------+--------------------------+-----------------------+---------------------------------------------------------------------------------------------------------------------+ | Inserting | beforeValidationOnCreate | YES | Is executed before the fields are validated for not nulls or foreign keys when an insertion operation is being made | +--------------------+--------------------------+-----------------------+---------------------------------------------------------------------------------------------------------------------+ | Updating | beforeValidationOnUpdate | YES | Is executed before the fields are validated for not nulls or foreign keys when an updating operation is being made | +--------------------+--------------------------+-----------------------+---------------------------------------------------------------------------------------------------------------------+ | Inserting/Updating | onValidationFails | YES (already stopped) | Is executed after an integrity validator fails | +--------------------+--------------------------+-----------------------+---------------------------------------------------------------------------------------------------------------------+ | Inserting | afterValidationOnCreate | YES | Is executed after the fields are validated for not nulls or foreign keys when an insertion operation is being made | +--------------------+--------------------------+-----------------------+---------------------------------------------------------------------------------------------------------------------+ | Updating | afterValidationOnUpdate | YES | Is executed after the fields are validated for not nulls or foreign keys when an updating operation is being made | +--------------------+--------------------------+-----------------------+---------------------------------------------------------------------------------------------------------------------+ | Inserting/Updating | afterValidation | YES | Is executed after the fields are validated for not nulls or foreign keys | +--------------------+--------------------------+-----------------------+---------------------------------------------------------------------------------------------------------------------+ | Inserting/Updating | beforeSave | YES | Runs before the required operation over the database system | +--------------------+--------------------------+-----------------------+---------------------------------------------------------------------------------------------------------------------+ | Updating | beforeUpdate | YES | Runs before the required operation over the database system only when an updating operation is being made | +--------------------+--------------------------+-----------------------+---------------------------------------------------------------------------------------------------------------------+ | Inserting | beforeCreate | YES | Runs before the required operation over the database system only when an inserting operation is being made | +--------------------+--------------------------+-----------------------+---------------------------------------------------------------------------------------------------------------------+ | Updating | afterUpdate | NO | Runs after the required operation over the database system only when an updating operation is being made | +--------------------+--------------------------+-----------------------+---------------------------------------------------------------------------------------------------------------------+ | Inserting | afterCreate | NO | Runs after the required operation over the database system only when an inserting operation is being made | +--------------------+--------------------------+-----------------------+---------------------------------------------------------------------------------------------------------------------+ | Inserting/Updating | afterSave | NO | Runs after the required operation over the database system | +--------------------+--------------------------+-----------------------+---------------------------------------------------------------------------------------------------------------------+ 为了使模型对事件作出å应,我们必须实现一个方法具有相åŒå称的事件: .. code-block:: php <?php class Robots extends \Phalcon\Mvc\Model { public function beforeValidationOnCreate() { echo "This is executed before create a Robot!"; } } äº‹ä»¶åŒæ ·å¯ä»¥åœ¨æ‰§è¡Œä¸€ä¸ªæ“作之å‰åšèµ‹å€¼æ“ä½œï¼Œè¿™å°†ä¼šå¾ˆæœ‰ç”¨ï¼Œä¸‹é¢æ˜¯ç¤ºä¾‹ï¼š .. code-block:: php <?php class Products extends \Phalcon\Mvc\Model { public function beforeCreate() { //Set the creation date $this->created_at = date('Y-m-d H:i:s'); } public function beforeUpdate() { //Set the modification date $this->modified_in = date('Y-m-d H:i:s'); } } æ¤å¤–,该组件将与 :doc:`Phalcon\\Events\\Manager <../api/Phalcon_Events_Manager>` 一åŒå·¥ä½œï¼Œè¿™æ„味ç€å½“äº‹ä»¶è¢«è§¦å‘æ—¶ï¼Œæˆ‘们å¯ä»¥åˆ›å»ºç›‘å¬å™¨ã€‚ .. code-block:: php <?php $eventsManager = new Phalcon\Events\Manager(); //Attach an anonymous function as a listener for "model" events $eventsManager->attach('model', function($event, $robot) { if ($event->getType() == 'beforeSave') { if ($robot->name == 'Scooby Doo') { echo "Scooby Doo isn't a robot!"; return false; } } return true; }); $robot = new Robots(); $robot->setEventsManager($eventsManager); $robot->name = 'Scooby Doo'; $robot->year = 1969; $robot->save(); 在上é¢çš„例åä¸ï¼Œäº‹ä»¶ç®¡ç†åªæ˜¯ä½œä¸ºå¯¹è±¡å’Œç›‘å¬å™¨ï¼ˆåŒ¿å函数)之间的桥æ¢ã€‚如果我们想è¦åœ¨æˆ‘们的应用程åºä¸åˆ›å»ºçš„æ‰€æœ‰å¯¹è±¡ä½¿ç”¨ç›¸åŒçš„事件管ç†ï¼Œé‚£ä¹ˆæˆ‘们就需è¦åˆ°æŒ‡å®šçš„æ¨¡åž‹ç®¡ç†å™¨ï¼š .. code-block:: php <?php //Registering the modelsManager service $di->setShared('modelsManager', function() { $eventsManager = new Phalcon\Events\Manager(); //Attach an anonymous function as a listener for "model" events $eventsManager->attach('model', function($event, $model){ if (get_class($model) == 'Robots') { if ($event->getType() == 'beforeSave') { if ($modle->name == 'Scooby Doo') { echo "Scooby Doo isn't a robot!"; return false; } } } return true; }); //Setting a default EventsManager $modelsManager = new Phalcon\Mvc\Models\Manager(); $modelsManager->setEventsManager($eventsManager); return $modelsManager; }); Implementing a Business Rule ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 当执行insert,update或delete的时候,如果有任何方法å称与上表列出的事件å称相åŒï¼Œæ¨¡åž‹éªŒè¯å°†èµ·ä½œç”¨ã€‚ æˆ‘ä»¬å»ºè®®éªŒè¯æ–¹æ³•被声明为protected,以防æ¢ä¸šåŠ¡é€»è¾‘ä¸è¢«å…¬å¼€ã€‚ 下é¢çš„示例实现验è¯åœ¨update或insert时,yearä¸å°äºŽ0的事件: .. code-block:: php <?php class Robots extends \Phalcon\Mvc\Model { public function beforeSave() { if ($this->year < 0) { echo "Year cannot be smaller than zero!"; return false; } } } 有些事件返回falseç”¨äºŽæŒ‡ç¤ºåœæ¢å½“剿“作。如果一个事件没有返回任何东西,:doc:`Phalcon\\Mvc\\Model <../api/Phalcon_Mvc_Model>` å°†å‡è®¾å®ƒè¿”回true。 Validating Data Integrity ^^^^^^^^^^^^^^^^^^^^^^^^^ :doc:`Phalcon\\Mvc\\Model <../api/Phalcon_Mvc_Model>` æä¾›äº†å‡ 个事件æ¥éªŒè¯æ•°æ®ï¼Œå¹¶å®žçŽ°ä¸šåŠ¡è§„åˆ™ã€‚ç‰¹æ®Šçš„"validation"事件能使我们能够调用内置的验è¯å™¨ã€‚Phalconå‘布了一些内置的验è¯å™¨ï¼Œå¯ç”¨äºŽåœ¨è¿™ä¸ªé˜¶æ®µçš„验è¯ã€‚ 以下示例显示了如何使用它: .. code-block:: php <?php use Phalcon\Mvc\Model\Validator\InclusionIn; use Phalcon\Mvc\Model\Validator\Uniqueness; class Robots extends \Phalcon\Mvc\Model { public function validation() { $this->validate(new InclusionIn( array( "field" => "type", "domain" => array("Mechanical", "Virtual") ) )); $this->validate(new Uniqueness( array( "field" => "name", "message" => "The robot name must be unique" ) )); return $this->validationHasFailed() != true; } } 上é¢çš„例åä¸ï¼Œä½¿ç”¨å†…置的验è¯å™¨â€œInclusionInâ€æ‰§è¡ŒéªŒè¯ã€‚检查值在域列表ä¸çš„“typeâ€ã€‚如果该值没有被包括在该方法ä¸ï¼Œé‚£ä¹ˆéªŒè¯ç¨‹åºå°†å¤±è´¥å¹¶è¿”回false。下列内置的验è¯å™¨æ˜¯å¯ç”¨çš„: +--------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------+ | Name | Explanation | Example | +==============+==================================================================================================================================================================+===================================================================+ | PresenceOf | Validates that a field's value isn't null or empty string. This validator is automatically added based on the attributes marked as not null on the mapped table | :doc:`Example <../api/Phalcon_Mvc_Model_Validator_PresenceOf>` | +--------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------+ | Email | Validates that field contains a valid email format | :doc:`Example <../api/Phalcon_Mvc_Model_Validator_Email>` | +--------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------+ | ExclusionIn | Validates that a value is not within a list of possible values | :doc:`Example <../api/Phalcon_Mvc_Model_Validator_Exclusionin>` | +--------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------+ | InclusionIn | Validates that a value is within a list of possible values | :doc:`Example <../api/Phalcon_Mvc_Model_Validator_Inclusionin>` | +--------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------+ | Numericality | Validates that a field has a numeric format | :doc:`Example <../api/Phalcon_Mvc_Model_Validator_Numericality>` | +--------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------+ | Regex | Validates that the value of a field matches a regular expression | :doc:`Example <../api/Phalcon_Mvc_Model_Validator_Regex>` | +--------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------+ | Uniqueness | Validates that a field or a combination of a set of fields are not present more than once in the existing records of the related table | :doc:`Example <../api/Phalcon_Mvc_Model_Validator_Uniqueness>` | +--------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------+ | StringLength | Validates the length of a string | :doc:`Example <../api/Phalcon_Mvc_Model_Validator_StringLength>` | +--------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------+ 除了使用这些内置验è¯å™¨ï¼Œä½ 还å¯ä»¥åˆ›å»ºä½ 自己的验è¯å™¨ï¼š .. code-block:: php <?php use \Phalcon\Mvc\Model\Validator, \Phalcon\Mvc\Model\ValidatorInterface; class UrlValidator extends Validator implements ValidatorInterface { public function validate($model) { $field = $this->getOption('field'); $value = $model->$field; $filtered = filter_var($value, FILTER_VALIDATE_URL); if (!$filtered) { $this->appendMessage("The URL is invalid", $field, "UrlValidator"); return false; } return true; } } æŠŠä½ ç¼–å†™çš„éªŒè¯å™¨ç»‘定到模型上: .. code-block:: php <?php class Customers extends \Phalcon\Mvc\Model { public function validation() { $this->validate(new UrlValidator( array( "field" => "url", ) )); if ($this->validationHasFailed() == true) { return false; } } } 创建自定义验è¯å™¨ï¼Œä¸»è¦æƒ³æ³•是让他们å¯ä»¥åœ¨ä¸åŒçš„æ¨¡åž‹ä¸ä½¿ç”¨ï¼Œå³ä»£ç å¤ç”¨ã€‚一个验è¯å™¨ä¹Ÿå¯ä»¥æŒ‰ä»¥ä¸‹æ–¹å¼å®žçŽ°ï¼š .. code-block:: php <?php class Robots extends \Phalcon\Mvc\Model { public function validation() { if ($this->type == "Old") { $message = new Phalcon\Mvc\Model\Message( "Sorry, old robots are not allowed anymore", "type", "MyType" ); $this->appendMessage($message); return false; } return true; } } é¿å…SQL注入攻击 ^^^^^^^^^^^^^^^^^^^^^^^ æ¯ä¸ªè¢«èµ‹å€¼åˆ°æ¨¡åž‹å±žæ€§ä¸Šçš„值在ä¿å˜åˆ°æ•°æ®åº“之å‰éƒ½å°†æŒ‰ç…§æ•°æ®ç±»åž‹è¢«è½¬ä¹‰ï¼Œå¼€å‘人员ä¸éœ€è¦æ‰‹å·¥è½¬ä¹‰æ¯ä¸ªå€¼ã€‚Phalcon内部使用 `bound parameters <http://php.net/manual/en/pdostatement.bindparam.php>`_ PDOæä¾›è½¬ä¹‰ã€‚ .. code-block:: bash mysql> desc products; +------------------+------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------------+------------------+------+-----+---------+----------------+ | id | int(10) unsigned | NO | PRI | NULL | auto_increment | | product_types_id | int(10) unsigned | NO | MUL | NULL | | | name | varchar(70) | NO | | NULL | | | price | decimal(16,2) | NO | | NULL | | | active | char(1) | YES | | NULL | | +------------------+------------------+------+-----+---------+----------------+ 5 rows in set (0.00 sec) 如果我们åªä½¿ç”¨PDOæ¥å®‰å…¨çš„å˜å‚¨ä¸€æ¡è®°å½•,我们需è¦ç¼–写以下代ç : .. code-block:: php <?php $productTypesId = 1; $name = 'Artichoke'; $price = 10.5; $active = 'Y'; $sql = 'INSERT INTO products VALUES (null, :productTypesId, :name, :price, :active)'; $sth = $dbh->prepare($sql); $sth->bindParam(':productTypesId', $productTypesId, PDO::PARAM_INT); $sth->bindParam(':name', $name, PDO::PARAM_STR, 70); $sth->bindParam(':price', doubleval($price)); $sth->bindParam(':active', $active, PDO::PARAM_STR, 1); $sth->execute(); å¥½æ¶ˆæ¯æ˜¯ï¼ŒPhalcon自动为您åšåˆ°è¿™ä¸€ç‚¹ï¼š .. code-block:: php <?php $product = new Products(); $product->product_types_id = 1; $product->name = 'Artichoke'; $product->price = 10.5; $product->active = 'Y'; $product->create(); Skipping Columns ---------------- 有时候,有一些数æ®ä½¿ç”¨æ•°æ®åº“系统的触å‘å™¨æˆ–é»˜è®¤å€¼ï¼Œå› æ¤æˆ‘们在insert/update的时候,会忽略掉这些属性: .. code-block:: php <?php class Robots extends \Phalcon\Mvc\Model { public function initialize() { //Skips fields/columns on both INSERT/UPDATE operations $this->skipAttributes(array('year', 'price')); //Skips only when inserting $this->skipAttributesOnCreate(array('created_at')); //Skips only when updating $this->skipAttributesOnUpdate(array('modified_in')); } } 这时,在整个应用程åºä¸æ‰§è¡Œinsert/updateçš„æ—¶å€™ï¼Œéƒ½ä¼šå¿½ç•¥è¿™äº›å€¼çš„ä¼ é€’ã€‚ 强制一个默认值,å¯ä»¥ä»¥ä¸‹åˆ—æ–¹å¼è¿›è¡Œï¼š .. code-block:: php <?php $robot = new Robots(); $robot->name = 'Bender'; $robot->year = 1999; $robot->created_at = new Phalcon\Db\RawValue('default'); $robot->create(); åˆ é™¤è®°å½• ---------------- Phalcon\\Mvc\\Model::delete() å…è®¸åˆ é™¤ä¸€æ¡è®°å½•ï¼Œä½ å¯ä»¥æŒ‰å¦‚下方å¼ä½¿ç”¨ï¼š .. code-block:: php <?php $robot = Robots::findFirst(11); if ($robot != false) { if ($robot->delete() == false) { echo "Sorry, we can't delete the robot right now: \n"; foreach ($robot->getMessages() as $message) { echo $message, "\n"; } } else { echo "The robot was deleted successfully!"; } } ä½ ä¹Ÿå¯ä»¥é€šè¿‡ä½¿ç”¨foreaché历一个结果集的方å¼åˆ 除多æ¡è®°å½•: .. code-block:: php <?php foreach (Robots::find("type='mechanical'") as $robot) { if ($robot->delete() == false) { echo "Sorry, we can't delete the robot right now: \n"; foreach ($robot->getMessages() as $message) { echo $message, "\n"; } } else { echo "The robot was deleted successfully!"; } } å½“æ‰§è¡Œä¸€ä¸ªåˆ é™¤æ“ä½œæ—¶ï¼Œä½ å¯ä»¥ä½¿ç”¨ä»¥ä¸‹äº‹ä»¶å®šä¹‰ä¸€ä¸ªè‡ªå®šä¹‰çš„业务规则: +-----------+--------------+---------------------+------------------------------------------+ | Operation | Name | Can stop operation? | Explanation | +===========+==============+=====================+==========================================+ | Deleting | beforeDelete | YES | Runs before the delete operation is made | +-----------+--------------+---------------------+------------------------------------------+ | Deleting | afterDelete | NO | Runs after the delete operation was made | +-----------+--------------+---------------------+------------------------------------------+ Validation Failed Events ------------------------ å¦ä¸€ç§ç±»åž‹çš„äº‹ä»¶æ˜¯ï¼Œå½“ä½ éªŒè¯æ•°æ®è¿‡ç¨‹ä¸å‘现任何ä¸ä¸€è‡´æ—¶ï¼š +--------------------------+--------------------+--------------------------------------------------------------------+ | Operation | Name | Explanation | +==========================+====================+====================================================================+ | Insert or Update | notSave | Triggered when the INSERT or UPDATE operation fails for any reason | +--------------------------+--------------------+--------------------------------------------------------------------+ | Insert, Delete or Update | onValidationFails | Triggered when any data manipulation operation fails | +--------------------------+--------------------+--------------------------------------------------------------------+ 事务管ç†(Transactions) ------------------------- 当一个进程执行多个数æ®åº“æ“作时,如果è¦ä¿è¯æ•°æ®çš„完整性,那么它æ¯ä¸ªæ¥éª¤çš„æ‰§è¡Œéƒ½å¿…é¡»ä¿è¯æ˜¯æˆåŠŸçš„ã€‚äº‹åŠ¡æä¾›äº†åœ¨æ•°æ®è¢«æäº¤åˆ°æ•°æ®åº“之å‰ï¼Œä¿è¯æ‰€æœ‰æ•°æ®åº“æ“作被æˆåŠŸæ‰§è¡Œçš„èƒ½åŠ›ã€‚ 在Phalconä¸ï¼Œäº‹åŠ¡å…è®¸ä½ æäº¤æ‰€æœ‰æ“ä½œï¼Œå¦‚æžœå‡ºçŽ°äº†é”™è¯¯ï¼Œä½ å¯ä»¥å›žæ»šæ‰€æœ‰çš„æ“ä½œã€‚ .. code-block:: php <?php try { //Create a transaction manager $manager = new Phalcon\Mvc\Model\Transaction\Manager(); // Request a transaction $transaction = $manager->get(); $robot = new Robots(); $robot->setTransaction($transaction); $robot->name = "WALL·E"; $robot->created_at = date("Y-m-d"); if ($robot->save() == false) { $transaction->rollback("Cannot save robot"); } $robotPart = new RobotParts(); $robotPart->setTransaction($transaction); $robotPart->type = "head"; if ($robotPart->save() == false) { $transaction->rollback("Cannot save robot part"); } //Everything goes fine, let's commit the transaction $transaction->commit(); } catch(Phalcon\Mvc\Model\Transaction\Failed $e) { echo "Failed, reason: ", $e->getMessage(); } Transactions can be used to delete many records in a consistent way: .. code-block:: php <?php use Phalcon\Mvc\Model\Transaction\Manager as Tx, Phalcon\Mvc\Model\Transaction\Failed as TxFailed; try { //Create a transaction manager $manager = new Tx(); //Request a transaction $transaction = $manager->get(); //Get the robots will be deleted foreach (Robots::find("type='mechanical'") as $robot) { $robot->setTransaction($transaction); if ($robot->delete() == false) { //Something goes wrong, we should to rollback the transaction foreach ($robot->getMessages() as $message) { $transaction->rollback($message->getMessage()); } } } //Everything goes fine, let's commit the transaction $transaction->commit(); echo "Robots were deleted successfully!"; } catch(TxFailed $e) { echo "Failed, reason: ", $e->getMessage(); } 事务总是被é‡å¤ä½¿ç”¨ã€‚æˆ‘ä»¬å¸Œæœ›åªæœ‰å½“commit()或rollback()被执行的时候,æ‰ä¼šäº§ç”Ÿä¸€ä¸ªäº‹åŠ¡çš„å®žä¾‹ï¼Œä½ å¯ä»¥æŠŠäº‹åŠ¡æ³¨å†Œä¸ºæ•´ä¸ªåº”ç”¨ç¨‹åºçš„一个æœåŠ¡ï¼Œå½“ä½œä¸€ä¸ªæ•´ä½“çš„äº‹åŠ¡ç®¡ç†å™¨ä½¿ç”¨ï¼š .. code-block:: php <?php $di->setShared('transactions', function(){ return new Phalcon\Mvc\Model\Transaction\Manager(); }); ç„¶åŽæˆ‘们å¯ä»¥åœ¨æŽ§åˆ¶å™¨å’Œè§†å›¾ä¸ç›´æŽ¥è®¿é—®å®ƒï¼š .. code-block:: php <?php class ProductsController extends \Phalcon\Mvc\Controller { public function saveAction() { //Obtain the TransactionsManager from the DI container $manager = $this->di->getTransactions(); //Request a transaction $transaction = $manager->get(); } } Independent Column Mapping -------------------------- ORM支æŒç‹¬ç«‹çš„åˆ—æ˜ å°„ï¼Œå®ƒå…许开å‘人员在模型ä¸çš„属性ä¸åŒäºŽæ•°æ®åº“çš„å—æ®µå称。Phalcon能够识别新的列å,并会相应的进行é‡å‘½å,以对应数æ®åº“ä¸çš„å—æ®µã€‚ è¿™æ˜¯ä¸€ä¸ªä¼Ÿå¤§çš„åŠŸèƒ½ï¼Œå½“ä½ éœ€è¦é‡å‘½åæ•°æ®åº“ä¸çš„å—æ®µï¼Œè€Œä¸å¿…担心代ç 䏿‰€æœ‰çš„æŸ¥è¯¢ã€‚示例如下: .. code-block:: php <?php class Robots extends Phalcon\Mvc\Model { public function columnMap() { //Keys are the real names in the table and //the values their names in the application return array( 'id' => 'code', 'the_name' => 'theName', 'the_type' => 'theType', 'the_year' => 'theYear' ); } } ç„¶åŽä½ å°±å¯ä»¥åœ¨ä½ 的代ç ä¸ç†æ‰€å½“然的使用新的属性å称: .. code-block:: php <?php //Find a robot by its name $robot = Robots::findFirst("theName = 'Voltron'"); echo $robot->theName, "\n"; //Get robots ordered by type $robot = Robots::find(array('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(); 当有下é¢çš„æƒ…å†µæ—¶ï¼Œä½ å¯ä»¥è€ƒè™‘使用新的别å: * 在relationships/validatorsä¸ï¼Œå¿…须使用新的åç§° * 列å会导致ORM的异常å‘生 Models Meta-Data ---------------- ä¸ºäº†åŠ å¿«å¼€å‘ :doc:`Phalcon\\Mvc\\Model <../api/Phalcon_Mvc_Model>` å¸®åŠ©ä½ ä»Žæ•°æ®è¡¨ä¸æŸ¥è¯¢å—æ®µä»¥åŠæŸ¥è¯¢æ•°æ®åº“的约æŸã€‚è¦åšåˆ°è¿™ä¸€ç‚¹ï¼Œ:doc:`Phalcon\\Mvc\\Model\\MetaData <../api/Phalcon_Mvc_Model_MetaData>` 用于管ç†å’Œç¼“å˜è¿™äº›å…ƒæ•°æ®ã€‚ 有时,需è¦ä½¿ç”¨æ¨¡åž‹èŽ·å–那些元数æ®çš„ï¼Œä½ å¯ä»¥é€šè¿‡ä»¥ä¸‹ç¤ºä¾‹èŽ·å¾—ï¼š .. code-block:: php <?php $robot = new Robots(); // Get Phalcon\Mvc\Model\Metadata instance $metaData = $robot->getDI()->getModelsMetaData(); // Get robots fields names $attributes = $metaData->getAttributes($robot); print_r($attributes); // Get robots fields data types $dataTypes = $metaData->getDataTypes($robot); print_r($dataTypes); Caching Meta-Data ^^^^^^^^^^^^^^^^^ 应用程åºåœ¨ä¸€ä¸ªç”Ÿäº§é˜¶æ®µæ—¶ï¼Œæ²¡æœ‰å¿…è¦æ€»æ˜¯ä»Žæ•°æ®åº“ç³»ç»Ÿä¸æŸ¥è¯¢å…ƒæ•°æ®ï¼Œä½ å¯ä»¥ä½¿ç”¨ä»¥ä¸‹çš„å‡ ç§é€‚é…器把这些元数æ®ç¼“å˜èµ·æ¥ï¼š +---------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------+ | Adapter | Description | API | +=========+===============================================================================================================================================================================================================================================================================================================================================+===========================================================================================+ | Memory | This adapter is the default. The meta-data is cached only during the request. When the request is completed, the meta-data are released as part of the normal memory of the request. This adapter is perfect when the application is in development so as to refresh the meta-data in each request containing the new and/or modified fields. | :doc:`Phalcon\\Mvc\\Model\\MetaData\\Memory <../api/Phalcon_Mvc_Model_MetaData_Memory>` | +---------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------+ | Session | This adapter stores meta-data in the $_SESSION superglobal. This adapter is recommended only when the application is actually using a small number of models. The meta-data are refreshed every time a new session starts. This also requires the use of session_start() to start the session before using any models. | :doc:`Phalcon\\Mvc\\Model\\MetaData\\Session <../api/Phalcon_Mvc_Model_MetaData_Session>` | +---------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------+ | Apc | The Apc adapter uses the `Alternative PHP Cache (APC)`_ to store the table meta-data. You can specify the lifetime of the meta-data with options. This is the most recommended way to store meta-data when the application is in production stage. | :doc:`Phalcon\\Mvc\\Model\\MetaData\\Apc <../api/Phalcon_Mvc_Model_MetaData_Apc>` | +---------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------+ | Files | This adapter uses plain files to store meta-data. By using this adapter the disk-reading is increased but the database access is reduced | :doc:`Phalcon\\Mvc\\Model\\MetaData\\Files <../api/Phalcon_Mvc_Model_MetaData_Files>` | +---------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------+ 作为其他ORMçš„ä¾èµ–,元数æ®éœ€è¦ä»ŽæœåС容噍ä¸èŽ·å¾—ï¼š .. code-block:: php <?php $di->setShared('modelsMetadata', function() { // Create a meta-data manager with APC $metaData = new Phalcon\Mvc\Model\MetaData\Apc( array( "lifetime" => 86400, "suffix" => "my-suffix" ) ); return $metaData; }); Manual Meta-Data ^^^^^^^^^^^^^^^^ Phalconå¯ä»¥è‡ªåŠ¨çš„èŽ·å¾—å…ƒæ•°æ®ï¼Œä¸å¼ºåˆ¶å¼€å‘人员必须手工设定他们。 请注æ„ï¼Œæ‰‹å·¥å®šä¹‰å…ƒæ•°æ®æ—¶ï¼Œæ·»åŠ /修改/åˆ é™¤ æ•°æ®è¡¨å—æ®µçš„æ—¶å€™ï¼Œå¿…é¡»æ‰‹å·¥æ·»åŠ ï¼ä¿®æ”¹ï¼åˆ 除 元数æ®å¯¹åº”列,以ä¿è¯ä¸€åˆ‡æ£å¸¸å·¥ä½œã€‚ 下é¢çš„例忼”示了如何手工定义元数æ®ï¼š .. code-block:: php <?php use Phalcon\Mvc\Model\MetaData; use Phalcon\Db\Column; class Robots extends Phalcon\Mvc\Model { public function metaData() { return array( //Every column in the mapped table MetaData::MODELS_ATTRIBUTES => array( 'id', 'name', 'type', 'year' ), //Every column part of the primary key MetaData::MODELS_PRIMARY_KEY => array( 'id' ), //Every column that isn't part of the primary key MetaData::MODELS_NON_PRIMARY_KEY => array( 'name', 'type', 'year' ), //Every column that doesn't allows null values MetaData::MODELS_NOT_NULL => array( 'id', 'name', 'type', 'year' ), //Every column and their data types MetaData::MODELS_DATA_TYPES => array( 'id' => Column::TYPE_INTEGER, 'name' => Column::TYPE_VARCHAR, 'type' => Column::TYPE_VARCHAR, 'year' => Column::TYPE_INTEGER ), //The columns that have numeric data types MetaData::MODELS_DATA_TYPES_NUMERIC => array( 'id' => true, 'year' => true, ), //The identity column MetaData::MODELS_IDENTITY_COLUMN => 'id', //How every column must be bound/casted MetaData::MODELS_DATA_TYPES_BIND => array( 'id' => Column::BIND_PARAM_INT, 'name' => Column::BIND_PARAM_STR, 'type' => Column::BIND_PARAM_STR, 'year' => Column::BIND_PARAM_INT, ), //Fields that must be ignored from INSERT/UPDATE SQL statements MetaData::MODELS_AUTOMATIC_DEFAULT => array('year') ); } } Pointing to a different schema ------------------------------ å¦‚æžœæ¨¡åž‹æ˜ å°„çš„è¡¨ä¸æ˜¯é»˜è®¤çš„schemas/databasesï¼Œä½ å¯ä»¥é€šè¿‡ getSchema 方法手工指定它: .. code-block:: php <?php class Robots extends \Phalcon\Mvc\Model { public function getSchema() { return "toys"; } } 建立多个数æ®åº“连接 ----------------------------------- 在Phalconä¸ï¼Œæ‰€æœ‰çš„æ¨¡åž‹éƒ½å±žäºŽä¸€ä¸ªæ•°æ®åº“连接,实际上,当 :doc:`Phalcon\\Mvc\\Model <../api/Phalcon_Mvc_Model>` 需è¦è¿žæŽ¥æ•°æ®åº“时,它请求æœåС容噍ä¸çš„"db"æœåŠ¡ï¼Œåœ¨initialize方法ä¸ï¼Œæ‚¨å¯ä»¥è¦†ç›–æ¤æœåŠ¡ï¼š .. code-block:: php <?php //This service returns a MySQL database $di->set('dbMysql', function() { return new \Phalcon\Db\Adapter\Pdo\Mysql(array( "host" => "localhost", "username" => "root", "password" => "secret", "dbname" => "invo" )); }); //This service returns a PostgreSQL database $di->set('dbPostgres', function() { return new \Phalcon\Db\Adapter\Pdo\PostgreSQL(array( "host" => "localhost", "username" => "postgres", "password" => "", "dbname" => "invo" )); }); ç„¶åŽï¼Œåœ¨æ¨¡åž‹çš„Initialize方法ä¸ï¼Œæˆ‘们å¯ä»¥é€šè¿‡ä»¥ä¸‹æ–¹å¼è®¿é—®ä¸€ä¸ªæ•°æ®åº“连接: .. code-block:: php <?php class Robots extends \Phalcon\Mvc\Model { public function initialize() { $this->setConnectionService('dbPostgres'); } } 记录SQL日志 -------------------------------- 当使用高层次的抽象组件,比如 :doc:`Phalcon\\Mvc\\Model <../api/Phalcon_Mvc_Model>` 访问数æ®åº“时,很难ç†è§£è¿™äº›è¯å¥æœ€ç»ˆå‘é€åˆ°æ•°æ®åº“æ—¶æ˜¯ä»€ä¹ˆæ ·çš„ã€‚ :doc:`Phalcon\\Mvc\\Model <../api/Phalcon_Mvc_Model>` 内部由 :doc:`Phalcon\\Db <../api/Phalcon_Db>` 支æŒã€‚:doc:`Phalcon\\Logger <../api/Phalcon_Logger>` 与 :doc:`Phalcon\\Db <../api/Phalcon_Db>` 交互工作,å¯ä»¥æä¾›æ•°æ®åº“抽象层的日志记录功能,从而使我们能够记录下SQLè¯å¥ã€‚ .. code-block:: php <?php $di->set('db', function() { $eventsManager = new Phalcon\Events\Manager(); $logger = new Phalcon\Logger\Adapter\File("app/logs/debug.log"); //Listen all the database events $eventsManager->attach('db', function($event, $connection) use ($logger) { if ($event->getType() == 'beforeQuery') { $logger->log($connection->getSQLStatement(), \Phalcon\Logger::INFO); } }); $connection = new \Phalcon\Db\Adapter\Pdo\Mysql(array( "host" => "localhost", "username" => "root", "password" => "secret", "dbname" => "invo" )); //Assign the eventsManager to the db adapter instance $connection->setEventsManager($eventsManager); return $connection; }); 当模型访问默认的数æ®åº“连接时,所有的SQLè¯å¥éƒ½ä¼šè¢«è®°å½•在该文件ä¸ï¼š .. code-block:: php <?php $robot = new Robots(); $robot->name = "Robby the Robot"; $robot->created_at = "1956-07-21" if ($robot->save() == false) { echo "Cannot save robot"; } 如上文所述,文件 *app/logs/db.log* 包å«è¿™æ ·çš„内容: .. code-block:: irc [Mon, 30 Apr 12 13:47:18 -0500][DEBUG][Resource Id #77] INSERT INTO robots (name, created_at) VALUES ('Robby the Robot', '1956-07-21') 剖æžSQLè¯å¥ ------------------------ 感谢 :doc:`Phalcon\\Db <../api/Phalcon_Db>` ,作为 :doc:`Phalcon\\Mvc\\Model <../api/Phalcon_Mvc_Model>` 的基本组æˆéƒ¨åˆ†ï¼Œå‰–æžORM产生的SQLè¯å¥å˜å¾—å¯èƒ½ï¼Œä»¥ä¾¿åˆ†æžæ•°æ®åº“çš„æ€§èƒ½é—®é¢˜ï¼ŒåŒæ—¶ä½ å¯ä»¥è¯Šæ–性能问题,并å‘现瓶颈。 .. code-block:: php <?php $di->set('profiler', function(){ return new Phalcon\Db\Profiler(); }); $di->set('db', function() use ($di) { $eventsManager = new Phalcon\Events\Manager(); //Get a shared instance of the DbProfiler $profiler = $di->getProfiler(); //Listen all the database events $eventsManager->attach('db', function($event, $connection) use ($profiler) { if ($event->getType() == 'beforeQuery') { $profiler->startProfile($connection->getSQLStatement()); } if ($event->getType() == 'afterQuery') { $profiler->stopProfile(); } }); $connection = new \Phalcon\Db\Adapter\Pdo\Mysql(array( "host" => "localhost", "username" => "root", "password" => "secret", "dbname" => "invo" )); //Assign the eventsManager to the db adapter instance $connection->setEventsManager($eventsManager); return $connection; }); Profiling some queries: .. code-block:: php <?php // Send some SQL statements to the database Robots::find(); Robots::find(array("order" => "name"); Robots::find(array("limit" => 30); //Get the generated profiles from the profiler $profiles = $di->getShared('profiler')->getProfiles(); foreach ($profiles as $profile) { echo "SQL Statement: ", $profile->getSQLStatement(), "\n"; echo "Start Time: ", $profile->getInitialTime(), "\n"; echo "Final Time: ", $profile->getFinalTime(), "\n"; echo "Total Elapsed Time: ", $profile->getTotalElapsedSeconds(), "\n"; } æ¯ä¸ªç”Ÿæˆçš„profile文件,都是以毫秒为å•ä½ã€‚ Injecting services into Models ------------------------------ ä½ å¯èƒ½éœ€è¦åœ¨æ¨¡åž‹ä¸è®¿é—®æœåŠ¡å®¹å™¨çš„ä¸€ä¸ªæœåŠ¡ï¼Œä¸‹é¢çš„ç¤ºä¾‹å°†ä¸ºä½ å±•ç¤ºå¦‚ä½•ä½¿ç”¨ï¼š .. code-block:: php <?php class Robots extends \Phalcon\Mvc\Model { public function notSave() { //Obtain the flash service from the DI container $flash = $this->getDI()->getFlash(); //Show validation messages foreach ($this->getMesages() as $message) { $flash->error((string) $message); } } } "create"或"update"æ“作失败的时候,"notSave"事件总是被触å‘,所以我们通过访问æœåС容噍ä¸çš„"flash"æœåŠ¡æ¥è¾“å‡ºéªŒè¯æ¶ˆæ¯ã€‚ .. _Alternative PHP Cache (APC): http://www.php.net/manual/en/book.apc.php .. _PDO: http://www.php.net/manual/en/pdo.prepared-statements.php