New to Cake, and trying to set up model associations. I'm creating an execution planner, to be used by multiple teams, aka 'groups' where the base unit of a plan is an 'event'. Each event is owned by one team, but it can have many supporting teams.
When I use $scaffold in the controllers, the model associations work as expected and when adding a new event I get a select box with all the groups to select who owns an event. However, when I bake the controller using console, the select box for group_id is blank. I've included the baked add() code from the controller, and from the baked add view. I also tried debug($this) in add.ctp, and found that the list of groups is indeed being passed to the view. I'm thus mostly confused about why it works with $scaffold, but not otherwise.
Any advice or pointers in the right direction would be greatly appreciated.
Tables:
CREATE TABLE IF NOT EXISTS `events` (
`id` int(5) NOT NULL AUTO_INCREMENT,
`group_id` int(5) NOT NULL COMMENT 'Group that owns event',
`version` int(5) NOT NULL,
`type` varchar(5) NOT NULL,
`stime` time NOT NULL,
`etime` time NOT NULL,
`description` text NOT NULL,
`comment` text NOT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE IF NOT EXISTS `events_groups` (
`id` int(5) NOT NULL AUTO_INCREMENT,
`event_id` int(5) NOT NULL,
`group_id` int(5) NOT NULL,
PRIMARY KEY (`id`)
) ;
CREATE TABLE IF NOT EXISTS `groups` (
`id` int(5) NOT NULL AUTO_INCREMENT,
`code` varchar(4) NOT NULL,
`name` varchar(25) NOT NULL,
`liveversion` int(4) NOT NULL,
`lastupdated` date NOT NULL,
PRIMARY KEY (`id`)
);
Models:
Event:
public $belongsTo = array(
'Prigroup' => array(
'className' => 'Group',
'foreignKey' => 'group_id',
)
);
public $hasAndBelongsToMany = array(
'Secgroup' => array(
'className' => 'Group',
'joinTable' => 'events_groups',
'foreignKey' => 'event_id',
'associationForeignKey' => 'group_id',
)
);
Group:
public $hasMany = array(
'Prievent' => array(
'className' => 'Event',
'foreignKey' => 'group_id',
)
);
public $hasAndBelongsToMany = array(
'Secevent' => array(
'className' => 'Event',
'joinTable' => 'events_groups',
'foreignKey' => 'group_id',
'associationForeignKey' => 'event_id',
)
);
EventsController Snippet:
public function add() {
if ($this->request->is('post')) {
$this->Event->create();
if ($this->Event->save($this->request->data)) {
$this->Session->setFlash(__('The event has been saved'));
$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('The event could not be saved. Please, try again.'));
}
}
$prigroups = $this->Event->Prigroup->find('list');
$secgroups = $this->Event->Secgroup->find('list');
$this->set(compact('prigroups', 'secgroups'));
}
Add view:
<?php echo $this->Form->create('Event'); ?>
<fieldset>
<legend><?php echo __('Add Event'); ?></legend>
<?php
echo $this->Form->input('group_id');
echo $this->Form->input('version');
echo $this->Form->input('type');
echo $this->Form->input('stime');
echo $this->Form->input('etime');
echo $this->Form->input('description');
echo $this->Form->input('comment');
echo $this->Form->input('Secgroup');
?>
</fieldset>
<?php echo $this->Form->end(__('Submit')); ?>
Add HTML Output
<div class="input select"><label for="EventGroupId">Group</label><select name="data[Event][group_id]" id="EventGroupId">
</select></div>
Can u try something for me please? Rename the $prigroups variable to "groups" and pass it to the view. See if this is working. Otherwise use this one with your old code:
echo $this->Form->input(
'group_id',
array(
'options' => $prigroups
)
);
I also recommend you to use camel case for all those PriGroups and SecGroups. Those are two words in english so camel case advisable.
Greetings
func0der
Related
I'm a beginner. I am using CakeDC ratings to make a star rating in my site. I managed to make it work but it is displaying radio choices.
And it show me error:
Undefined variable: item
and After pressing "Rate", the url changes to avis/post/view/redirect:1
I download the plugin and I unzip in C:\wamp\www\avis\app\Plugin
after I write CakePlugin::load('Ratings') in app/Config/bootstrap.php my database is
CREATE TABLE IF NOT EXISTS `posts` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(100) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ;
CREATE TABLE IF NOT EXISTS `ratings` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`user_id` char(36) NOT NULL DEFAULT '',
`foreign_key` char(36) NOT NULL DEFAULT '',
`model` varchar(100) NOT NULL DEFAULT '',
`value` float NOT NULL,
`created` datetime DEFAULT NULL,
`modified` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `rating` (`user_id`,`model`,`foreign_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
in Post Model
public $hasMany = array(
'Rating' => array(
'className' => 'Rating',
'foreignKey' => 'foreign_key',
'dependent' => false,
)
);
in PostController
public $components = array('Paginator', 'Flash', 'Session','Ratings.Ratings');
public $actsAs = array('Ratings.Ratable');
public function index() {
$this->Post->recursive = 0;
$this->set('posts', $this->Paginator->paginate());
}
public function view($id = null) {
if (!$this->Post->exists($id)) {
throw new NotFoundException(__('Invalid post'));
}
$options = array('conditions' => array('Post.' . $this->Post->primaryKey => $id));
$this->set('post', $this->Post->find('first', $options));
}
in view
<h2><?php echo __('Post'); ?></h2>
<dl>
<dt><?php echo __('Id'); ?></dt>
<dd>
<?php echo h($post['Post']['id']); ?>
</dd>
<dt><?php echo __('Title'); ?></dt>
<dd>
<?php echo h($post['Post']['title']); ?>
</dd>
<?php
echo $this->Rating->display(array(
'item' => $post['Post']['id'],
'type' => 'radio',
'stars' => 5,
'value' => $item['rating'],
'createForm' => array(
'url' => array(
$this->passedArgs, 'rate' => $item['id'],
'redirect' => true
)
)
));
?>
</dl>
</div>
<script>
$('#ratingform').stars({
split:2,
cancelShow:false,
callback: function(ui, type, value) {
ui.$form.submit();
}
});
</script>
First Download Jquery Raty Js plugin From : https://github.com/wbotelhos/raty
Include js,css into your view
<div class="jrating" data-score="5"></div>
<script>
$('.jrating').raty({ score: function() {return $(this).attr('data-score');} });
</script>
This will create a hidden text field when you post the form
On controller :
$rating = $this->data['score'];
Now you can save rating in your table
What I'm trying to do:
I'm using josegonzales/cakephp-upload to handle my upload by attaching an upload behaviour to a model called attachments. When I use the attachments add action there isn't any issue uploading a file but my goal is to upload a file with a hasMany relationship from Node. In The Future I'm planning to attach the AttachmentsTable to multiple other models like users for storing multiple user images.
My Setup:
Database Schema
CREATE TABLE IF NOT EXISTS `nodes` (
`id` int(10) unsigned NOT NULL,
`title` varchar(255) NOT NULL,
`body` text NOT NULL,
`created` datetime NOT NULL,
`modified` datetime NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `attachments` (
`id` int(10) unsigned NOT NULL,
`foreign_key` int(10) unsigned NOT NULL,
`model` varchar(255) NOT NULL,
`title` varchar(255) NOT NULL,
`file` varchar(255) NOT NULL,
`file_dir` varchar(255) NOT NULL,
`file_size` varchar(255) NOT NULL,
`file_type` varchar(255) NOT NULL,
`created` datetime NOT NULL,
`modified` datetime NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;
AttachmentsTable
class AttachmentsTable extends Table
{
public function initialize(array $config)
{
parent::initialize($config);
$this->table('attachments');
$this->displayField('title');
$this->primaryKey('id');
$this->addBehavior('Timestamp');
$this->addBehavior('Josegonzalez/Upload.Upload', [
'file' => [
'fields' => [
'dir' => 'file_dir',
'size' => 'file_size',
'type' => 'file_type',
],
'nameCallback' => function($data, $settings) {
$newName = uniqid() . str_replace(" ", "_", $data['name']);
return $newName;
},
'keepFilesOnDelete' => false,
'pathProcessor' => 'Josegonzalez\Upload\File\Path\DefaultProcessor',
'transformer' => 'BaseKit\Model\Table\FileTransformer',
'writer' => 'BaseKit\Model\Table\FileWriter',
],
]);
}
}
NodesTable
class NodesTable extends Table
{
public function initialize(array $config)
{
parent::initialize($config);
$this->table('nodes');
$this->displayField('title');
$this->primaryKey('id');
$this->addBehavior('Timestamp');
$this->hasMany('Attachments', [
'foreignKey' => 'foreign_key',
'conditions' => array('Attachments.model' => 'Nodes'),
'dependent' => true
]);
}
}
Nodes add.ctp
<?= $this->Form->create($node, ['type' => 'file']) ?>
<fieldset>
<legend><?= __('Add Node') ?></legend>
<?php
echo $this->Form->input('title');
echo $this->Form->input('body');
echo $this->Form->input('attachments.0.title');
echo $this->Form->input('attachments.0.file', ['type' => 'file']);
echo $this->Form->input('attachments.0.file_dir', ['type' => 'hidden']);
echo $this->Form->input('attachments.0.file_type', ['type' => 'hidden']);
echo $this->Form->input('attachments.0.file_size', ['type' => 'hidden']);
echo $this->Form->input('attachments.0.model', ['value' => 'Nodes', 'type' => 'hidden']);
?>
</fieldset>
<?= $this->Form->button(__('Submit')) ?>
<?= $this->Form->end() ?>
The Issue:
When I'm trying to upload a File using the Nodes add.ctp the file wont be uploaded and the attached uploadbehaviour won't get used. The title and model field from attachment will be saved and the foreign_key also is correctly set.
Can somebody help me and explain why the behaviour of the related attachmenttable won't be used?
If theres a major issue with my approach, how would I achieve my goal on having a flexible upload table which I can attach to any model?
I set up more than one models, which i want to associate with a master-model like this:
class CommonType extends AppModel {
public $useDbConfig = 'common';
public $hasOne = array(
'CommonTypeDescription' => array(
'className' => 'CommonTypeDescription',
'foreignKey' => 'type_description_id',
'dependent' => true
),
'CommonTypeExperience' => array(
'className' => 'CommonTypeExperience',
'foreignKey' => 'type_expirience_id',
'dependent' => true
),
'CommonTypeProperty' => array(
'className' => 'CommonTypeProperty',
'foreignKey' => 'type_property_id',
'dependent' => true
),
);
public $belongsTo = array(
'CommonTypeCategory' => array(
'className' => 'CommonTypeCategory',
'foreignKey' => 'common_type_id',
));
}
In Heidi SQL (for example) i set the foreignkeys for the tables type_experiences, type_properties and type_descriptions as above, so that foreignkeys in my code and in my tables have the same name.
But now, i don't want to name the primary key as the foreign key, the primary key of the (e.g.) type_descriptions table should named "id". This is the model of type_description:
class CommonTypeDescription extends AppModel {
public $name = 'CommonTypeDescription';
public $useDbConfig = 'common';
public $useTable = 'type_descriptions';
public $primaryKey = 'id';
}
and here is the database creation code:
CREATE TABLE `type_descriptions` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`common_type_id` INT(11) NULL DEFAULT NULL,
`short_description` VARCHAR(50) NULL DEFAULT NULL,
`long_description` VARCHAR(50) NULL DEFAULT NULL,
`notes` VARCHAR(50) NULL DEFAULT NULL,
`modified` DATETIME NULL DEFAULT NULL,
`created` DATETIME NULL DEFAULT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `type_description_id` FOREIGN KEY (`common_type_id`) REFERENCES `common_types` (`id`) ON UPDATE NO ACTION ON DELETE NO ACTION) ...
But when i try to get the data from my model CommonType ($this->find('all')) i get an error like "Unknown column 'CommonTypeDescription.type_description_id' in 'on clause'"
First, i thought that i have to declare the primary key of the description model as "id", but it seams, that the foreign key definition overwrites the naming convention from cake of the primary key from the associated model.
Many thanks for your help & Greetings,
Guido
ps. i've changed the Model-Names of this post (if there is a type-mistake). So when i rename the primary keys of the associated tables, it all works fine, but i want them to name only to "id".
How about update your CommonType Model to this:
'CommonTypeDescription' => array(
'className' => 'CommonTypeDescription',
'foreignKey' => 'common_type_id', //or try type_description_id if there's an error
'dependent' => true
)
Then on your CommonTypeDescription model add this relationship, I think you forgot adding belongs to relationship to CommonType Model:
public $belongsTo = array(
'CommonType' => array(
'className' => 'CommonType ',
'foreignKey' => 'common_type_id',
));
I am building a CakePHP 2.3 application, and can't for the life of me figure out why saveAll keeps failing.
I have the following models:
Artist hasMany ArtistPreview
ArtistPreview belongsTo Artist
I have no problem saving my Artist model with the save() function, however as soon as I try to use the saveAll method, Cake seems to fail validation - which is crazy because I have even totally removed all my validation rules from my models.
Here's my view, nothing crazy:
<?php
echo $this->Form->create('Artist', array('inputDefaults' => array('class' => 'input-block-level')));
echo $this->Form->input('Artist.name', array('label' => __('Name')));
echo $this->Form->input('Artist.artist_section_id', array('label' => __('Section'), 'empty' => true));
?>
<label>Tags</label>
<div class="checkboxes">
<?php echo $this->Form->input('Tag', array('label' => false, 'multiple' => 'checkbox', 'class' => false));; ?>
</div>
<?php
echo $this->Form->input('Artist.website', array('label' => __('Website')));
echo $this->Form->input('ArtistPreview.0.url', array('label' => __('Artist Preview')));
echo $this->Form->input('Artist.description', array('label' => __('Description')));
?>
<?php
echo $this->Form->submit(__('Add'), array('class' => 'btn btn-primary btn-large', 'div' => false)) . ' ';
echo $this->Form->button(__('Cancel'), array('type' => 'button', 'class' => 'btn btn-large', 'div' => false, 'onclick' => 'closeDialog()'));
echo $this->Form->end();
?>
And here's my add controller method:
public function add() {
$this->layout = 'form';
if (!$this->request->is('ajax')) {
throw new ForbiddenException();
}
if ($this->request->is('post')) {
$this->setData($this->request->data['Artist'], array(
'user_id' => AuthComponent::user('id'),
'date' => $this->Date->gmt()
));
if ($this->Artist->saveAll($this->request->data)) {
$this->redirectAjax('/artists/view/' . $this->Artist->id);
}
}
$this->set(array(
'title_for_layout' => 'Add Artist',
'artistSections' => $this->Artist->ArtistSection->find('list', array('order' => 'name')),
'tags' => $this->Artist->Tag->find('list', array('order' => 'name'))
));
}
As I mentioned above, the saveAll method fails everytime. I have inspected what is getting output and Cake is outputting a bunch of blank <div>s with the error-message class, but there is no message. Every field on the form gets one of these divs.
Now, when I change my code to use the save function instead of saveAll, everything gets saved properly except the ArtistPreview data, which is to be expected. Even my HABTM Tags are being saved properly, and nothing fails validation.
I have been using Cake since the 1.3 days so I'm quite familiar with it, and have even done exactly what I'm asking in previous projects. I'm leaning towards this being a bug, unless I'm missing something here. Any ideas?
EDIT
I have also tried the saveAssociated method, along with setting the deep key to true when trying to save. This will let me save the artist, but it will not save the related data (ArtistPreview).
EDIT 2
As requested, here are the two tables:
-- -----------------------------------------------------
-- Table `artists`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `artists` ;
CREATE TABLE IF NOT EXISTS `artists` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT ,
`user_id` INT UNSIGNED NULL ,
`artist_section_id` INT UNSIGNED NULL ,
`artist_status_id` INT UNSIGNED NULL ,
`name` VARCHAR(200) NULL ,
`website` VARCHAR(500) NULL ,
`description` TEXT NULL ,
`date` DATETIME NULL ,
`comment_count` INT UNSIGNED NOT NULL DEFAULT 0 ,
PRIMARY KEY (`id`) ,
INDEX `FK_artists_users_idx` (`user_id` ASC) ,
INDEX `FK_artists__artist_sections_idx` (`artist_section_id` ASC) ,
INDEX `FK_artists__artist_statuses_idx` (`artist_status_id` ASC) ,
CONSTRAINT `FK_artists__users`
FOREIGN KEY (`user_id` )
REFERENCES `users` (`id` )
ON DELETE SET NULL
ON UPDATE CASCADE,
CONSTRAINT `FK_artists__artist_sections`
FOREIGN KEY (`artist_section_id` )
REFERENCES `artist_sections` (`id` )
ON DELETE SET NULL
ON UPDATE CASCADE,
CONSTRAINT `FK_artists__artist_statuses`
FOREIGN KEY (`artist_status_id` )
REFERENCES `artist_statuses` (`id` )
ON DELETE SET NULL
ON UPDATE CASCADE)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `artist_previews`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `artist_previews` ;
CREATE TABLE IF NOT EXISTS `artist_previews` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT ,
`artist_id` INT UNSIGNED NULL ,
`url` VARCHAR(500) NULL ,
`date` DATETIME NULL ,
PRIMARY KEY (`id`) ,
INDEX `FK_artist_previews__artists_idx` (`artist_id` ASC) ,
CONSTRAINT `FK_artist_previews__artists`
FOREIGN KEY (`artist_id` )
REFERENCES `artists` (`id` )
ON DELETE CASCADE
ON UPDATE CASCADE)
ENGINE = InnoDB;
Here is my setData function, which just merges two arrays. Instead of using hidden fields, I set fields in the controller which aren't populated by user input:
public function setData(&$original, $new) {
$original = array_merge($original, $new);
}
So I finally figured this out, and wow was it a rookie mistake. I think I must've been half asleep yesterday when I was coding. The problem was in my model:
class Artist extends AppModel {
public $belongsTo = array(
'User',
'ArtistSection',
'ArtistStatus'
);
public $hasMany = array(
'ArtistPicture',
'Artist' // <--- problem was here, had 'Artist' instead of 'ArtistPreview'
);
public $hasAndBelongsToMany = array(
'Tag'
);
public function __construct($id = false, $table = null, $ds = null) {
parent::__construct($id, $table, $ds);
$this->validate = array(
'name' => array(
'rule' => 'notEmpty',
'message' => __('Artist name cannot be blank.')
),
'website' => array(
'rule' => 'url',
'message' => __('Must be a valid URL.'),
'allowEmpty' => true
),
'artist_section_id' => array(
'rule' => 'notEmpty',
'message' => __('Section is required.')
)
);
}
}
Thank you to those of you who took the time to look at this question, even though I posted the wrong information.
I was wondering if someone could help me out.
I am using cakePHP 1.3 and I am having trouble getting the edit view to update the main Model and a hasOne related Model. I am fairly positive this has to do with my setup of the edit.ctp view. I am using the media plugin which I got working on another model, so I don't believe that has anything to do with that. Specifically I am working on getting the Media Plugin, Monolithic Attachment Model with a hasOne relationship working.
I have checked out the cake docs
http://book.cakephp.org/#!/view/1032/Saving-Related-Model-Data-hasOne-hasMany-belongsTo read the majority of the docs in the Media Plugin this like is the most relevant
https://github.com/davidpersson/media/blob/next/docs/RECIPES
and spent extensive time searching google.
Any help would be appreciated.
Thanks,
James
Model - client.php
<?php
class Client extends AppModel {
var $name = 'Client';
var $displayField = 'name';
var $actsAs = array(
'Media.Transfer',
'Media.Coupler',
'Media.Generator'
);
[...]
var $hasOne = array(
'Logo' => array(
'className' => 'Attachment',
'foreignKey' => 'foreign_key',
'conditions' => array('Logo.model' => 'Client'),
'dependent' => true,
));
[...]
?>
Controller - clients_controller.php
<?php
class ClientsController extends AppController {
var $name = 'Clients';
[...]
function edit($id = null) {
if (!$id && empty($this->data)) {
$this->Session->setFlash(__('Invalid client', true));
$this->redirect(array('action' => 'index'));
}
if (!empty($this->data)) {
if ($this->Client->saveAll($this->data, array('validate'=>'first') )) {
$this->Session->setFlash(__('The client has been saved', true));
$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('The client could not be saved. Please, try again.', true));
}
}
if (empty($this->data)) {
$this->Client->recursive = 0;
$this->data = $this->Client->read(null, $id);
}
$statuses = $this->Client->Status->find('list');
$this->set(compact('statuses'));
}
[...]
?>
View - edit.ctp
<h1><?php __('Edit Clients');?></h1>
<div class="clients form">
<?php echo $this->Form->create('Client', array('type' => 'file'))."\n";?>
<fieldset>
<?php
echo $this->Form->input('Client.id')."\n";
echo $this->Form->input('Client.name')."\n";
echo $this->Form->input('Client.address1')."\n";
echo $this->Form->input('Client.address2')."\n";
[...]
echo $form->input('Logo.id')."\n";
echo $form->input('Logo.file', array('type' => 'file'))."\n";
echo $form->hidden('Logo.foreign_key')."\n";
echo $form->hidden('Logo.model', array('value' => 'Client'))."\n";
?>
</fieldset>
<?php echo $this->Form->end(__('Submit', true));?>
</div>
clients sql
CREATE TABLE `clients` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(255) NOT NULL,
`address1` varchar(255) NOT NULL,
`address2` varchar(255) NOT NULL,
`created` datetime default NULL,
`modified` datetime default NULL,
PRIMARY KEY (`id`)
)
attachments sql
CREATE TABLE `attachments` (
`id` int(10) NOT NULL auto_increment,
`model` varchar(255) NOT NULL,
`foreign_key` int(10) NOT NULL,
`dirname` varchar(255) default NULL,
`basename` varchar(255) NOT NULL,
`checksum` varchar(255) NOT NULL,
`group` varchar(255) default NULL,
`alternative` varchar(50) default NULL,
`created` datetime default NULL,
`modified` datetime default NULL,
PRIMARY KEY (`id`)
)
var $hasOne = array(
'Logo' => array(
'className' => 'Attachment',
'foreignKey' => 'foreign_key', // 'foreign_key' ? is that a name for your fk?
'conditions' => array('Logo.model' => 'Client'),
'dependent' => true,
));
here you haven't defined the foreign_key which binds your logo to your client. look up in your database for the foreign key and put it's name here.
Thanks for all the responses. I finally got it working. It appears that for some reason the saveAll was causing me grief. When I saved the Client then saved the Logo everything worked.
I posted the code below.
Controller Code
function edit($id = null) {
if (!$id && empty($this->data)) {
$this->Session->setFlash(__('Invalid client', true));
$this->redirect(array('action' => 'index'));
}
if (!empty($this->data)) {
$client = $this->Client->save($this->data);
if (!empty($client)) {
$this->data['Logo']['foreign_key'] = $this->Client->id;
$this->data['Logo']['model'] = 'Client';
$this->Session->setFlash(__('The client has been saved', true));
$this->Client->Logo->save($this->data);
$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('The client could not be saved. Please, try again.', true));
}
}
if (empty($this->data)) {
$this->Client->recursive = 0;
$this->data = $this->Client->read(null, $id);
}
$statuses = $this->Client->Status->find('list');
$this->set(compact('statuses'));
}