Code Monkey home page Code Monkey logo

php-activerecord's Introduction

PHP ActiveRecord - Version 1.0

Build Status

by

http://www.phpactiverecord.org/

Introduction

A brief summarization of what ActiveRecord is:

Active record is an approach to access data in a database. A database table or view is wrapped into a class, thus an object instance is tied to a single row in the table. After creation of an object, a new row is added to the table upon save. Any object loaded gets its information from the database; when an object is updated, the corresponding row in the table is also updated. The wrapper class implements accessor methods or properties for each column in the table or view.

More details can be found here.

This implementation is inspired and thus borrows heavily from Ruby on Rails' ActiveRecord. We have tried to maintain their conventions while deviating mainly because of convenience or necessity. Of course, there are some differences which will be obvious to the user if they are familiar with rails.

Minimum Requirements

  • PHP 5.3+
  • PDO driver for your respective database

Supported Databases

  • MySQL
  • SQLite
  • PostgreSQL
  • Oracle

Features

  • Finder methods
  • Dynamic finder methods
  • Writer methods
  • Relationships
  • Validations
  • Callbacks
  • Serializations (json/xml)
  • Transactions
  • Support for multiple adapters
  • Miscellaneous options such as: aliased/protected/accessible attributes

Installation

Setup is very easy and straight-forward. There are essentially only three configuration points you must concern yourself with:

  1. Setting the model autoload directory.
  2. Configuring your database connections.
  3. Setting the database connection to use for your environment.

Example:

ActiveRecord\Config::initialize(function($cfg)
{
   $cfg->set_model_directory('/path/to/your/model_directory');
   $cfg->set_connections(
     array(
       'development' => 'mysql://username:password@localhost/development_database_name',
       'test' => 'mysql://username:password@localhost/test_database_name',
       'production' => 'mysql://username:password@localhost/production_database_name'
     )
   );
});

Alternatively (w/o the 5.3 closure):

$cfg = ActiveRecord\Config::instance();
$cfg->set_model_directory('/path/to/your/model_directory');
$cfg->set_connections(
  array(
    'development' => 'mysql://username:password@localhost/development_database_name',
    'test' => 'mysql://username:password@localhost/test_database_name',
    'production' => 'mysql://username:password@localhost/production_database_name'
  )
);

PHP ActiveRecord will default to use your development database. For testing or production, you simply set the default connection according to your current environment ('test' or 'production'):

ActiveRecord\Config::initialize(function($cfg)
{
  $cfg->set_default_connection(your_environment);
});

Once you have configured these three settings you are done. ActiveRecord takes care of the rest for you. It does not require that you map your table schema to yaml/xml files. It will query the database for this information and cache it so that it does not make multiple calls to the database for a single schema.

Basic CRUD

Retrieve

These are your basic methods to find and retrieve records from your database. See the Finders section for more details.

$post = Post::find(1);
echo $post->title; # 'My first blog post!!'
echo $post->author_id; # 5

# also the same since it is the first record in the db
$post = Post::first();

# finding using dynamic finders
$post = Post::find_by_name('The Decider');
$post = Post::find_by_name_and_id('The Bridge Builder',100);
$post = Post::find_by_name_or_id('The Bridge Builder',100);

# finding using a conditions array
$posts = Post::find('all',array('conditions' => array('name=? or id > ?','The Bridge Builder',100)));

Create

Here we create a new post by instantiating a new object and then invoking the save() method.

$post = new Post();
$post->title = 'My first blog post!!';
$post->author_id = 5;
$post->save();
# INSERT INTO `posts` (title,author_id) VALUES('My first blog post!!', 5)

Update

To update you would just need to find a record first and then change one of its attributes. It keeps an array of attributes that are "dirty" (that have been modified) and so our sql will only update the fields modified.

$post = Post::find(1);
echo $post->title; # 'My first blog post!!'
$post->title = 'Some real title';
$post->save();
# UPDATE `posts` SET title='Some real title' WHERE id=1

$post->title = 'New real title';
$post->author_id = 1;
$post->save();
# UPDATE `posts` SET title='New real title', author_id=1 WHERE id=1

Delete

Deleting a record will not destroy the object. This means that it will call sql to delete the record in your database but you can still use the object if you need to.

$post = Post::find(1);
$post->delete();
# DELETE FROM `posts` WHERE id=1
echo $post->title; # 'New real title'

Contributing

Please refer to CONTRIBUTING.md for information on how to contribute to PHP ActiveRecord.

php-activerecord's People

Contributors

al-the-x avatar anther avatar brianmuse avatar cvanschalkwijk avatar faulkner avatar ftwbzhao avatar greut avatar gsuess avatar hising avatar igorsantos07 avatar jcs avatar jpfuentes2 avatar kilida avatar kla avatar koenpunt avatar lfglopes avatar masonm avatar mheijkoop avatar nannehuiges avatar rican7 avatar rlerdorf avatar shaneiseminger avatar shmax avatar srsbiz avatar suxxes avatar teague2 avatar thijsw avatar tomzx avatar tuupola avatar vendanandrews avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

php-activerecord's Issues

Relationship issue, construct_inner_join_sql()

There is a problem in how relationships pick foreign keys to join on when using has_many through. Here is a sample fix to the problem, Relationship.php, ~line 204.

public function construct_inner_join_sql(Table $from_table, $using_through=false) { if ($using_through) { $join_table = $from_table; $join_table_name = $from_table->get_fully_qualified_table_name(); $from_table_name = Table::load($this->class_name)->get_fully_qualified_table_name(); } else { $join_table = Table::load($this->class_name); $join_table_name = $join_table->get_fully_qualified_table_name(); $from_table_name = $from_table->get_fully_qualified_table_name(); }
    // need to flip the logic when the key is on the other table
    if ($this instanceof HasMany || $this instanceof HasOne)
    {
        $this->set_keys($from_table->class->getName());

        if ($using_through)
        {
            $join_model = null;
            $assocs = $from_table->get_relationships();
            foreach ($assocs as $assoc)
            {
                if ($assoc->class_name == $this->class_name && ($assoc instanceof HasMany || $assoc instanceof BelongsTo))
                {
                    $join_model = $assoc;
                    break;
                }
            }

            if (!$join_model)
                throw new HasManyThroughAssociationException('has_many through can only use a belongs_to or has_many association');

            $join_primary_key = $join_model->primary_key[0];
            $foreign_key = $join_model->foreign_key[0];
        }
        else
        {
            $join_primary_key = $this->foreign_key[0];
            $foreign_key = $this->primary_key[0];
        }
    }
    else
    {
        $foreign_key = $this->foreign_key[0];
        $join_primary_key = $this->primary_key[0];
    }

    return "INNER JOIN $join_table_name ON($from_table_name.$foreign_key = $join_table_name.$join_primary_key)";
}

A way to fetch validation rules out of Models

Hey all,

php-activerecord is very impressive and IMO it is growing in the right direction.

Fetch validations from Models

About validations, I think there should be a way to fetch validations out of the models so they could be used for client side validation without having to duplicate them.

For example, ActiveRecord\Model::getValidationRules() would return an array of validation rules and their messages. This way I could format it to whatever structure I want and send it to client so a javascript validation lib could use those rules.

Feature request: "Belongs-to-or-maybe-not" relationship, a.k.a. left join

The joins via $belongs_to are handy, but there's no way to declare a relationship where an object either has an associated one (via FK'd field in its table) or not, and making a left join requires writing it by hand. Such a relationship will be nice. (Am I missing a belongs_to option or something else?)

creating a belongs_to association to a model's same class doesn't work

a class that has a belongs_to relationship to the same class can't be defined without causing an infinite regression until php runs out of memory.

with a sql schema of

create table posts (id int, parent_post_id int)

and a Post.php of

class Post extends ActiveRecord\Model {
    static $belongs_to = array(
         array("parent_post", "class_name" => "Post")
    );
}

it will regress infinitely when trying to load the model.

Model::count() probably shouldn't use a real Model object

Model::count() creates a new Model object with only a fake "n" attribute set with the resulting count, but it should probably use something else for doing that (a direct sql query and read the one column?)

i'm using an after_construct callback to do something with an attribute, which normally works, except where i have a validates_uniqueness_of validation, which internally calls count(), which then instantiates a new object without all of its normal attributes set (only "n"), which raises an error when the callback tries to read an attribute that is supposed to be there.

so either count() shouldn't be using real model objects to read that one "n" column or the after_construct callback shouldn't be run on those bogus objects.

Prefix for table name

Sometime is need to add a prefix for table names of all models, but there is no way to define static variables with expressions. It will be great that prefix can be defined in connection string as a query (.../db_name?table_prefix_) and can be overrided (or added) in model by static $table_prefix

Uncaught exception

Hi all,

I've just installed PHP 5.3.2 but an error has occurred.
"Fatal error: Uncaught exception 'ActiveRecord\DatabaseException' with message 'exception 'PDOException' with message 'could not find driver' in" ...

The DB settings are ok, what could be the problem?

Is It necessary to load the PDO extension?

Thanks in adavance.

Sorry for my english.

Lowercase/Uppercase entries in db inaccessible according to operation

For some reason, if you create a new instance of a Model and assign values to columns which have a uppercased name, or first letter uppercased, you must access it via UpperCasedName. If then, you want to access the property, you must use lowercase. Or something similar.

The problem is the accessibility isn't consistent through the whole model. I'd suggest supporting the exact name in the database.

For example, modifying the mysqladapter.php code from

$c->inflected_name  = Inflector::instance()->variablize($column['field']);
$c->name            = $column['field'];

to:

$c->inflected_name  = Inflector::instance()->variablize(strtolower($column['field']));
$c->name            = strtolower($column['field']);

gives it more consistency. Now you can only access columns via their lowercase name.

Model::delete and relations, aka rails :dependent => :delete

Hi,

first of all, great work! this simple&poweful lib is really needed for php, without having to use very big frameworks.

I was looking into the proper way to delete a model, respecting relations.
In rails, I can set ":dependent => :delete" so the associated object is deleted also.
This is to avoid "orphans" lying around in the database.

With this release of php-ar seems not. maybe can be a good idea to implement it?
Or maybe a check to raise an error if you try to delete an object which has_[many/one] related models.

Example: I refer to orders example in php-ar sources.
If I just call person->delete() the orders and payments will be orphaned.
To avoid that, I need to check if person has orders or payments, delete them and then delete the person.

But we have only the before_destroy callback, what is needed is a before_delete,
because the before destroy has already deleted the data...

The before_destroy is useful to delete all related data, but not to check if the deletion process can go on.
Or I'm missing something ?

Also, there's a way to get what relations an object has, without having to use self::$relation_name (ex. self::$has_many) which duplicates code ?

thanks for you patience!

matteo

MysqlAdapter & raw_type

Columns fetched through Model::table()->columns don't always have the raw_type attribute filled in.

I applied this quick fix to the MysqlAdapter class at line 64 :

if (sizeof($matches) > 0)
{
  $c->raw_type = $matches[1];
  $c->length = intval($matches[2]);
} else {
  $c->raw_type = $column['type'];
}

No need of static::$getters and static::$setters

In current version, to make the library use a custom getter and setter, we must override the $setters and $getters in the inherited model class.

But, there is a way to remove the $getters and $setters arrays: checking if the set_ method exists.

Check this in the function __get (line 374):

// Current code
if (in_array("get_$name",static::$getters))

// New code, checking if method exists
if (method_exists($this, "get_$name"))

The same is valid for __set. And the static::$getters and static::$setters can be removed, cause they're used only on this checking.

This attends the following principles: KISS, least surprise, and convention over configuration.

Namespace in class name and table name issue

Using namespace for models causing following error

ActiveRecord\DatabaseException: 42S02, 1146, Table 'db.some\namespace\models' doesn't exist

Maybe cut namespace string from the get_called_class for table name?

Ideas for this association?

What I have

I have this association: http://img28.imageshack.us/img28/6849/relations.png

Models

class User extends ActiveRecord\Model {
    static $belongs_to = array(array('user_groups'));
    static $has_many = array(array('groups', 'through' => 'user_groups'));
}

class Group extends ActiveRecord\Model {
    static $belongs_to = array(array('user_groups'),array('group_permissions'));
    static $has_many = array(array('permissions', array('through' => 'group_permissions')));
}

class UserGroup extends ActiveRecord\Model {
}

class GroupPermission extends ActiveRecord\Model {
}

class Permission extends ActiveRecord\Model {
    static $belongs_to = array(array('group_permissions'));
}

What I need

Given a user with id = $id i need to get all of its permissions. Something like this:
User::find(1)->groups->permissions();

Any ideas?

"Not unique table/alias" when a model belongs_to another model twice

When there are two relations to the same table, find() with both JOINs will throw an exception. Example code:

class Message extends ActiveRecord\Model {
static $belongs_to = array(
    array('from', 'class_name' => 'User', 'readonly' => true,
        'select' => 'fname', 'foreign_key' => 'from_id'),
    array('to', 'class_name' => 'User', 'readonly' => true,
        'select' => 'fname', 'foreign_key' => 'to_id')
);
}

Selecting like this:

$messages = Message::all(array('joins' => array('to', 'from')));

Will raise:

Uncaught exception 'ActiveRecord\\DatabaseException'
with message '42000, 1066, Not unique table/alias:
'user'' in /usr/share/php/php-activerecord/lib/Connection.php:224

Please help... has_many through causing strange error.

First, thanks for an awesome library.

I've been staring at this for a while now and can't figure it out. I've tried a number of variations but still keep hitting the same wall and was hoping you could spare a moment to take a look. Here's the code: https://gist.github.com/0c402e686f832ae98074

What's strange is that the first league does show the number of members but the second explodes as soon as it gets to that point.

Any ideas?

Why are relations an array?

Hi,

I'm wondering why you're returning an array when a relation is resolved:

// this returns an array

$addresses = Person::find('first')->addresses;

But because this is an array, I cannot use it any further as an activerecord object like this:

$addresses = Person::find('first')->addresses;

$addresses = $addresses->find( 'all', array( 'conditions' => array( 'zip = ?', '8010' ) ) );

Are there any reason this is not implementent like this?

Greez, Florian

cast of default column value CURRENT_TIMESTAMP in MysqlAdatper throws exception

When declaring a model for a table with a CURRENT_TIMESTAMP set as the default value, the MysqlAdapter throws an exception. Ideally one would not use CURRENT_TIMESTAMP on a database while using the ActiveRecord pattern but we all have the fun of working with legacy databases, especially since this is very new to PHP.

The Exception is thrown from DateTime::__construct =>
>> $a = new DateTime('CURRENT_TIMESTAMP');
Exception (code: 0) got thrown
exception 'Exception' with message 'DateTime::__construct(): Failed to parse time string (CURRENT_TIMESTAMP) at position 0 (C): The timezone could not be found in the database' in...

Possible Fix (mimics Rails which just returns nil on a bad time parse):

case self::DATETIME:
case self::DATE:
  try {
    return ($value instanceof \DateTime) ? $value : new \DateTime($value);
  } catch(\Exception $e) {
    return null;
  }

"Column 'id' in where clause is ambiguous" when using find by PK and join

How to reproduce:
class One extends ActiveRecord\Base {
static $belongs_to = array('two');
}
class Two extends ActiveRecord\Base { }

This runs fine:
One::find(1);
However, this causes an error:
One::find(1, array('joins' => array('two'))); // error: Column 'id' in where clause is ambiguous

The produced SQL looks like
SELECT one.* FROM one INNER JOIN two ON(one.two_id = two.id) WHERE id=?

feature - tarcking on user-sets attribute [for logging for ex.]

on my model I wrote:

static $after_save      = array('logging');
/**
 * Logging
 */
private $_user_modified_attributes = array();
public function __set($name, $value) {
    $this->_user_modified_attributes[]=$name;
    parent::__set($name, $value);
}
public function logging() {
    if($this->is_new_record()) log::log("Adding new user", $this->id);
    else {
        $changes = array();
        foreach($this->_user_modified_attributes as $attr) $changes[]="`".$attr."`('".$this->$attr."')";
        log::log("Changing the fields ". implode(', ',$changes)." on exist ".__CLASS__, $this->id);
    }
}

it could be nice to make it built-in on the class.. what do you think?

tables are not having updated_at fields updated automatically

in Model.php:update(), $dirty is getting set first, then the callbacks are run, and then it only updates the columns in $dirty. since the callback set in Table.php to run set_timestamps() will add updated_at to dirty_attributes() but not $dirty, that column never gets updated.

so basically $dirty needs to be reloaded after calling before_* callbacks.

i have a commit doing it but my code is with experimental transaction support.

http://github.com/jcs/halfmoon/commit/b91127f3b9293151efdc7e6bbb95e82669a58363

Multi-connection models do not work

I have two connections configured:
$cfg->set_connections(
array('first' => 'mysql://user1:password1@host/one',
'second' => 'mysql://user2:password2@host/two'),
'first');
And a model for the second, per http://www.phpactiverecord.org/guides/configuration:
class MyModel extends ActiveRecord\Model {
static $db = 'second';
}
MyModel::find('first');
However, what it really does (and fails) is, per logger:
SHOW COLUMNS FROM second.my_model
on the "first" connection (database "one").

Is this really the expected behavior? I thought I can have two connections defined and used simultaneously.

DateTime objects cannot be used in conditions

Say a model named One has a DATETIME field named time. Then
One::find('first')->time
will return a DateTime object (or ActiveRecord\DateTime), but both
One::find_by_time(new DateTime());
and
One::find_by_time(new ActiveRecord\DateTime());
will throw an error:
Object of class DateTime could not be converted to string
and
Object of class ActiveRecord\DateTime could not be converted to string

This is on the latest (as of now) integration branch.

Model::__isset() should return true for aliased attributes

Currently, if you do isset($model->myAliasedAtribute) it returns false. I think it should return true because an aliased attribute is meant to act like a real one, it will just not be stored in a save().

A fix would be this small change to ActiveRecord\Model::__isset():

public function __isset($attribute_name)
{
    return array_key_exists($attribute_name,array_merge_recursive($this->attributes, static::$alias_attribute));
}

pdo exceptions have no useful information

using a mysql connection, issuing an invalid query:

>> Area::find("all", array("conditions" => array("blah = 1")));

PHP Fatal error:  Uncaught exception 'ActiveRecord\DatabaseException' with message '00000, , ' in /*/common/activerecord/lib/Connection.php:198
Stack trace:
#0 /*/common/activerecord/lib/Table.php(168): ActiveRecord\Connection->query('SELECT * FROM `...', Array)

note the 'message 00000' and no actual error message from mysql. i think this is because the Connection->connection object doesn't carry any error info, only the $sth object in Connection->query(). if i pass up $sth and use $sth->errorInfo(), i get the actual error response from mysql.

>> Area::find("all", array("conditions" => array("blah = 1")));
PHP Fatal error:  Uncaught exception 'ActiveRecord\DatabaseException' with message '42S22, 1054, Unknown column 'blah' in 'where clause'' in /*/common/activerecord/lib/Connection.php:198
Stack trace:
#0 /*/common/activerecord/lib/Table.php(168): ActiveRecord\Connection->query('SELECT * FROM `...', Array)

using conditions in a belongs_to, doesn't allow a model attribute

First of all, I know that the fields names don't meet some of the active record rules, especially because of the names, but as you know, active record should be flexible to allow this, and its awesome to find that php-ar does.

Well, thing is I have a Question model (obviously for storing questions) and a QuestionOption model (for the answers of a question)

The Question's model primary key is composed by two fields 'id_test_page' and 'question_index', and so is the QuestionOptions's foreign key.

My workaround to solve this was to use the conditions option of the belongs_to. For example:

static $belongs_to = array(
array( 'question',
'class' => 'Question',
'foreign_key' => 'id_test_page',
'conditions' => 'question.question_index = #{question_index}'));

But the #{question_index} wouldn't refer to the field question_index in this model. Do you have an Idea of how could i do this?

Below is the QuestionOption model as i'm using it right now, i had to create a function to retrieve the associated instance of the Question model.

// Class QuestionOption
class QuestionOption extends ActiveRecord\Model {
    static $table_name = "question_option";

    // QuestionOption belongs to an ImageData and a Question
    static $belongs_to = array(
        array(  'image_data',
                'class'       => 'ImageData',
                'foreign_key' => 'option_image'));
        // array(   'question',
        //      'class'       => 'Question',
        //      'foreign_key' => 'id_test_page',
        //      'conditions'  => 'question.question_index = #{question_index}'));

    public function question() {
        return Question::find_by_id_test_page_and_question_index($this->id_test_page, $this->question_index);
    }

} // end class

Thanks for your help guys!

has_one issue

perhaps someone can point me in the right direction.
I've got the following models, but can't seem to get $user->contactinfo
Any ideas or things I should try?

class User extends ActiveRecord\Model
{
static $has_one = array(
array('contactinfo','class_name' => 'Contactinfo','foreign_key' => 'refid');
);
}

class Contactinfo extends ActiveRecord\Model
{
static $table_name = 'contactinfo';
}

thanks and nice work!
-L

feature - Associate models via dynamic attributes

Hi! I have been trying to associate two objects like this:

/* assign sheep to herd */
$sheep->herd = $herd;

Currently this does not work. Sheep::herd is not known as an attribute. As the 'herd' attribute can already be read it would be great to write it that way as well. If herd has not been saved to the db yet and does not have a valid PK an Exception could be thrown. Otherwise append_record_to_associate could be used to associate the two.

This is how I tried to use it:

/*
SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL';

DROP SCHEMA IF EXISTS `sheeps` ;
CREATE SCHEMA IF NOT EXISTS `sheeps` DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci ;
USE `sheeps`;

-- -----------------------------------------------------
-- Table `sheeps`.`herds`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `sheeps`.`herds` (
  `id` INT NOT NULL AUTO_INCREMENT ,
  `location` VARCHAR(45) NULL ,
  PRIMARY KEY (`id`) )
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `sheeps`.`sheep`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `sheeps`.`sheep` (
  `id` INT NOT NULL AUTO_INCREMENT ,
  `name` VARCHAR(45) NULL ,
  `herd_id` INT NULL ,
  PRIMARY KEY (`id`) ,
  INDEX `fk_sheep_herd` (`herd_id` ASC) ,
  CONSTRAINT `fk_sheep_herd`
    FOREIGN KEY (`herd_id` )
    REFERENCES `sheeps`.`herds` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;

SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
*/

require_once '../php-activerecord/ActiveRecord.php';
ActiveRecord\Config::initialize(function($cfg)
{
    $cfg->set_model_directory('.');
    $cfg->set_connections(array(
        'development' => 'mysql://root:@localhost/sheeps'));
});

class Sheep extends \ActiveRecord\Model {
    static $belongs_to = array(
        array('herd')
    );
}

class Herd extends \ActiveRecord\Model {
    static $has_many = array(
        array('sheep')
    );
}

$herd = new Herd();
$herd->location = "On the hill";
$herd->save();

$sheep = new Sheep();
$sheep->name = "Ludmilla";
$sheep->save();

/* assign sheep to herd */
$sheep->herd = $herd;

dynamic relationships / getters

Hi there,

I was wondering if there was any "clean" way to have dynamic relationships. For instance,
let's say I have a model called Post and another one called I18nPost.
Post has_one I18nPost for each language.
I managed to make it work by overriding the __construct and setting the $static::$has_one there but then I need to instantiate the model first... and this just doesn't feel right.

On a totally different topic, would it be possible to have getters working exactly the same as setters ? It would allow transparent serialization / unserialization of an attribute for instance.

New validation rule: "validates_equal_values_of"

validate_equal_values_of

A simple rule that checks if two or more specified fields have the same value. This might be useful for password confirmation fields.

Example:

class User extends ActiveRecord\Model {
    # used when creating new user
    static $alias_attribute = array(
        'passConfirm' => 'pass'
    );

    static $validates_equal_values_of= array(
        array(array('pass', 'passConfirm'), 'message' => 'password fields must match')
    );
}

Perhaps not often used but still useful.

validates_length_of compare with incorrect value

Hi,

I've recently tried to use the validates_length_of validator, but soon realized there was a small problem with it.

I have, for example:
static $validates_length_of = array(
array('username', 'maximum' => 12),
array('password', 'is' => 32)
);

Which would compare the strlen of username (returning 5 for example) but comparing it with 0 since the $option is equal to say 'tomzx', which isn't right.

Here is how I fixed it:

500c500
<                   if ('maximum' == $range_option && strlen($attribute_value) > $option)

---
>                   if ('maximum' == $range_option && strlen($attribute_value) > $attr['maximum'])
503c503
<                   if ('minimum' == $range_option && strlen($attribute_value) < $option)

---
>                   if ('minimum' == $range_option && strlen($attribute_value) < $attr['minimum'])
506c506
<                   if ('is' == $range_option && strlen($attribute_value) === $option)

---
>                   if ('is' == $range_option && strlen($attribute_value) === $attr['is'])
513d512
<

i18n or l10n

Hi,

I have some problems using the validation process as the message errors generated follow the $variable message format. I was wondering how one would go about using your system in another, if not, various languages.

Thanks.

Use MySQL native functions

Hi,

I'd like to know if there's a way to call mysql functions without having to use something like

$this->connection()->query('UPDATE mysql.user SET password = PASSWORD(?) WHERE User = ?', $data);

Thank you.

small issue

Parse error: syntax error, unexpected ':' in /library/php-activerecord/lib/Table.php on line 192

resolve:
$attach_associations_from_eager = empty($models_for_eager_load) : false : true;
to
$attach_associations_from_eager = empty($models_for_eager_load) ? false : true;

Problem with model relationships

Sorry, I don't know how to format this correctly...

I have two models called OsVersion and OsType. OsVersion belongs to OsType and OsType has many OsVersions. Like this:

Class OsVersion extends ActiveRecord\Model {

static $belongs_to = array(
    array('os_type', 'foreign_key' => 'os_name_id'),
);

}

Class OsType extends ActiveRecord\Model {

static $has_many = array(
    array('os_versions', 'primary_key' => 'os_version_id')
);

}

When I try to get os versions I get 'Class OType does not exist'. And when I try to get os types I get 'Class OVersion does not exist'.

As you can see, it's taking out the 's' in both of the class names. My other models that dont have 'Os' in the name work fine.

Here's the full error message for OsType:

Fatal error: Uncaught exception 'ReflectionException' with message 'Class OVersion does not exist' in /Users/nick/work/mix4/lib/php-activerecord/lib/Reflections.php:33 Stack trace: #0 /Users/nick/work/mix4/lib/php-activerecord/lib/Reflections.php(33): ReflectionClass->__construct('OVersion') #1 /Users/nick/work/mix4/lib/php-activerecord/lib/Relationship.php(259): ActiveRecord\Reflections->add('OVersion') #2 /Users/nick/work/mix4/lib/php-activerecord/lib/Relationship.php(254): ActiveRecord\AbstractRelationship->set_class_name('OVersion') #3 /Users/nick/work/mix4/lib/php-activerecord/lib/Relationship.php(437): ActiveRecord\AbstractRelationship->set_inferred_class_name() #4 /Users/nick/work/mix4/lib/php-activerecord/lib/Table.php(454): ActiveRecord\HasMany->__construct(Array) #5 /Users/nick/work/mix4/lib/php-activerecord/lib/Table.php(63): ActiveRecord\Table->set_associations() #6 /Users/nick/work/mix4/lib/php-activerecord/lib/Model.php(678): ActiveRecord\Table::load('OsType') #7 /Users/nick/work/mix4/lib/php-activerecord/li in /Users/nick/work/mix4/lib/php-activerecord/lib/Reflections.php on line 33

And here's the full error for OsVersion:

Fatal error: Uncaught exception 'ReflectionException' with message 'Class OType does not exist' in /Users/nick/work/mix4/lib/php-activerecord/lib/Reflections.php:33 Stack trace: #0 /Users/nick/work/mix4/lib/php-activerecord/lib/Reflections.php(33): ReflectionClass->__construct('OType') #1 /Users/nick/work/mix4/lib/php-activerecord/lib/Relationship.php(259): ActiveRecord\Reflections->add('OType') #2 /Users/nick/work/mix4/lib/php-activerecord/lib/Relationship.php(254): ActiveRecord\AbstractRelationship->set_class_name('OType') #3 /Users/nick/work/mix4/lib/php-activerecord/lib/Relationship.php(606): ActiveRecord\AbstractRelationship->set_inferred_class_name() #4 /Users/nick/work/mix4/lib/php-activerecord/lib/Table.php(462): ActiveRecord\BelongsTo->__construct(Array) #5 /Users/nick/work/mix4/lib/php-activerecord/lib/Table.php(63): ActiveRecord\Table->set_associations() #6 /Users/nick/work/mix4/lib/php-activerecord/lib/Model.php(678): ActiveRecord\Table::load('OsVersion') #7 /Users/nick/work/mix4/lib/php-activerecord/lib/Model in /Users/nick/work/mix4/lib/php-activerecord/lib/Reflections.php on line 33

Model::delete and Model::update

It very useful to have these static methods for removing or updating bunch of records without selecting them. For example:
Post::delete(array(
'conditions' => array('user_id = ?', $user->id), 'limit' => 5
))

Post::update(array(
    'conditions' => array('`user_id` = ?', $user->id),
    'set' => array('`views_count` = `views_count` + 1, `last_viewer` = ?', $user->id)
))

validates_uniqueness_of should respect aliases

As the other validators already do, it would be good to enable to validate uniqueness of aliased attributes:

class User extends ActiveRecord\Model {
    static $alias_attribute = array( 'login' => 'meh_this_is_a_legacy_table' );
    static $validates_uniqueness_of = array( 'login' );
}

Currently, the code fails with

Uncaught exception 'ActiveRecord\\DatabaseException' with message '42S22, 1054, Unknown column 'login' in 'where clause''

Using the original name for the attribute is a workaround, but will not look nice with validators:

Meh this is a legacy table is not unique

Model::set_attributes_via_mass_assignment raises exceptions

the comment for this function says:

 * Passing strict as true will throw an exception if an attribute does not exist.

however there's no strict argument to be passed, and the function always raises exceptions for attributes that don't exist. i'm not sure if this argument is supposed to be passed from __construct and default to false, or it's just not supposed to raise exceptions, but in either case i think it should default to not raising exceptions.

Many to many relationships in a loop

Given the following models :

class Group extends ActiveRecord\Model {
    static $has_many = array(
        array('user_groups'),
        array('users', 'through' => 'user_groups')
    );
}

class User extends ActiveRecord\Model {
    static $has_many = array(
        array('user_groups'),
        array('groups', 'through' => 'user_groups')
    );
}

class UserGroup extends ActiveRecord\Model {
    static $belongs_to = array(
        array('user'),
        array('group')
    );
}

In my controller, I tried the following code :

foreach(User::all() as $user) {
    print $user->name.' ('.count($user->groups).')<br />';
}

Here is what I got :

exception 'ActiveRecord\DatabaseException' with message '42S22, 1054, Unknown column 'user_groups.user_groups.user_id' in 'where clause'

Possible Fix -> in 'lib/Relationship.php' on line 373 :

foreach ($this->foreign_key as $index => &$key) {
    if(strpos($key, $through_table_name) !== 0) {
        $key = "$through_table_name.$key";
    }
}

The point of the strpos is to check if the field has already been prefixed with the "through_table_name", in my example "user_groups".

return values for Model::create

Hi!

according to Model.php, in function create, we always have a model even if
the save has failed:

public static function create($attributes, $validate=true)
    {
        $class_name = get_called_class();
        $model = new $class_name($attributes);
        $model->save($validate);
        return $model;
    }

what is the best way to handle a failed save? I'm using is_new_record attribute to check if the model has been saved or not.
Is this the correct way ?
Or better not use create and use initialize+save directly ?

Or, is reasonable to change the create function to:

public static function create($attributes, $validate=true)
    {
        $class_name = get_called_class();
        $model = new $class_name($attributes);
        $res = $model->save($validate);
        if ( $res )
            return $model;
        else
            return $res;
    }

regards,
mat

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.