Jonathan Stanton Fri Apr 08 14:21:51 -0400 2011

Subject: My First Association :(

This is what my table structure looks like:
http://www.gliffy.com/publish/2602152/

It's as basic as it comes. one "users" table, one "projects" table, one relationship "users_to_projects" table to tie them together. the field name are accurate as well.

The goal is to see which projects user "1" has (can have many). This is how I set up my activerecord and the error I get:
http://pastie.org/1772961


Yoan B Sat Apr 09 06:31:27 -0400 2011

try to change this:

array('users', 'through' => 'users_to_projects')

into:

array('users', 'class_name' => 'users', 'through' => 'users_to_projects')

same with the other relation.

And you naming convention is ugly, but that's your problem.

Jonathan Stanton Sat Apr 09 12:28:55 -0400 2011

What do you mean my naming convention is ugly? users_to_projects? what would you call a database that was a mix between users and projects? users_projects maybe? hmmm. ok noted.

Like this?:
http://pastie.org/1775819

that's the error I am getting now. I am thinking it's not matching users id column with users_to_projects user_id column. That's my thought.

Yoan B Sat Apr 09 13:49:39 -0400 2011

about your mistake: users_to_project vs users_to_projects

The ugly, or unconventional, thing is to not have camel cased classes.

Jonathan Stanton Sun Apr 10 12:40:12 -0400 2011

ah well ok I can live with that. So any ideas on the error? I really despise how Active Record messes with your class names to tables names. by making things not plural. It doesn't make sense when you get to things like users_to_projects. Because it should be user_to_projects, or users_to_projects or users_to_projects but you don't know because if it's plural it means a one to many relationship which Active Record doesn't know about. That's my opinion on the matter for naming conventions.

Jonathan Stanton Sun Apr 10 12:40:18 -0400 2011

ah well ok I can live with that. So any ideas on the error? I really despise how Active Record messes with your class names to tables names. by making things not plural. It doesn't make sense when you get to things like users_to_projects. Because it should be user_to_projects, or users_to_project or users_to_projects but you don't know because if it's plural it means a one to many relationship which Active Record doesn't know about. That's my opinion on the matter for naming conventions.

Yoan B Sun Apr 10 12:45:58 -0400 2011

Did you read my answer above?

“about your mistake: users_to_project vs users_to_projects” (line 14)

And AR knows if it's a one to many relation since you tell it using `$has_many`

Jonathan Stanton Sun Apr 10 13:29:50 -0400 2011

I've tried combo, here I isolated my project and my database into one project file.
Still getting the error. Have a see for yourself:
http://www.2shared.com/file/l3Nf-_E_/up_test.html

Yoan B Sun Apr 10 13:45:45 -0400 2011

I won't dive into your code because I prefer to use my time in a better way (sorry mate), but I'm sure you can do it by yourself. Add some breakpoints into `Relationship.php` to figure out what's going on. It's a good exercise I'm sure.

Jonathan Stanton Sun Apr 10 13:50:10 -0400 2011

Ah lame. Well, I think it's AR's limited functionality, my teammates warned me against using an active record pattern and I probably should have listed. PHP is not RoR for a reason.

Is there a way to disable the naming conventions in AR?

Yoan B Sun Apr 10 14:10:53 -0400 2011

Apples and oranges.

Naming conventions are there for good, you can mess around but it's at your own risks.

Jan ten Bokkel Sun Apr 10 16:07:31 -0400 2011

Jonathan,

You might think that naming conventions are a bad thing, but they are not.
Being 'forced' to give your class or table a certain name makes sure that your names are uniform and that someone else understands them (if they are familiar with ActiveRecord).

The class 'users_to_project' is indeed a bad naming convention. Classes should be named after real-life things, not technical stuff. In my example I will use the class name "Task" instead. So here we go:

 1 class User extends ActiveRecord\Model {
 2     // Table 'Users'
 3     static $has_many = array(
 4         array('tasks'),
 5         array('projects', 'through' => 'tasks')
 6     );
 7 }
 8 
 9 class Project extends ActiveRecord\Model {
10     // Table 'Projects'
11     static $has_many = array(
12         array('tasks'),
13         array('users', 'through' => 'tasks')
14     );
15 }
16 
17 class Task extends ActiveRecord\Model{
18     // Table 'Tasks'
19     static $belongs_to = array(
20         array('user'),
21         array('project')
22     );
23 }

Now you can write for example:

1 $user = User::find(1);
2 print_r($user->projects);

Because I am following the naming conventions I don't have to specify custom class names or table names so I will make less mistakes and my code is more readable.
I hope that you are convinced now, otherwise feel free to ask.

Jonathan Stanton Sun Apr 10 16:44:51 -0400 2011

Ah I made a discovery, figures out part of the renaming pattern is not only does it automatically make a plural word singular, but it removes underscores and changes it to camel case.
So everything works as excepted Jan when we have tasks instead of users_to_projects. Just because of the underscores. So for a test I renames "tasks" to "t_asks" and I got the same error as I did before.

There is a reason why my table is named users_to_tasks and I still want to use that because the rest of my app is using is like that. so is there any way to get around AR automatically changing my underscore naming convention to camel case?
Jan ten Bokkel Sun Apr 10 17:02:23 -0400 2011

I would suggest that you keep the class name "Task" and then add a custom table name:

1 static $table_name = 'users_to_projects';

I don't think it is currently possible to turn of the naming convention. It shouldn't be too hard to add and option for that though.

Jonathan Stanton Sun Apr 10 19:05:20 -0400 2011

Ah nice Jan thanks so much, this is exactly what I was looking for. You've been very helpful :)
Are you one of the developers for AR?

Jan ten Bokkel Mon Apr 11 10:04:12 -0400 2011

No, I'm not one of the developers :)

Some time ago I created a database interface that has a lot in common with PHP ActiveRecord in the way of creating models and accessing attributes. And I used it successfully is a couple of small projects.

But now I use PHP AR, it has nice features like associations and validations. And it still reminds me of my own interface.

Jonathan Stanton Mon Apr 11 11:21:01 -0400 2011

I also just discovered that a model class name cannot have camel case in it, or it will freak out and error.

EDIT:
the model class name CAN have camel case in it, but if you refer to, lets say model class name
"UsersToProjects", from $has_many you have to have it all lowercase. even though the class name is camel case. like so:

 1 class UsersToProject extends ActiveRecord\Model{
 2      static $table_name = 'users_to_projects';
 3      static $belongs_to = array(
 4           array('user'),
 5           array('project')
 6     );
 7 }
 8 
 9 class user extends ActiveRecord\Model{
10     static $has_many = array(
11          array('userstoproject'), #actually it's UsersToProject
12          array('projects', 'through' => 'userstoproject')
13    );
14 }  

p.s. I figured out how to paste code :D

Yoan B Mon Apr 11 12:23:23 -0400 2011

`$has_many` implies that they are more than one item (aka an array of items) so you should pluralize it.

Jonathan Stanton Mon Apr 11 12:38:42 -0400 2011

I am assuming your talking about pluralizing array('usertoproject') but you cannot. or you'll get this error
it would make sense to pluralize anything in the $has_many variable. But AR thinks differently.

1 Fatal error:  Uncaught exception 'ActiveRecord\HasManyThroughAssociationException' with message 'Could not find the association userstoproject in model user' in /Users/Jonathan/Sites/test/lib/Relationship.php:464
2 Stack trace:
3 #0 /Users/Jonathan/Sites/test/lib/Model.php(493): ActiveRecord\HasMany->load(Object(user))
4 #1 /Users/Jonathan/Sites/test/lib/Model.php(386): ActiveRecord\Model->read_attribute('projects')
5 #2 /Users/Jonathan/Sites/test/index.php(13): ActiveRecord\Model->__get('projects')
6 #3 {main}
7   thrown in /Users/Jonathan/Sites/test/lib/Relationship.php on line 464
8 

Jan ten Bokkel Mon Apr 11 12:44:01 -0400 2011

No, he means this:

1 class user extends ActiveRecord\Model{
2     static $has_many = array(
3         array('userstoproject'),
4         array('projects', 'through' => 'userstoprojects') // <== HERE!
5     );
6 }  

Jonathan Stanton Mon Apr 11 12:45:03 -0400 2011

Why not also the line above? Wouldn't that make conventional sense as well?

Jan ten Bokkel Mon Apr 11 12:53:16 -0400 2011

Ehm... yes it would :)
Are you absolutely sure that it does work in singular form?
Did you run the same test on both versions?

AR might try to find the class "Userstoproject" for "userstoproject" and "Userstoprojects" for "userstoprojects" but that both fail because you used the camel case class name "UserToProject".

Jonathan Stanton Mon Apr 11 12:55:47 -0400 2011

everything is lowercase and in singular form, but when I try so user has many userstoprojets (plural). it says it doesn't exist. and when I don't it works just fine.

Yoan B Mon Apr 11 16:29:06 -0400 2011

www.chezmoicamarche.org ¹

 1 <?php
 2 
 3 include 'ActiveRecord.php';
 4 
 5 ActiveRecord\Config::initialize(function($cfg) {
 6     $cfg->set_model_directory('.');
 7     $cfg->set_connections(array(
 8         'development' => 'sqlite://db.db'
 9     ));
10 });
11 
12 /*
13 $ sqlite3 db.db # best name ever
14 
15 create table users (id integer primary key, name text);
16 create table projects (id integer primary key, name text);
17 create table users_to_projects (id integer primary key, user_id integer, project_id integer);
18 insert into users (name) values ('yoan');
19 insert into projects (name) values ('php-activerecord');
20 insert into projects (name) values ('lithium');
21 insert into users_to_projects (user_id, project_id) values (1,1);
22 insert into users_to_projects (user_id, project_id) values (1,2);
23 */
24 
25 class user extends ActiveRecord\Model
26 {
27     static $has_many = array(
28         array('users_to_projects', 'class_name' => 'users_to_project'),
29         array('projects', 'class_name' => 'project', 'through' => 'users_to_projects')
30     );
31 }
32 
33 class project extends ActiveRecord\Model
34 {
35     static $has_many = array(
36         array('users_to_projects', 'class_name' => 'users_to_project'),
37         array('users', 'class_name' => 'user', 'through' => 'users_to_projects')
38     );
39 }
40 
41 class users_to_project extends ActiveRecord\Model
42 {
43     static $belongs_to = array(
44         array('user'),
45         array('project')
46     );
47 }
48 
49 $user = user::find(1);
50 echo $user->name, ' has the following projects:', PHP_EOL;
51 foreach ($user->projects as $project) {
52     echo ' - ', $project->name, PHP_EOL;
53 }

¹ French joke

Jonathan Stanton Mon Apr 11 20:40:15 -0400 2011

That works for you huh? Hmmm I am trying to figure out where I went wrong. quite verbose way of doing it but I guess it's better than some alternatives . and I suppose it all makes sense. I'll give it a shot.

Yoan B Tue Apr 12 01:59:00 -0400 2011

I've already told you where you went wrong buddy.

Yoan Blanc wrote:

“about your mistake: users_to_project vs users_to_projects” (line 14)

(1-25/25)