• 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
ActiveRecord for CodeIgniter: Rails-style model interactions

#21
[eluser]Matthew Pennell[/eluser]
[quote author="gunter" date="1190567472"]as quick fix I used in the model the config item...[/quote]
Thanks for this suggestion, gunter - I've now incorporated it into the class, and updated the file in the wiki to version 0.3.2. Smile

#22
[eluser]sophistry[/eluser]
there are some comments on that overloading() php.net page that show how to use call_user_func() to sort of mimic the slick __call() style method overloading available in PHP5. in fact, the current latest comment on that call_user_func() page shows a nice example of how to do dynamic method mapping in PHP4.

so, i think it is possible to modify or adapt the ROR style AR class you've got here to support PHP4, but it would probably involve syntax changes - or some clever class structures. specifically, you would probably have to handle the name of the table column as a function parameter rather than tucking it into the method call.

#23
[eluser]Maurice Calhoun[/eluser]
Php 4 ready, just change the constructor and the __call method

Code:
/**
     * Constructor
     *
     * @access public
     */
    function ActiveRecord()
    {
        parent::Model();
        overload('ActiveRecord');
        log_message('debug', "ActiveRecord Class Initialized");
    }

    /**
     * __call
     *
     * Catch-all function to capture method requests for Active Record-style
     * functions and pass them off to private methods. If no function is
     * recognised, this acts as a getter/setter (depending on whether any
     * arguments were passed in).
     *
     * @access    public
     * @param    string
     * @param    array
     * @return    function || void
     */    
    function __call($method, $args, &$result)
    {
        if (stristr($method, 'find_by_')) $result = $this->_find_by(str_replace('find_by_', '', $method), $args);
        if (stristr($method, 'find_all_by_')) $result = $this->_find_all_by(str_replace('find_all_by_', '', $method), $args);
        if (stristr($method, 'fetch_related_')) $result  = $this->_fetch_related(str_replace('fetch_related_', '', $method), $args);
        if ( ! isset($args) ) eval('return $this->' . $method . ';');
        eval('$this->' . $method . ' = "' . $args[0] . '";');
        
        return $result;
        
    }

#24
[eluser]Matthew Pennell[/eluser]
That's awesome if that is all it takes, Maurice - thanks so much for doing that! Does it make any difference to the API of the class - do all the dynamic functions still work exactly the same in the controller?

#25
[eluser]nmweb[/eluser]
There is some other stuff that needs to be fixed. You use some method chaining which PHP4 doesn't support. Even then it's not fully functional 'cause in my situation it keeps on giving errors like:
Quote:Fatal error: Call to a member function on a non-object
If I use the
Code:
$data['page'] = $this->Page->find($id);
As expected but still.

#26
[eluser]sophistry[/eluser]
yes, trying this out on PHP4 i was able to get the basic find function-ality working! nice.

but, method chaining is not supported in PHP4 so i changed some code in the find function:
Code:
//line 261 from this:
$query = $this->db->where('id', $args)->from($this->_table)->get();

// to this:
$this->db->from($this->_table);
$this->db->where('id', $args);
$query = $this->db->get();

EDIT... this next code change is not right. something else was going wrong... read on.
and i changed the foreach loop at line 266 - I'm not sure this is completely right, but it stopped errors and i got the data from the find request:
Code:
// this:
foreach($this->_columns as $column)
     {
        eval('$return->' . $column->Field . ' = $found->' . $column->Field . ';');
     }

// EDIT... this is not right.
// becomes this:
$q = $this->_columns;
foreach($q->result() as $column)
     {
        eval('$return->' . $column->Field . ' = $found->' . $column->Field . ';');
     }

finally, i had to put a class var named for the model that is instantiated when extending the Controller class:

Code:
<?php

class Addresses extends Controller {

    var $Address;
    
    function Addresses()
    {
        parent::Controller();
        $Address =& $this->load->model('Address');
    }
    
    function index()
    {
        echo "Welcome to my address list!";
        
    }
    
    function viewer($id)
    {
        $the_address = $this->Address->find($id);
        print_r($the_address->host_domain);
    }
}
?>

oh yeah, two more things.
there is a syntax error in the latest download 032 on line 86:
Code:
$columns = $this->db->query('SHOW COLUMNS FROM ' . $this->_table)->result();
and in the wiki the example says to use view() as a controller method but i've found that using any reserved words is a recipe for strange errors and blank pages. better to use something like lookit or see or peruse.

anyhow, i look forward to exploring the full power of this contribution!
thanks.

#27
[eluser]sophistry[/eluser]
more stuff about PHP4 integration:

according to a comment on the overloading() php.net page referenced earlier you need to return true or false from the constructor otherwise the parser will spit an "unknown method" warning back at you (if your find request doesn't find any data). i modified my constructor from return $result to return true. it probably should return false on some occasion but i'm flying by the seat of my pants here and i really haven't thought it through. this stuff is spinning my head!

"Some useful things to know about overloading:
__call($m,$p,&$r) returns $r back to you, not whatever you put after the keyword return.
What you return determines whether or not the parser consideres the function defined.
__get($var,&$val) returns $val back to you, so fill up $val with what you want then return true or false, same as above."

and regarding my code changes above, i think i misunderstood what was happening with the column name loop in the find_ function. i believe it was a fluke that it worked. it seems that PHP4 overloading also requires the __get() and __set() methods to be defined while PHP5 automatically defines them. so, in PHP4 you can't define an arbitrary class variable without having those __get and __set methods in the class. (i think)

i haven't gotten to the point of adding them, but i wanted to post these few findings first.

#28
[eluser]Maurice Calhoun[/eluser]
My bad, and remove the chaining... I think that is it. I could not post the entire class.

Code:
/**
     * discover_table_columns
     *
     * Called on instantiation of a model to capture the field names
     * of the related table. By convention, the model name is singular
     * and the table name is plural.
     *
     * @access    public
     * @return    object
     */    
    function discover_table_columns()
    {
        $show = $this->db->query('SHOW COLUMNS FROM ' . $this->_table);
        return $show->result();
    }

Code:
/**
     * find
     *
     * Basic find function. Either pass in a numeric id to find that table row,
     * or an array of key/values for a more complex search. Note that passing in
     * an array of 1 is stupid, as you can use find_by_fieldname() instead.
     *
     * To simply return all records, use the ALL constant: $myobj->find(ALL);
     *
     * @access    public
     * @param    int || array
     * @return    object || array
     */    
    function find($args)
    {
        if (is_array($args))
        {
            foreach ($args as $key => $value)
            {
                if (!isset($first_key))
                {
                    $first_key = $key;
                    $first_value = $value;
                }
            }
            array_shift($args);
            $data = array(
                $first_value,
                $args
            );
            return $this->find_all_by($first_key, $data);
        }
        else
        {
            if ($args != ALL)
            {
                $this->db->where('id', $args);
                $this->db->from($this->_table);
                $query = $this->db->get();
                if ($query->num_rows() > 0)
                {
                    eval('$return = new ' . $this->_class_name . '();');
                    $found = $query->row();
                    foreach($this->_columns as $column)
                    {
                        eval('$return->' . $column->Field . ' = $found->' . $column->Field . ';');
                    }
                    return $return;
                }
                else
                {
                    return false;
                }
            }
            else
            {
                $this->db->from($this->_table);
                $query = $this->db->get();
                foreach ($query->result() as $row)
                {
                    eval('$x = new ' . $this->_class_name . '();');
                    foreach ($this->_columns as $column)
                    {
                        eval('$x->' . $column->Field . ' = $row->' . $column->Field . ';');
                    }
                    $return[] = $x;
                    $x = null;
                }
                
                return $return;
            }
        }
    }

#29
[eluser]ralf57[/eluser]
Hi all,
i apologize for the silly question but i'm very new to DB interactions and Active Record issue.
Do you suggest using Buddy Bradley's class instead of CI's built-in implementation?
Can you also explain the reason(s)?

#30
[eluser]sophistry[/eluser]
great! thanks for that helping hand...

I suggest using the gunter fix (in discover_table_columns fn) so it doesn't make multiple redundant queries to get columns.

EDIT... FIXED this problem! It was me. my search only returned one record when I thought it was a search that returned multiple records. Everything seems to be good now. more later.

also, now i've got another problem:

the object returned does not seem to give me access to all of the records when i do a find_all_by_ function.

EDIT... this code works fine...
Code:
<?php

class Addresses extends Controller {

    var $Address;
    
    function Addresses()
    {
        parent::Controller();
        $Address =& $this->load->model('Address');
    }
    
    function index()
    {
        echo "Welcome to my address list!";
        
    }
    
    function viewer($mb)
    {
        $addresses = $this->Address->find_all_by_mailbox($mb);
        print_r($addresses);
    }
}
?>

EDIT... fixed.
that just prints out a giant object and there is only one "record"represented in it.

how am i supposed to get multiple records if the class stamps them into the associative array? it seems like the records may be found but are not being "saved" into the returned object properly. maurice, any help here? i think there is still something wrong with the PHP4 rewrite and i'm digging around looking, but i'm a bit slower than you are!


Digg   Delicious   Reddit   Facebook   Twitter   StumbleUpon  


  Theme © 2014 iAndrew  
Powered By MyBB, © 2002-2021 MyBB Group.