`
love~ruby+rails
  • 浏览: 849067 次
  • 性别: Icon_minigender_1
  • 来自: lanzhou
社区版块
存档分类
最新评论

PHP5 Database Iterators <1>

阅读更多

One feature of PHP rarely seen in production code is PHP Iterators. Iterators are not unique to PHP, as Java and C++ have them, but they are a powerful mechanism to increase code usability. A very useful feature of PHP Iterators is the ability to extend them to iterate over any type of array or object. A unique implementation of PHP Iterators is to quickly and easily iterate over a result from a SQL query against a database. This provides a fast and very memory efficient implementation for loading up many objects.

Essentially, with an Iterator, you can use the foreach() loop as opposed the while() loop to load objects. Adding to their power, at each iteration, you can have the iterator load up additional data or do any side processing automatically.

Setting Up The Database Environment

You will need to set your environment up for this article to work properly. First, create a new database, `iterator_test` with two tables, `user` and `user_comment`:

CREATE DATABASE `iterator_test` DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;
 
CREATE TABLE `user` (
    `id` SMALLINT( 3 ) NOT NULL AUTO_INCREMENT PRIMARY KEY ,
    `email_address` VARCHAR( 255 ) NOT NULL ,
    `firstname` VARCHAR( 64 ) NOT NULL ,
    `lastname` VARCHAR( 64 ) NOT NULL ,
    `age` TINYINT( 1 ) NOT NULL
) ENGINE = MYISAM CHARACTER SET utf8 COLLATE utf8_unicode_ci;
 
CREATE TABLE `user_comment` (
    `id` SMALLINT( 3 ) NOT NULL AUTO_INCREMENT PRIMARY KEY,
    `from_id` SMALLINT( 3 ) NOT NULL ,
    `to_id` SMALLINT( 3 ) NOT NULL ,
    `comment` VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL
) ENGINE = MYISAM CHARACTER SET utf8 COLLATE utf8_unicode_ci;
 
INSERT INTO `user` (`id`, `email_address`, `firstname`, `lastname`, `age`)
VALUES
  (1, 'vmc@leftnode.com', 'vic', 'cherubini', 25),
  (2, 'joe@google.com', 'joe', 'perry', 43);
 
INSERT INTO `user_comment` (`id`, `from_id`, `to_id`, `comment`)
VALUES
  (1, 1, 2, 'this is a comment'),
  (2, 1, 2, 'this is also a comment!'),
  (3, 2, 1, 'howdy, thanks for contacting me!');

The table `user_comment` will hold a list of comments `from_id` makes to `to_id`. These both point to the `id` field of the `user` table.

Classes and Models

Two classes are needed to manage User’s and User_Comment’s. They will both extend from a base class, Model, that defines some common methods and variables between them.

<?php
 
/**
 * The abstract Model class that is the base for all other classes
 * that interact with a datastore.
 * @author vmc <vmc@leftnode.com>
 */
abstract class Model {
  ///< The ID of the object from the datastore.
  protected $_id = 0;
 
  ///< The data as a key-value array of the object.
  protected $_model = array();
 
  /**
   * The default constructor of the object.
   * @author vmc <vmc@leftnode.com>
   * @param $object_id The optional ID to load from the datastore.
   * @retval Object Returns a new Model object.
   */
  public function __construct($object_id=0) {
    $this->_load(intval($object_id));
  }
 
  /**
   * Destructor.
   * @author vmc <vmc@leftnode.com>
   * @retval NULL Returns NULL.
   */
  public function __destruct() {
    $this->_model = array();
  }
 
  /**
   * The magic method to get an element from the $_model array.
   * @author vmc <vmc@leftnode.com>
   * @param $name The name of the element to get.
   * @retval mixed Returns the value from the model, NULL if not found.
   */
  public function __get($name) {
    if ( true === isset($this->_model[$name]) ) {
      return $this->_model[$name];
    }
    return NULL;
  }
 
  /**
   * Sets a value in the model.
   * @author vmc <vmc@leftnode.com>
   * @param $name The name of the key to set.
   * @param $v The value of the key to set.
   * @retval bool Returns true.
   */
  public function __set($name, $v) {
    $this->_model[$name] = $v;
    return true;
  }
 
  /**
   * Returns the ID of the object.
   * @author vmc <vmc@leftnode.com>
   * @retval int Returns the ID of the object.
   */
  public function getId() {
    return $this->_id;
  }
 
  /**
   * Returns an array of elements from the model. By default, returns
   * all of them, however, the inclusion or exclusion of them can be defined.
   * @author vmc <vmc@leftnode.com>
   * @param $list An option array of elements to include or exclude from the result.
   * @param $ignore False to include the elements, true to include them.
   * @retval Object Returns a new Model object.
   */
  public function getArray($list=array(), $ignore=false) {
    $return = $this->_model;
 
    if ( count($list) > 0 ) {
      if ( false === $ignore ) {
        $return = array_intersect_key($this->_model, asfw_make_values_keys($list));
      } else {
        foreach ( $list as $v ) {
          unset($return[$v]);
        }
      }
    }
 
    return $return;
  }
 
  /**
   * Sets all of the model data from an array. If 'id' is a field
   * of the array, it is extracted out first, and set as the $_id.
   * @author vmc <vmc@leftnode.com>
   * @param $obj The object to load from.
   * @retval Object Returns $this for chaining.
   */
  public function loadFromArray($obj) {
    $this->_id = 0;
    if ( true === isset($obj['id']) ) {
      $this->_id = $obj['id'];
      unset($obj['id']);
    }
 
    $this->_model = (array)$obj;
    return $this;
  }
 
  /**
   * Writes all of the data to the datastore. If the object is already loaded,
   * ie, the ID is greater than 0, the data is updated, otherwise, it is inserted.
   * @author vmc <vmc@leftnode.com>
   * @retval Object Returns a new Model object.
   */
  public function write() {
    if ( $this->_id > 0 ) {
      $this->_update();
    } else {
      $this->_insert();
    }
    return $this->_id;
  }
 
  /**
   * Loads all of the data from the datastore for this model object.
   * @author vmc <vmc@leftnode.com>
   * @param $object_id The ID of the model to load.
   * @retval int Returns the ID of the loaded model.
   */
  protected function _load($object_id) {
    $table_name = strtolower(get_class($this));
 
    $object_id = intval($object_id);
    if ( $object_id > 0 ) {
      $result_model = LN::getDb()->select()
        ->from($table_name)
        ->where('id = ?', $object_id)
        ->query();
      if ( 1 == $result_model->numRows() ) {
        $model = $result_model->fetch();
        unset($model['id']);
 
        $this->_model = $model;
        $this->_id = $object_id;
      }
    }
    return $this->_id;
  }
 
  /**
   * Inserts the new model data into the datastore.
   * @author vmc <vmc@leftnode.com>
   * @retval int Returns the ID of the new model.
   */
  protected function _insert() {
    LN::getDb()->insert()
      ->into($this->_table)
      ->values($this->_model)
      ->query();
    return $this->_id;
  }
 
  /**
   * Updates the model data into the datastore.
   * @author vmc <vmc@leftnode.com>
   * @retval int Returns the ID of the new model.
   */
  protected function _update() {
    LN::getDb()->update()
      ->table($this->_table)
      ->set($this->_model)
      ->where('id = ?', $this->_id)
      ->query();
    return $this->_id;
  }
}

Fortunately, the Model object takes care of most of the methods and variables for User and User_Comment. The User class needs a simple way to get a list of comments quickly, in which the getCommentList() works well. The optional argument to the method allows the programmer to override the Lazy Loading method and to force it to always load from the database.

<?php
 
/**
 * Basic User class for handling users.
 * @author vmc <vmc@leftnode.com>
 */
class User extends Model {
  ///< The Db_Iterator object of comments.
  private $_commentList = NULL;
 
  /**
   * Returns a list of comments that the user has made.
   * @author vmc <vmc@leftnode.com>
   * @param $force Optional variable to force loading of the comment list even if not null.
   * @return Object Returns Db_Iterator object to cycle through the results.
   */
  public function getCommentList($force=false) {
    if ( NULL == $this->_commentList || true === $force ) {
      $result_comment = LN::getDb()->select()
        ->from('user_comment')
        ->where('from_id = ?', $this->_id)
        ->query();
      $this->_commentList = new Db_Iterator($result_comment, new User_Comment());
 
      /**
       * Calling $result_comment->free() here will cause the application to break
       * because the variables are copy-on-write. Because nothing was written,
       * the $result_comment variable in the iterator will be freed, and no results
       * can be fetched.
       * 
       * $result_comment->free();
       *
       * However, if you had an unset($result_comment), it will only unset this
       * locally scoped variable and not the variable in the iterator because the
       * variable in the iterator will be considered written to, and thus, copied
       * to a new memory location. You'll see the memory go up slightly if you
       * add the unset() because of the copy-on-write.
       *
       * unset($result_comment);
       *
       */
    }
 
    $this->_commentList->rewind();
    return $this->_commentList;
  }
}
<?php
 
/**
 * Basic User class for handling users.
 * @author vmc <vmc@leftnode.com>
 */
class User_Comment extends Model {
  /**
   * Allows the object to be printed directly.
   * @author vmc <vmc@leftnode.com>
   * @return string Returns a string representation of the object.
   */
  public function __toString() {
    $str  = 'User #' . $this->_model['from_id'] . ' made the comment, "';
    $str .= $this->_model['comment'] . '", to User #' . $this->_model['to_id'] . '.';
    return $str;
  }
}

Because User_Comment has no dependencies, it remains a nearly empty class. A single __toString() method was added to allow easy object display. User, however, needs to load up a list of User_Comment objects. It is at this point where this implementation becomes very memory efficient. Over the course of a User’s existence on a website, they could create hundreds or thousands of comments. Loading all of these up at a single time as an array of objects is very inefficient. The initial loop must iterate through each result to build the array. This array would be very large and take up unnecessary memory. A much better implementation is to store a pointer to the initial result set and only loop through the comments when necessary.

分享到:
评论

相关推荐

    C /C++库函数及文件大全 经典 chm

    各种函数以及库文件的...Ended Queues &lt;br&gt;C++ Lists &lt;br&gt;C++ Priority Queues &lt;br&gt;C++ Queues &lt;br&gt;C++ Stacks &lt;br&gt;C++ Sets &lt;br&gt;C++ Multisets &lt;br&gt;C++ Maps &lt;br&gt;C++ Multimaps &lt;br&gt;C++ Bitsets &lt;br&gt;Iterators &lt;br&gt;

    Visual C++ 编程资源大全(英文源码 网络)

    1,pop3.zip&lt;br&gt;CPop3Connection - an MFC Class to encapsulate the POP3 protocol(15KB)&lt;END&gt;&lt;br&gt;2,ipenum.zip&lt;br&gt;IPEnum - an MFC class and console app to allow IP address enumeration(11KB)&lt;END&gt;&lt;br&gt;3,smtp....

    The Art of Assembly Language Programming

    The 80x86 MOV Instruction&lt;br&gt;4.8 - Some Final Comments on the MOV Instructions&lt;br&gt;&lt;br&gt;4.9 Laboratory Exercises&lt;br&gt;4.9.1 The UCR Standard Library for 80x86 Assembly Language Programmers&lt;br&gt;4.9.2 ...

    C 标准程序库自修教程与参考手册

    通用工具&lt;br&gt;5.Standard Template Library(STL,标准模板库)&lt;br&gt;6.STL容器(STL Container)&lt;br&gt;7.STL 迭代器(STL Iterators)&lt;br&gt;8.STL 仿函数(functors)(又名函数对象,function objects)&lt;br&gt;9.STL算法...

    Apress.Accelerated.C.Sharp.2008.Nov.2007

    &lt;br&gt; &lt;br&gt; What you'll learn:&lt;br&gt; - How C# works with and exploits the CLR&lt;br&gt; - How to use arrays, collections, and iterators&lt;br&gt; - How to handle events with delegates and anonymous functions&lt;br&gt; - ...

    C++ Standard Library: A Tutorial and Reference

    Input/Output Using Stream Classes&lt;br/&gt;&lt;br/&gt;13.1 Common Background of I/O Streams&lt;br/&gt;&lt;br/&gt;13.2 Fundamental Stream Classes and Objects&lt;br/&gt;&lt;br/&gt;13.3 Standard Stream Operators &lt;&lt; and &gt;&gt;&lt;br/&gt;&lt;br/&gt;13.4 ...

    visual assist v 10.4.1632 with crack

    (case=9436) &lt;br&gt;STL list&lt;&gt; and vector&lt;&gt; member lists appear correctly following a "using namespace std::list" or "using namespace std::vector" directive. (case=12345) 7226 &lt;br&gt;Empty C++ preprocessor ...

    标准模板库STL

    STL容器部分主要由头文件&lt;vector&gt;、&lt;list&gt;、&lt;deque&gt;、&lt;set&gt;、&lt; map&gt;、&lt;stack&gt;和&lt;queue&gt;组成。 (2)算法(Algorithms)。包括各种基本算法,如比较、交换、查找、排序、遍历操作、复制、修改、移除、反转、合并...

    The-C++-Programming-Language(ch 3)

    vector&lt;int&gt; numbers = {3, 1, 4, 1, 5, 9}; sort(numbers.begin(), numbers.end()); for (int n : numbers) { cout &lt;&lt; n &lt;&lt; " "; } cout &lt;&lt; endl; return 0; } ``` #### 3.11 迭代器 (Iterators) 迭代器...

    C++标准库MSDN离线文档-微软2017-10月发布

    部分内容部分介绍了C++标准库中包含的头文件和功能模块,其中包括了常用的&lt;array&gt;、&lt;iostream&gt;、&lt;string&gt;等,这些都是构建C++程序不可或缺的部分。文档提及了算法(Algorithm)、分配器(Allocators)、迭代器...

    C++标准库和头文件名字

    1. **输入/输出流(I/O Streams)**:这是C++中最基础的库之一,主要包含在`&lt;iostream&gt;`头文件中。`std::cin`用于从标准输入读取数据,`std::cout`用于向标准输出(通常是显示器)打印数据。此外,还有`std::cerr`和...

    C++标准程序库

    std::vector&lt;int&gt; numbers = {1, 2, 3, 4, 5}; std::vector&lt;int&gt;::iterator it; // 输出所有元素 for (it = numbers.begin(); it != numbers.end(); ++it) { std::cout &lt;&lt; *it &lt;&lt; " "; } std::cout &lt;&lt; std::...

    C++标准函数库 C++标准函数库

    1. 输入/输出(I/O)库:C++的`&lt;iostream&gt;`库是处理输入输出的基础,包含了`cin`和`cout`对象,分别用于标准输入和输出。例如,可以使用`std::cin`获取用户输入,用`std::cout`打印输出信息。此外,`cerr`和`clog`用于...

    google interview problem of iterator

    public RotateIterator(List&lt;Iterator&lt;T&gt;&gt; iterators) { this.iterators = iterators; } @Override public boolean hasNext() { while (currentIteratorIndex &lt; iterators.size()) { if (iterators.get...

    C#设计模式迭代器示例

    1. **定义聚合对象**:创建一个类,实现 `IEnumerable` 或 `IEnumerable&lt;T&gt;` 接口。这些接口要求你提供一个返回 `IEnumerator` 或 `IEnumerator&lt;T&gt;` 的 `GetEnumerator` 方法。这个方法将返回一个迭代器实例,用于...

    C程序设计教学课件:chapter6templatePart2.pptx

    - 容器适配器(Container Adapters):例如`&lt;stack&gt;`、`&lt;queue&gt;`和`&lt;priority_queue&gt;`,它们是基于其他容器的特殊版本,提供了特定的操作行为,如先进先出(FIFO)或优先级排序。 3. 迭代器(Iterators): 迭代...

    sap 操作XML(自己的笔记)

    &lt;FIRSTNAME&gt;Walt&lt;/FIRSTNAME&gt; &lt;LASTNAME&gt;Whitman&lt;/LASTNAME&gt; &lt;/ITEM&gt; &lt;ITEM&gt; &lt;FIRSTNAME&gt;Walt2&lt;/FIRSTNAME&gt; &lt;LASTNAME&gt;Whitman2&lt;/LASTNAME&gt; &lt;/ITEM&gt; &lt;/ELEMENT&gt; &lt;/ROOTNODE&gt; ``` ##### 2. 解析XML 解析XML...

    C++STL vector list map set dqueue 等应用举例及PPT讲解示例,代码演示

    STL的核心组件包括容器(containers)、迭代器(iterators)、函数对象(functors)和算法(algorithms)。在这个主题中,我们将深入探讨vector、list、map、set和deque这五个主要的STL容器,并通过具体的例子和PPT...

    C# Language Specification 2.0.pdf

    - **实例化**:通过指定具体类型来创建泛型类型的实例,如`List&lt;int&gt; intList = new List&lt;int&gt;();`。 - **约束**:可以通过添加约束限制类型参数的范围,例如要求类型必须实现特定接口或继承自特定基类。 #### 泛型...

    自己写的基于C#的小型健康体检软件

    5. **HashSet&lt;T&gt;**:无序且不允许重复元素的集合,适合快速查找和插入操作。 6. **Dictionary&lt;TKey, TValue&gt;**:键值对集合,用于存储具有唯一键的对象。它提供了快速的查找和访问速度。 7. **Queue&lt;T&gt;**:FIFO...

Global site tag (gtag.js) - Google Analytics