I'm having problem in editing associated (BelongsTo) field...
Class ModelA extends AppModel {
public $belongsTo = array ('ModelB');
public $actsAs = array('Containable');
public function getModelA($id){
$modelA = $this->find('all', array('contain' => array('ModelB'),'conditions' => array('ModelA.id' => $id)));
if(count($modelA)>0){
$modelA = $modelA[0];
}
return $modelA;
}
}
class ModelAsController extends AppController {
public function edit($id = null) {
$modelA = $this->ModelA->getModelA($id);
$this->set('modelA', $modelA);
}
}
Data in the $modelA is in the format:
array(
'ModelA' => array(
...
),
'ModelB' => array(
...
)
)
So far, everything is fine...
But, I'm stuck with the edit.ctp. I need input for editing of the field1 from the ModelB, but I'm getting empty input box. Line of the code goes like this:
echo $this->Form->input('ModelB.field1');
When I try to debug:
Debugger::log($modelA['ModelB']['field1']);
then I have correct value.
What would be the way to have input box filled with field1 of the ModelB?
for a FormHelper to pickup the model data you have to set
$this->request->data = $modelA;
in the Controller, otherwise the helper has no idea where you've hidden the data :)
(tip: usually you can debug the helper code in such cases)
Related
I'm updating cakephp2 to cakephp3.
For model side, I can not get paginate parameters using func_get_arg() function in Model.
How can I get it?
I want to keep using func_get_arg() because other Model classes have also same logic.
class TopController extends AppController {
public function showTop() {
$query = array(
'limit' => 5,
'extra' => array(
'data' => 'test',
),
);
$topRawTable = TableRegistry::get('TopRawSql');
$pages = $this->Paginator->paginate($topRawTable->paginate($query)); //Error occur
}
}
class TopRawSqlTable extends Table {
public $useTable = false;
function paginate()
{
$extra = func_get_arg(6); // null
$limit = func_get_arg(3); // null
$page = func_get_arg(4); // null
// Execute custom query
}
}
I knew that CakePHP2 supports override paginate() and paginateCount() functions. But CakePHP3 DOESN'T support override paginate() and paginateCount(). That's why I couldn't get parameters by func_get_arg().
So, I switched to use CakePHP2 paginator feature.
I have two tables:
Property (..., postcode_id, ...)
Postcode (id, postcode, latitude, longitude)
The relationship:
class Property extends AppModel {
public $belongsTo = array(
'Postcode' => array(
'className' => 'Postcode'
)
);
class Postcode extends AppModel {
public $hasMany = array(
'Property' => array(
'className' => 'Property'
)
);
What I'd like to happen:
Enter a postcode in the Property add form.
Check entered postcode is in the Postcode table.
If not, so some logic (possibly add it, just return validation error for now).
If so, then record the Postcode.id in Properties.postcode_id (the foreign key).
I can't figure out the best way to do this with cake. A custom Property validation function that does the check and adds it to data to be added? Or in beforeValidation? Or does Cake deal with this?
Thanks!
EDIT
Based on the answer, I think this is the best solution...
I think this is the better way to do it, as it validates too. Thanks Anubhav for the putting me on the right track!
As per Anubhav's answer, in the Postcode model:
public function checkPostcode($postcode = null){
$record = $this->find('first', array(
'conditions'=>array('postcode' => $postcode),
'fields' => array('id')
));
if (!empty($record)){
return $record['Postcode']['id'];
} else {
return false;
// Logic for finding and adding new postcode
}
}
But and in the Property model:
public $validate = array(
'postcode' => array(
'exists' => array(
'rule' => 'postcodeExists',
'message' => 'Postcode does not exist'
)
)
);
public function postcodeExists($check) {
$id = $this->Postcode->checkPostcode($check);
if ($id) {
$this->data['Property']['postcode_id'] = $id;
return true;
}
return false;
}
public function beforeSave($options = array()) {
unset($this->data['Property']['postcode']);
}
By modifying the values to save here, the controller is kept skinny, and, rather than making sure postcode is real then finding the id, both are done at once, so only one query is needed.
Follow the steps:
Step 1: Create one action in Postcode.php
function checkPostcode($postcodeValue = null){
$returnData = $this->find('first',
array('conditions'=>array('postcode'=>$postcodeValue),
'fields'=>array('id'))
);
if(!empty($returnData)){
return $returnData['Postcode']['id'];
}else{
$saveData['Postcode']['postcode'] = $postcodeValue;
$this->save($saveData);
return $this->getLastInsertID();
}
}
The above function will check postcode from postcode model and insert postcode if not exist and returns the id of postcode table for corresponding row.
Step 2: Call this function controller action as
function someFuntion(){
$postcode_id = $this->Postcode->checkPostcode($userInputPostcodeHere);
// Write code to save property here
}
Hope this will give you some idea about the logic...
I want to do a simple cakephp association program but it's not working.
I have two database tables: users and sec_datas. When i run this program it just shows the result of first row of users table, not the result of both tables which have same sec_id value.
Controller code:
<?php
class UsersController extends AppController
{
public function index()
{
$this->autoRender = FALSE;
$this->loadModel('User');
$storeDivisions = $this->User->find();
echo "<pre>";
print_r($storeDivisions);
echo "</pre>";
}
}
Model code:
<?php
class User extends AppModel {
public $useTable='users';
public $hasOne = array(
'Sec_data' => array(
'ClassName' => 'Sec_data',
'Conditions' => array('User.sec_id=Sec_data.sec_id'),
'Dependent' => false
)
);
}
?>
If the primary key of User model is id then cake try to associate the foreign key in Sec_data with that key regardless the conditions you set.
First of all you should do domething like
public $hasOne = array(
'Sec_data' => array(
'ClassName' => 'Sec_data',
'ForeignKey' => 'sec_id',
'Dependent' => false
)
);
But it would work only if sec_id is related to User.id
if you want Sec_data.sec_id related to User.sec_id (and User.sec_id is different from User.id) then you have to join the tables manually
edit: see comments
There are a couple of things going wrong here. You should read up on CakePHP's conventions.
Your associated model class should be called SecData, then it will automatically map to the sec_datas table.
No need to use loadModel.
No need to specify conditions. Set the correct recursive level instead.
For hasOne, only one table has a foreign key. By default, the other Model has it ("User hasOne SecData" -> SecData has foreign key). So in your example, you should remove the column sec_id from User, and add a user_id column to your SecData Model.
See also: CakePHP Book on associations
Updated Controller code:
<?php
class UsersController extends AppController {
public $uses = array('User'); // Do this instead of loadModel
public function index() {
$this->autoRender = FALSE;
$this->User->recursive = 1; // Make User load associated records
$storeDivisions = $this->User->find('all');
pr($storeDivisions); // pr() is a Cake shorthand for print_r wrapped in <pre>
}
}
For your model:
<?php
class User extends AppModel {
// Unecessary. Convention is to use lowercase classname + 's', which
// already gives us 'users'
// public $useTable='users';
public $hasOne = array(
'SecData' => array( // Model class Sec_Data must be renamed accordingly
'dependent' => false
)
);
}
?>
Study model association in cake php . Here is very good explanation http://www.phpsupercoder.com/model-association-cake-php
I have added translate behaviour to a model, the model comes here
App::uses('AppModel', 'Model');
class Category extends AppModel
{
public $hasMany = "Product";
public $validate = array(
'name' => array(
'rule' => 'notEmpty'
)
);
public $actsAs = array(
'Translate' => array(
'name','folder','show'
)
);
public $name = "Category";
public $translateModel = 'KeyTranslate';
}
And heres the controller for updating the model
public function admin_edit_translate($id,$locale)
{
$this->Category->locale = $locale;
$category = $this->Category->findById($id);
if ($this->request->is('post') || $this->request->is('put')) {
$this->Category->id = $id;
if ($this->Category->save($this->request->data)) {
$this->Session->setFlash('Category translate has been updated');
//$this->redirect(array('action' => 'edit',$id));
} else {
$this->Session->setFlash('Unable to update category');
}
}
if (!$this->request->data) {
$this->request->data = $category;
}
}
My Problem is that i have a name field in the categories database and when i update or create a new translation it gets updated with the translated value. How do i avoid that
You must use Model::locale value to set code language for save in database
This happens because the TranslateBehavior uses callbacks like beforeSave and afterSave to save translated content, so it needs to let the model's save operation continue and thus will contain the last translated content.
You could get around this by tricking the TranslateBehavior into thinking the model is saving something by calling the beforeSave and afterSave like this:
$Model = $this->Category;
$Model->create($this->request->data);
$Model->locale = $locale;
$beforeSave = $Model->Behaviors->Translate->beforeSave($Model, array(
array(
'callbacks' => true
)
));
if($beforeSave) {
$Model->id = $id;
$Model->Behaviors->Translate->afterSave($Model, true);
}
This way the translation will be saved and the main table will be left untouched. Might not be the best way to save translations though. Why do you need to leave the main table untouched?
Callback Behavior::beforeSave is before Model::beforeSave...
but, the simplest way to modify data in Model::beforeSave before Behavior::beforeSave before realy saving is:
$this->Behaviors->Behavior_Name->runtime[Model_Name]['beforeSave'][Field_Name] = '...';
I am in development of a personal project.
I have two models 'Show' and 'Episode'. I have one controller 'Ops'.
Show model:
class Show extends AppModel
{
var $name = 'Show';
var $hasMany = array(
'Episode' => array(
'className' => 'Episode',
'foreignKey' => 'show_id'
)
);
}
Episode model:
class Episode extends AppModel
{
var $name = 'Episode';
var $belongsTo = array(
'Show' => array(
'className' => 'Show',
'foreignKey' => 'show_id'
)
);
}
Ops controller:
class OpsController extends AppController
{
var $name = 'Ops';
var $uses = array('Show','Episode');
function index()
{
$episodes = $this->Episode->find('all',array(
'limit' => 10,
'order' => array('Episode.first_aired' => 'DESC'),
)
);
debug($this->Episode);
debug($episodes);
}
}
When running the Ops controller I get the 'Episode' records like I want but don't get the associated 'Show' record based on the 'show_id' in the 'belongsTo' configuration. It appears that it is not referencing the model at all as I can purposefully break the model class an the request still goes on.
After doing a lot of checking, researching, and testing, I was able to get it to work by adding the following into the Ops controller before the find() request:
$this->Episode = ClassRegistry::init('Episode');
$this->Episode->bindModel(
array('belongsTo' => array(
'Show' => array(
'className' => 'Show'
)
)
)
);
Now while this works I would still like to know why my models are not being called properly. Any help would be most appreciated. Thanks!
What happens if you query Show in the same way?
Are you certain the id fields are defined correctly?
On both tables you should have id(INTsize) and on episodes there should also be show_id(INTsize).
If it's set up according to Cake convention, you should be able to remove the 'foreignKey' => 'show_id' line and Cake will sort it out itself.`
It sounds like Cake isn't using your model files and instead automagically generating some based on the tables.
It sounds dumb, but check the folders and file names for spelling errors and that they are lowercase.
In your Show model, you have the Episode foreign key set to show_id, which should be episode_id. However, I don't think that is causing the problem.
You aren't changing any CakePHP naming conventions, from what I can tell, so just remove the arrays that define the association and leave as string, e.g.
class Show extends AppModel
{
var $name = 'Show';
var $hasMany = array(
'Episode'
);
}
class Episode extends AppModel
{
var $name = 'Episode';
var $belongsTo = array(
'Show'
);
}
This may not work, but I have bumped into similar issues before and this resolved it. Good luck.