php-activerecord
php-activerecord copied to clipboard
Foreign Key with custom class problem
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!!!
just an update, i realized im using the nightly build, not the stable version. but still... any help would be appreciated, thanks!
@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.
@al-the-x Sorry, thats not working for me, not sure why
@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.
Ah, that's cool! I've update the issue! Thanks guys
@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...
@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.
Ah, I see now. Thanks for the clarification @sameerkanda...!
@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.
If you can write tests for it and fix it they're more than welcome to you writing bug fixes :).
cool, i'll give it a shot at fixing it, probably won't be done anytime soon though. Thanks!
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;
}
}
}
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.
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...
@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.
@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.
@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 ;)