Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Foreign Key with custom class problem #272

Open
sameerkanda opened this issue Mar 20, 2013 · 17 comments
Open

Foreign Key with custom class problem #272

sameerkanda opened this issue Mar 20, 2013 · 17 comments

Comments

@sameerkanda
Copy link

My code:

class ZP_Package extends ActiveRecord\Model {
    static $table_name = 'packages';
    static $has_many = array(
        array('items_packages', 'class_name'=>'ZP_Item_Package'),
        array('items', 'through'=>'items_packages', 'class_name'=>'ZP_Item', 'foreign_key'=>'item_id')
    );
}

class ZP_Item extends ActiveRecord\Model {
    static $table_name = 'items';
    static $has_many = array(
        array('items_packages', 'class_name'=>'ZP_Item_Package'),
        array('packages', 'through'=>'items_packages', 'class_name'=>'ZP_Package', 'foreign_key'=>'package_id')
    );

}

class ZP_Item_Package extends ActiveRecord\Model {
    static $table_name = 'items_packages';
    static $belongs_to = array(
        array('items', 'class_name'=>'ZP_Item', 'foreign_key'=>'item_id'),
        array('packages', 'class_name'=>'ZP_Package', 'foreign_key'=>'package_id')
    );
}

When I do:

$item = ZP_Item::find(24); //works fine
$item->pakages; //doesn't work!!!!!

Basically, I don't get any errors, but the query is not generated properly (actually I do get a mysql error, but what I mean to say is I don't get any PHP errors). My mysql query that is generated is something like this:

...INNER JOIN items_packages ON(packages.id = items_packages.zp_package_id)....

Notice it says "items_packages.zp_package_id" instead of "items_packages.package_id". ActiveRecord simply takes the class name and uses it as a foreign key... even though the foreign key is defined (it seems to ignore it). I'm new to PHPActiveRecord, but I'm a pro PHP programmer. I've digged into the problem, and came to a conclusion that it was a bug in ActiveRecord, where it simply uses the classname instead of the foriegn key that's defined (look at class "HasMany extends AbstractRelationship", method "load", in particular this line "$this->set_keys......", it's in the "php-activerecord/lib/Relationship.php" file).

If this is not a bug, and I'm just doing something stupid, please let me know. I know my class names don't match with the DB table names, I know I should change that, but that's not the point.... I want to know if this is a bug or not? I'm using PHPActiveRecord 1.0 (haven't tested the nightly build).

Thanks!!!

@sameerkanda
Copy link
Author

just an update, i realized im using the nightly build, not the stable version. but still... any help would be appreciated, thanks!

@al-the-x
Copy link
Collaborator

@samerkanda, would you mind adding code delimiters to your example? It would make reading it a lot easier. If you don't know, you can start and end a block of text with with three backticks (on the tilde key) to mark an extended code block. Thanks.

@sameerkanda
Copy link
Author

@al-the-x Sorry, thats not working for me, not sure why

@jpfuentes2
Copy link
Owner

@sameerkanda You can read about code formatting in Markdown/GitHub format here (specifically the "Syntax highlighting" section). You can also use gists and choose PHP as your language and it will format it appropriately.

@sameerkanda
Copy link
Author

Ah, that's cool! I've update the issue! Thanks guys

@al-the-x
Copy link
Collaborator

al-the-x commented Apr 1, 2013

@sameerkanda Can you also include your model definition for ZP_Package? Or at least verify that you're setting the primary key field on that model to "package_id"...? Almost every ORM I've ever worked with assumes that my PKs are just "id", but I usually name them after the entity to make NATURAL JOIN easier. If you haven't set the PK field manually...

@sameerkanda
Copy link
Author

@al-the-x The database primary key's are set to "id" for all tables, that's why they are not included in the definition. As stated above, the mysql is generated properly, except for the fact that:

...INNER JOIN items_packages ON(packages.id = items_packages.zp_package_id)....

should be

...INNER JOIN items_packages ON(packages.id = items_packages.package_id)....

Notice that "package_id" is a foreign key, and it's defined in the table definition as such, but PHPActiveRecord ignores it, and just assumes the class name is the foreign.

@al-the-x
Copy link
Collaborator

al-the-x commented Apr 1, 2013

Ah, I see now. Thanks for the clarification @sameerkanda...!

@sameerkanda
Copy link
Author

@al-the-x So is this a bug? Or am I doing something wrong? Sorry for being ignorant, but should I expect this to be fixed in the next version of so, or should I attempt to fix it myself and do a pull request? I haven't really contributed to any open-source project before... but then again, there hasn't really been any project that interested me as much as php-activerecord.

@anther
Copy link
Contributor

anther commented Apr 1, 2013

If you can write tests for it and fix it they're more than welcome to you writing bug fixes :).

@sameerkanda
Copy link
Author

cool, i'll give it a shot at fixing it, probably won't be done anytime soon though. Thanks!

@imkebe
Copy link

imkebe commented Nov 17, 2013

I've got same issue and I made a quick fix. It seems like to be most clean solution.

Relationship.php

protected function set_keys($model_class_name, $override=false)
    {
        if (!$this->foreign_key || $override)
        {   
            if($this instanceof HasMany || $this instanceof HasOne) {
                $relation_opt = $this->get_table()->get_relationship($this->through)->options;
                if(isset($relation_opt['foreign_key'])) {
                    $this->foreign_key = array($relation_opt['foreign_key']);
                } else {
                    $this->foreign_key = array(Inflector::instance()->keyify($model_class_name));
                }

            } else {
                $this->foreign_key = array(Inflector::instance()->keyify($model_class_name));
            }
        }
        if (!$this->primary_key || $override)
        {
            if($this instanceof HasMany || $this instanceof HasOne) {
                $relation_opt = $this->get_table()->get_relationship($this->through)->options;
                if(isset($relation_opt['primary_key'])) {
                    $this->primary_key = array($relation_opt['primary_key']);
                } else {
                    $this->primary_key = Table::load($model_class_name)->pk;
                }

            } else {
                $this->primary_key = Table::load($model_class_name)->pk;
            }
        }

    }

@sameerkanda
Copy link
Author

Thanks @imkebe, but I decided it was not worth dealing with phpactiverecord, since a friend recommended me to Laravel 4, it has a mind blowing ORM, has superior to phpactiverecord, and it has never caused any problems.

@rmetel
Copy link

rmetel commented Jan 29, 2015

Hallo @sameerkanda,
i started a project with php activerecord and after half work was done i got the same error: it takes the class name 'HpbView' and converts to 'hpb_view_id' instead of assigned 'foreign_key' => 'view_id' in my 'has_many' association. :(
Im going to try the solution of @imkebe , otherwise have to rewrite my project or rename table's primary keys, OMG...

@sameerkanda
Copy link
Author

@rmetel I've been using Laravel's ORM for over a year now, it's been awesome. I recommend it if you want to take a look (maybe for future projects). It has a bit of a learning curve though.

@rmetel
Copy link

rmetel commented Jan 29, 2015

@sameerkanda im also using laravel in few projects already, great framework indeed! In case of 'php activerecord' i wanted fast integration without complete MVC model.

@rmetel
Copy link

rmetel commented Jan 30, 2015

@sameerkanda a combination of @imkebe's bugfix + a trick did it for me!
Following situation: 'user' is connected with 'pictures' via pivot table 'collections', so my classes looked like

class Picture extends ActiveRecord\Model {
    static $has_many = array(
        array('collections')
    );
}
class Collection extends ActiveRecord\Model
{
    static $belongs_to = array(
        array('user'),
        array('picture')
    );
}
class User extends ActiveRecord\Model
{
    static $has_many = array(
        array('collections'),
        array('pictures', 'through' => 'collections', 'foreign_key' => 'user_id')
    );
}

Interesting part is the foreign_key in class User, it's NOT the foreign_key of pictures (picture_id) its the foreign_key of user table. The generated query looks like

SELECT pictures.* FROM collections INNER JOIN pictures ON (pictures.id = collections.picture_id) WHERE user_id=?

So, you can imagine, if you put a foreign_key of pictures (picture_id) your result would be just one picture and not all pictures of same user. After that php activerecord used the right foreign key and havent tried to use convenient foreign key.
In your case it would mean u must set the foreign key of ZP_Package:

class ZP_Package extends ActiveRecord\Model {
    static $table_name = 'packages';
    static $has_many = array(
        array('items_packages', 'class_name'=>'ZP_Item_Package'),
        array('items', 'through'=>'items_packages', 'class_name'=>'ZP_Item', 'foreign_key'=>'zp_package_id')
    );
}

I hope i could explain it ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants