Sébastien Corne Thu Mar 17 05:40:33 -0400 2011

Subject: Better support of Namespace for Model

I found some problems with using namespace models.
This is the changes I made ​​to resolve these problems.
I assume that models will always be in the same namespace.

(I'm using the 2011/02/28 nightly build)


Add this into ActiveRecord\Config :

private $model_namespace = '\\';

public function set_model_namespace($namespace) {
    $this->model_namespace = '\\' . trim($namespace, '\\') . '\\';

public function get_model_namespace() {
    return $this->model_namespace;

Now, we can set namespace in configuration like that :

ActiveRecord\Config::initialize(function($cfg) use ($connections) {


Update ActiveRecord\Reflections::get_class() :

private function get_class($mixed=null)
// Start modification
    if (is_string($mixed) && strpos($mixed, '\\') === false) {
        $config = Config::instance();
        return $config->get_model_namespace() . $mixed;
// End modification

    if (is_object($mixed))
        return get_class($mixed);

    if (!is_null($mixed))
        return $mixed;

    return $this->get_called_class();


Update ActiveRecord\AbstractRelationship::set_class_name() :

protected function set_class_name($class_name)
// Start modification
    if (strpos($class_name, '\\') === false) {
        $config = Config::instance();
        $class_name = $config->get_model_namespace() . $class_name;
// End modification

    $reflection = Reflections::instance()->add($class_name)->get($class_name);

    if (!$reflection->isSubClassOf('ActiveRecord\\Model'))
        throw new RelationshipException("'$class_name' must extend from ActiveRecord\\Model");

    $this->class_name = $class_name;


"has_many through" doesn't work with namespace, we must also update ActiveRecord\HasMany::load() :

public function load(Model $model)
    $class_name = $this->class_name;

    // since through relationships depend on other relationships we can't do
    // this initiailization in the constructor since the other relationship
    // may not have been created yet and we only want this to run once
    if (!isset($this->initialized))
        if ($this->through)
            // verify through is a belongs_to or has_many for access of keys
            if (!($through_relationship = $this->get_table()->get_relationship($this->through)))
                throw new HasManyThroughAssociationException("Could not find the association $this->through in model " . get_class($model));

            if (!($through_relationship instanceof HasMany) && !($through_relationship instanceof BelongsTo))
                throw new HasManyThroughAssociationException('has_many through can only use a belongs_to or has_many association');

            // save old keys as we will be reseting them below for inner join convenience
            $pk = $this->primary_key;
            $fk = $this->foreign_key;

            $this->set_keys($this->get_table()->class->getName(), true);

// Start modification
            //$through_table = Table::load(classify($this->through, true));
            $through_table = Table::load($through_relationship->class_name);
// End modification
            $this->options['joins'] = $this->construct_inner_join_sql($through_table, true);

            // reset keys
            $this->primary_key = $pk;
            $this->foreign_key = $fk;

        $this->initialized = true;

    if (!($conditions = $this->create_conditions_from_keys($model, $this->foreign_key, $this->primary_key)))
        return null;

    $options = $this->unset_non_finder_options($this->options);
    $options['conditions'] = $conditions;
    return $class_name::find($this->poly_relationship ? 'all' : 'first',$options);


Now, this code works fine :


namespace app\models;

class User extends \ActiveRecord\Model {
    static $has_many = array(
        array('roles_users', 'class_name' => 'RoleUser'),
        array('roles', 'through' => 'roles_users'),

class Role extends \ActiveRecord\Model {
    static $has_many = array(
        array('roles_users', 'class_name' => 'RoleUser'),
        array('users', 'through' => 'roles_users'),

class RoleUser extends \ActiveRecord\Model {
    static $table_name = 'roles_users';
    static $belongs_to = array(
$user  = \app\models\User::first();
$roles = $user->roles;

This tweaks are made for resolving "has_many" issues. Sames issue may appears with "belongs_to", but i don't look at this yet.

(sorry for my bad English, this message was translated from french)

Yoan B Wed Mar 23 09:38:30 -0400 2011

Hi / Salut!

One way of doing that is to give the full namespace to class_name, it gets repetitive but you should manage that.

static $has_many = array(
array('users', 'classe_name' => 'app\\models\\User')

Yes, PHP-AR has some trouble with relations and so, but they can be fixed. As I'm using this too I manage to fix some (not all of them yet).

Try to use the version from github and report bugs there too.

Sébastien Corne Wed Mar 23 11:06:46 -0400 2011

Hi Yoan Blanc, thanks for your reply

i have tried but that cause some issues with `has_many through`.

Yoan B Wed Mar 23 11:11:38 -0400 2011


This is the branch I did some work on this: https://github.com/greut/php-activerecord/tree/gh101-has_many-through

And it seems that it has been integrated as the tests are now part of kla's repository: https://github.com/kla/php-activerecord/blob/master/test/HasManyThroughTest.php

Add yours if you find anything that isn't working as expected.

Sébastien Corne Wed Mar 23 13:02:32 -0400 2011

Don't work if you named the `through association` `users_newsletters` (note the 'S' on users).
With my modification on ` ActiveRecord\HasMany::load()`, it work fine.

The other suggestions are for facility. Why repeat all class names while "Inflector" works well and in most time namespace will be the same for all classes ?

(sorry again for my bad English)

Yoan B Wed Mar 23 13:08:27 -0400 2011

On va passer en céfran (sorry guys) :

Est-ce que tu arriverais à écrire un test simple sur comment tu aimerais que ça fonctionne ?

Oui c'est chiant d'avoir à répéter le class_name à chaque fois, c'est clair. Il y aurait de quoi regarder dans le même namespace que la classe définie également. Ça semble être intéressant et ne limite pas autrement ce qu'il est possible de faire. Et `through` doit aller rechercher la relation du même nom et non assumer qu'il y a une classe nommée de la même manière.

C'est un peu le merdier, mais on devrait réussir à pondre un truc solide si on s'y met à plusieurs. Ils ne sont pas hyper réactif sur GH mais une fois toutes les deux semaines, l'un deux se réveille. Pas terrible…

Yoan B Sun Mar 27 14:32:08 -0400 2011


I've made an issue and wrote a patch too. Try it, fix the tests, and continue the discussion other there.




J'ai tenté une approche où le système va aller rechercher la classe dans l'espace de nom courant si la classe dans l'espace de nom global n'existe pas. Je te laisse tester ça et commenter sur github directement. J'ai eu la flemme de faire une branche.


Sébastien Corne Mon Mar 28 09:03:36 -0400 2011

Work "like a charm", must be implemented in the next nightly !

Milles merci Yoan ;)

Luke Stokes Thu Mar 15 16:30:42 -0400 2012

Are there plans to implement this change? I spent quite a bit of time being really confused because of what I think is a namespace issue. The activerecord_autoload was duplicating the namespace in the path and failing to load. By adding $class_name = ActiveRecord\denamespace($class_name); that seems to fix it but I don't know enough about namespaces to know what other problems that could cause.

Yoan B Thu Mar 15 18:30:01 -0400 2012

Now that I have super power on the repository, I can apply my patch on it. I'll try to redo a clean pull request and see if anyone yells against it.

Yoan B Tue Apr 03 07:19:00 -0400 2012

It's in the master on github.