Question

I'm using php.activerecord, and I am trying to link tables together. I'm not using their structure, but php.activerecord assumes I am, so it doesn't always work. I'm trying to use it on an already made app, so I can't change the database.

I learned from my previous question - Model association with custom table and key names - that I need to be as explicit as possible with the primary_key and foreign_key fields.

I'm having issues now using has_many through. I keep getting NULL, and I have no idea why.

So, here's a scenario: I have 3 tables, contacts, contactPrefs, and preferences. Those tables are as follows

contacts
--------
contactID
name
status

contactPrefs
------------
contactID
prefID
prefValue

preferences
-----------
prefID
name
description

Each contact has multiple contactPrefs. Each contactPrefs has one preferences. I tried to use has_many to get this working, but it's not. Here are my models:

Contacts.php:

<?php
class Contact extends ActiveRecord\Model {
    static $primary_key = 'contactID';

    static $has_many = array(
        array(
            'prefs',
            'foreign_key' => 'contactid',
            'primary_key' => 'contactid',
            'class_name' => 'ContactPref'
        ),
        array(
            'preferences',
            'foreign_key' => 'prefid',
            'primary_key' => 'prefid',
            'through' => 'prefs',
            'class_name' => 'Preference'
        )
    );
}

ContactPref.php:

<?php
class ContactPref extends ActiveRecord\Model {
    static $table_name = 'contactPrefs';

    static $belongs_to = array(
        array(
            'contact',
            'foreign_key' => 'contactid',
            'primary_key' => 'contactid'
        ),
        array(
            'preference',
            'foreign_key' => 'prefid',
            'primary_key' => 'prefid'
        )
    );
}

Preference.php:

<?php
class Preference extends ActiveRecord\Model {
    static $primary_key = 'prefID';

    static $has_many = array(
        array(
            'prefs',
            'foreign_key' => 'prefid',
            'primary_key' => 'prefid',
            'class_name' => 'ContactPref'
        )
    );
}

According to the docs, I now should be able to the following:

<?php
var_dump(Contact::find(1234)->preference);

I cannot. I get NULL. Oddly, I can do this:

<?php
var_dump(Contact::find(1234)->prefs[0]->preference);

That works correctly. But, shouldn't I be able to access the preference object directly through the contact object? Am I misunderstanding the docs (they aren't the greatest, in my opinion)? Am I doing something wrong?

Était-ce utile?

La solution

First you are reading the docs with a small flaw. In the docs you are shown:

$order = Order::first();
# direct access to users
print_r($order->users); # will print an array of User object

Which you are already doing via Contact::find(1234)->prefs. Let me boil it down a bit

$contact = Contact::find(1234);
# direct access to prefs
print_r($contact->prefs); # will print an array of ContactPref object

Second, what you actually want is undefined. What should Contact::find(1234)->preference actually do? Return the preference of the first ContactPref? Return an array of Preference objects?

I feel like offering both:

<?php
class Contact extends ActiveRecord\Model {
    static $primary_key = 'contactID';

    static $has_many = array(
        array(
            'prefs',
            'foreign_key' => 'contactid',
            'primary_key' => 'contactid',
            'class_name' => 'ContactPref'
        ),
        array(
            'preferences',
            'foreign_key' => 'prefid',
            'primary_key' => 'prefid',
            'through' => 'prefs',
            'class_name' => 'Preference'
        )
    );

    public function get_preference() {
        return isset($this->prefs[0])
            ? $this->prefs[0]->preference
            : null
        ;
    }
    public function get_preferences() {
        $preference=array();
        foreach($this->prefs as $pref) {
            $preference[]=$pref;
        }
        return $preference;
    }
}

Let me explain a little bit what I have done. The ActiveRecord\Model class has a __get($name) function that looks for another function called get_$name, where $name in your case is preference (for the first result) and preference (for the entire collection). This means you can do Contact::find(1234)->preference which would be the same as doing Contact::find(1234)->prefs[0]->preference (but safer, due to the check) and Contact::find(1234)->preferences to get the entire collection of preferences.

This can be made better or optimized in numerous ways, so please don't take it as it is, but do try and adapt it to your specific situation. For example you can either use the id of the preference as an index in the array or either not force a load of more data from ContactPrefs than the ones you are going to use and try a more intricate query to get the preference objects that you specifically need.

If I find a better implementation by getting through to work in the relationship definition, I'll return. But seeing the Unit Tests for active record, I'm skeptical.

Autres conseils

There are several things that look strange, so it's not easy to come to a "this will fix it" for you, but this is an issue at least:

Fieldnames should always be lower-case in phpactiverecord. SQL doesn't mind it either way (not that table names ARE case-sensitive, but column names aren't). So make this:

static $primary_key = 'contactID';

into

static $primary_key = 'contactid';

The connections // find commands can be used in SQL, in which case it doesn't really matter how your key-string is 'cased', so some stuff works. But if the connection goes trough the inner-workings of phpmyadmin, it will fail. So check out this contactID but also the prefID.

Again, this goes only for COLUMN names, so don't go changing classnames or table-names to lowercase.

(extra point: phpmyadmin has trouble with combined primary keys. So while it might be ugly, you could add an extra row to your contactprefs table (if you don't allready have it) called id, to make that table actually have something to work with. It wouldn't give you much trouble, and it would help the activerecord library a lot)

Try the following:

<?php 
var_dump(Contact::find(1234)->preferences);

The documentation says that with a has_many relationship, it should be referenced by a plural (http://www.phpactiverecord.org/projects/main/wiki/Associations#has_many_through). The Contact::find(1234) returns a Contact object which has multiple contactPrefs with their each Preference. In addition, in your Contact model, you specify the has_many as preferences .

static $has_many = array(
    array(
        'prefs',
        'foreign_key' => 'contactid',
        'primary_key' => 'contactid',
        'class_name' => 'ContactPref'
    ),
    array(
        'preferences',
        'foreign_key' => 'prefid',
        'primary_key' => 'prefid',
        'through' => 'prefs',
        'class_name' => 'Preference'
    )
);

Edit Through Modification:

Try the following Contact model

<?php
class Contact extends ActiveRecord\Model {
static $primary_key = 'contactID';

static $has_many = array(
    array(
        'prefs',
        'foreign_key' => 'contactid',
        'class_name' => 'ContactPref'
    ),
    array('preferences', 
          'through' => 'prefs', 
          'class_name' => 'Preference',
          'primary_key' => 'prefID')
);
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top