Proper Method For Setting Up One-to-One in CodeIgniter Datamapper - php

I am reviewing all of my models and corresponding tables for CodeIgniter Datamapper. I'm wondering what the correct way of setting up a one-to-one relationship with a defined parent and child is. The way I have been doing it is similar to this:
class Customer ... {
public $has_one = array('address');
}
class Address ... {
public $has_one = array('customer');
}
with the Customer being the parent (ie, has either one or zero Addresses) and the address being the child (always has one Customer). In the database I have a customer_id on addresses which is not null, indexed and associated with customer.id and set to CASCADE on update and delete , and address_id on customers which is null and associated with address.id and set to NO ACTION on update and delete.
Is this the proper configuration for a CI Datamapper one-to-one-or-zero (parent/child) relationship?

There is a good explanation in the FAQ section of the docs:
When configuring a relationship, even a self-relationship, you must define both "sides" of the relationship. This means for a parent/child relationship, you have to specify the parent on the child, and you have to specify the child on the parent.
Here is an example for a complex self-relationship [User (as Boss) has many Employees, and Employees have one Boss]:
// In User
$has_one = array(
// define the relationship to the boss
'boss' => array(
'class' => 'user',
'other_field' => 'employee'
)
);
$has_many = array(
// define the relationship to the employees
'employee' => array(
'class' => 'user',
'other_field' => 'boss'
)
);

Related

CakePHP Associations - hasMany but need to match column in 3rd table?

I am new to CakePHP (2.x) and am creating a post and comments feature. Everything works except I cannot figure out how to get the user's username out of the registrations (third) table (linked with "registration_id"). My associations currently look like:
class Article extends AppModel {
public $hasMany = array('ArticleComment');
public $belongsTo = array(
'ArticleRegistration' => array(
'className' => 'Registration',
'foreignKey' => 'Article.registration_id' //(doesn't work)
),
'ArticleCommentRegistration' => array(
'className' => 'Registration',
'foreignKey' => 'ArticleComment.registration_id' //(doesn't work)
)
);
class ArticleComment extends AppModel {
public $belongsTo = array('Registration','Article');
I am not sure if the associations from ArticleComment are being applied since it is being called through the Article model. I am retrieving the data by:
$this->set('articles', $this->Article->find('all', array('order' => 'Article.created desc', 'limit' => '3')));
I have tried a join and passing two separate variables for the articles and comments array but then I have to remove my associations which leads me to believe it's not proper coding.
Tables are:
articles
__________
id
registration_id
body
article_comments
__________
id
article_id
registration_id
body
registration
__________
id
username
I am fetching the information with:
$this->set('articles', $this->Article->find('all', array('order' => 'Article.created desc')));
TIA!
The generated SQL query from cakephp when you make a find() call is as follows
SELECT `Article`.`id`,
`Article`.`registration_id`,
`Article`.`body`,
`Registration`.`id`,
`Registration`.`username`
FROM `test`.`articles` AS `Article`
LEFT JOIN `test`.`registration` AS `Registration`
ON (`Article`.`registration_id` = `Registration`.`id`)
WHERE 1 = 1 LIMIT 20
So, in order to access the username you just need to write something like this in your view
$articles['Registration']['username'];
I recommend you to use the cakephp bake commands, I could make this proyect like in 10 minutes with them. And also I recommend using Netbeans as IDE, the cakephp plugin is awesome.
Here is a link to an example project that you can clone with git.

FuelPHP Orm: set() for model with many_to_many relation and table_through

I have two models, Model_Post and Model_Category. I've managed to "find" all the related data (easy as $post->$categories), but now I need to create relationships (in the posts_categories table) between a post and multiple categories upon the post's create/update/delete.
Here's Model_Post
protected static $_many_many = array(
'categories' => array(
'table_through' => 'posts_categories',
'key_through_from' => 'post_id',
'model_to' => 'Model_Category'
)
);
Model_Category
protected static $_properties = array(
'id',
'name',
'created_at',
'updated_at'
);
protected static $_many_many = array(
'posts' => array(
'table_through' => 'posts_categories',
'key_through_from' => 'id',
'key_through_to' => 'post_id',
'model_to' => 'Model_Post'
)
);
posts_categories table fields: id, name.
I'm stuck here. How should I build the query?
$post->categories = Model_Category::forge()->set(array(
// I can't get any further
),
);
Do I need to make a Model for the relationship table as well?
For a many to many relation to work between the Post and Category models, you should have three tables in your database: posts, categories and categories_posts.
The first two shouldn't need explanation and the third one is to handle the many/many relation between the two models. It's structure should be similar to this:
CREATE TABLE `categories_posts`
(
`category_id` BIGINT UNSIGNED NOT NULL,
`post_id` BIGINT UNSIGNED NOT NULL,
PRIMARY KEY (`category_id`, `post_id`)
);
Being $post a Model_Post object and $post->categories an array for associated categories, we can start working.
To start associating, we forge a new Model_Category object and add it to the array:
// forge object
$category = Model_Category::forge();
// set name
$category->name = 'Brand New Category';
// associate with Posts
$post->categories[] = $category;
/**
* we can repeat the last three steps as many times as we want
* or as many times as we need
*/
// finally, save the relation
if ($post->save(true, true)) {
echo "Everything OK";
} else {
echo "Houston, we've got a problem!";
}
Notice the two boolean parameters passed to the save() method. They are to cascade through the relations and to use transactions respectively. It's a good idea to use that when relating models in one go.
You should read the ORM documentation, were you'll find a similar example on many to many relations, among other things.

Getting All groups that belong to user

I have HABTM for Users and Groups. I want to show in a Group view - all the Groups that belong to a User. Or to put it differently - all the Groups that have the User.
I am getting tangled in the MVC and am not able to figure it out. Here are my two models:
class Course extends AppModel
public $name = 'Course';
public $hasAndBelongsToMany = array('User' =>
array(
'unique' => false
)
);
And...
public $name = 'User';
public $hasAndBelongsToMany = array('Course' =>
array(
'unique' => true
)
);
The table name in the database is courses_users - this table houses group ids and user ids.
Should be simple enough but I'm new to CakePHP so I'd love some help. Thank you!
CakePHP has recursive set to 1 by default, which means that assuming you have not changed the recursive setting, it will automatically fetch all associated courses when you call find on a user, assuming you set up the HABTM relationship when doing the find. Therefore, all you have to do is:
$this->User->find('first', array('conditions' => array('User.id' => $this->Auth->user('id'))));
In your User model, I don't think it's strictly necessary, but I like to specify the join table and such on HABTM relationships:
public $hasAndBelongsToMany = array('Course' =>
array(
'unique' => true,
'dependent' => false,
'joinTable' => 'courses_users',
'foreignKey' => 'user_id',
'associationForeignKey' => 'course_id',
)
);
Keep in mind that in HABTM relationships, you don't ever really touch the joinTable beyond specifying which table to use as the joinTable when setting up the relationship. CakePHP will automatically do the rest of the work.

Accessing data in a many to many and one to many relations

I dont know howto accessing data from a related table (with ActiveRecords) and use all those records as a dataSource for a CActiveDataProvider. The main idea is that a user must be able toCRUD` only the courses from his classes.
I have the following tables:
user (id, name, age, email...),
user_class (user_id, class_id),
class (id, name, description, ...),
course (id, class_id, name, description);
User and Class tables have a MANY TO MANY and Class and Course tables have a ONE TO MANY relation
I also defined the following relations:
User model relations:
'classes'=>array(
self::MANY_MANY,
'Class',
'user_class(user_id, class_id)',
),
'courses'=>array(
self::HAS_MANY,
'Course',
array('class_id','id'),
'through'=>'classes'
)
Class model relations:
'users' => array(
self::MANY_MANY,
'User',
'user_class(class_id, user_id)'
),
'courses' => array(
self::HAS_MANY,
'Class',
'class_id'
),
Course model relations:
'class' => array(
self::BELONGS_TO,
'Class',
'class_id'
)
In my CourseController , on actionManage() I have something like this:
public function actionManage() {
$currentUser = User::model()->with('classes', 'courses')->findByPk(Yii::app()->user->id);
$userCourses = $currentUser->classes->courses; //--> NO courses!!!
/* add users` courses to an CActiveDataProvider //--> how to do this?!
}
If you try to var_dump($currentUser->classes); you'll notice that it's an array.
$userCourses = $currentUser->classes[0]->courses; will work if there are classes associated with $currentUser
Since you're loading many things lazily (each "class" loads its' "courses" the first time they're requested) this could become slow down the road, but for now, run with it I guess.

Model Associations and Data Modelling

I am developing a web app for an art gallery, and I want to check I have set up the data model correctly.
I have a people table and an artists table. Some of the people are artists and some are not.
I also have a products table and each product belongs to one artist.
Below is a simplified version of the tables:
products
********
id
title
artist_id
artists
*******
id
profile
rating
person_id
people
******
id
first_name
last_name
I need to be able to retrieve the name of the artist when performing the find() method on the Product model.
Have I set up the data model correctly and if so what is the best way to retrieve the artist's name without getting lots of unwanted data?
It's possible to use CakePHP's bindModel & unbindModel to build up relations that don't rely on Cake's automagic goodness.
Since you were interested in doing the find on the Product model, I'll set out an example that can be called from the Products controller:
// unbind Artist
$this->Product->unbindModel(array(
'belongsTo' => array('Artist')
));
// bind Artist & Person using explicit conditions
$this->Product->bindModel(array(
'hasOne' => array(
'Artist' => array(
'foreignKey' => false,
'conditions' => array('Artist.id = Product.artist_id')
),
'Person' => array(
'foreignKey' => false,
'conditions' => array('Person.id = Artist.person_id')
),
)
));
// return the person
$person = $this->Product->find('first', array(
'conditions' => array(
'Product.id' => 1 // or any other condition
),
'fields' => array(
'Person.first_name',
'Person.last_name'
)
));
What's happening here?
Firstly, we unbind the Product model's relationship with the Artist model.
Secondly, we bind the Artist and Person models by explicitly defining the relationships and disabling Cake's automatic forigenKey connections.
Lastly, we do a 'first' find on the Product model and only request the 'first_name' & 'last_name' of the Person.
Each 'Product' belongs to an 'Artist' and each 'Artist' hasAndBelongsToMany 'Product'
You will also need the following table:
artists_products
****************
id
product_id
artist_id
http://book.cakephp.org/#!/view/1044/hasAndBelongsToMany-HABTM

Categories