Why am I having trouble saving objects to the database in a typo3 extension? - database

Why won't my user and coupon objects get saved to the database when a new record is created?
I am using typo3 v 4.5.30 and making a little extension (my first) to manage some coupons. When I create a coupon I save the creator (a frontenduser) and it gets saved to the DB correctly. But when I do the same thing for a coupon user that user ( and the coupon ) do not get saved to the db. Consider this code frag which attempts to save the user and the coupon in a usedcoupon table. The usedcoupon table basically just has 2 columns one for the user and one for the coupon.
To get a usedcoupon object I called the objectmanagers create method. The user and coupon objects I already have and look right when I var_dump them. Even when I get them from the usedcoupon object they look ok but they don't get saved to the db even when the new record gets created. This code in in my CouponController in an action method.
$used = $this->objectManager>create('Tx_BpsCoupons_Domain_Model_UsedCoupon');
$used->setCoupon($coupon);
$used->setUser($user);
$used->setGuest("myemail#ddd.com");
$userx = $used->getUser();
$coupx = $used->getCoupon();
/// var_dumps of userx and coupx show good objects
$this->usedCouponRepository->add($used);
//after this I can examine the db and see the new record but the user and coupon fields are empty, and no errors are seen
Thanks
PS here is my TCA from Usedcoupon.php
<?php
if (!defined ('TYPO3_MODE')) {
die ('Access denied.');
}
$TCA['tx_bpscoupon_domain_model_usedcoupon'] = array(
'ctrl' => $TCA['tx_bpscoupon_domain_model_usedcoupon']['ctrl'],
'interface' => array(
'showRecordFieldList' => 'sys_language_uid, l10n_parent, l10n_diffsource, hidden, user, coupon, guest',
),
'types' => array(
'1' => array('showitem' => 'sys_language_uid;;;;1-1-1, l10n_parent, l10n_diffsource, hidden;;1, user, coupon, guest,--div--;LLL:EXT:cms/locallang_ttc.xml:tabs.access,starttime, endtime'),
),
'palettes' => array(
'1' => array('showitem' => ''),
),
'columns' => array(
'sys_language_uid' => array(
'exclude' => 1,
'label' => 'LLL:EXT:lang/locallang_general.xml:LGL.language',
'config' => array(
'type' => 'select',
'foreign_table' => 'sys_language',
'foreign_table_where' => 'ORDER BY sys_language.title',
'items' => array(
array('LLL:EXT:lang/locallang_general.xml:LGL.allLanguages', -1),
array('LLL:EXT:lang/locallang_general.xml:LGL.default_value', 0)
),
),
),
'l10n_parent' => array(
'displayCond' => 'FIELD:sys_language_uid:>:0',
'exclude' => 1,
'label' => 'LLL:EXT:lang/locallang_general.xml:LGL.l18n_parent',
'config' => array(
'type' => 'select',
'items' => array(
array('', 0),
),
'foreign_table' => 'tx_bpscoupon_domain_model_usedcoupon',
'foreign_table_where' => 'AND tx_bpscoupon_domain_model_usedcoupon.pid=###CURRENT_PID### AND tx_bpscoupon_domain_model_usedcoupon.sys_language_uid IN (-1,0)',
),
),
'l10n_diffsource' => array(
'config' => array(
'type' => 'passthrough',
),
),
't3ver_label' => array(
'label' => 'LLL:EXT:lang/locallang_general.xml:LGL.versionLabel',
'config' => array(
'type' => 'input',
'size' => 30,
'max' => 255,
)
),
'hidden' => array(
'exclude' => 1,
'label' => 'LLL:EXT:lang/locallang_general.xml:LGL.hidden',
'config' => array(
'type' => 'check',
),
),
'starttime' => array(
'exclude' => 1,
'l10n_mode' => 'mergeIfNotBlank',
'label' => 'LLL:EXT:lang/locallang_general.xml:LGL.starttime',
'config' => array(
'type' => 'input',
'size' => 13,
'max' => 20,
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => array(
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
),
),
),
'endtime' => array(
'exclude' => 1,
'l10n_mode' => 'mergeIfNotBlank',
'label' => 'LLL:EXT:lang/locallang_general.xml:LGL.endtime',
'config' => array(
'type' => 'input',
'size' => 13,
'max' => 20,
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => array(
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
),
),
),
'user' => array(
'exclude' => 0,
'label' => 'LLL:EXT:bpscoupon/Resources/Private/Language/locallang_db.xml:tx_bpscoupon_domain_model_usedcoupon.user',
'config' => array(
'type' => 'select',
'foreign_table' => 'fe_users'
),
),
'coupon' => array(
'exclude' => 0,
'label' => 'LLL:EXT:bpscoupon/Resources/Private/Language/locallang_db.xml:tx_bpscoupon_domain_model_usedcoupon.coupon',
'config' => array(
'type' => 'select',
'foreign_table' => 'tx_bpscoupons_domain_model_coupon'
),
),
'guest' => array(
'exclude' => 0,
'label' => 'LLL:EXT:bpscoupon/Resources/Private/Language/locallang_db.xml:tx_bpscoupon_domain_model_usedcoupon.guest',
'config' => array(
'type' => 'input',
'size' => 30,
'eval' => 'trim'
),
),
),
);
?>
PPS adding usedcoupon model code:
<?php
/***************************************************************
* Copyright notice
*
* (c) 2013 Cory Taylor <cory#bigplayersystems.com>, Big Player Systems
*
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
/**
*
*
* #package bps_coupons
* #license http://www.gnu.org/licenses/gpl.html GNU General Public License, version 3 or later
*
*/
class Tx_BpsCoupons_Domain_Model_UsedCoupon extends Tx_Extbase_DomainObject_AbstractEntity {
/**
* Used By
*
* #var Tx_BpsCoupons_Domain_Model_FrontendUser
*/
protected $user;
/**
* Returns the $user
*
* #return Tx_BpsCoupons_Domain_Model_FrontendUser $user
*/
public function getUser() {
echo "<br/>" . __FUNCTION__ . __LINE__ . " <br/>";
return $this->user;
}
/**
* Sets the $user
*
* #param #param Tx_BpsCoupons_Domain_Model_FrontendUser $user
* #return void
*/
public function setUser(Tx_BpsCoupons_Domain_Model_FrontendUser $user) {
$this->user = $user;
}
/**
* the used coupon
*
* #var Tx_BpsCoupons_Domain_Model_Coupon
*/
protected $coupon;
/**
* Returns the $coupon
*
* #return Tx_BpsCoupons_Domain_Model_Coupon $coupon
*/
public function getCoupon() {
return $this->coupon;
}
/**
* Sets the $coupon
*
* #param #param Tx_BpsCoupons_Domain_Model_Coupon $coupon
* #return void
*/
public function setCoupon(Tx_BpsCoupons_Domain_Model_Coupon $coupon) {
$this->coupon = $coupon;
}
/**
* the guest email
*
* #var string
*/
protected $guest;
/**
* Returns the $guest
*
* #return string $guest
*/
public function getGuest() {
return $this->guest;
}
/**
* Sets the $guest email
*
* #param string $guest
* #return void
*/
public function setGuest( $guest) {
$this->guest = $guest;
}
}
?>
PPPS: I tried adding a basic text field for email addresses but it turns out they don't get saved either. I had thought the issue was that the user and coupon filed were references to rows in other tables but now it turns out that simpler things do not get saved either.
PP PP S: may as well look into my ext_tables file too:
<?php
if (!defined('TYPO3_MODE')) {
die ('Access denied.');
}
Tx_Extbase_Utility_Extension::registerPlugin(
$_EXTKEY,
'Bpscoupons',
'BPS_Coupons'
);
t3lib_extMgm::addStaticFile($_EXTKEY, 'Configuration/TypoScript', 'BPS_Coupons');
t3lib_extMgm::addLLrefForTCAdescr('tx_bpscoupons_domain_model_coupon', 'EXT:bps_coupons/Resources/Private/Language/locallang_csh_tx_bpscoupons_domain_model_coupon.xml');
t3lib_extMgm::allowTableOnStandardPages('tx_bpscoupons_domain_model_coupon');
$TCA['tx_bpscoupons_domain_model_coupon'] = array(
'ctrl' => array(
'title' => 'LLL:EXT:bps_coupons/Resources/Private/Language/locallang_db.xml:tx_bpscoupons_domain_model_coupon',
'label' => 'name',
'tstamp' => 'tstamp',
'crdate' => 'crdate',
'cruser_id' => 'cruser_id',
'dividers2tabs' => TRUE,
'sortby' => 'sorting',
'versioningWS' => 2,
'versioning_followPages' => TRUE,
'origUid' => 't3_origuid',
'languageField' => 'sys_language_uid',
'transOrigPointerField' => 'l10n_parent',
'transOrigDiffSourceField' => 'l10n_diffsource',
'delete' => 'deleted',
'enablecolumns' => array(
'disabled' => 'hidden',
'starttime' => 'starttime',
'endtime' => 'endtime',
),
'searchFields' => 'name,description,expiry,hall,date_created,creator,barcode,',
'dynamicConfigFile' => t3lib_extMgm::extPath($_EXTKEY) . 'Configuration/TCA/Coupon.php',
'iconfile' => t3lib_extMgm::extRelPath($_EXTKEY) . 'Resources/Public/Icons/tx_bpscoupons_domain_model_coupon.gif'
),
);
t3lib_extMgm::addLLrefForTCAdescr('tx_bpscoupons_domain_model_usedcoupon', 'EXT:bpscoupons/Resources/Private/Language/locallang_csh_tx_bpscoupons_domain_model_usedcoupon.xml');
t3lib_extMgm::allowTableOnStandardPages('tx_bpscoupons_domain_model_usedcoupon');
$TCA['tx_bpscoupons_domain_model_usedcoupon'] = array(
'ctrl' => array(
'title' => 'LLL:EXT:bpscoupons/Resources/Private/Language/locallang_db.xml:tx_bpscoupons_domain_model_usedcoupon',
'label' => 'user',
'tstamp' => 'tstamp',
'crdate' => 'crdate',
'cruser_id' => 'cruser_id',
'dividers2tabs' => TRUE,
'versioningWS' => 2,
'versioning_followPages' => TRUE,
'origUid' => 't3_origuid',
'languageField' => 'sys_language_uid',
'transOrigPointerField' => 'l10n_parent',
'transOrigDiffSourceField' => 'l10n_diffsource',
'delete' => 'deleted',
'enablecolumns' => array(
'disabled' => 'hidden',
'starttime' => 'starttime',
'endtime' => 'endtime',
),
'searchFields' => 'user,coupon,guest,',
'dynamicConfigFile' => t3lib_extMgm::extPath($_EXTKEY) . 'Configuration/TCA/Usedcoupon.php',
'iconfile' => t3lib_extMgm::extRelPath($_EXTKEY) . 'Resources/Public/Icons/tx_bpscoupons_domain_model_usedcoupon.gif'
),
);
?>

If that happens, most probably your objects are not persisted. Normally Extbase persists changes to objects after the end of a request. But if you e.g. return your response as JSON and then exit your action, the persistenceManager is not called.
You can persist manually by injecting the persistenceManager to your controller:
/**
* #var Tx_Extbase_Persistence_Manager
*/
protected $persistenceManager;
/**
* #param Tx_Extbase_Persistence_Manager $persistanceManager
* #return void
*/
public function injectPersistenceManager(Tx_Extbase_Persistence_Manager $persistenceManager) {
$this->persistenceManager = $persistenceManager;
}
and then call it after you added the new object:
$persistenceManager->persistAll();
In TYPO3 4.7+, you can use the #inject annotation in the doc comment of $persistenceManager to inject the persistenceManager and don't need the inject function.
If this doesn't solve the problem, maybe there's a problem with your TCA.

Related

Cakephp 2 use the id of the record as index when retrieving data

Is it possible to retrieve records where the index is the record id?
I have a model with a hasMany Relation. So my study can have multiple texts and questions etc.
The custom retrieve function looks like this:
protected function _findStudyWithSequence($state, $query, $results=array()){
if ($state === 'before') {
$query['contain'] = array(
'SubjectFilter'=>array('fields'=>'SubjectFilter.studyCode'),
'Text'=>array('fields'=>'Text.position,Text.id,Text.text','order'=>array('position')),
'Image'=>array('fields'=>'Image.position, Image.id','order'=>array('position')),
'Video'=>array('fields'=>'Video.position, Video.id','order'=>array('position')),
'Web'=>array('fields'=>'Web.position, Web.id','order'=>array('position')),
'Likert'=>array('fields'=>'Likert.position, Likert.id','order'=>array('position')),
'Question'=>array('fields'=>'Question.position, Question.id','order'=>array('position')),
'Star'=>array('fields'=>'Star.position, Star.id','order'=>array('position')),
'Multiple_choice'=>array('fields'=>'Multiple_choice.position, Multiple_choice.id','order'=>array('position'))
);
$query['recursive']=-1;
$query['order']=array('Study.started DESC');
$query['fields']=array(
'Study.id', 'Study.user_id','Study.title','Study.description','Study.numberParticipants','Study.state','Study.created','Study.modified','Study.started','Study.completed','Study.numberParticipants');
return $query;
}
}
return $results;
}
As you can see I am using containable
The results look like this:
array(
'Study' => array(
'id' => '1845346986',
'user_id' => '1402402538',
'title' => 'bla bla',
'description' => '',
'numberParticipants' => '10',
'state' => 'active',
'created' => '2017-08-21 14:06:11',
'modified' => '2017-08-21 14:29:56',
'started' => '2017-08-21 14:30:06',
'completed' => '0000-00-00 00:00:00',
),
'SubjectFilter' => array(
'studyCode' => null
),
'Text' => array(
(int) 0 => array(
'position' => '0',
'id' => '1423909242',
'text' => '<p>Bla <b>bla </b></p><p>blub</p><p><br /></p>',
'study_id' => '1845346986'
)
),
'Question' => array(
(int) 0 => array(
'position' => '4',
'id' => '729521908',
'study_id' => '1845346986'
)
), etc
But I want the Text and Question index to be the ids. Like this:
'Text' => array(
(int) 1423909242 => array(
'position' => '0',
'id' => '1423909242',
'text' => '<p>Bla <b>bla </b></p><p>blub</p><p><br /></p>',
'study_id' => '1845346986'
)
),
How can i manage this?
You can use CakePHP's amazing Hash::combine() method.
Something like:
$results['Text'] = Hash::combine($results['Text'], '{n}.id', '{n}');
$results['Question'] = Hash::combine($results['Question'], '{n}.id', '{n}');
return $results;
It's not tested code, but I believe it's correct (or at least close), and you should get the idea from here.
Notes on Hash::combine:
Creates an associative array using a $keyPath as the path to build its
keys, and optionally $valuePath as path to get the values. If
$valuePath is not specified, or doesn’t match anything, values will be
initialized to null. You can optionally group the values by what is
obtained when following the path specified in $groupPath.
Hash::combine(array $data, $keyPath, $valuePath = null, $groupPath =
null)

how to use countercache for different alias in cakephp model?

I have the following models: Attachment and PurchaseOrder hence the datatables attachments and purchase_orders.
In PurchaseOrder, I have
class PurchaseOrder extends AppModel {
public $hasMany = array(
'Pdf' => array(
'className' => 'Attachment',
'foreignKey' => 'foreign_key',
'conditions' => array(
'Pdf.model' => 'PurchaseOrder'
)
),
'Zip' => array(
'className' => 'Attachment',
'foreignKey' => 'foreign_key',
'conditions' => array(
'Zip.model' => 'PurchaseOrder'
)
),
In Attachment, I have the following:
public $belongsTo = array(
'PurchaseOrder' => array(
'className' => 'PurchaseOrder',
'foreignKey' => 'foreign_key',
'counterCache' => array(
'attachment_count' => array('Pdf.model' => 'PurchaseOrder'),
)
),
My problem is when I try to use $this->PurchaseOrder->Zip->save($data); I run into problem because the alias Pdf is not found.
How do I overcome this while maintaining the countercache behavior of updating the attachment_count inside purchase_orders?
Note that if a PurchaseOrder is associated with 3 Pdf Attachments and 2 Zip Attachments, the attachment_count should read 3.
I am using cakephp 2.4.2
I stopped using counterCache and used afterSave instead.
/**
* Called after each successful save operation.
*
* #param boolean $created True if this save created a new record
* #param array $options Options passed from Model::save().
* #return void
* #link http://book.cakephp.org/2.0/en/models/callback-methods.html#aftersave
* #see Model::save()
*/
public function afterSave($created, $options = array()) {
// update the PurchaseOrder based on pdf
if (isset($this->data['Pdf'])) {
$this->updatePurchaseOrderAttachmentCount($this->data);
}
}
/**
*
* #param Array $data where we expect key Pdf
*/
public function updatePurchaseOrderAttachmentCount($data) {
// check for Pdf
$pdfSet = isset($data['Pdf']);
if (!$pdfSet) {
throw new Exception('we expect Pdf as a key in your $data');
}
// check for foreign_key and model
$pdfFKSet = isset($data['Pdf']['foreign_key']);
$pdfModelSet = isset($data['Pdf']['model']);
if (!$pdfFKSet || !$pdfModelSet) {
throw new Exception('we expect foreign_key and model as keys in your $data["Pdf"]');
}
$count = $this->find('count', array(
'conditions' => array(
'model' => 'PurchaseOrder',
'type' => 'application/pdf',
'foreign_key' => $data['Pdf']['foreign_key'],
)
));
if (!is_numeric($count)) {
throw new Exception('we expect numeric in the $count');
}
$poData = array(
'PurchaseOrder' => array(
'id' => $data['Pdf']['foreign_key'],
'attachment_count' => $count,
)
);
return $this->PurchaseOrder->save($poData);
}
I know this post is very old, but I recently faced a similar issue and found another way to handle the same problem. You can continue using the counterCache approach with multiple counterCache, the only difference would be, you need to add you association on the fly, in the __construct method of your model.
public function __construct($id = false, $table = null, $ds = null) {
parent::__construct($id, $table, $ds);
/**
* When using Aliases, associations need to be added on the fly, otherwise, the CounterScope conditions would result in an SQL Error, during counterCache updates
*/
$this->bindModel(
array('belongsTo' => array(
'PurchaseOrder' => array(
'className' => 'PurchaseOrder',
'foreignKey' => 'foreign_key',
'counterCache' => array(
'attachment_count' => array($this->alias . '.model' => 'PurchaseOrder'),
'attachment_count' => array($this->alias . '.model' => 'PurchaseOrder')
)
)
)
)
);
}

Stuck with HasMany through saving and error array_merge()

I'm trying to set up a pretty simple website to create and edit recipes ('Recettes' in french).
I'm an experienced frontend developer with a good knowledge (not advanced) of php, and I have been working on CakePhp with a team of developers in the past. I'm kind of learning the bases of starting a project from scratch and it is really not that easy.
I've set up a project, database (Ingredients, Recette and IngredientRecette) and used Bake to generate most of the code.
I have a working CRUD workflow for my 3 models and the only thing left to do is to be able to add ingredients from the Recette/add page but i'm stuck. I followed the instructions on the cake php website (http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#hasmany-through-the-join-model) and other website (cause the help on cakephp's doc is really not that helpful) and I beleive I have setup a correct Hasmany Through relationship.
But the saveAll function doesn't save anything in the join model (IngredientRecette) and I've came accross a particular error (see below) after I've successfully solved another one that took me a couple of hours to work out !
So I have the feeling that I've checked, and double checked, and triple checked everything in my code and I've read every questions and answers on forums etc...
And I am stuck with this problem, maybe there is something obvious I didn't figured, or maybe and definitely don't understand how Cakephp really wordk :(
Anyway, thanks in advance for those who will be able to bring there advices or help on this :
Here is the error I get after I submitted /recettes/add (I added some form elements to add an ingredient and a quantity to my Recette) :
Warning (2): array_merge() [function.array-merge]: Argument #2 is not an array [CORE\Cake\Model\Model.php, line 2250]
Here is the array I'm passing to the saveAll() method in the controller (output from debug()):
array(
'Recette' => array(
'auteur' => '7',
'nom' => 'Nouvelle Recette',
'cout' => 'Pas cher',
'niveau' => '5',
'tps_realisation' => '30 min',
'nb_personnes' => '6',
'description' => 'Description data',
'remarque' => ''
),
'AssociatedIngredient' => array(
'id_ingredient' => '6',
'quantite' => '70 cl',
'id_recette' => '7654'
)
)
Here is my controller code :
<?php
App::uses('AppController', 'Controller');
/**
* Recettes Controller
*
* #property Recette $Recette
*/
class RecettesController extends AppController {
/**
* index method
*
* #return void
*/
public function index() {
$this->Recette->recursive = 0;
$this->set('recettes', $this->paginate());
}
/**
* view method
*
* #param string $id
* #return void
*/
public function view($id = null) {
$this->Recette->id = $id;
if (!$this->Recette->exists()) {
throw new NotFoundException(__('Invalid recette'));
}
$this->set('recette', $this->Recette->read(null, $id));
}
/**
* add method
*
* #return void
*/
public function add() {
if ($this->request->is('post')) {
debug($this->request->data);
$this->Recette->create();
if ($this->Recette->saveAll($this->request->data)) {
$this->Session->setFlash(__('The recette has been saved'));
//$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('The recette could not be saved. Please, try again.'));
}
}
$this->loadModel('Ingredient');
$liste_ingr = $this->Ingredient->find('all');
$this->set('liste_ingr', $liste_ingr);
}
The IngredientRecette model
<?php
App::uses('AppModel', 'Model');
/**
* Recette Model
*
* #property IngredientRecette $ingredient
*/
class Recette extends AppModel {
/**
* Use database config
*
* #var string
*/
public $useDbConfig = 'default';
/**
* Use table
*
* #var mixed False or table name
*/
public $useTable = 'recette';
/**
* Primary key field
*
* #var string
*/
public $primaryKey = 'id_recette';
/**
* Display field
*
* #var string
*/
public $displayField = 'nom';
public $recursive = 2;
/**
* Validation rules
*
* #var array
*/
public $validate = array(
'id_recette' => array(
'numeric' => array(
'rule' => array('numeric'),
),
),
'auteur' => array(
'numeric' => array(
'rule' => array('numeric'),
),
),
'nom' => array(
'notempty' => array(
'rule' => array('notempty'),
),
),
'niveau' => array(
'numeric' => array(
'rule' => array('numeric'),
),
),
'nb_personnes' => array(
'numeric' => array(
'rule' => array('numeric'),
),
),
);
//The Associations below have been created with all possible keys, those that are not needed can be removed
/**
* hasMany associations
*
* #var array
*/
public $hasMany = array(
'AssociatedIngredient' => array(
'className' => 'IngredientRecette',
'foreignKey' => 'id_recette',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
)
);
}
And IngredientRecette model (the join model)
<?php
App::uses('AppModel', 'Model');
/**
* IngredientRecette Model
*
* #property ingredient $ingredient
* #property recette $recette
*/
class IngredientRecette extends AppModel {
/**
* Use database config
*
* #var string
*/
public $useDbConfig = 'default';
/**
* Use table
*
* #var mixed False or table name
*/
public $useTable = 'ingredient_recette';
/**
* Primary key field
*
* #var string
*/
public $primaryKey = 'id_ingredient_recette';
public $recursive = 2;
//The Associations below have been created with all possible keys, those that are not needed can be removed
/**
* belongsTo associations
*
* #var array
*/
public $belongsTo = array(
'IngredientLiaison' => array(
'className' => 'Ingredient',
'foreignKey' => 'id_ingredient',
'conditions' => '',
'fields' => '',
'order' => ''
),
'RecetteLiaison' => array(
'className' => 'Recette',
'foreignKey' => 'id_recette',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
}
So nothing is saved in the join model and I have this warning...
Any help appreciated and of course please let me know if anything is unclear !
thanks a lot !!
I think your data is not formatted correctly. How about trying this instead (notice the extra array I put inside Associated Ingredient). Since recipe hasMany associated ingredient, the data for Associated Ingredient should be a series of arrays.
array(
'Recette' => array(
'auteur' => '7',
'nom' => 'Nouvelle Recette',
'cout' => 'Pas cher',
'niveau' => '5',
'tps_realisation' => '30 min',
'nb_personnes' => '6',
'description' => 'Description data',
'remarque' => ''
),
'AssociatedIngredient' => array(
array(
'id_ingredient' => '6',
'quantite' => '70 cl',
'id_recette' => '7654'
),
array( /*ingredient 2 data*/ ),
)
);

CakePHP: Keep the same checkboxes checked after submit

How can I keep the same checkboxes checked after submit? All the other input fields on the form automatically keeps the values. I thought this would also go for checkboxes, but nope.
echo $this->Form->input('type_id', array(
'multiple' => 'checkbox',
'options' => array(
'1' => 'Til salgs',
'2' => 'Ønskes kjøpt',
'3' => 'Gis bort'
),
'div' => false,
'label' => false
));
I believe this can be done in the controller, but how?
Edit:
Since I posted this question I've changed to CakeDcs Search plugin, because I've gotten this to work with that before. Still... I can't get it to work this time.
Adding model and controller code:
AppController
public $components = array('DebugKit.Toolbar',
'Session',
'Auth' => array(
'loginAction' => '/',
'loginRedirect' => '/login',
'logoutRedirect' => '/',
'authError' => 'Du må logge inn for å vise denne siden.',
'authorize' => array('Controller'),
),
'Search.Prg'
);
public $presetVars = true; //Same as in model filterArgs(). For Search-plugin.
AdsController
public function view() {
$this->set('title_for_layout', 'Localtrade Norway');
$this->set('show_searchbar', true); //Shows searchbar div in view
$this->log($this->request->data, 'debug');
//Setting users home commune as default filter when the form is not submitted.
$default_filter = array(
'Ad.commune_id' => $this->Auth->user('User.commune_id')
);
$this->Prg->commonProcess(); //Search-plugin
$this->paginate = array(
'conditions' => array_merge($default_filter, $this->Ad->parseCriteria($this->passedArgs)), //If Ad.commune_id is empty in second array, then the first will be used.
'fields' => $this->Ad->setFields(),
'limit' => 3
);
$this->set('res', $this->paginate());
}
Model
public $actsAs = array('Search.Searchable');
public $filterArgs = array(
'search_field' => array('type' => 'query', 'method' => 'filterSearchField'),
'commune_id' => array('type' => 'value'),
'type_id' => array('type' => 'int')
);
public function filterSearchField($data) {
if (empty($data['search_field'])) {
return array();
}
$str_filter = '%' . $data['search_field'] . '%';
return array(
'OR' => array(
$this->alias . '.title LIKE' => $str_filter,
$this->alias . '.description LIKE' => $str_filter,
)
);
}
/**
* Sets the fields which will be returned by the search.
*
* #access public
* #return array Database table fields
* #author Morten Flydahl
*
*/
public function setFields() {
return array(
'Ad.id',
'Ad.title',
'Ad.description',
'Ad.price',
'Ad.modified',
'User.id',
'User.first_name',
'User.middle_name',
'User.last_name',
'User.link',
'User.picture_url',
'Commune.name',
'Type.id',
'Type.name'
);
}
You have to set manually the selected option of the input, as an array with "keys = values = intval(checkbox id)"
I cannot explain why this format, but this is the only way I get it to work.
Here is my code:
echo $this->Form->create('User');
// Read the submitted value
$selected = $this->Form->value('User.Albums');
// Formats the value
if (empty($selected)) {
$selected = array(); // avoid mess
} else {
$selected = array_map('intval', $selected);
$selected = array_combine ($selected, $selected);
}
// Renders the checkboxes
echo $this->Form->input('Albums',array(
'type' => 'select',
'multiple' => 'checkbox',
'options' => $albums, // array ( (int)id => string(label), ... )
'selected' => $selected, // array ( (int)id => (int)id, ... )
));
Hope this helps.
++

CakePHP 2.1 - testing a simple admin_add() controller action

New to unit testing... testing an articles controller and I am getting a fail on the $this->assertNotEmpty();
Shouldn't this be displaying an array full of validation errors? Instead I am getting an empty array.
It seems my validation rules are not being picked up... as further inspection show that Article::save() is returning true on data that should fail....
/**
* Admin Add
* #see controllers/MastersController::_admin_add()
* #return void
*/
public function admin_add(){
//parent::_admin_add();
if(!empty($this->request->data){
$this->Article->save($this->request->data);
}
}
/**
* Test Admin Add
*
* #return void
*/
public function testAdminAdd() {
#define sample passing data
$sampleDataPass = array(
'Article'=>array(
'title'=>'Test Article Add Will Pass',
'body'=>'Test Article Add Body',
'status_id'=>1,
'category_id'=>1,
)
);
#test action
$this->testAction('admin/articles/add', array('data'=>$sampleDataPass));
$this->assertEmpty($this->Articles->Article->validationErrors); #####PASSES#####
#define sample failing data
$sampleDataFail = array(
'Article'=>array(
'title'=>'Test Article Add Will Fail',
)
);
$this->testAction('admin/articles/add', array('data'=>$sampleDataFail));
$this->assertNotEmpty($this->Articles->Article->validationErrors); #####FAILS#####
}
class Article extends AppModel {
/*
* Name
*/
public $name = 'Article';
/*
* Validation Rules
*/
public $validate = array(
'title' => array(
'notempty' => array(
'rule' => array('notempty'),
'message' => 'You must supply an article title in order to save.',
),
),
'body' => array(
'notempty' => array(
'rule' => array('notempty'),
'message' => 'You must supply an article title in order to save.',
),
),
'status_id' => array(
'numeric' => array(
'rule' => array('numeric'),
'message' => 'You must choose a status.',
'allowEmpty' => false,
),
),
'category_id' => array(
'numeric' => array(
'rule' => array('numeric'),
'message' => 'You must choose a category.',
'allowEmpty' => false,
),
)
);
}
CakePHP will ignore validation rules if the field is not present in the data.
By setting the option 'required' to true the validation rule will always be checked.
For example:
'title' => array(
'notempty' => array(
'rule' => array('notempty'),
'message' => 'You must supply an article title in order to save.',
'required' => true
),
),
Documention on validation in CakePHP can be found here: http://book.cakephp.org/2.0/en/models/data-validation.html#one-rule-per-field

Resources