I am trying to update a profile in the database without passing the profile id as a paramater and instead of updating I am adding new row. I tried to use the getLastInsertId() but it did not work.
public function editProfile(){
if (isset($this->data)){
$Client = $this->Client->find('first', array(
'fields' => array('email','username','first_name','surname','country','phone_prefix','phone'),
'conditions' => array(
'Client.email' => $this->request->query['email'],
'Client.client_type' => $this->request->query['client_type']
),
)
);
if($this->request->is('get')) {
if($data = $this->Client->save($this->request->query,array('first_name','surname','country','phone_prefix','phone')))
{
$this->Client->id = $this->Client->getLastInsertId();
It looks like you are trying to update a record in your Client model based on the email and client type because (I assume) you don't know the id.
Try changing your code to get the id based on the information you have-
public function editProfile(){
if($this->request->is('get')) {
if (isset($this->data)){
$conditions = array('conditions' => array(
'email' => $this->request->query['email'],
'client_type' => $this->request->query['client_type']
));
$this->Client->id = $this->Client->field('id', $conditions);
if($this->Client->save($this->request->query,array('id', 'first_name','surname','country','phone_prefix','phone')))
{
$this->setFlash('success');
}else{
$this->setFlash('fail');
}
}
}
Looking at the code it seems you have two possible keys:
The client email
The last saved id
First, using the last saved id seems very fragile - how can you be sure this will always be the record you want to update? Best to avoid this approach.
However, if you have the user's email you can use the following method:
$client = $this->Client->findByEmail($this->request->data['Client']['email']);
$id = $client['Client']['id'];
You now have the client record id available to use in your save. Something like:
$data = array();
$data['Client']['id'] = $id;
$data['Client']['fieldFromForm'] = $this->data['Client']['fieldFromForm'];
$this->Client->save($data);
...
Without the id, cake will assume that you are adding a new Client. To update via save you'll need to tell it which Client.id to update in some way. Is there some reason that you can't provide the Client.id? The usual approach is to have Client.id present in a hidden input on your edit view which you can then pass to the edit action...
Related
I am looking for input/help on how to do this. Might be some PHP/cake developers could provide some good solutions here. Cakephp 2.3 something :)
Problem; How to put shortcodes in wysiwyg editor (example: [slideshow=1]slideshow here[/slideshow]) and render an element (in this case, loading and displaying the slideshow with ID=1).
ShortcodeHelper.php
App::import('Helper', 'Html', 'Router');
class ShortcodeHelper extends AppHelper {
public $shortcodes = array(
'slideshow' => '/(\[slideshow=)(.+?)(\])(.+?)(\[\/slideshow\])/'
);
public $returncodes = array(
//'slideshow' => $this->render('/elements/slideshow', array('id'=>'\\2'))
'slideshow' => '<strong rel="\\2">\\4</strong>'
);
public function render($content, $render=null) {
$shortcodes = $this->shortcodes;
$returncodes = $this->returncodes;
if(isset($render)) {
$temp_shortcodes = array();
$temp_returncodes = array();
foreach ($render as $key => $value) {
$temp_shortcodes[$key] = $shortcodes[$value];
$temp_returncodes[$key] = $returncodes[$value];
}
$returncodes = $temp_returncodes;
$shortcodes = $temp_shortcodes;
}
$return = preg_replace($shortcodes, $returncodes, $content);
return $this->output($return);
}
}
view.ctp (call render function from helper, and run the page-content trough it):
<?php echo $this->Shortcode->render($page['Page']['body']); ?>
Thanks. You are awesome!! :)
-Tom
You need to turn the short code string into a method call, parse it.
Your helper will need to be able to detect them and then break them up. Your code needs to be mapped somehow to a callback.
// [slideshow=1]slideshow here[/slideshow]
$this->requestAction(array('controller' => 'slideshows', 'action' => 'view', $id);
For example.
I think the best way here would be to just always map the first arg, the "function call" to an element instead and pass all other args to the element. This way you can do there whatever you want and request the data or just simply display HTML only.
I would put the mapping of short codes into something like Configure::write('ShortCodes', $shortCodeArray); this way plugins could even register their callback mapping by simply adding them to that array.
array(
'slideshow' => array('controller' => 'slideshows', 'action' => 'view')
);
You'll have to merge that with args from the parsed short code.
Why requestAction()? You should not violate the MVC pattern, for this reason you'll have to request the data via requestAction().
I have a parent/child self association table where when the id = parent_id, that id is it's own parent. However I'm having trouble saving data into my table from the add action/view
From add.ctp view - when adding a new record, I select a parent_id from the drop down box and enter a name.
<?php
echo $this->Form->input('parent_id', array('empty' => 'No Parent'));
echo $this->Form->input('name');
?>
If user selects "No Parent" this means I would like the parent_id = id where id is the unique ID automatically created in DB at time it is saved.
This is what is passed into $this->request->data when 'No Parent' is selected.
array(
'Item' => array(
'parent_id' => '',
'name' => 'testname'
)
)
I have tried to set the parent_id = id in the beforeSave but since id does not yet exist, there is nothing to assign parent_id to. I have also tried calling the "parent" model save first and saveAll in the controller but those don't work either.
Controller
public function add() {
if ($this->request->is('post')) {
$this->Item->create();
//have tried calling parent model in self association first but
//if ($this->Item->ParentItem->save($this->request->data)) {
if ($this->Item->saveAll($this->request->data)) {
$this->Session->setFlash(__('The item has been saved'));
$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('The item could not be saved. Please, try again.'));
}
}
Item.php / Model relationship
public $belongsTo = array(
'ParentItem' => array(
'className' => 'Item',
'foreignKey' => 'parent_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
public $hasMany = array(
'ChildItem' => array(
'className' => 'Item',
'foreignKey' => 'parent_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
)
);
How can I take an ID that is just created/saved and save that to another field, in this case parent_id?
UPDATE:
I have been working on this some more and I have used getInsertId() to get the last inserted Id and I am trying to save that into the parent_id, but there is something that prevents this. I have removed all model validation to make sure it wasn't that. But is there something in Cake (or my association setup) that does not allow parent_id = id (i.e. a row is it's own parent?
This is the latest code in my add action... This saves a row to the DB, but w/o a parent_id. I then try to edit using the add action and set the parent_id = id, but even the edit wont allow a save.
public function add() {
if ($this->request->is('post')) {
$this->Item->create();
if ($this->Item->save($this->request->data)) {
$last_id = $this->Item->getInsertId();
$this->Item->saveField('parent_id', $last_id);
$this->Session->setFlash(__('The item has been saved'));
$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('The item could not be saved. Please, try again.'));
}
}
I have also tried calling $this->Item->ParentItem->save(), saveAll, 'deep' => true, but still nothing is allowing me to update the parent_id column. A row ge
Thanks in advance
In Your model of association You can set foregin_key to parent_id, and will be automatic filled
So the problem I was having... trying to set a parent_id = id (making id it's own parent basically) in a self join model is due to this piece of code in my Model file
public $actsAs = array('Tree');
After reading through the Tree Behavior again, I realized that $this->Model->save (or saveField) does not really work well with Tree structures and updating parent IDs. Should use the behaviors functions instead form my understanding. Also the TreeBehavior expects some parent_ids to be null (at least top level parent_ids), so if I were to leave this as a tree, the ids with a null parent_id would be considered the parent.
http://book.cakephp.org/2.0/en/core-libraries/behaviors/tree.html
"The parent field must be able to have a NULL value! It might seem to work if you just give the top elements a parent value of zero, but reordering the tree (and possible other operations) will fail."
So I need to decide if I want to use tree behavior or have simple parent/child relationship without using Tree... think I will do the later as I don't have a need for multilevel tree, just a parent and one level of children.
Thanks for replies.
i got a problem with designing my Database and the CakePHP code around it.
I have 4 Models,
ServerName
VirtualMachine
ServerHousing
ManagedServer
In ServerName, i want to save all ServerNames which then can be used in either one of the three other models. How can i achieve that i am only able to link one ServerName to either one of the other models?
Thank you guys in advance.
EDIT:
I now did it a little bit different. First of all it need to be done in the Model itself.
I used the validate option in cakePHP's models.
The code is like this:
public $validate = array(
'server_name_id' => array(
'rule' => 'serverNameTaken',
'message' => 'This Servername has already been taken.'
)
);
public function serverNameTaken()
{
$this->ManagedServer = ClassRegistry::init("ManagedServer");
// Assuming the server_name_id was passed from the form...
$server_name_id = $this->data['VirtualMachine']['server_name_id'];
// Check if this servername_id is already saved in virtual_machines
if ($this->ManagedServer->find('count', array(
'conditions' => array(
'ManagedServer.server_name_id' => $server_name_id
)
)) > 0
) {
// Found the server_name in the VirtualMachine model!
return false; // Prohibit saving the data
}
if ($this->find('count', array(
'conditions' => array(
'VirtualMachine.server_name_id' => $server_name_id
)
)) > 0
) {
// Found the server_name in the VirtualMachine model!
return false; // Prohibit saving the data
}
// Do this for the other models too. If a return false is not hit by now,
// everything should be fine and you can...
return true;
}
Same code I used in the other models, only the code had to be altered.
Thanks again for you answer!!!
You can't do this on the Model layer. Models are either associated or they are not. What you are probably looking for is some logic in your Controller that checks if a row for ServerName X has already been saved in any of the other models, prior to saving it from another model.
The beforeSave function is useful for this. For example, in your Controller, put this:
public $uses = array('VirtualMachine', 'ServerHousing', 'ManagedServer');
public function beforeSave() {
// Assuming the servername_id was passed from the form...
$servername_id = $this->request->data['servername_id'];
// Check if this servername_id is already saved in virtual_machines
if($this->VirtualMachine->find('count', array(
'conditions' => array(
'VirtualMachine.servername_id' => $servername_id
)
)) > 0) {
// Found the servername in the VirtualMachine model!
return false; // Prohibit saving the data
}
// Do this for the other models too. If a return false is not hit by now,
// everything should be fine and you can...
return true;
}
Hope this will get you going in the right direction.
In my app Quotes belongTo a product, which in turn belongs to a material. As I can't get the product model afterFind array to include the material when it is accessed from the Quote model I have associated the Quote directly with a material.
The problem I'm having now is that the material_id for the quote needs to be automatically saved based on the product which is selected for the quote
i.e. pulling the value of Product.material_id from the selected product and saving it to the Quote.material_id field automatically before the Quote has been saved to the database.
I'm quite new to cakePHP. Does anyone know how this can be done?
EDIT:
Here is an example to help explain. In my Quote model i can have:
public function beforeSave($options) {
$this->data['Quote']['material_id'] = 4;
return true;
}
but i need to do something more like this which doesn't work:
public function beforeSave($options) {
$this->data['Quote']['material_id'] = $this->Product['material_id'];
return true;
}
I'm shocked this hasn't been properly answered yet....
Oldskool's response is semi-correct, but not entirely right. The use of "$this->Quote" is incorrect, as the beforeSave function itself resides in the Quote class. I'll explain using an example.
-> We have a model Subscription which belongsTo a SubscriptionsPlan
-> Model SubscriptionsPlan hasMany Suscriptions
To access the SubscriptionsPlan data in a beforeSave function in the Subscription model, you would do the following:
public function beforeSave($options = array()){
$options = array(
'conditions' => array(
'SubscriptionsPlan.subscriptions_plan_id' => $this->data[$this->alias]['subscriptions_plan_id']
)
);
$plan = $this->SubscriptionsPlan->find('first', $options);
//REST OF BEFORE SAVE CODE GOES HERE
return true;
}
It should probably work by using a find instead.
public function beforeSave($options) {
// Assuming your Product model is associated with your Quote model
$product = $this->Quote->Product->find('first', array(
'conditions' => array(
'Product.material_id' => $this->data['Quote']['material_id']
)
));
$this->data['Quote']['material_id'] = $product['material_id'];
return true;
}
i have the following queries common on few methods of the controllers. so is there a way to organise it ? i will need all the variables in the controller so i cant create a private method and return it.
// Checks if the User is logged in if yes gathers the ID
$id = $this->_loggedIN();
// Find the ItemID from the Item Table
$itemId = $this->User->Item->itemId('1', $id);
// Finding the User Data and last Status Message
$user = $this->User->Item->find('first', array('conditions' => array('Item.id' => $itemId), 'contain' => array('User', 'StatusMessage' => array('limit' => 1, 'order' => 'StatusMessage.created DESC'))));
Since this seems to pertain to the logged in user, you should do this once and save the data in the session. If you're using the AuthComponent (which you probably should), there already is a generic way to find out whether a user is logged in and what his id is:
$this->Auth->user('id');
All the other data of the user model is accessible in the same way. This is simply stored in the session under the key 'Auth' and is accessible like $this->Session->read('Auth.User.id'). If you want to store even more data about the user in the session (like related items or whatnot), do it once in the login method.
function beforeFilter() {
$this->Auth->autoRedirect = false;
}
function login() {
if ($this->Auth->user()) {
$item = /* find item */;
$user = /* find user */;
$this->Session->write('Auth.Item', $item);
$this->Session->write('Auth.User', $user);
$this->redirect($this->Auth->redirect());
}
}
How about a function in the AppController? Or even better - AppModel?