A
A
ainu2011-08-07 08:21:10
PHP
ainu, 2011-08-07 08:21:10

Correct implementation of ActiveRecord in PHP in the manner of Rails?

Good afternoon!
I implement ActiveRecord in PHP, in the manner of a dozen analogues (Rails, Yii, and so on).
Let's say we have the following code that gets a list of objects:
$users = Users::model()->findAll()->all;
There was a question about the correctness of the implementation. The fact is that according to Wikipedia, “each instance of this class corresponds to one table entry;”. It seems to be ideologically correct to return an array of objects.
Yes, and for example the record

foreach ($users as $user){<br/>
 print $user-&gt;name;<br/>
}<br/>

also seems logical. Rails seems to do that too.
On the other hand, $users = Users::model()->findAll()->all; may not be an array of objects, but simply an array of arrays with data.
I realized that the first option seems to be correct (when requesting a list, return an array of objects), but then the question arose:
If we have 800 comments (or other entities) on the page, is it really possible to store 800 instances of objects somewhere? And ->all somewhere in the loop creates objects, binds them to a table and fills them with the received data?

Answer the question

In order to leave comments, you need to log in

7 answer(s)
V
Vladimir Chernyshev, 2011-08-07
@VolCh

If we have 800 comments (or other entities) on the page, is it really possible to store 800 instances of objects somewhere? And ->all somewhere in the loop creates objects, binds them to a table and fills them with the received data?

If you use a descendant of ArrayObject, then it is not necessary to store all 800 in memory. For example,
UserCollection extends ArrayObject {
  private $ids;

  function __construct($where) {
     $ids = db::query("SELECT id FROM Users WHERE $where")->getColumnValues('id');
  }

  public function offset($index) {
    return new User(db::query("SELECT * FROM users WHERE id={$ids[$index]}"));
  }
}

The example is crude, unsafe, actually pseudocode. But with 800 records, there will be a problem known as "1 + N" - foreach in the template will cause 801 queries to the database. Keeping 800 is not good, 801 database queries are even worse. The output is a "page" cache. When requesting, for example, $users[73], we select (if not already selected) the first hundred records (with [173] the second hundred), instantiate 100 objects in the cache (an array of objects), overwriting those that were before (the second hundred overwrites the first ) and return the 73rd (in both cases). It is especially effective if all processing goes sequentially (foreach, etc.) - 9 requests for 800 records and a maximum of 100 objects in memory (or rather, it depends on the work of the garbage collector, you can force its work when overwriting an unnecessary hundred through gc_collect_cycles()), the main thing don't store them in another array :)

S
Sergey Beresnev, 2011-08-07
@sectus

You can do things or classes:
table
class record
class table field
class select query
class select response class
Then we knead the whole thing and voila, there are no contradictions.

L
LastDragon, 2011-08-07
@LastDragon

I recommend reading: "The Architecture of Enterprise Software Applications" by Martin Fowler - it describes in great detail about several approaches for obtaining records from the database.
Well, as a solution to your problem - when selecting many records, implement a wrapper for an array (a collection that is almost identical to a regular array), which will automatically receive new rows from the database as necessary (implementation is about an order of magnitude more complicated).

T
Tucker56, 2011-08-07
@Tucker56

I usually do this. If I need to display many objects on the page, then I simply select from the database according to the necessary criteria and display the result. If I need to change an object or make a mass change of objects, then I select a list of idioms from the database, for each of them in a cycle I create an instance of the class and pull the necessary methods with subsequent saving.

A
Andrey Shaydurov, 2011-08-07
@GearHead

> If we have 800 comments (or other entities) on the page, is it really possible to store 800 instances of objects somewhere?
I don’t remember about ActiveRecord, but in Mongoid in Rails there is a good example of how to overcome this. Until the special to_a method is called, any query like User.where(rights: 'admin').limit(200).offset(200) will return not an array of strings or objects, but an object of type MongoID::Criteria, which has a method each (similar to foreach) in such a way that it gradually takes line by line from the base and wraps it into an object. Thus, the garbage collector manages to hawp unnecessary objects quite quickly.

D
Dmitry Andreev, 2013-12-05
@dag

see how it's done here laravel.com/docs/eloquent

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question