I am trying to create a relationship between three models. My models are Roles, Responsibilities, and Employees. Conceptually, I believe i would set the relationships up like this:
Employee table:
id:int Pk
fName: varChar
lName: varChar
Role Table:
roldId: int pk
roleName: varChar
Responsibility Table:
reponsibilityId: PK int
responsibilityName: varChar
role_responsbility table:
role_responsibility_id: PK int
roleId: int FK
responsibilityId int FK
employee_role_responsibility Table:
id: int PK
role_responsibility_id int FK
employee_id: int FK
So, my question is, how do I model these relationships in my CakePHP model? This is what i have so far:
App::uses('AppModel', 'Model');
class Employee extends AppModel {
public $name = "Employee";
}
App::uses('AppModel', 'Model');
class Role extends AppModel {
public $name = "Role";
public $hasAndBelongsToMany = array(
'MemberOf' => array(
'className' => 'Responsibility',
)
);
}
App::uses('AppModel', 'Model');
class Responsibility extends AppModel {
public $name = "Responsibility";
public $hasAndBelongsToMany = array(
'MemberOf' => array(
'className' => 'Role',
)
);
}
How do I join the roles_responsbility table with the employee table?
Thanks
I believe you're looking for HasMany Through [see it in the CakePHP book], which basically explains that you can add additional fields to your join table, create model for that join table, and associate it however you want.
So, in your responsibilities_roles table, you'd add an employee_id field.
Then, you make a model called RepsponsibilitiesRole and set it to belongsTo Employee.
You can literally think of it as model of it's own, because that's what it is - so you can make methods in it, or run finds through it...etc etc etc.
Related
I'm trying to understand Cakephp. I follow the manual from the official page http://book.cakephp.org/2.0/en/tutorials-and-examples/blog/blog.html and now i can display a few posts on my page. For some reasons all files and functions has to have the name "Posts" but when you want to search for a database table you have to write "post" [$this->set('posts', $this->Post->find('all'));]. WHY? I read about CakePHP Conventions and it doesn't make sense. What if i have a hello.ctp page and a "goodmorning" table name? I spent 2 days on this and i ALWAYS end up to this: Error: Call to a member function find() on a non-object. I want to display my posts and my "goodmorning" table on the same page.
Just to make it clear, I have two tables:
CREATE TABLE `Posts` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(50),
`body` TEXT,
`created` DATETIME DEFAULT NULL,
`modified` DATETIME DEFAULT NULL
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `Comments` (
`id` int(11) DEFAULT NULL,
`name` VARCHAR(50),
`comment` TEXT,
`created` DATETIME DEFAULT NULL,
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
PostController.php
<?php
class PostsController extends AppController {
public function index() {
$posts = $this->Post->find('all');
$this -> set ('posts',$posts);
$comments = $this->comment->find('all');
$this -> set ('comments',$comments);
}
}
?>
PostsModel.php
<?php
class Posts extends AppModel {
}
?>
CommentsModel.php
<?php
class Comments extends AppModel {
}
?>
If i remove those lines from PostController:
$comments = $this->comment->find('all');
$this -> set ('comments',$comments);
the "Posts" table will show up (localhost/cake/Posts). In pure php i can build this in a few minutes but in cakephp i really feel hopeless
The line $this->set('posts', $this->Post->find('all')); is going to your Posts model, executing the find() function, and then setting that data to the view variable $posts. You could change the variable name to 'Posts' in the first argument of $this->set() and the view variable the data would be set to would be $Posts.
CakePHP's naming conventions don't apply to variable names. You should define a style that you like for them and stick to it. The help documents typically use all lower case names with words separated by underscores, but that has no bearing on anything unless you're referencing one of CakePHP's pre-defined values.
The issue with your goodmorning table name is that table names by CakePHP convention should be lower case, plural, and the words should be separated by underscores. If you want the "Cake Magic" to connect all of your names together invisibly by convention, what you want to create is a database table named good_mornings, a Model named GoodMorning and a Controller named GoodMorningsController.
In Cakephp
For using table with custom names
<?php
class Post extends AppModel {
public $useTable = 'goodmorning';
}
?>
For using view with custom names
public function index() {
$this->autoRender = false;
$this->render('hello');
}
table name must be lowercase and plurals (e.g. posts)
model name must be lower case except for the first letter, and singular (e.g. Post)
controller name must be plural and attached to the word "Controller" (e.g. PostsController)
table id must be named "id" (but could be named with other names, you must specify the different name inside the Model class. e.g. $this->Post->id = $newId)
I've got a Cakephp Project with an 'Addresses' table with the following structure:
CREATE TABLE `addresses` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL,
`name` varchar(50) NOT NULL,
`company` varchar(50) NOT NULL,
`address1` varchar(50) NOT NULL,
`address2` varchar(50) DEFAULT NULL,
`city` varchar(40) NOT NULL,
`state` varchar(2) NOT NULL,
`country` varchar(2) NOT NULL,
`zip` varchar(5) NOT NULL,
PRIMARY KEY (`id`)
)
There is a page in this project which asks the user for both a Shipping Address and a Billing Address, and im not certain how to structure the names of the form inputs to allow multiple instances of the same database fields on one page
In the View, I've attempted to use an alias to seperate the two instances of the Address fields
I.E.-
<?=$this->Form->input('Shipaddress.zip', array('label' => 'Zip Code'));?>
...
<?=$this->Form->input('Billaddress.zip', array('label' => 'Zip Code'));?>
then in the view, i tried to seperate the two instances, validate both, and set the appropriate $this->validationError values to properly display the errors to the correct field views
// place in arrays with proper model name ['Address']
$ship_array['Address'] = $this->request->data['Shipaddress'];
$bill_array['Address'] = $this->request->data['Billaddress'];
//Set Data to model, Validate Against model, change model name in validationErrors to match aliased fields, and remove validationErrors for ['Address']
$this->Address->set($ship_array);
$shipping_valid = $this->Address->validates(array('fieldList' => array('name', 'company', 'address1', 'address2', 'city', 'state', 'country', 'zip')));
$this->validationErrors['Shipaddress'] = $this->validationErrors['Address'];
$this->validationErrors['Address'] = array();
//Do it again for Billing Address fields
$this->Address->set($bill_array);
$billing_valid = $this->Address->validates(array('fieldList' => array('name', 'company', 'address1', 'address2', 'city', 'state', 'country', 'zip')));
$this->validationErrors['Billaddress'] = $this->validationErrors['Address'];
$this->validationErrors['Address'] = array();
unfortunately, this doesnt appear to work, and i'm afraid that I've gone too far trying to make this work...
can someone give my a kick in the right direction on how this can be done properly?
Figured out how to do it on my own...
in /app/Model i created 'ShippingAddress.php' and 'BillingAddress.php', Both Extend "Address"
//ShippingAddress.php
<?php
App::uses('Address', 'Model');
class ShippingAddress extends Address {
}
//BillingAddress.php
<?php
App::uses('Address', 'Model');
class BillingAddress extends Address {
}
To prevent the new models from using tables named after them, we edit the parent Address.php and set $useTable so that both extended models use Addresses Table
//Address.php
...
public $useTable = 'addresses';
...
then its just a matter of inserting the two instances of the input fields into the view... no renaming models, no modifying validationErrors, it just works :)
Page table
(1)
|
|
(*)
User_Moderate_Page (*)----------- (1)Access_level table
(*)
|
|
(1)
User table
How do i Model such a ternary relationship in CakePhp?
User to Page can be modelled using the hasBelongtoMany Relationship. But User_Moderate_page is just an association table, should I even write a Model for User_Moderate_Page in Cake?
I'm not sure CakePHP accepts this, but what you should do is create the table with a primary key and the 3 foreign keys. Somewhat like this:
CREATE TABLE IF NOT EXISTS `mydb`.`access_levels_pages_users` (
`id` INT NOT NULL AUTO_INCREMENT ,
`page_id` INT NOT NULL ,
`access_level_id` INT NOT NULL ,
`user_id` INT NOT NULL ,
PRIMARY KEY (`id`) ,
INDEX `fk_access_levels_pages_users_pages` (`page_id` ASC) ,
INDEX `fk_access_levels_pages_users_access_levels1` (`access_level_id` ASC) ,
INDEX `fk_access_levels_pages_users_users1` (`user_id` ASC) ,
CONSTRAINT `fk_access_levels_pages_users_pages`
FOREIGN KEY (`page_id` )
REFERENCES `mydb`.`pages` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_access_levels_pages_users_access_levels1`
FOREIGN KEY (`access_level_id` )
REFERENCES `mydb`.`access_levels` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_access_levels_pages_users_users1`
FOREIGN KEY (`user_id` )
REFERENCES `mydb`.`users` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB
Now, I'm not sure Cake will bake this table, so you might have to try and make the models by hand, this table would have belongsTo the 3 other tables. The other 3 tables non association tables will have to have a HMABTM relationship with the other 2, as in users HMABTM access_levels and pages and so on.
Again, not sure if it will work. I would suggest maybe trying to see if you can model it in a different manner.
Let me generalize it by describing it with generic tables:
Lets say you have 3 tables - firsts, seconds, thirds - each with primary keys 'id'.
let your join table be called 'firsts_seconds_thirds' with foreign keys to each main table:
first_id
second_id
third_id
<additional fields of this association table>
usually we define HABTM relationships between tables, in this case we need to create a cake model for the join table - lets call it FirstsSecondsThird (By cakePhp's naming conventions)
The relationship between models you need to define is:
First hasMany FirstsSecondsThird
Second hasMany FirstsSecondsThird
Third hasMany FirstsSecondsThird
FirstsSecondsThird belongsTo First,Second,Third
The need for this is explained here - Associations: Linking Models Together
Code for the same:
class First extends AppModel {
public $hasMany = array(
'FirstsSecondsThird' => array(
'className' => 'FirstsSecondsThird',
'foreignKey' => 'first_id'
)
);
}
//Same for classes 'Second' and 'Third'
class FirstsSecondsThird extends AppModel {
public $belongsTo = array(
'First' => array(
'className' => 'First',
'foreignKey' => 'first_id'
),
'Second' => array(
'className' => 'Second',
'foreignKey' => 'second_id'
),
'Third' => array(
'className' => 'Third',
'foreignKey' => 'third_id'
)
);
}
The models are perfectly setup, but now inserting/updating/deleting from the main/join table should be done correctly or they are of no use.
Model::saveMany() and Model::saveAssociated(), using the option 'deep' etc. need to be used. Read about it here. You also need to think about ON DELETE RESTRICT/CASCADE for these join tables and model them appropriately.
Look up ACL. It is complicated, but it is what you need (especially if you need the flexibility in setting up who can moderate what page)
Hi i am new in cake php and can't solve the problem. The problem is I have a table like;
id varchar(16)
parent_id varchar(16)
text text
user_id bigint(20)
is_deleted_by_user bit(1)
is_deleted_by_us bit(1)
who_deleted bigint(20)
who_answered bigint(20)
modified_at datetime
created_at datetime
in this table i want to give relations between users table and user_id, who_deleted, who_answered. I mean user_id, who_deleted and who_answered are one user id. How can i give relations between users table and this table?
It's relatively easy to create multiple relationships to the same model. There's a section of the documentation dedicated to it. Here's how I've done it for a Resource model that has multiple fields associated with a Binary model:
class Resource extends AppModel {
public $belongsTo = array (
'PDF' => array (
'className' => 'Binary',
'foreignKey' => 'pdf_file_id'
),
'MSWord' => array (
'className' => 'Binary',
'foreignKey' => 'msword_file_id'
)
);
... other class code ...
}
The resources table contains pdf_file_id and msword_file_id fields which each reference a Binary record.
Hope that helps.
With the following (simplified) MySQL table definitions:
create table items (
item_id int unsigned auto_increment primary key,
purchase_date date
) engine = innodb;
create table computers (
item_id int unsigned primary key,
processor_type varchar(50),
foreign key item_fk (item_id) references items (item_id)
on update restrict on delete cascade
) engine = innodb;
create table printers (
item_id int unsigned primary key,
is_duplex boolean,
foreign key item_fk (item_id) references items (item_id)
on update restrict on delete cascade
) engine = innodb;
Being new to DBIx::Class, I would like to model an inheritance relationship between database entities (computers and printers both being items), but with the provided belongs_to relationship type, this seems to be awkward, because the association with the base class is not hidden, so one must still manually create entities for both classes, and access to base class attributes in the derived classes is different from accessing their own attributes.
Is there an elegant solution which would allow me to say:
$printer = $printer_rs->create({purchase_date => $date, is_duplex => 0});
or (on a fetched printer row):
$date = $printer->purchase_date;
$duplex = $printer->is_duplex;
?
You can use the proxy attribute on a relationship to enable the accessors -- it's documented in add_relationship of DBIx::Class::Relationship::Base and you can use it with belongs_to like:
__PACKAGE__->belongs_to(
'item' => 'MyApp::Schema::Item',
'item_id',
{ proxy => [ qw/purchase_date/ ] }
);
which will make all of your Printer objects have purchase_date accessors that refer to the associated Item object.
As for create, you can't do it without overriding new_result, which is actually pretty easy. You just have to take advantage of the create-from-related behavior to turn
->create({
is_duplex => 1,
purchase_date => $dt,
})
into
->create({
is_duplex => 1,
item => {
purchase_date => $dt,
},
})
Or you could just foist the knowledge of what columns are in item off on your users and have them provide that hashref directly ;)