I am developing an Application using 2 models, Users and profiles.
when I am trying to editing both models at a time, I am getting continuously this error
Array to string conversion [CORE\Cake\Model\Datasource\DboSource.php, line 1006]
my $this->request->data array is in
array(
'User' => array(
'password' => '*****',
'id' => '6',
'first_name' => 'some',
'middle_name' => 'kjkj',
'last_name' => 'one',
'username' => 'kadjssa',
'group_id' => '5',
'mobile_no' => '7867868'
),
'Profile' => array(
'skype' => 'shrinet'
)
)
and my User controller edit function contain
public function editClient($id = null) {
$this->User->id = $id;
if (!$this->User->exists()) {
throw new NotFoundException(__('Invalid user'));
}
debug($this->request->data);
if ($this->request->is('post') || $this->request->is('put')) {
$this->request->data['User']['user_id'] = $this->Auth->User('id');
$this->request->data['Profile']['user_id'] = $id;
debug($this->request->data);
print_r (debug($this->User->invalidFields()));
if ($this->User->saveAll($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.'));
}
} else {
$this->request->data = $this->User->read(null, $id);
}
$groups = $this->User->Group->find('list');
$this->set(compact('groups'));
}
in debugging its giving
SQL Query: INSERT INTO `mvs`.`users` (`id`, `first_name`, `middle_name`, `last_name`, `username`, `password`, `group_id`, `mobile_no`, `user_id`, `modified`, `created`) VALUES (Array, Array, Array, Array, Array, Array, Array, Array, Array, '2013-07-05 00:07:13', '2013-07-05 00:07:13')
Please help me... thanks in advance
The docs recommend to use saveAssociated or saveMany depending on the case
This function receives the same options as the former two, and is generally a backwards compatible function. It is recommended using either saveMany or saveAssociated depending on the case
So the first choice is to change that saveAll to saveAssociated, since I think there's just one user and one profile.
All saveAll does is to call Set::numeric(array_keys($data)), and if that is true, calls saveMany, otherwise saveAssociated. The problem probably is in that Set::numeric comparison. If you want to get to the bottom of that, do a debug of
Set::numeric(array_keys($this->request->data));
If that gives you true, it must consider your array to be numerically indexed. Then you'll need to start debugging why is considered that way, check the Set::numeric function, where does the bad check, and change it or report it as a bug.
I think that trying to debug that is more troublesome than to change saveAll to saveAssociated, though, since you commented that the last gives you no problem.
Thank god,
I found resolution......
just use $this->User->saveAll($this->request->data, array('atomic' => false, 'deep' => true)); instead of $this->User->saveAll($this->request->data); because
Be careful when checking saveAssociated calls with atomic option set to false. It returns an array instead of boolean
ch http://book.cakephp.org/2.0/en/models/saving-your-data.html#model-saveassociated-array-data-null-array-options-array
Related
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 trying to get my CakePHP app to use slugs instead of ids. I have read several tutorials and the CakePHP book about it, but I must be missing something simple.
My table has a "slug" field that I want to use for the URL instead of the default id.
I changed my ItemsController view to this:
public function view($slug = null) {
if (!$this->Item->exists($slug)) {
throw new NotFoundException(__('Invalid item'));
}
$this->set('item', $this->Item->findBySlug($slug));
}
And added this to my routes.php
Router::connect(
'/items/:slug',
array('controller' => 'items', 'action'=>'view'),
array('pass'=>array('slug'))
);
Yet I still get "Invalid Item, requested address not found..." when going to:
mycakeapp/items/slug-value
However, if I change everything from 'slug' to 'id' then the URL:
mycakeapp/items/id-value
works just fine
Can someone help me? Thanks in advance.
Well, read the documentation for Model::exists().
Returns true if a record with particular ID exists.
If $id is not passed it calls Model::getID() to obtain the current
record ID, and then performs a Model::find('count') on the currently
configured datasource to ascertain the existence of the record in
persistent storage.
It expects an id not a slug.
Here is a proper example from a model method to display an artist:
public function view($id = null, $options = array()) {
$defaults = array(
'contain' => array(
/* ... */
),
'conditions' => array(
'OR' => array(
$this->alias . '.' . $this->primaryKey => $id,
$this->alias . '.slug' => $id
)
)
);
$artist = $this->find('first', Hash::merge($defaults, $options));
if (empty($artist)) {
throw new NotFoundException(__('Invalid Artist'));
}
return $artist;
}
The controllers try/catches the exception and sets the exception message to the session by calling Session->setFlasH(). Easy. :)
How can I create a custom Rule-Action which will successfully save a value as a replacement pattern for use in the other actions?
I got some very good help here on retrieving Product-Display information from a Product-Order.
As I said, the linked answer helped a great deal but the returned path data for the Product-Display comes back in the http://www.mysite/node/77 format. However, I really just need the numeric value only so I can load the node by performing a Fetch entity by id action supplying the numeric value and publishing the Product-Display node etc.
So, I implemented a custom action which will take the Product-Display URL(node/77) and return 77.
I copied the Fetch entity by id code and modified it so my returned numeric value can be saved and used in other Actions. The code is below:
function my_custom_action_info(){
$actions['publish_product_display_node'] = array(
'label' => t('Fetch product-display id'),
'parameter' => array(
'type' => array(
'type' => 'uri',
'label' => t('My Action'),
'options list' => 'rules_entity_action_type_options2',
'description' => t('Specifies the product-display url.'),
),
),
'provides' => array(
'entity_fetched' => array('type' => 'integer', 'label' => t('Fetched entity')),
),
'group' => t('Entities'),
'access callback' => 'rules_entity_action_access',
);
return $actions;
}
function publish_product_display_node($path = null){
$parts = explode('node/', $path);
return $parts[1];
}
function rules_entity_action_type_options2($element, $name = NULL) {
// We allow calling this function with just the element name too. That way
// we ease manual re-use.
$name = is_object($element) ? $element->getElementName() : $element;
return ($name == 'entity_create') ? rules_entity_type_options2('create') : rules_entity_type_options2();
}
function rules_entity_type_options2($key = NULL) {
$info = entity_get_info();
$types = array();
foreach ($info as $type => $entity_info) {
if (empty($entity_info['configuration']) && empty($entity_info['exportable'])) {
if (!isset($key) || entity_type_supports($type, $key)) {
$types[$type] = $entity_info['label'];
}
}
}
return $types;
}
function rules_action_entity_createfetch_access2(RulesAbstractPlugin $element) {
$op = $element->getElementName() == 'entity_create' ? 'create' : 'view';
return entity_access($op, $element->settings['type']);
}
As I said I copied the modified code so I don't claim to thoroughly understand all the functions aside from publish_product_display_node.
My code modifications work as far as setting the Product-Display URL token as the argument and also setting an entity variable label(Display NID) and value(display_nid).
The problem is when I check display_nid in newly created actions, the value is empty.
I need help figuring out the how to successfully save my entity value so I can use it in following Actions.
in the function publish_product_display_node, can you verify that you don't need to be returning $parts[0], instead of $[parts[1]?
It's just that Drupal paths are frequently in the form 'node/7' or 'taxonomy/term/6', and if you explode with 'node/' as the separator, you'd only have a single value which would start at index 0 for nodes...
So, just wondering if that would solve your issue...
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))
I'm using the Cupcake Forum plugin within CakePHP. There's a form for selecting the desired posts, and then submitting the form to delete the posts. The form data is apparently being sent to the 'moderate' function within the 'topics' controller using POST and GET methods simultaneously. The function first checks to see if the data sent in is POST. However, when the data is received, it shows that it's GET. A fellow programmer and I don't want to completely change someone else's internal code, but we can't figure out how the data is being sent by both methods and being received as GET. The code from the plugin is below:
--------------moderate.ctp (view)---------------------
<?php echo $form->create('Post', array('url' => array('controller' => 'topics', 'action' => 'moderate', $topic['Topic']['slug']))); ?>
-------------topics_controller.php (controller)-------
public function moderate($id) {
if ($this->RequestHandler->isGet()){
$this->log('Is GET!');
}
$user_id = $this->Auth->user('id');
$topic = $this->Topic->getTopicForViewing($id, $user_id, 'id');
// Access
$this->Toolbar->verifyAccess(array(
'exists' => $topic,
'permission' => $topic['ForumCategory']['accessRead'],
'moderate' => $topic['Topic']['forum_category_id']
));
$this->log('ID: '.$id.'\n');
if ($this->RequestHandler->isPost()){
$this->log('Is POST!');
}
if ($this->RequestHandler->isGet()){
$this->log('Is GET!');
}
$this->log($this->RequestHandler->getReferer());
$this->log(serialize($this->data));
// Processing
if ($this->RequestHandler->isPost()) {
$this->log('INSIDE POST!');
if (!empty($this->data['Post']['items'])) {
$items = $this->data['Post']['items'];
$action = $this->data['Post']['action'];
foreach ($items as $post_id) {
$this->log('Action: '.$action.'\n');
$this->log('PostID: '.$post_id.'\n');
if (is_numeric($post_id)) {
if ($action == 'delete') {
$this->Topic->Post->destroy($post_id);
$this->Session->setFlash(sprintf(__d('forum', 'A total of %d post(s) have been permanently deleted', true), count($items)));
}
}
}
}
}
We added the log checks, which show the result of 'Is GET!' in Cake's log file. Since the method is GET, the statement 'if ($this->RequestHandler->isPost())' is never true; therefore, the submitted posts aren't deleted. What are we missing?
Try changing moderate.ctp to
<?php
echo $form->create('Post', array(
'url' => array(
'controller' => 'topics',
'action' => 'moderate',
$topic['Topic']['slug'],
),
'type' => 'post',
));
?>