How to create a many to many self-referencing relationship cakephp - cakephp

The cakephp docs state the following:
You can even create self-associated tables to create parent-child relationships:
class CategoriesTable extends Table
{
public function initialize(array $config)
{
$this->hasMany('SubCategories', [
'className' => 'Categories'
]);
$this->belongsTo('ParentCategories', [
'className' => 'Categories'
]);
}
}
Which sounds good, but the code above doesn't actually do anything by itself. Like the code above looks like it should describe a self-referencing many to many relationship but if you take an existing Table class and just add that to the initialize function, nothing happens. There has to be some associated schema presumably which isn't shown.
So it's not clear how to actually set up a self-referencing relationship.
I've tried this:
CREATE TABLE `categories` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
CREATE TABLE `categories_categories` (
`parent_id` int(11) NOT NULL,
`child_id` int(11) NOT NULL,
PRIMARY KEY (`parent_id`,`child_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
And then baked that... but it's not clear to me how to get that to actually work with models and which models to create and what associations to put in them nor how to get those to be represented by a form element.

Related

How To Force Insert In CakePHP?

I have a MySQL archive table with the following structure:
`histories`
`uid` int(10) unsigned NOT NULL,
`type` varchar(255) NOT NULL,
`param` int(11) NOT NULL,
`param2` varchar(255) NOT NULL,
`time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
ENGINE=ARCHIVE DEFAULT CHARSET=latin1;
I do not want to add an auto-incrementing `id' column. This table's engine is archive and it does exactly what I need it to, with this structure.
I wish to save like this:
$this->History->create();
$this->History->save(['uid' => uid, 'type' => 'deleted_entry', 'param' => $thing_id]);
But CakePHP forces an update, which I don't want and the ARCHIVE engine doesn't support. Cake seems to be looking for a primary key of uid, finding it and deciding that it should update and there seems to be no flag available to force an insert. I do not want to resort to $this->Model->query().
UPDATE:
Set $primaryKey = null in AppModel. $this->create(); will then insert.
class History extends AppModel {
public $primaryKey = null;
}
If you want to do an update after, simply:
$this->History->primaryKey = 'uid'; before the save()
You can tell Cake 2 that you have no primary key by setting the model's $primaryKey property to null:
$this->History->primaryKey = null;
$this->History->create();
$this->History->save(['uid' => uid, 'type' => 'deleted_entry', 'param' => $thing_id]);

How to create more than one reference to a table in cakephp?

Let's say I have two tables:
CREATE TABLE drinks (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL
);
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
morning_drink_id INT,
evening_drink_id INT
);
How can I make those drink_id references valid?
I’ve tried:
adding a view
CREATE VIEW morning_drinks AS SELECT * FROM drinks;
CREATE VIEW evening_drinks AS SELECT * FROM drinks;
adding foreign keys
FOREIGN KEY morning_drink_key (morning_drink_id) REFERENCES drinks(id),
FOREIGN KEY evening_drink_key (evening_drink_id) REFERENCES drinks(id)
bake craches in both cases... Is there a proper way?
public $belongsTo = array(
'Drink' => array(
'className' => 'Drink',
'foreignKey' => 'morning_drink_id'
)
);
this you can add to Users model. And this is how to do it.
edit:
Bake is expecting tables for your two foreign keys, and it’s complaining about them. I got the same error as you when I deleted the ‘morning_drinks’ view, else it created all. Add those two views and try again.

Design parent-child controllers

I am new to cakePHP and trying to get my head around the framework to see how to implement my requirements.
My application allows (admin) users to define Forms (parent) and FormElements (child) that later on will be composed on the fly and presented to the end-users.
To start prototyping I baked all the pieces and I can enter rows in both tables as expected.
edit to simplify the question:
The Forms controller already shows a list of Forms and when one is selected (view action), a list of the FormElements for that Form.
But... when I add a new FormElement, I have to select again a Form the Element will be associated to.
Instead I want the FormElements controller/model to know which Form was initially selected and fill the form_id automagically.
Is there a "best practice" approach on how to handle this ?
Just in case its needed:
CREATE TABLE IF NOT EXISTS `forms` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`description` varchar(60) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `form_elements` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`form_id` int(11) NOT NULL,
`name` varchar(40) NOT NULL,
`type` int(11) NOT NULL,
`widget` int(11) NOT NULL,
`mandatory` tinyint(4) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
This happens more often than you think. I had QuestionModel hasMany AnswerModel and wanted to add Answers within my AnswersController. I needed to show QuestionModel name and other attributes of the "parent" object. This is what my add action inside my AnswersController looks like:
public function add($question_id = null) {
$this->Answer->Question->id = $question_id;
if (!$this->Answer->Question->exists()) {
throw new NotFoundException(__('Invalid question'));
}
if ($this->request->is('post')) {
$this->request->data['Answer']['question_id'] = $question_id;
$this->request->data['Answer']['user_id'] = $this->Auth->user('id');
if ($this->Answer->save($this->request->data)) {
$this->Session->setFlashSuccess(__('Your answer has been saved'));
} else {
$this->Session->setFlashError(__('Your answer could not be saved. Please, try again.'));
}
$this->redirect(array('controller'=>'questions','action' => 'view', $question_id));
}
$question = $this->Answer->Question->read();
$this->set('question', $question);
}
You will notice that I am passing the Question.id to the AnswersController add action. Having this allows me to pull the Question from the DB and allows me to be able to redirect the user back to the specific question they were on before they clicked on "Add Answers to this Question".

CakePHP Bake association problem

I have only two tables in my database with a one-to-many relationship between them (user hasMany messages) and am trying to get basic CRUD functionality going. Bake detects the associations correctly and specifies them correctly inside the model classes, but in controllers and views it looks like Cake doesn't know anything about those associations -- I don't even get a select tag for user_id when I go add a new message. Has anyone come across this problem before? What can I be doing wrong?
Table structure appears to be fine:
CREATE TABLE users (
id int(11) NOT NULL AUTO_INCREMENT,
username varchar(255) NOT NULL,
`password` varchar(255) NOT NULL,
email varchar(255) NOT NULL,
created datetime NOT NULL,
modified datetime NOT NULL,
PRIMARY KEY (id)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `messages` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`content` varchar(255) NOT NULL,
`created` datetime NOT NULL,
`modified` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
If you're using the console to bake your MVC files you could try that.
First bake the model files. Then bake the controller files using scaffold.
Then bake the view files.
Finally go back and bake the controller files without scaffold.
This should get you all basic CRUD functionality with all the associations you may have.
Hope that helps ...

How do I use the TranslateBehavior in CakePHP?

There is no documentation on cakephp.org and I am unable to find one on google. Please link me some documentation or supply one!
The translate behavior is another of CakePHP's very useful but poorly documented features. I've implemented it a couple of times with reasonable success in multi-lingual websites along the following lines.
Firstly, the translate behavior will only internationalize the database content of your site. If you've any more static content, you'll want to look at Cake's __('string') wrapper function and gettext (there's some useful information about this here)
Assuming there's Contents that we want to translate with the following db table:
CREATE TABLE `contents` (
`id` int(11) unsigned NOT NULL auto_increment,
`title` varchar(255) default NULL,
`body` text,
PRIMARY KEY (`id`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
The content.php model then has:
var $actsAs = array('Translate' => array('title' => 'titleTranslation',
'body' => 'bodyTranslation'
));
in its definition. You then need to add the i18n table to the database thusly:
CREATE TABLE `i18n` (
`id` int(10) NOT NULL auto_increment,
`locale` varchar(6) NOT NULL,
`model` varchar(255) NOT NULL,
`foreign_key` int(10) NOT NULL,
`field` varchar(255) NOT NULL,
`content` mediumtext,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Then when you're saving the data to the database in your controller, set the locale to the language you want (this example would be for Polish):
$this->Content->locale = 'pol';
$result = $this->Content->save($this->data);
This will create entries in the i18n table for the title and body fields for the pol locale. Finds will find based on the current locale set in the user's browser, returning an array like:
[Content]
[id]
[titleTranslation]
[bodyTranslation]
We use the excellent p28n component to implement a language switching solution that works pretty well with the gettext and translate behaviours.
It's not a perfect system - as it creates HABTM relationships on the fly, it can cause some issues with other relationships you may have created manually, but if you're careful, it can work well.
For anyone searching the same thing, cakephp updated their documentation. For Translate Behavior go here..

Resources