How can I use has_one and has_many in codeigniter 3.0?
I would like to create relations through tables like in Ruby on Rails.
I.E
Class User
{
has_many: comments
}
Class Comments
{
belongs_to: users
}
This is the most common usage, and is used in almost every project. There is a simple pattern to defining this relationship.
Post has a creator and an editor, which may be different users. Here's how to set that up.
Post
class Post extends DataMapper {
$has_one = array(
'creator' => array(
'class' => 'user',
'other_field' => 'created_post'
),
'editor' => array(
'class' => 'user',
'other_field' => 'edited_post'
)
);
}
User
class User extends DataMapper {
$has_many = array(
'created_post' => array(
'class' => 'post',
'other_field' => 'creator'
),
'edited_post' => array(
'class' => 'post',
'other_field' => 'editor'
)
);
}
A couple of things to note here.
The relationship is now defined by the relationship key on either
side, not the model name. This has now become the only way to look
up the relationship.
The key on one side of the relationship becomes the other_field on
the opposite side, and vice-versa.
Because we need a way to specify the difference between posts that
were edited and those that were created, we have to declare the
slightly unusual edited_post and created_post relationships. These
could have any name, as long as they were unique and mirrored on
Post.
http://datamapper.wanwizard.eu/pages/advancedrelations.html
Related
I just cant get data from 2 tables in a HABTM relationship and I cant get the answer for these cakephp docs (again). Sorry for asking the question on something that should be explained in the docs.
This should be a simple but I keep getting undefined index and the relationship I set up seems in accordance with the docs. I have another post on a more complicated matter but this issue needs to be isolated .
I dont know how to get related data from the lessons table and the students table via this lessons-students HABTM table.
I want all the lessons a student does. I want the name of the student from the student table and lesson details from the lesson table which has lesson_id as the common link in this lessons-students table. Sounds simple but I cant do it.
public $hasAndBelongsToMany = array(
'Student' => array(
'className' => 'Student',
'joinTable' => 'lessons_students',
'foreignKey' => 'lesson_id',
'associationForeignKey' => 'student_id',
'unique' => 'keepExisting',
)
);
class LessonsController extends AppController {
....
$this->Lesson->recursive = -1;
$options['joins'] = array(
array('table' => 'lessons_students',
'alias' => 'LessonsStudent',
'type' => 'LEFT',
'conditions' => array(
'Lesson.id = LessonsStudent.lesson_id',
)
),
array('table' => 'students',
'alias' => 'Student',
'type' => 'LEFT',
'conditions' => array(
'LessonsStudent.student_id=Student.id',
)
),
);
$options['conditions'] = array('Student.id' => 2);
// $Item->find('all', $options);
$student=$this->set( 'student',$this->Lesson->find('all', $options));
In the view I get the error undefined index Student
<?php
// debug($student);
foreach ($student as $item2):
echo '<td>'. $item2['Lesson']['id'].'</td>';
echo '<td>'. $item2['Student']['id'].'</td>';
http://stackoverflow.com/questions/17250215/cant-get-all-user-data-from-habtm-in-cakephp
http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#hasandbelongstomany-habtm
If you want to use model association you need to remove $this->Lesson->recursive = -1; because it disable any associations defined in model. And than you can remove $options['joins'].
If you have defined many association in your Lesson model and don't want to fetch all of it use unbindModel() or use Containable behavior
$this->Lesson->Behaviors->load('Containable');
$this->Lesson->contain('Student');
If you need to do find many times in many actions it's better to enable Containable in model itself
class Lesson extends AppModel {
public $actsAs = array('Containable');
}
so you can avoid $this->Lesson->Behaviors->load('Containable'); line;
I have a function where a user can request a project. The request has 2 fields for other employees to be added.
It's got a field for a person who is responsible for the project (person_responsible) and the employee who is supposed to attend the opening meeting (person_attending).
What I want to know is, since both these fields (person_responsible and person_attending) will be pulling it's data from hr_employees table, how would I set this up in my Project-Model.
At the moment I have the one field set up like this:
public $belongsTo = array(
'HrEmployee' => array(
'className' => 'HrEmployee',
'foreignKey' => 'responsible_person',
'fields' => 'HrEmployee.employeename',
)
);
How would I set up the other field?
What I do in this cases is to make two associations. Since cake allow to customize relations, you can have two relations to the same model with different names.
public $belongsTo = array(
'ResponsibleEmployee' => array(
'className' => 'HrEmployee',
'foreignKey' => 'responsible_person',
'fields' => 'HrEmployee.employeename',
),
'AttendingEmployee' => array(
'className' => 'HrEmployee',
'foreignKey' => 'person_attending',
'fields' => 'HrEmployee.employeename',
)
);
Change the names to adjust your needs. Now, if your model is set as containable and you retrieve the Project model with those models in it, you'll get something like
array('Project' => array(/*data project*/),
'ResponsibleEmployee' => array(/*name*/),
'AttendingEmployee' => array(/*name*/)
)
(or another variation of that array depending on how the query was made).
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?
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.
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')
);
}
When I was working on my current project, I ran into a rather complex issue. I'll point out my problem much more detailed right now:
There are three Models: User, UsersExtendedField and UsersExtended.
UsersExtendedField contains custom fields that can be managed manually. All custom fields can be shown in the Users view as well, and be filled out of course. The values are then stored in UsersExtended, which has got two foreignKeys: user_id and field_id.
The relations look like this: User hasMany UsersExtendedField, UsersExtendedField hasMany UsersExtended, UsersExtended belongsTo User, UsersExtendedField.
The problem: When accessing the Users view, a form with user information input is shown. Any UsersExtendedFields are available as well, and since these hasMany UsersExtended, they've got plenty of UsersExtended values. But I want to reduce those to only the value(s) that belong to the User, whose view is shown at the moment. Here are my (desired) relations:
Croogo::hookBehavior('User', 'Crooboard.ExtendedUser', array(
'relationship' => array(
'hasMany' => array(
'UsersExtendedField' => array(
'className' => 'Crooboard.UsersExtendedField',
'foreignKey' => '',
'conditions' => array('status' => 1)
),
),
),
));
class UsersExtendedField extends AppModel {
var $name = 'UsersExtendedField';
var $displayField = 'fieldname';
var $hasMany = array(
'UsersExtended' => array(
'className' => 'Crooboard.UsersExtended',
'foreignKey' => 'field_id',
'conditions' => array(
'UsersExtended.user_id = User.id'
)
),
);
}
This is not the full code, these are the important parts. The problem starts right where I wrote 'UsersExtended.user_id = User.id'. Obviously, this won't work. But I do not have any idea how to access the User.id here. I also could not imagine a HABTM structure to solve this task. Do you have any idea how to get the semantics of this 'UsersExtended.user_id = User.id' to work?
Thank your very much for taking the time to read through this and helping me!
It sounds like you need to set up your HABTM relationship properly.
You already have the join table, UsersExtended, which contains your foreign keys.
Remove all previous relationships and set up HABTM in each of your User and UserExtendedField models.
The relationship code in your User model would look like this:
var $hasAndBelongsToMany = array(
'UsersExtended' => array(
'className' => 'UsersExtended',
'joinTable' => 'UsersExtended', //assuming this is the
//name of that model's table
'foreignKey' => 'user_id',
'associationForeignKey' => 'field_id'
)
);
For more information check out the page in the cakephp book
In addition, this blog post helped me grasp the relationship concepts when I was learning cakephp.
Im using cakephp and Im stuck about the idea of listing multiple models, here's my scenario:
I have two major models namely Task and Events. I used the Events model to track all changes. this has the ff fields: id, model, model_id, changed
I used Events table to track multiple model changes. so when something changed to Task model it will be logged to Events models (get the idea??)
What I want to do is whenever i used the find method for Event model, i want to list the model information (for example, the Task model) together with my Events information like this:
array(
[0] => array (
[Event] => array(
id => 1
model => Task
model_id => 2
changed => array()
)
[Task] => array(
id => 2
name => Task 1
descp => Test Task
)
)
)
Note: The model can be any model, it can be Projects model.
At my task.php, it is no problem because i can easily declare:
var $hasMany = array(
'Events' => array(
'className' => 'Events',
'foreignKey' => 'model_id',
'dependent' => false,
'conditions' => array('Events.model' => 'Task')
)
);
I can get Events information using the find method on task although using
$this->Task->Event->find('all');
will not work, i dont know why.
So as soon as I declare, something like this in my Event Model:
var $belongsTo = array(
'Task' => array(
'className' => 'Task',
'foreignKey' => 'model_id',
'conditions' => array('Event.model' => 'Task', 'Event.model_id' => 'Task.id'),
'fields' => '',
'order' => ''
)
);
will throw an SQL error. Do you guys have an idea how to implement it??
Thanks in advanced. :)
I use polymorphic models all the time. The reason that $this->Task->Event->find('all') doesn't work is that you were performing the find on the Event model which, until you defined its belongsTo association, didn't have any means of retrieving the Task info.
As for now, the only thing I see that looks odd is that you've aliased your Task association as Events (plural) rather than its singular version which is the convention for models. You also identified the className as the plural version.