I can't figure out why this code isn't working. The beforeSave is not being called. It's supposed to fail the save and put some lines in the debug log, but it actually does save OK and no lines are written in the debug log.
<?php
class Link extends AppModel {
var $name = "Link";
var $belongsTo = array('Category' => array('className' => 'Category', 'foreignKey' => 'category_id'));
public function beforeSave(){
if ($this->data[$this->alias]['id'] == null) {
$this->log("new record", 'debug');
$link = $this->find('first',array('conditions' => array('Link.status = 1 AND Link.category_id = '.$this->data[$this->alias]['category_id']), 'order' => array('Link.order DESC') ));
if (is_null($link)) {
$this->data[$this->alias]['order'] = 1;
}else{
$this->data[$this->alias]['order'] = $link['Link']['order'] + 1;
}
}
else {
$this->log("old record", 'debug');
}
return false;
}
}
?>
I am launching a save in the controller like this:
public function add($category_id = null)
{
if ($category_id == null) {
$this->Session->setFlash(__('Category id cant be null'),'default', array('class' => 'error-message'));
$this->redirect(array('action' => 'index', 'controller' => 'categories'));
}
else
{
if($this->request->is('post'))
{
$this->Link->create();
$this->Link->set('category_id' => $category_id));
if($this->Link->save($this->request->data))
{
$this->Session->setFlash(__('The link has been saved'),'default', array('class' => 'success'));
$this->redirect(array('action' => 'index/'.$category_id));
}
else
$this->Session->setFlash(__('The link could not be saved. Please, try again.'),'default', array('class' => 'error-message'));
}
$this->set('category_id',$category_id);
}
}
Another question in StackOverflow points out that the beforeSave method needs to be declared in the Model. I've also done the same thing with another model.
Here's some general advice and some comments on your code for an answer:
1) If a model callback or any model method isn't working, make sure the correct model is being used and not the default model (AppModel). Check filename, class name, extension (in your case), and location.
2) You're using conditions array incorrectly (in this case).
array('conditions' => array('Link.status = 1 AND Link.category_id = '.$this->data[$this->alias]['category_id'])
You should really be doing:
array('conditions' => array('Link.status' => 1, 'Link.category_id' => $this->data[$this->alias]['category_id'])
3) Your redirect is being used wrong
$this->redirect(array('action' => 'index/'.$category_id));
Should be:
$this->redirect(array('action' => 'index', $category_id));
As written, your save() will always fail with this beforeSave(). beforeSave() must return true in order for the save function to succeed.
In fact, yours appears to always return false, guaranteeing a failed save.
From the Cake manual:
Be sure that beforeSave() returns true, or your save is going to fail.
Related
I m having trouble in updating the student profile form,after i gave the user_id to the student profile.Every time when i try to edit the form, on saving it provide new id to the existing user. I don't know where I'm getting wrong. Plz help.Thanks in Advance.
Here is my add and edit function in Student Profiles controller:
public function add() {
if ($this->request->is('post')) {
$this->StudentProfile->create();
$data = $this->request->data;
$data['StudentProfile']['user_id'] = $this->Auth->user('id');
// code implemented below
//$this->loadModel('User');
if ($this->StudentProfile->save($data)) {
//Update user here if Profile saved successfully
//$this->StudentProfile->id = $this->Auth->user('id');
$this->Session->setFlash(__('Your account profile has been created'));
$this->redirect(array('controller' => 'studentprofiles', 'action' => 'index'));
}else{
$this->Session->setFlash(__('Your Profile was saved, but an error has occurred while updating the Users table'));
//Email your self the user ID here or something ??
}
}
$h=array(
'fields' => array('Height.height'),
'recrusive' => 0
);
$this->loadModel('Height');
$height = $this->Height->find('list',$h);
$this->set(compact('height'));
}
public function edit($id = null) {
if (!$this->StudentProfile->exists($id)) {
throw new NotFoundException(__('Invalid student profile'));
}
if ($this->request->is(array('post', 'put'))) {
$this->request->data['StudentProfile']['user_id'] = $this->Auth- >user('id');
// code implemented below
// $this->loadModel('User');
if ($this->StudentProfile->save($this->request->data)) {
//$this->StudentProfile->id = $this->Auth->user('id');
$this->Session->setFlash(__('The student profile has been saved.'), 'default', array('class' => 'alert alert-success'));
return $this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('The student profile could not be saved. Please, try again.'), 'default', array('class' => 'alert alert-danger'));
}
} else {
$options = array('conditions' => array('StudentProfile.' . $this- >StudentProfile->primaryKey => $id));
$this->request->data = $this->StudentProfile->find('first', $options);
}
$h=array(
'fields' => array('Height.height'),
'recrusive' => 0
);
$this->loadModel('Height');
$height = $this->Height->find('list',$h);
$this->set(compact('height'));
}
Can't believe I'm replying to my own question . Well In case anyone have the same above problem,the solution is just add the following code after this-
$this->request->data['StudentProfile']['user_id'] = $this->Auth->user('id');
// add this code to avoiding giving new id to existing user on edit:
$this->request->data['StudentProfile']['id'] = $id;
A better way of saving edited information is to set the ID on the model before the save like so
$this->StudentProfile->id = $this->Auth->user('id');
If you initiate a save after setting the above then the row will be edited and not add a new row.
Well,
I created a Model with the following restriction
public $validate = array(
'player_id' => array(
'rule' => array(
'checkUnique',
array(
'player_id',
'game_id'
),
true
),
'required' => true,
'allowEmpty' => false,
'on' => 'create',
'message' => 'Same player_id y game_id'
)
);
So each time I try to create a game record in the table it is created only if it is not created yet.
So I created an action in one controller that get recent games of one player and use saveAll to save into the database.
If the database is empty there is no a single problem, of course. But if I receive some games and some of them are already being inserted previously saveAll fails because SOME of the games are already into the database.
public function getRecentGames($server = null, $player = null){
$this->autoRender = false;
if( !empty($server) && !empty($player) ){
$r = $this->_getRecentGames($server, $player, $gamesData);
if ($r['code'] == 200) {
if ($this->Game->saveAll($gamesData, array('deep' => true))) {
pr($gamesData);
prd('Saved');
} else {
pr($this->Game->invalidFields());
prd('Not saved');
}
} else {
}
}
return print_r($gamesData, true);
}
Basically saveAll(..) calls internally validateMany(..) which returns false because not every entry is valid and saveAll does not try to save. This is the normal behavior of CakePHP and the way developers want it to work.
So, what should I do?
Check each game and try to save it?
foreach ($games as $game) {
$this->Model->saveAssociated(..);
}
Modify the behavior of saveAll(..) in order to save the valid games and not the invalid ones. (Do you think this should be the default behavior of CakePHP?)
Other solutions I didn't think(?). Please show me then
Thank you
Well this is the best approach I could think of:
$validations = $this->Game->validateMany( $gamesData, array('deep' => true, 'atomic' => false) );
for ($i=count($gamesData)-1; $i>=0; $i--) {
if (!$validations[$i]) {
unset($gamesData[$i]);
}
}
if (!empty($gamesData)) {
$result = $this->Game->saveAll($gamesData, array('deep' => true, 'validate' => false));
}
I am stuck at the loop function of cakephp.
The logic is I need to compare the data entered by users with the data already in a table. I have two tables, one is Bookings and one is Inventories_Bookings. Below is my coding but it doesnot work. any help! Thanks
public function add2() {
if ($this->request->is('post')) {
foreach ($invbook as $invenbook)
{
if ($this->request->data['Booking']['bookings_location'] == $invenbook['InventoriesBooking']['test'])
{
$this->Session->setFlash(__('The booking cannot be created'));
$this->redirect(array('action' => 'add2'));
debug($this->request->data['Booking']['bookings_location'] == $invenbook['InventoriesBooking']['test']);
}
}
$this->Booking->create();
$invbook = $this->Booking->InventoriesBooking->find('list',array('fields' => array('InventoriesBooking.id', 'InventoriesBooking.test')));
$this->set(compact('invbook'));
}
}
I would use a custom validation function for this.
You are able to create your own functions in the model, and from here you can access the database to do the lookup. If it matches you can return true.
You can read about custom validation methods in the book.
There is an example of a rule like this using the db in the book.
Quoted for great justice.
class User extends AppModel {
public $validate = array(
'promotion_code' => array(
'rule' => array('limitDuplicates', 25),
'message' => 'This code has been used too many times.'
)
);
public function limitDuplicates($check, $limit) {
// $check will have value: array('promotion_code' => 'some-value')
// $limit will have value: 25
$existing_promo_count = $this->find('count', array(
'conditions' => $check,
'recursive' => -1
));
return $existing_promo_count < $limit;
}
}
I'm trying to build on the example code at http://book.cakephp.org/2.0/en/models/saving-your-data.html in the section that starts off with "Saving Related Model Data (hasOne, hasMany, belongsTo)". However, I'm getting the following error message when I call unset:
Indirect modification of overloaded property AppModel::$MealContent has no effect [APP\Controller\MealsController.php, line 15]
Attempt to modify property of non-object [APP\Controller\MealsController.php, line 15]
Naturally, the data isn't saved either.
As you can see, my application doesn't have Companies or Accounts. Instead, I have Meals and MealContents but the relationships seem to have been set up the same. Obviously, there's a problem somewhere though so here's some code.
Meals.php:
class Meals extends Entry {
public $hasMany = 'MealContents';
public $validate = array(
'Timestamp' => array(
'validDate' => array(
'rule' => array('garbageDateChecker'),
'required' => true,
'message' => 'The date could not be understood.'),
'noTimeTravel' => array(
'rule' => array('noTimeTravel'),
'required' => true,
'message' => 'You cannot enter times in the future.')
)
);
}
MealContents.php:
class MealContents extends Entry {
public $belongsTo = 'Meals';
public $validate = array(
'Meals_ID' => array(
'notEmpty' => array(
'rule' => 'notEmpty',
'required' => true,
'message' => 'This field cannot be blank')
),
'Item_ID' => array(
'notEmpty' => array(
'rule' => 'notEmpty',
'required' => true,
'message' => 'This field cannot be blank')
)
);
}
And finally, the controller's index() function:
public function index() {
$this->set('title_for_layout', "Food Log");
if ($this->request->is('post')) {
$this->Meal->create();
unset($this->Meal->MealContent->validate['Meals_ID']);
if ($this->Meal->saveAssociated($this->request->data)) {
$this->Session->setFlash('The meal was logged.');
$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash("Couldn't save the meal.");
}
}
}
Entry.php
abstract class Entry extends AppModel {
public function garbageDateChecker($dateToCheck) {
date_default_timezone_set("America/Tijuana");
$result = TRUE;
try {
new DateTime($dateToCheck['Timestamp']);
} catch (Exception $e) {
$result = FALSE;
}
return $result;
}
public function noTimeTravel($dateToCheck) {
date_default_timezone_set("America/Tijuana");
$result = TRUE;
$objectifiedDateToCheck = new DateTime($dateToCheck['Timestamp']);
$currentTime = new DateTime("now");
if ($objectifiedDateToCheck > $currentTime) {
$result = FALSE;
}
return $result;
}
}
I'm pretty sure the save() failure isn't due to the validation because even when I comment out the validation rule and the unset() line, the data isn't saved.
It would be easy to blame it on bad data or a screwed up view. However, the data looks OK:
$this->request->data
--MealContents
---[0]
-----[Food_Item_ID] = "0"
--Meals
---[Comments] = ""
---[Timestamp]
-----[day] = "12"
-----[hour] = ...
What have I missed when I read the CakePHP book?
In all of your model class declarations, you should be extending the AppModel class not "Entry". Also, you need to change your model names to singular nouns. Meal instead of Meals.
class Meal extends AppModel {
//your model's code
}
class MealContent extends AppModel {
//your model's code
}
In your controller, if you would like to skip validation on the saveAssociated call, you can pass an options array, with element "validate" set to False as the second parameter. You should never use unset on your model like that as it will affect the rest of your app.
$this->Meal->saveAssociated($this->request->data, array("validate" => false));
This my link in newsses/index.ctp
$this->Html->link(__("Read more >>", TRUE), array('action'=>'view', $newss['Newsse']['title']));
and this my view code in newsses_controller.php:
function view($title = NULL){
$this->set('title_for_layout', __('News & Event', true));
if (!$id) {
$this->Session->setFlash(__('Invalid News.', true), 'default', array('class' => 'error'));
$this->redirect(array('action'=>'index'));
}
$this->set('newsse', $this->Newsse->read(NULL,$title));
$this->set('newsses', $this->Newsse->find('all'));
}
but it does't showing anything,
i want to make route like:
"newsses/view/2" to "newsses/view/title_of_news"
please help me....
You're using the Model::read() method method which takes as the second argument the id of the row in your Model's table that you want to access. It's better to use find in this case. You don't need to build a new method in your model or your controller, you can just edit the current view method.
# in newsses_controller.php:
function view($title = null) {
$this->set('title_for_layout', __('News & Event', true));
if (!$id) {
$this->Session->setFlash(__('Invalid News.', true), 'default', array('class' => 'error'));
$this->redirect(array('action'=>'index'));
}
$this->set('newsse', $this->Newsse->find('first', array(
'conditions' => array('Newsse.title' => $title)
));
$this->set('newsses', $this->Newsse->find('all'));
}
Or, you can make a more hybrid form in which viewing by id is still possible when a numerical title is given (this assumes you never have news items which have a title consisting of only numeric characters, e.g. '12345').
# in newsses_controller.php:
function view($title = null) {
$this->set('title_for_layout', __('News & Event', true));
if (!$id) {
$this->Session->setFlash(__('Invalid News.', true), 'default', array('class' => 'error'));
$this->redirect(array('action'=>'index'));
} else if (is_numeric($title)) {
$this->set('newsse', $this->Newsse->read(NULL, $title));
} else {
$this->set('newsse', $this->Newsse->find('first', array(
'conditions' => array('Newsse.title' => $title)
));
}
$this->set('newsses', $this->Newsse->find('all'));
}
Finally, you can also replace the find method in my example with a (shorter) custom findBy method (see the documentation for more info about this).
$this->Newsse->findByTitle($title);
For this you need to create a new method in your model Which will display result by news title. At this time your using $this->Newsse->read(NULL,$title)). You are using $title in read method while this read method search against news id in model. So you just need to create a new method in model class like readByTitle($title){ write query here to fetch news by title }. And use this method in your controller. $this->Newsse->readByTitle(NULL,$title))