I am trying to save associated data in Cakephp 2.0, in the database I have two tables, table entities (id, main_name) and adresses(id, entity_id, city)
In the Entities model I made the association:
public $hasMany = array(
'Address' => array(
'className' => 'Address',
'foreignKey' => 'entity_id'
)
);
In the AdressesController I saved with the following data:
public function add() {
if ($this->request->is('post')) {
if($this->Entity->save($this->request->data)) {
$this->Flash->success('Entity successfully registered!');
$this->redirect(array('action' => 'add'));
} else {
$this->Flash->error(Oops, we could not register this entity!
Make sure it already exists.');
}
}
}
And in the view, my form looks like this:
<?php
echo $this->Form->input(
'Entity.main_name',
array(
'type' => 'text',
'class' => 'form-control',
'label' => false
)
);
?>
<?php
echo $this->Form->input(
'Address.city',
array(
'type' => 'text',
'class' => 'form-control',
'label' => false
)
);
?>
The data of the entity normally saved in the database, but does not relate the entity_id and does not save the city in the adresses table, do I have to do anything else in the controller?
There are several ways of solving your problem. Like described in CakeBook: Saving your data you can use saveAssociated() or Save each Model step by step.
Saving using saveAssociated()
In your Controller/EntitiesController.php:
public function add() {
if ($this->request->is('post')) {
$this->Entity->create();
// Use saveAssociated() instead of save() here
if ($this->Entity->saveAssociated($this->request->data)) {
$this->Flash->success(__('The entity has been saved.'));
return $this->redirect(array('action' => 'index'));
} else {
$this->Flash->error(__('The entity could not be saved. Please, try again.'));
}
}
$addresses = $this->Entity->Address->find('all');
$this->set($addresses);
}
In your View/Entities/add.ctp:
<?php
echo $this->Form->input('main_name', array(
'type' => 'text',
'class' => 'form-control',
'label' => false
));
// Make sure you use Address.0.city
echo $this->Form->input('Address.0.city', array(
'type' => 'text',
'class' => 'form-control',
'label' => false
));
?>
Since you use a hasMany Association a Entity can have several Addresses. For this reason you have to set the 0 in Address.0.city. This will result in a data array like this:
array(
'Entity' => array(
'main_name' => 'Fancy Name'
),
'Address' => array(
(int) 0 => array(
'city' => 'Cool City'
)
)
)
Saving Models step by step
Another approach would be to save the Entity and then save the Address with the entity_id like described in CakeBook:
In your Controller/EntitiesController.php:
public function add() {
if (!empty($this->request->data)) {
// save Entity
$entity = $this->Entity->save($this->request->data);
if (!empty($entity)) {
// Set the EntityId to the data array and save the Address with the EntityId
$this->request->data['Address']['entity_id'] = $this->Entity->id;
$this->Entity->Address->save($this->request->data);
}
}
}
In this case your View/Entities/add.ctp Address form would look like:
echo $this->Form->input('Address.city', array(
'type' => 'text',
'class' => 'form-control',
'label' => false
));
Best, variables
Related
i'm using cakeDC's search plugin in my app. can;t seem to figure out why the results are empty if i pass no value in the username field i'm searching but have the account type filter set to either admin or user For the record, having the filter set to all with the username field empty will output all the users in the system trying to replicate the behavior of searches having the account type filter set to all with an empty username search field for having the account type filter set to either 2 user types
here's the relevant code if needed:
controller
public $components = array('Paginator', 'Session','Search.Prg');
public $presetVars = array(
array('field' => 'username', 'type' => 'value'),
array('field' => 'account_type', 'type' => 'value'));
public function admin_index() {
$this->Prg->commonProcess();
$this->paginate = array(
'conditions' => $this->User->parseCriteria($this->passedArgs));
$this->set('users', $this->Paginator->paginate(),'session',$this->Session);
model
public $actsAs = array('Search.Searchable');
public $filterArgs = array( array('name' => 'username', 'type' => 'query', 'method' => 'filterName'),
array('name' => 'account_type', 'type' => 'value'),
);
public function filterName($data, $field = null) {
$nameField = '%' . $data['username'] . '%';
return array(
'OR' => array(
$this->alias . '.username LIKE' => $nameField,
));
}
view search form
<div><?php echo $this->Form->create('User', array(
'novalidate' =>true,'url' => array_merge(array('action' => 'index','admin'=> true), $this->params['pass'])
)); ?>
<?php
$account_types = array(
'' => __('All', true),
0 => __('admin', true),
1 => __('user', true),);
echo $this->Form->input('username', array('div' => false, 'empty' => true)); // empty creates blank option.
echo $this->Form->input('account_type', array('label' => 'account_type', 'options' => $account_types));
echo $this->Form->submit(__('Search', true), array('div' => false));
echo $this->Form->end();
?></div>
Try to put 'empty' and allowEmpty in username and account_type like this:
public $filterArgs = array( array('name' => 'username', 'type' => 'query', 'method' => 'filterName', 'empty'=>true, 'allowEmpty'=>true),
array('name' => 'account_type', 'type' => 'value','empty'=>true, 'allowEmpty'=>true),
);
I had this kind of behavior as well from CakeDC/search and this two configs saved me, hope it will do for you as well.
It's a good idea to check the queries being executed as well using DebugKit.Toolbar.
I'm new to CakePHP and I'm trying to implement the Simple Acl Controlled Application tutorial, and I've reached the part where you try to add new users and groups..
I successfully added the groups but when i try and add new users I receive the "The user could not be saved. Please, try again." Part of the function.
public function add() {
if ($this->request->is('post')) {
$this->User->create();
if ($this->User->save($this->request->data)) {
$this->Session->setFlash(__('The user has been saved'));
$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('The user could not be saved. Please, try again.'));
}
}
}
I noticed that the form tries to create a drop down box of all the different groups that I created but the drop box is empty and I have created three different groups (Admin, Responder and Volunteer).
Here is a copy of the add user view..
<div class="users form">
<?php echo $this->Form->create('User'); ?>
<fieldset>
<legend><?php echo __('Add User'); ?></legend>
<?php
echo $this->Form->input('id');
echo $this->Form->input('username');
echo $this->Form->input('password');
echo $this->Form->input('group_id');
?>
</fieldset>
<?php echo $this->Form->end(__('Submit')); ?>
</div>
<div class="actions">
<h3><?php echo __('Actions'); ?></h3>
<ul>
<li><?php echo $this->Html->link(__('List Users'), array('action' => 'index')); ?></li>
</ul>
</div>
Model as requested:
<?php
App::uses('AppModel', 'Model');
App::uses('AuthComponent', 'Controller/Component');
class User extends AppModel {
public $belongsTo = array('Group');
public $actsAs = array('Acl' => array('type' => 'requester'));
public function parentNode() {
if (!$this->id && empty($this->data)) {
return null;
}
if (isset($this->data['User']['group_id'])) {
$groupId = $this->data['User']['group_id'];
} else {
$groupId = $this->field('group_id');
}
if (!$groupId) {
return null;
} else {
return array('Group' => array('id' => $groupId));
}
}
public $primaryKey = 'id';
public $validate = array(
'id' => array(
'numeric' => array(
'rule' => array('numeric'),
),
),
'username' => array(
'notempty' => array(
'rule' => array('notempty'),
),
),
'password' => array(
'notempty' => array(
'rule' => array('notempty'),
),
),
'group_id' => array(
'numeric' => array(
'rule' => array('numeric'),
),
),
);
//The Associations below have been created with all possible keys, those that are not needed can be removed
//old belongs to
// public $belongsTo = array(
// 'Group' => array(
// 'className' => 'Group',
// 'foreignKey' => 'group_id',
// 'conditions' => '',
// 'fields' => '',
// 'order' => ''
// )
// );
public $hasMany = array(
'Post' => array(
'className' => 'Post',
'foreignKey' => 'user_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
)
);
public function beforeSave($options = array()) {
$this->data['User']['password'] = AuthComponent::password($this->data['User']['password']);
return true;
}
}
Debug message:
array(
'User' => array(
'password' => '*****',
'id' => '',
'username' => 'iwanjones'
)
)
Any help would be appreciated.
Thanks
In the form within your view, you create the id field as an input field (although through Cake's automagic it should convert it to a hidden input). When creating a new user, there is no id yet. It will be determined upon saving (creating) the record. In most applications this will be done by the MySQL backend's AUTO_INCREMENT functionality, which picks the first available "free" id.
In case of adding a new user, the id field is therefor not necessary. You only need it when you want to edit an existing user and make sure Cake edits the proper user, by setting it's id.
At this moment you set the id field in your view, but it gets no value, since it's a new user. In your Model you have added a validation rule that requires the id field to be numeric. But, the value is actually empty. You should do two things to get this working:
Drop the echo $this->Form->input('id'); line from the add view.
Remove the validation rule for the id field from your model (it's pretty uncommon to validate your primary key field, as Cake already handles this the proper way).
This should allow for the user to be saved successfully.
I'm using a form with multiplefields with the same name. After a false validation of the form i return to it with the validation messages. That all works. While the validation messages of the fields are shown correct, the entered data in the fields are gone.
Its cakephp 2.2 application without a database.
(saving the data to a session for 3 pages then write it to XML)
step2.ctp:
debug($this->Form);
echo $this->Form->input('Invoice.0.InvoiceOrderNumber', array('label' => false));
echo $this->Form->input('Invoice.0.NetAmount', array('label' => false));
echo $this->Form->input('Invoice.0.CostBearer', array('label' => false));
echo $this->Form->input('Invoice.0.CostCenter', array('label' => false));
echo $this->Form->input('Invoice.0.LedgerAccount', array('label' => false));
Controller:
public function step2() {
$invoice = $this->Session->read('Invoice');
if (!isset($invoice) && is_array($invoice))
$this->redirect(array('action' => 'step1'));
else
{
$this->set('title_for_layout', 'Nieuwe interne doorbelasting');
if ($this->request->is('post')) {
debug($this->request->data);
if ($this->Invoice->validateMany($this->request->data['Invoice']))
{
if ($this->Session->write('Invoice', $this->request->data['Invoice'])) {
$this->Session->setFlash(__('The invoice has been saved'));
$this->redirect(array('action' => 'step3'));
} else {
$this->Session->setFlash(__('The invoice could not be saved. Please, try again.'));
}
}
}
}
}
debug of ($this->Form) in step2.ctp:
.....
request => object(CakeRequest) {
params => array(
'plugin' => null,
'controller' => 'invoices',
'action' => 'step2',
'named' => array(),
'pass' => array(),
'models' => array(
'Invoice' => array(
'plugin' => null,
'className' => 'Invoice'
)
)
)
data => array(
'Invoice' => array(
(int) 0 => array(
'Invoice' => array(
'InvoiceOrderNumber' => 'adfas',
'NetAmount' => '2',
'CostBearer' => '',
'CostCenter' => '',
'LedgerAccount' => ''
)
),
(int) 1 => array(
'Invoice' => array(
'InvoiceOrderNumber' => 'adaaaaaaaaa',
'NetAmount' => 'bbbbbbbbb',
'CostBearer' => '',
'CostCenter' => '',
'LedgerAccount' => ''
)
)
)
)
....
So the data is there, but cake doesn't put it in the fields. But i can't figger out why...
I found the solution after the hint. Validation indeed changes the data. Theres a warning in the model i missed: "* Warning: This method could potentially change the passed argument $data, * If you do not want this to happen, make a copy of $data before passing it * to this method"
So my solution:
$invoiceData = $this->request->data['Invoice'];
if ($this->Invoice->validateMany($this->request->data['Invoice']))
{
...
}
$this->request->data['Invoice'] = $invoiceData;
Try this:
$this->Form->create('Invoice');
$this->Form->input('0.OrderNumber');
$this->Form->input('0.NetAmount');
...
$this->Form->input('1.OrderNumber');
$this->Form->input('1.NetAmount');
First : sorry for my long message.
I'm trying to learn Fuel, but I have some problems with Fieldset class and Orm.
I've create a Model which extends ORM, in order to get an automatic generated form, according to my database.
My Model
class Model_Product extends \Orm\Model
{
protected static $_table_name = 'products';
protected static $_properties = array(
'id' => array(
'data_type' => 'int'
),
'name' => array(
'data_type' => 'varchar',
'label' => 'Name',
'validation' => array(
'required', 'trim', 'max_length'=>array(30), 'min_length'=>array(3)
),
),
'description' => array(
'data_type' => 'varchar',
'label' => 'Description',
'validation' => array(
'max_length' => array(290)
),
'form' => array(
'type' => 'textarea'
),
),
'price' => array(
'data_type' => 'integer',
'label' => 'Price',
'validation' => array(
'required', 'trim', 'valid_string' => array('numeric','dots')
),
),
'pic' => array(
'data_type' => 'varchar',
'label' => 'Path to the pic',
'validation' => array(
'required', 'trim'
),
),
'registered' => array(
'data_type' => 'date',
'label' => 'Registration date',
'validation' => array(
'required', 'trim'
) //'valid_string' => array('numeric','dashes')
),
);
} //end of class Model_Product
Then I create the controller which will validate the form.
My function from the controller
function action_add()
{
$fieldset = Fieldset::forge('add_product')->add_model('Model_Product')->repopulate();
$form = $fieldset->form();
$form->add('submit', '', array('type' => 'button', 'value' => 'Add item', 'class' => 'button-link' ));
$validation = $fieldset->Validation();
if($validation->run() === true)
{
$fields = $fieldset->validated();
//create a new Product, with validated fields
$product = new Model_Product;
$product->name = $fields['name'];
$product->description = $fields['description'];
$product->price = $fields['price'];
$product->pic = $fields['pic'];
$product->registered = $fields['registered'];
try
{
//if the product is successfully inserted in the database
if($product->save())
{
Session::set_flash('success', 'Product successfully added !');
\Response::redirect('products/product_details/'.$product->id);
}
}
catch(Exception $e)
{
Session::set_flash('error', 'Unable to save the product into the database !'.$e->getMessage());
}
}
//If the validation doesn't pass
else
{
Session::set_flash('error', $fieldset->show_errors());
}
$this->template->set('content', $form->build(), false);
} // end of method add()
My first question :
How and where in my function from controller can i add a 'fieldset' tag with a specific class, in order to 'beautify' my auto-generated form ?
Let's say
<fieldset class="add_product">
Second question :
What do I have to do in order to correctly validate de 'price' field, because in MySQL is set as decimal(5,2), but when I'm trying to validate with my actual validation rule, it doesn't pass (it works only with integer values Ex.: 42, but not with decimal Ex.: 42.35). I have tried to change the type from 'integer' to 'double', but it doesn't work .
If you can point to some specific documentation regarding my problems, which I possible didn't read yet, please do feel free.
Gabriel
I can answer the first question To change the automatically generated form you will need to copy fuel/core/config/form.php to the fuel/app/config directory and edit this file to suit your needs.
I have a few different contact forms in my CakePHP 2.0 application. All of the contact forms are emailing as they should, but I need this particular one to also save the form results to the database. The post data is populating, and I can print_r() and pr() the form data. I can even email the post data. However, it is not actually saving the data to the model table. The database table is named contacts and has the following fields: id, publication, company, name, email, phone, message, contact_method, selections, received.
Here is my model:
class Contact extends AppModel {
public $name = 'Contact';
public $useTable = 'contacts';
public $validate = array(
'name' => array(
'rule' => 'notEmpty'
),
'email' => array(
'rule' => 'notEmpty'
)
);
Here is my controller:
App::uses('CakeEmail', 'Network/Email');
class ContactsController extends AppController
{
public $name = 'Contacts';
public $helpers = array('Html', 'Form', 'Js');
public $components = array('Email', 'Session');
...
public function contact_att() {
if ($this->request->is('post')) {
//pr($this->data);
if ($this->Contact->save($this->request->data)) {
$this->redirect('/pages/publications-alabama-turf-times');
$this->Session->setFlash("Mesage Saved!");
}
else {
print_r($this->data);
Configure::write('debug', 2);
debug($this->Contact->validationErrors);
exit;
}
}
Here is the form in my view:
echo $this->Form->create('Contact', array(
'action' => 'contact_att',
'label' => '',
'class' => 'pubs'));
echo $this->Form->input('publication', array(
'type' => 'hidden',
'value' => 'A',
'label' => ''));
echo $this->Form->input('company', array(
'default' => 'company name (required)',
'onfocus' => 'clearDefault(this)',
'label' => array(
'text' => 'Company Name',
'style' => 'position:absolute;')));
echo $this->Form->input('name', array(
'default' => 'name (required)',
'onfocus' => 'clearDefault(this)',
'label' => array(
'text' => 'Your Name',
'style' => 'position:absolute;')));
echo $this->Form->input('phone', array(
'default' => 'phone number (required)',
'onfocus' => 'clearDefault(this)',
'label' => array(
'text' => 'Your Phone Number',
'style' => 'position:absolute;')));
echo $this->Form->input('email', array(
'default' => 'email (required)',
'onfocus' => 'clearDefault(this)',
'label' => array(
'text' => 'Your Email Address',
'style' => 'position:absolute;')));
echo $this->Form->input('message', array(
'label' => array(
'text' => 'Your Message',
'style' => 'position:absolute;')));
echo $this->Form->input('contact_method', array(
'type' => 'radio',
'style' => 'padding-right:20px;',
'legend' => 'Preferred contact method:',
'options' => array(
'phone' => 'phone',
'email' => 'email'
)
));
echo $this->Form->input('selections', array(
'type' => 'select',
'label' => array(
'text' => 'I am interested in the following:',
'style' => 'display:block; width:250px; margin-left:-12px;padding-bottom:15px;'),
'multiple' => 'checkbox',
'options' => array(
'ABC' => 'ABC',
'DEF' => 'DEF',
'GHI' => 'GHI'
)
));
echo $this->Form->end('Submit');
What am I missing?
After much banging my head on the desk, the answer turned out to be simple -- of course. I simply removed this line from my model. I thought that having it set to the correct table would be fine, but turns out, it needed to be removed:
public $useTable = 'contacts';
You can try with this:
$this->Contact->save($this->request->data, false);
Try with:
debug($this->model->invalidFields());
sometimes, model have errors on validations, and not shows with validationErrors()
also note this..
If $fieldList is not supplied, a malicious user can add additional fields to the form data (if you are not using SecurityComponent), and by this change fields that were not originally intended to be changed.
http://book.cakephp.org/2.0/en/models/saving-your-data.html
take some time and read this documentation is very important, I hope have helped you.
Friend, I see your problem, check..
This line is missing in your controller
public $uses = array('Contact');
put and try, then you told me...
Is post will only return true when the form sets the posted hidden flag. So try this instead.
if(!empty($this->request->data))
Pls add this line
$this->Contact->create();
before you tried to save using
if ($this->Contact->save($this->request->data)) {