j4mie / paris

A lightweight Active Record implementation for PHP5, built on top of Idiorm.

Home Page:http://j4mie.github.com/idiormandparis/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Neither short table name nor user defined table name used in has_many_through() method

bgillet opened this issue · comments

I have a model class using namespace : \BenGee\Slim\Auth\User

This class contains :

public static $_table_use_short_name = true;

I have two other model classes using namespace also :

\BenGee\Slim\Auth\Group
\BenGee\Slim\Auth\GroupUser

These model classes also contain :

public static $_table_use_short_name = true;

I have the three following tables in database :

  • user
  • group
  • group_user

In my User class, I have a method :

public function isMember($group)
{
  $isMember = false;
  $groups = $this->has_many_through('\\BenGee\\Slim\\Auth\\Group')->where('label', $group)->find_many();
  return $isMember;
}

When running, I get the following exception :

exception 'PDOException' with message 'SQLSTATE[42S02]: Base table or view not found: 1146 Table 'slim.user_ben_gee_slim_auth_group' doesn't exist' in D:\DEV\GIT\slim-auth\vendor\j4mie\idiorm\idiorm.php:429
Stack trace:
#0 D:\DEV\GIT\slim-auth\vendor\j4mie\idiorm\idiorm.php(429): PDOStatement->execute()
#1 D:\DEV\GIT\slim-auth\vendor\j4mie\idiorm\idiorm.php(1828): ORM::_execute('SELECT `group`....', Array, 'default')
#2 D:\DEV\GIT\slim-auth\vendor\j4mie\idiorm\idiorm.php(638): ORM->_run()
#3 D:\DEV\GIT\slim-auth\vendor\j4mie\idiorm\idiorm.php(627): ORM->_find_many()
#4 D:\DEV\GIT\slim-auth\vendor\j4mie\paris\paris.php(146): ORM->find_many()
#5 D:\DEV\GIT\slim-auth\src\BenGee\Slim\Auth\User.php(56): ORMWrapper->find_many()

It should look for \BenGee\Slim\Auth\Group class and use table name from it according model class settings or then guess one if the class doesn't exist.

Could you make a pull request with a regression test for this issue please?

Next time perhaps.

I had a look and found the bug.

The self::_get_table_name() method is called in has_many_through() method so no problem with the use of the model short name.

In fact, the problem is somewhere else when has_many_through() method is called only with classname of the associated class. In this case, classname of the join class is guessed by the following code :

        // The class name of the join model, if not supplied, is
        // formed by concatenating the names of the base class
        // and the associated class, in alphabetical order.
        if (is_null($join_class_name)) {
            $model = explode('\\', $base_class_name);
            $model_name = end($model);
            if (substr($model_name, 0, strlen(self::$auto_prefix_models)) == self::$auto_prefix_models) {
                $model_name = substr($model_name, strlen(self::$auto_prefix_models), strlen($model_name));
            }
            $class_names = array($model_name, $associated_class_name);
            sort($class_names, SORT_STRING);
            $join_class_name = join("", $class_names);
        }

Just after that, the base, associated and join table name are determined using self::_get_table_name() method :

        // Get table names for each class
        $base_table_name = self::_get_table_name($base_class_name);
        $associated_table_name = self::_get_table_name(self::$auto_prefix_models . $associated_class_name);
        $join_table_name = self::_get_table_name(self::$auto_prefix_models . $join_class_name);

The origin of the problem is how the join class name is guessed from base and associated classes.

In my example :

base class = \BenGee\Slim\Auth\User
associated class = \BenGee\Slim\Auth\Group

Guessed join class is : User\BenGee\Slim\Auth\Group

Expected join class is : \BenGee\Slim\Auth\GroupUser

I would propose the following changes :

        // The class name of the join model, if not supplied, is
        // formed by concatenating the names of the base class
        // and the associated class, in alphabetical order.
        if (is_null($join_class_name)) {
            $model = explode('\\', $base_class_name);
            $model_name = end($model);
            if (substr($model_name, 0, strlen(self::$auto_prefix_models)) == self::$auto_prefix_models) {
                $model_name = substr($model_name, strlen(self::$auto_prefix_models), strlen($model_name));
            }
            $model_namespace = substr($base_class_name, 0, strlen($base_class_name) - strlen($model_name));
            $associated_model = explode('\\', $associated_class_name);
            $associated_model_name = end($associated_model);
            if (substr($associated_model_name, 0, strlen(self::$auto_prefix_models)) == self::$auto_prefix_models) {
                $associated_model_name = substr($associated_model_name, strlen(self::$auto_prefix_models), strlen($associated_model_name));
            }
            //$class_names = array($model_name, $associated_class_name);
            $class_names = array($model_name, $associated_model_name);
            sort($class_names, SORT_STRING);
            $join_class_name = $model_namespace . join("", $class_names);
        }

This will guess correctly the join class name.

Ok I don't use this feature. Does anyone who does want to write a
regression test and patch and roll it up as a pull request? Otherwise I
propose deprecating the feature and caveating it in the docs.

I don't understand why this feature should be deprecated. You may not use it but other do (like me). Moreover, it is used internaly and inside unitary tests. When I will have time, I will look for doing a pull request. I have juste never done it so I don't know how to do.

Does anybody could explain to me how to make a pull request ?
I cloned j4mie/paris repository on my GitHub desktop. I modified code to make it working and I am ready committing it on develop branch. I tried looking at how writing an unit test but I have no idea how to do it as it requires three model classes using a common namespace and that the bug is inside the method called.
My first idea was modifying test/bootstrap.php file to add a namespace inside but I will have impact on tests results. I have never used PHPUnit so I don't know how to check.
Any help would be welcome. This bug fixing is quite important because you could not use models with many to many relations if your classes use namespace and you don't give explicitly to the has_many_through() method the right join model full class name as second parameter.

I concur: This should not be deprecated. I’ll work to get a test and PR by the end of the week, if @bgillet doesn’t do it first.

On Dec 3, 2014, at 3:42 AM, bgillet <notifications@github.com mailto:notifications@github.com> wrote:

I don't understand why this feature should be deprecated. You may not use it but other do (like me). When I will have time, I will look for doing a pull request. I have juste never done it so I don't know how to do.


Reply to this email directly or view it on GitHub #105 (comment).

Thanks for your help. I let you do but for next time, if you have a link to a good tutorial about how to do a pull request correctly and how to use PHPUnit, I would also appreciate.

Regards

The PHPUnit documentation is pretty good so I'd start there in combination
with reviewing the tests currently in the project.

As for doing pull requests you can't go far wrong with github's own
documentation on the matter.

I came across this issue as well today.
I've implemented the fix with tests and have created a pull request.

@bgillet: Here's a decent tutorial on how Pull Requests work: http://yangsu.github.io/pull-request-tutorial/

Fixed by #109 and merged into develop. Thanks.