Skip to end of metadata
Go to start of metadata

<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[

<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[

Zend Framework: Zend\Db\ActiveRecord Component Proposal

Proposed Component Name Zend\Db\ActiveRecord
Developer Notes http://framework.zend.com/wiki/display/ZFDEV/Zend\Db\ActiveRecord
Proposers Arthur Bodera
Zend Liaison TBD
Revision 0.2 - 19 July 2011: Use cases, description, requirements. It's looking good!
0.1 - 19 July 2011: Initial Draft. (wiki revision: 19)

Table of Contents

1. Overview

This component is an easy to use ActiveRecord pattern implementation for ZF.

I believe it had a long time coming, as there were some small flame-wars around this subject. There was some confusion and there were people thinking that Zend_Table was an ActiveRecord implementation. Zend\Db\Table implementation has been created specifically to implement Table Data Gateway and Row Data Gateway patterns, which are not the same as ActiveRecord. It has been stated several times that ActiveRecord pattern is not row or table gateway, nor is it an ORM.

As per Martin Fowler summary:

ActiveRecord is an object that wraps a row in a database table or view, encapsulates the database access, and adds domain logic on that data.

It is a very convenient, easy to use, easy to maintain pattern, because:

An object carries both data and behavior ... Active Record uses the most obvious approach, putting data access logic in the domain object. This way all people know how to read and write their data to and from the database.

A quick summary:

  • ActiveRecord class contains both business logic and data
  • ActiveRecord is designed to be easy to use and understand
  • ActiveRecord is a simplification of ORM
  • ActiveRecord is not Table Data Gateway
  • ActiveRecord is not Row Data Gateway
  • ActiveRecord is not an ORM (it might be a subset)
  • ActiveRecord is not a model, but can be a base for one.



This component was created for ZF2 only.
If you need a ZF1 version, vote below

Should ActiveRecord be also available for ZF1 ? (Log In to vote.)
Choices Your Vote
Yes, please (ZF1 is important)
No need (ZF2 is the future)
I don't care

2. References

3. Component Requirements, Constraints, and Acceptance Criteria

  • This component will require PHP 5.3+
  • This component will be lightweight, modular and extendable
  • This component will be ease-of-use oriented (AKA fun to use )
  • This component will be performance oriented
    • Lazy initializing (flywheel, deferred init)
    • Lazy updates (and lazy metadata loading)
    • Lazy loading ("load on demand", "deferred load")
    • Bulk loading ("range loading", "set loading")
    • Eager loading ("read-ahead", implicit join loading)
    • Meta-data hinting
    • Caching of data and meta-data
  • This component will support dependencies (associations)
    • one-to-many (has many of ... )
    • one-to-one (has one of ... )
    • many-to-many
    • reverse one-to-many (belongs to ...)
    • one-to-many of self (has many of self)
  • This component will support persistence out-of-the-box
  • This component will return query results in smart, efficient, iterable Collections of ActiveRecords.
  • This component will be prepared for advanced setups:
    • Database sharding (clustering, distributed db)
    • Master-slave databases (read-only, write-only, etc.)
    • Server load-balancing (round-robin, selection logic, etc.)
    • Software database failover (if db goes down, connect to another in the pool)
    • Multiple caches (read/write, local/remote etc.)
  • This component will will use Zend\Db components for connecting and querying databases
  • This component will be cross-platform and db brand indepenent (because of the above)
  • This component will NOT conflict with developer-implemented business logic (a Model)
  • This component will NOT use Zend\Db\Table.
  • This component will NOT replace or augment Zend\Db\Table behavior.
  • This component will NOT try to be a full blown ORM (use Propel or Doctrine for that)
  • This component will NOT use code generation (it will instead depend on subclassing, configuration and developer-oriented efforts)

4. Dependencies on Other Framework Components

  • Zend\Cache*
  • Zend\Db\Adapter*
  • Zend\Db\Select*
  • Zend\Db\Statement*
  • Zend\Filter*

5. Theory of Operation

In order to use ActiveRecord, developer creates a subclass extending Zend\Db\ActiveRecord\AbstractActiveRecord. Newly created class will work out-of-the box with some defaults. There are number of configuration options available for subclasses, including table name, dependencies, primary key column name, auto table name resolver etc. In order to configure and fine-tune the class, one has to override protected static properties, such as $_dbTable or $_pk.

By default, AbstractActiveRecord determines db table name from subclass name (i.e. Application\Model\User becomes table user). It fetches default db adapter from Zend\Db\Table\Table::getDefaultAdapter() and loads column list from the database. One can create an object instance with standard constructor ($user = new Application\Model\User(); and assign values ($user->name = "Bob";). By calling $user->save(); a new database record is inserted.

Existing records can be fetched from database by using a factory() or findById() static methods. For example assuming Bob has an id of 1, we can instantiate his user object with $user = Application\Model\User::factory(1);. At this stage nothing is loaded from the database, until we try fetch object properties i.e. echo $user->name;. If there is no such user with id 1, a NotFoundException will be thrown.
The second style of record fetching, findById(), will try to find the record in the database before instantiating. For example $user = Application\Model\User::findById(1); will send a SELECT ... WHERE id=1 to the database. If no such record can be found, false is returned instead of throwing an exception.

All ActiveRecord-specific methods are protected and prefixed with _ to avoid conflicts with user-defined business logic. There is a minimal number of non-prefixed public methods that can be overridden as needed (for example save() or delete()).

6. Milestones / Tasks

  • Milestone 1: [DONE] Create first working version of the component
  • Milestone 2: [DONE] Create test suite
  • Milestone 3: [DONE] Create first version of a proposal
  • Milestone 4: Implement associations and create test suite for that
  • Milestone 5: Write advanced examples (sharding, clustering, master-slave etc.)
  • Milestone 6: Write a tutorial.

7. Class Index

namespace Zend\Db\ActiveRecord

  • AbstractActiveRecord
  • Collection
  • CollectionIterator
  • Exception\Exception
  • Exception\BadMethodCallException
  • Exception\NotFoundException
  • Exception\UndefinedPropertyException

8. Use Cases

Most use cases are already implemented! Download code from https://github.com/Thinkscape/zf2/branches/ActiveRecord

There are additional features you can look at in the test suite: tests/Zend/Db/ActiveRecord/ActiveRecordTest.php

Basic usage

Finding records (queries)

Associations

9. Class Skeletons

Skeletons are good for Halloween \;-)
This component is alive and kicking. Download from https://github.com/Thinkscape/zf2/branches/ActiveRecord

]]></ac:plain-text-body></ac:macro>

]]></ac:plain-text-body></ac:macro>

Labels:
None
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.
  1. Jul 19, 2011

    <p>I'm really glad you created this. It's partly what I'm missing in ZF and reinventing all over again <ac:emoticon ac:name="wink" /> I hated that I can't use the well known classes from Zend\Db to do stuff with the other AR projects. </p>

    <p>Not everytime you write something in ZF you need extreme performance and maintainability. Sometimes all you need is just to get things done (think: SMB website with simple CMS - pages and simple catalogue with few categories). That's where Zend_Db_Table_Row is not enough (associations are PITA) and AR is the answer. I'd think there is no need for overoptimizing it (for serious stuff I'd use some full-scale ORM or POSS (Plain Old Simple SQL). It's enough it works fine for regular amounts of data (you can cache after all <ac:emoticon ac:name="wink" /> ). </p>

    <p>Really looking forward to see it working with associations and possibly included in ZF as another option even though it will be hard fight against "best practice evangelists" <ac:emoticon ac:name="smile" /> </p>

    <p>Only thing I'm missing in the proposal is dynamic User::findBySomething method - eg. <em>User::findByStatus($statusRecord) / ::findByStatusId($statusRecord->id)</em>, that will translate to <em>WHERE status_id = ?</em></p>

    1. Jul 19, 2011

      <p>Associations - <strong>done</strong><br />
      ::findByProperty() - <strong>done</strong></p>

      <p>You have two flavors of the latter:<br />
      ::findByName('name') - this will always return a Collection of matching records (->count() == 0 if nothing was found)<br />
      ::findOneByName('name') - this will return first matching object or null (if there are no records matching that name)</p>

      <p>Checkout last revision to find out. Thanks!</p>

  2. Jul 20, 2012

    <p>Is this project/branch still active?? Github says there has been no commit in last year in this branch.</p>