<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                _有些裁縫只會塞塞襯里,修修補補,而有些裁縫卻能做出新東西--他們之間隔著一條深淵。 -- 《外套》_ ##1.15.1 NotORM官網 這里使用了NotORM進行DB操作,具體的數據庫操作使用文檔請見NotORM官網: [http://www.notorm.com](http://www.notorm.com) ###(1) 基本CURD ```javascript //查詢 $row = DI()->notorm->user->where('id', 1)->fetch(); //更新 $data = array('name' => 'test', 'update_time' => time()); DI()->notorm->user->where('id', 1)->update($data); //插入(須是同一個對象才能正確獲取插入的ID) $data = array('name' => 'phalapi'); $userORM = DI()->notorm->user; $userORM->insert($data); $id = $userORM->insert_id(); //刪除 DI()->notorm->user->where('id', 1)->delete(); ``` ###(2)update相同的數據的判斷 在使用update操作時,如果更新的數據和原來的一樣,則會返回0(影響0行)。這時,會和更新失敗(同樣影響0行)混淆。 但NotORM是一個優秀的類庫,所以提供了優秀的解決文案。我們在使用update時,只須了解這兩者返回的結果的微妙區別即可。 因為失敗異常時,返回false;而相同數據更新會返回0。即: + 1、update相同的數據時,返回0,嚴格來說是:int(0) + 2、update失敗時,如更新一個不存在的字段,返回false,即:bool(false) 用代碼表示,就是: ```javascript $rs = DI()->notorm->user->where('id', $userId)->update($data); if ($rs >= 1) { //成功 } else if ($rs === 0) { //相同數據,無更新 } else if ($rs === false) { //更新失敗 } ``` 以下單元測試代碼,可以驗證上面的判斷: ```javascript public function testUpdateOk() { $userId = 87; $rs = DI()->notorm->user->where('id', $userId)->update(array('reg_time' => time())); $this->assertSame(1, $rs); } public function testUpdateZero() { $userId = 1; $rs = DI()->notorm->user->where('id', $userId)->update(array('username' => 'aevit')); $this->assertSame(0, $rs); } public function testUpdateFail() { $userId = 1; $rs = DI()->notorm->user->where('id', $userId)->update(array('wrong_username' => 'aevit')); $this->assertSame(FALSE, $rs); } ``` ###(3)簡單的關聯查詢 如果是簡單的關聯查詢,可以使用NotORM支持的寫法,這樣的好處在于我們使用了一致的開發,并且能讓PhalApi框架保持分布式的操作方式(注意,關聯的表仍然需要在同一個數據庫)。 以下是一個簡單的示例。 假設我們有這樣的數據: ```javascript INSERT INTO `phalapi_user` VALUES ('1', 'wx_edebc877070133c65161d00799e00544', 'weixinName', '******', '4CHqOhe1Jxi3X9HmRfPOXygDnU267eCA', '1431790647', 'phpunit.png'); INSERT INTO `phalapi_user_session_0` VALUES ('1', '1', 'ABC', '', '0', '0', '0', null); ``` 那么對應關聯查詢的代碼如下面: ```javascript public function testLeftJoin() { $rs = DI()->notorm->user_session_0 ->select('expires_time, user.username, user.nickname') ->where('token', 'ABC') ->fetchRow(); var_dump($rs); } ``` 運行一下,我們可以看到這樣的輸出: ```javascript SELECT expires_time, user.username, user.nickname FROM phalapi_user_session_0 LEFT JOIN phalapi_user AS user ON phalapi_user_session_0.user_id = user.id WHERE (token = 'ABC') LIMIT 1; .[1 - 0.06318s]SELECT expires_time, user.username, user.nickname FROM phalapi_user_session_0 LEFT JOIN phalapi_user AS user ON phalapi_user_session_0.user_id = user.id WHERE (token = 'ABC') LIMIT 1;<br> array(3) { ["expires_time"]=> string(1) "0" ["username"]=> string(35) "wx_edebc877070133c65161d00799e00544" ["nickname"]=> string(10) "weixinName" } ``` 這樣,我們就可以實現關聯查詢的操作。按照NotORM官網的說法,則是: > If the dot notation is used for a column anywhere in the query ("$table.$column") then NotORM automatically creates left join to the referenced table. Even references across several tables are possible ("$table1.$table2.$column"). Referencing tables can be accessed by colon: $applications->select("COUNT(application_tag:tag_id)"). ->select('expires_time, user.username, user.nickname')這一行調用將會【自動產生關聯操作】,而ON 的字段,則是這個字段關聯你配置的【表結構】,外鍵默認為: 表名_id 。 ###(4)加1操作 NotORM已提供了NotORM_Literal,其用法如下: ```javascript DI()->notorm->user->where('id', 1)->update(array('age' => new NotORM_Literal("age + 1"))); ``` 當需要更新為當前時間,可以: ```javascript $array = array( "title" => "NotORM", "author_id" => null, "created" => new NotORM_Literal("NOW()"), ); ``` ##1.15.2 NotORM的優化 但為了更符合項目的開發,這里對NotORM的底層作了升級修改,以下為主要修改點和新的使用: ###(1)將原來返回的結果全部從對象改成數組 對原來的大部分使用無特別影響,可按原來的方式開發。主要目的是為了更方面處理返回的數據,以及簡化對結果的再解析,簡單明了。 如: ```javascript DI()->notorm->user->where('username = ?', 'dogstar')->fetch(); ``` 返回的將是一個數組: ```javascript array(7) { ["id"]=> string(3) "180" ["username"]=> string(17) "dogstar" ["regtime"]=> string(10) "1414811954" //... } ``` ###(2)提供獲取全部結果的接口 - fetchAll() / fetchRows() 如: ```javascript $rows = DI()->notorm->event_picurl->where('eid', $eids)->fetchAll(); ``` 或: ```javascript $rows = DI()->notorm->event_picurl->where('eid', $eids)->fetchRows(); ``` 即可獲取全部的數據,不再受限于分頁。 這里提供了fetchAll()和fetchRows()兩種等效的操作,是為了減少記憶的痛苦,下同。 ###(3)提供更靈活的查詢方式 - queryAll() / queryRows() 當需要進行復雜的SQL查詢時,可以使用此接口,如: (注意:limit替換值:start和:num必須使用int類型) ```javascript $sql = 'select * from example AS ep LEFT JOIN user AS u ON ep.ui d = u.id where ep.touid = :userId ORDER BY dateline desc LIMIT :start,:num'; $params = array(':userId' => $userId, ':start' => $start, ':num' => $num); $rs= DI()->notorm->example->queryAll($sql, $params); ``` 或: ```javascript $rs= DI()->notorm->example->queryRows($sql, $params); ``` ###(4)limit 操作的調整 取消了NotORM中對OFFSET關鍵字的使用,改用逗號的寫法,修改后正確的使用方法應該是: ```javascript $table->limit(10); // limit 10 # 查詢前10個 $table->limit(5, 10); // limit 5,10 # 從第5個位置開始,查詢前10個 ``` ###(5)禁止全表刪除,防止誤刪 出于對數據的保護,當執行刪除操作卻又沒有任何where條件時,將會禁止進行全表操作。如: ```javascript public function testDeleteAll() { DI()->notorm->user->delete(); } ``` 可以看到: ```javascript $ phpunit --filter testDeleteAll ./Api/Api_User_Test.php PHPUnit 4.3.4 by Sebastian Bergmann. E Time: 315 ms, Memory: 6.25Mb There was 1 error: 1) PhpUnderControl_ApiUser_Test::testDeleteAll Exception: sorry, you can not delete the whole table --dogstar ``` ###(6)添加& \_\_sql\_\_ =1請求參數,可開啟HTTP調試模式 當處于debug模式時,可以輸入執行的全部SQL語句,以便調試。 如: ```javascript SELECT times FROM tpl_user_session_10 WHERE (user_id = ?); -- '74110' {"ret":0,"data":{"code":0},"msg":""} ``` ###(7)關于NotORM中fetch()操作沒有limit 1的處理方案 - fetchOne() / fetchRow() 之前,有開發同學提及到,為什么notorm的基類fetch為啥沒用limit(1)呢。后來,我去發了下NotORM的寫法,確實做得很微妙。 其實NotORM之所以沒有在fetch()里面自動limit 1是因為,你可以循環地獲取數據,如: ```javascript $user = DI()->notorm->user->select('id, username, nickname')->where('id > ?', 0)->limit(3); while(($row = $user->fetch())) { var_dump($row); } ``` 但是,更多情況下,我們只需要獲取某一行的數據,上面的做法會造成不必要的SQL查詢。為了保留原來的寫法,我特意添加擴展了一個新的操作:fetchRow(),用法同fetch(),但只會取第一條。 以下是使用示例: ```javascript $rs = DI()->notorm->user->select('id, username, nickname')->where('id > ?', 0)->fetchRow()); var_dump($rs); //結果輸出類如: array(3) { ["id"]=> string(1) "1" ["username"]=> string(5) "aevit" ["nickname"]=> string(4) "test" } //對應執行的SQL語句: [2 - 0.06544s]SELECT id, username, nickname FROM fami_user WHERE (id > ?) LIMIT 1; -- 0<br> ``` 如果,我們只需要獲取這一行的某個字段,也可以像fecth()那樣使用,即: ```javascript $rs = DI()->notorm->user->select('id, username, nickname')->where('id > ?', 0)->fetchRow('nickname')); var_dump($rs); //結果輸出類如: string(4) "test" //紀錄不存在時,返回 bool(false) ``` ###(8)顯式的SQL語法異常提示 很多時候,在開發時,我們對數據庫操作一開始會存在一些SQL語法的問題,PDO會返回false,且原來NotORM也是使用 **靜默方式** 來處理這類錯誤,從而使得開發人員有時難以發現這些問題,除非將調試的SQL手動放到數據庫執行才能發現問題所在。 為了能給開發同學更早、更直觀的方式查看問題的所在,這里對NotORM底層進行了調整,使用了 **顯式方式** 的策略來處理,即:直接拋出PDO異常。 如: ```javascript $userId = 1; //OK $rs = DI()->notorm->user->select('username')->where('id', $userId)->fetchOne(); //WRONG $rs = DI()->notorm->user->select('wrong_username')->where('id', $userId)->fetchOne(); ``` 將會看到: ```javascript [1 - 0.06437s]SELECT username FROM fami_user WHERE (id = 1) LIMIT 1;<br> [2 - 0.06496s]SELECT wrong_username FROM fami_user WHERE (id = 1) LIMIT 1;<br> PDOException: Unknown column 'wrong_username' in 'field list' ``` ###(9)復雜的關聯查詢 如果是復雜的關聯查詢,則是建議使用原生態的SQL語句,但我們仍然可以保持很好的寫法,如這樣一個示例: ```javascript $sql = 'SELECT t.id, t.team_name, v.vote_num ' . 'FROM phalapi_team AS t LEFT JOIN phalapi_vote AS v ' . 'ON t.id = v.team_id ' . 'ORDER BY v.vote_num DESC'; $rows = $this->getORM()->queryAll($sql, array()); ``` 注意,此時的表需要使用全名,即自帶前綴。這樣也可以實現更自由的關聯查詢。 ###(10)事務操作 關于事務操作,可以參考 [NotORM官網](http://www.notorm.com/#api) 的說明: ``` $db->transaction = $command Assign 'BEGIN', 'COMMIT' or 'ROLLBACK' to start or stop transaction ``` 即: ``` //第一步:先指定待進行事務的數據庫(通過獲取一個notorm表實例來指定;否則會提示:PDO There is no active transaction) $user = DI()->notorm->user; //第二步:開啟事務開關(此開關會將當前全部打開的數據庫都進行此設置) DI()->notorm->transaction = 'BEGIN'; //第三步:進行數據庫操作 $user->insert(array('name' => 'test1',)); $user->insert(array('name' => 'test2',)); //第四:提交/回滾 DI()->notorm->transaction = 'COMMIT'; //DI()->notorm->transaction = 'ROLLBACK'; ``` ####推薦使用PhalApi的事務操作方式 PhalApi一開始對事務這塊考慮不周,后來發現很多同學、很多項目都需要用到數據庫事務操作。 基于此,在不破壞原來的代碼基礎上,我們決定在PhalApi_DB_NotORM上添加對數據庫維度的事務操作支持。 示例簡單如下: ``` public function testTransactionCommit() { //Step 1: 開啟事務 $this->notorm->beginTransaction('db_demo'); //Step 2: 數據庫操作 $this->notorm->user>insert(array('name' => 'test1')); $this->notorm->user>insert(array('name' => 'test2')); //Step 3: 提交事務 $this->notorm->commit('db_demo'); } ``` > 溫馨提示: 以上操作,須PhalApi 1.3.1 及以上版本才能支持。 ###(11)擴展對非MySQL數據庫的支持 PhalApi使用的是NotORM來進行數據庫操作,而NotORM底層則是采用了PDO。目前,NotORM支持: MySQL, SQLite, PostgreSQL, MS SQL, Oracle (Dibi support is obsolete)。 但需要注意的是,PhalApi本身對NotORM進行了修改,需要調整一下代碼才能更好地支持除MySQL外的數據庫。 即使NotORM不支持的數據庫,你也可以輕松通過添加擴展的方式來支持。如: 首先,定制自己的數據庫連接的PDO。 ``` class Common_MyDB extends PhalApi_DB_NotORM { protected function createPDOBy($dbCfg) { /* Connect to an ODBC database using driver invocation */ $dsn = 'uri:file:///usr/local/dbconnect'; return new PDO($dsn, $dbCfg['user'], $dbCfg['password']); } } ``` 隨后,在初始化文件init.php中重新注冊DI()->notorm即可,如: ``` //數據操作 - 基于NotORM,$_GET['__sql__']可自行改名 DI()->notorm = function() { $debug = !empty($_GET['__sql__']) ? true : false; return new Common_MyDB(DI()->config->get('dbs'), $debug); }; ``` ##1.15.3 可選的Model基類 ###(1)表數據入口模式 我們一直在考慮,是否應該提供數據庫的基本操作支持,以減少開發人員重復手工編寫基本的數據操作。 最后,我們認為是需要的。然后就引發了新的問題:是以繼承還是以委托來支持? 委托有助于降低繼承的層級,但仍然需要編寫同類的操作然后再次委托。所以,這里提供了基于NotORM的Model基類:PhalApi_Model_NotORM。 然而提供這個基類還是會遇到一些問題,例如:如何界定基本操作?如何處理分表存儲?如何支持定制化? 由于我們這里的Model使用了 **“表數據入口”** 模式,而不是“行數據入口”,也不是“活動紀錄”,也不是復雜的“數據映射器”。所以在使用時可以考慮是否需要此基類。即使這樣,你也可以很輕松轉換到“行數據入口”和“活動紀錄”模式。這里,PhalApi中的Model是更廣義上的數據源層(后面會有更多說明),因此對應地PhalApi_Model_NotORM基類充當了數據庫表訪問入口的對象,處理表中所有的行。 ###(2)規約層的CURD 在明白了Model基類的背景后,再來了解其具體的操作和如何繼承會更有意義。 而具體的操作則與數據表的結構相關,在“約定編程”下:即每一個表都有一個主鍵(通常為id,也可以自由配置)以及一個序列化LOB字段ext_data。我們很容易想到Model接口的定義(注釋已移除,感興趣的同學可查看源碼): ```javascript interface PhalApi_Model { public function get($id, $fields = '*'); public function insert($data, $id = NULL); public function update($id, $data); public function delete($id); } ``` 上面的接口在規約層上提供了基于表主鍵的CURD基本操作,在具體實現時,需要注意兩點:一是分表的處理;另一點則是LOB字段的序列化。 ###(3)不使用Model基類的寫法 由于我們使用了NotORM進行數據庫的操作,所以這里也提供了基于NotORM的基類:PhalApi_Model_NotORM。下面以我們熟悉的獲取用戶的基本信息為例,說明此基類的使用。 為喚醒記憶,下面貼上Model_User類原來的代碼: ```javascript // $ vim ./Demo/Model/User.php <?php class Model_User { public function getByUserId($userId) { return DI()->notorm->user->select('*')->where('id = ?', $userId)->fetch(); } } ``` 對應的調用: ```javascript $model = new Model_User(); $rs = $model->getByUserId($userId); ``` ###(4)繼承Model基類的寫法 若繼承于PhalApi_Model_NotORM,則是: ```javascript // $ vim ./Demo/Model/User.php <?php class Model_User extends PhalApi_Model_NotORM { protected function getTableName($id) { return 'user'; } } ``` 從上面的代碼可以看出,基類已經提供了基于主鍵的CURD操作,但我們需要鉤子函數以返回對應的表名。相應地,外部調用則調整為: ```javascript $model = new Model_User(); $rs = $model->get($userId); ``` 再進一步,我們可以得到其他的基本操作: ```javascript $model = new Model_User(); //查詢 $row = $model->get(1); $row = $model->get(1, 'id, name'); //取指定的字段 $row = $model->get(1, array('id', 'name')); //可以數組取指定要獲取的字段 //更新 $data = array('name' => 'test', 'update_time' => time()); $model->update(1, $data); //基于主鍵的快速更新 //插入 $data = array('name' => 'phalapi'); $id = $model->insert($data); //$id = $model->insert($data, 5); //如果是分表,可以這樣指定 //刪除 $model->delete(1); ``` ##1.15.4 定制化你的Model基類 正如上面提及到的兩個問題:LOB序列化和分表處理。所以,如果PhalApi現有就此兩問題的解決方案不能滿足項目的需求,可作定制化處理。 ###(1)LOB序列化 先是LOB序列化,考慮到有分表的存在,當發生數據庫變更時(特別在線上環境)會有一定的難度和風險,因此引入了擴展字段ext_data。當然,此字段也應對數據庫變更的同時,也可以作為簡單明了的值對象的大對象。序列化LOB首先要考慮的問題是使用二進制(BLOB)還是文本(CLOB),出于通用性、易讀性和測試性,我們目前使用了json格式的文本序列化。所以,如果考慮到空間或性能問題(在少量數據下我認為問題不大,如果數據量大,應該及時重新調整數據庫表結構),可以重寫formatExtData() & parseExtData()。 如改成serialize序列化: ```javascript abstract class Common_Model_NotORM extends PhalApi_Model_NotORM { /** * 對LOB的ext_data字段進行格式化(序列化) */ protected function formatExtData(&$data) { if (isset($data['ext_data'])) { $data['ext_data'] = serialize($data['ext_data']); } } /** * 對LOB的ext_data字段進行解析(反序列化) */ protected function parseExtData(&$data) { if (isset($data['ext_data'])) { $data['ext_data'] = unserialize($data['ext_data'], true); } } // ... } ``` 將Model類繼承于Common_Model_NotORM后, ```javascript // $ vim ./Demo/Model/User.php <?php class Model_User extends Common_Model_NotORM { //... } ``` 就可以輕松切換到序列化,如: ```javascript $model = new Model_User(); //帶有ext_data的更新 $extData = array('level' => 3, 'coins' => 256); $data = array('name' => 'test', 'update_time' => time(), 'ext_data' => $extData); $model->update(1, $data); //基于主鍵的快速更新 ``` ###(2)分表處理 其次是分表處理,同樣考慮到分表的情況,以及不同的表可能配置不同的主鍵表,而基于主鍵的CURD又必須要先知道表的主鍵名才能進行SQL查詢。所以,問題就演變成了如何找到表的主鍵名。這里可以自動匹配,也可以手工指定。自動匹配是智能的,因為當我們更改表的主鍵時,可以自動同步更新而不需要擔心遺漏(雖然這種情況很少發生)。手工指定可以大大減少系統不必要的匹配操作,因為我們開發人員也知道數據庫的主鍵名是什么,但需要手工編寫一些代碼。在這里,提供了可選的手工指定,即可重寫getTableKey($table)來指定你的主鍵名。 如,當我們的表的主鍵都固定為id時: ```javascript abstract class Common_Model_NotORM extends PhalApi_Model_NotORM { protected function getTableKey($table) { return 'id'; } } ```
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看