Cakephp 3 - save data to belongstomany table - cakephp

In Cakephp3, I have the following tables:
CREATE TABLE `users` (
`id` int(11) NOT NULL,
`name` varchar(45) DEFAULT NULL,
`created` datetime NOT NULL,
`modified` datetime DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `roles` (
`id` int(11) NOT NULL,
`title` varchar(45) DEFAULT NULL,
`created` datetime NOT NULL,
`modified` datetime DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `roles_users` (
`id` int(11) NOT NULL,
`note` text,
`created` datetime DEFAULT NULL,
`modified` datetime DEFAULT NULL,
`role_id` int(11) NOT NULL,
`user_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
My intention is, to edit the available fields in table 'roles_users' within the Users add & edit view (and the related controller actions). I considereded https://book.cakephp.org/3.0/en/views/helpers/form.html#associated-form-inputs, my user view (edit) look like:
<div class="users form large-9 medium-8 columns content">
<?= $this->Form->create($user) ?>
<fieldset>
<legend><?= __('Edit User') ?></legend>
<?php
echo $this->Form->control('name');
echo $this->Form->control('roles.0._joinData.role_id', ['type'=>'select', 'options' => $roles]);
echo $this->Form->control('roles.1._joinData.role_id', ['type'=>'select', 'options' => $roles]);
echo $this->Form->control('roles.2._joinData.role_id', ['type'=>'select', 'options' => $roles]);
?>
</fieldset>
<?= $this->Form->button(__('Submit')) ?>
<?= $this->Form->end() ?>
</div>
the underlying controller action is:
public function edit($id = null)
{
$user = $this->Users->get($id, [
'contain' => ['Roles']
]);
if ($this->request->is(['patch', 'post', 'put'])) {
$user = $this->Users->patchEntity($user,$this->request->getData());
pr($this->request->getData());
if ($this->Users->save($user)) {
$this->Flash->success(__('The user has been saved.'));
return $this->redirect(['action' => 'index']);
}
$this->Flash->error(__('The user could not be saved. Please, try again.'));
}
$roles = $this->Users->Roles->find('list', ['limit' => 200]);
$this->set(compact('user', 'roles'));
}
The correct data are displayed, while accessing the edit action. But when submitting the data (even unchanged) new records are saved to table 'roles'. How do I have to update the controller actions and the related views, to write data to the table 'roles_users'?

You need to add associated model in your patchEntity like below
$user = $this->Users->patchEntity($user,$this->request->getData(), [
'associated' => ['RolesUsers']
]);
https://book.cakephp.org/3.0/en/orm/saving-data.html#

echo $this->Form->control('roles.0._joinData.role_id', ['type'=>'select', 'options' => $roles])
to:
echo $this->Form->control('roles.0.id', ['type'=>'select', 'options' => $roles])
echo $this->Form->control('roles.1.id', ['type'=>'select', 'options' => $roles])

Related

Cakephp five star rating using CakeDC plugin

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

issue with table to store files for multiple other tables

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?

How to show associated data in add form in cakephp 3.x

I have two tables
CREATE TABLE `user_roles` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`role_name` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=39 DEFAULT CHARSET=latin1
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(20) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL,
`user_role_id` int(11) DEFAULT NULL,
`status` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
I created the insertion and deletion for user roles . No i have to add the users with user roles. In the user adding form user role is displaying as empty. here is my code:
UserTable.php:
namespace App\Model\Table;
use Cake\ORM\Table;
use Cake\Validation\Validator;
class UsersTable extends Table
{
public function initialize(array $config)
{
$this->hasMany('UserRoles', [
'foreignKey' => 'id',
'sort' => ['UserRoles.role_name', 'ASC']
]);
$this->displayField('role_name');
} }
UserController.php
public function add()
{
$this->set('title', 'Add Users');
$users = $this->Users->newEntity();
if($this->request->is('post')):
$users = $this->Users->patchEntity($user, $this->request-
>data);
if($this->Users->save($user)):
$this->Flash->success(__('User was created
successfully.'));
return $this->redirect(['action' => 'index']);
endif;
endif;
$this->set('users', $this->users);
$this->set('user_roles', $this->Users->UserRoles->find('list',
['id', 'role_name']));
}
**add.ctp**
<?php
//print_r($user_roles);
echo $this->Form->create($users, ['class' => 'form']);
echo $this->Form->input('username');
echo $this->Form->input('email');
echo $this->Form->input('user_role_id');
echo $this->Form->input('password');
echo $this->Form->button(__("<i class='fa fa-save'></i> Save"));
echo $this->Form->end();
?>
**UserRolesTable.php**
namespace App\Model\Table;
use Cake\ORM\Table;
use Cake\Validation\Validator;
class UserRolesTable extends Table
{
public function initialize(array $config)
{
$this->belongsToMany('Users', [
'foreignKey' => 'user_role_id'
]);
}
public function validationDefault(Validator $validator)
{
$validator->notEmpty('role_name', 'Role name cannot be empty.');
return $validator;
}}
Its showing empty select box for user_role_id. Please help me.
You need to camelCase the View variable for the options for the magic to happen, so it should be $userRoles rather than $user_roles:-
$this->set('userRoles', $this->Users->UserRoles->find('list', ['id', 'role_name']));
Cake will then know to associate the variable with the form field. Otherwise you will have to specify the variable used for the options as suggested by Jun.
You have to add options to your input in your view :
echo $this->Form->input('user_role_id', ['options' => $user_roles]);
instead of
echo $this->Form->input('user_role_id');

cakephp foreign key in multiple rows not working

i have 2 table:
tickets :
`CREATE TABLE IF NOT EXISTS `tickets` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`nom_model` varchar(32) NOT NULL,
`is_active` tinyint(1) NOT NULL,
`date_modif` date NOT NULL,
PRIMARY KEY (`id`));`
details:
`CREATE TABLE IF NOT EXISTS `details` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`ticket_id` int(11) NOT NULL,
`nom` varchar(32) NOT NULL,
`text` varchar(32) NOT NULL,
`taille` enum('8','9','10','11','12','13','14','15','16','17','18','19','20') NOT NULL,
`police` enum('Impact','courier new','times new roman','comic sans ms') NOT NULL,
`bold` int(2) NOT NULL,
`italic` int(2) NOT NULL,
PRIMARY KEY (`id`),
KEY `ticket_id` (`ticket_id`));`
the tickets have 11 zone (msg_acc, nom_buro,heure, date ....)
which are the attributes of fields nom in table details
and in one zone have (text, police, bold, italic)
therefore must save 11 rows in table (details)
i want to creat form (contains nom model in table ticket and save 11 rows in table details with foreign key ticket_id in table details is the new ticket create (nom_model) ):
I create form save 11 rows market is well but th foreing key not worked
nom_model not save in table (tickets) and the 11 rows save with ticket_id equal 0
the code in model Ticket.php
<?php
class Ticket extends AppModel
{
var $name = 'Ticket';
public $displayField='id';
public $hasMany = array(
'Detail'
);
}
?>
the code in model Detail.php
<?php
class Detail extends AppModel{
var $name = 'Detail';
public $belongsTo = array(
'Ticket'
);
}
?>
the code in controller detailsController.php
$this->set('title_for_layout', __('Add Details'));
$tickets= $this->Detail->Ticket->find('list');
if (!empty($this->request->data)) {
if($this->Detail->saveAll($this->request->data['Detail'])){
$this->Session->setFlash(__('The Model has been saved'));
$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('The Model could not be saved. Please, try again.'));
}
$this->redirect(array('action' => 'index'));
}
$this->set('tickets', $tickets);
th code in view add.ctp
<?php echo $this->form->create('Detail');?>
<fieldset>
<legend>Add New Detail</legend>
<?php
echo $this->form->input('Ticket.$i.nom_model');//not working
for($i=0;$i<$count;$i++){
echo $this->form->input("Detail.$i.name");
echo $this->form->input("Detail.$i.text");
echo $this->form->input("Detail.$i.taille");
echo $this->form->input("Detail.$i.police");
echo $this->form->input("Detail.$i.bold");
echo $this->form->input("Detail.$i.italic");
$this->form->hidden('ticket_id');} ?> </fieldset>
<?php echo $this->form->end('Submit');?>
You are saving your request data by saveAll without your associated Ticket.
The associated data of Ticked is located in $this->request->data['Ticket'] and will be ignored:
//(Line 4 of your posted controller)
$this->Detail->saveAll($this->request->data['Detail'])
Update:
Your $this->request->data should have the following structure:
Array
(
[Ticket] => Array
(
[id] => 1
...
)
[Detail] => Array
(
[0] => Array
(
[ticket_id] => 1
...
)
[1] => Array
(
[ticket_id] => 1
...
)
..........
)
)
Adapt your View, generating that datafield and you can try to save your posted data via $this->Detail->saveAll($this->request->data)
Update
Your View in basic should look like the following:
<fieldset>
<legend>Add New Detail</legend>
<?php
echo $this->form->input("Ticket.nom_model");
echo $this->form->hidden("Ticket.id", array('value' => '1'));
for($i=0;$i < $count;$i++){
echo $this->form->input("Detail.$i.name");
echo $this->form->input("Detail.$i.text");
echo $this->form->input("Detail.$i.taille");
echo $this->form->input("Detail.$i.police");
echo $this->form->input("Detail.$i.bold");
echo $this->form->input("Detail.$i.italic");
echo $this->form->hidden("Detail.$i.ticket_id", array('value' => '1'));} ?>
<?php echo $this->form->end('Submit');?>
</fieldset>
This is an example view, that references Detail to Ticket via Ticked.id = 1
Update:
You should define the foreign key from Detail to Ticket in your Models/Detail.php:
public $belongsTo = array(
'Ticket' => array(
'className' => 'Ticket',
'foreignKey' => 'ticket_id'
)
);

Associations ONLY work with $scaffold

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

Resources