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
Related
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] = '...';
Every time a user views a submission, I track the count and tie it to a session:
SubmissionsController:
// Count view
if ($this->Session->check('viewed_submission_' . $submissionId) !== true) {
$clientIp = ip2long($this->request->clientIp());
$this->SubmissionsViews->countView($submissionId, $clientIp);
$this->Session->write('viewed_submission_' . $submissionId, true);
}
I'm keeping track of them in a SubmissionsViews table.
SubmissionsViews Model:
class SubmissionsViews extends AppModel {
var $name = 'SubmissionsViews';
var $belongsTo = array(
'Submission' => array(
'className' => 'Submission'
)
);
public function countView($submissionId, $clientIp) {
$this->set('submission_id', $submissionId);
$this->set('user_ip', $clientIp);
$this->save($this->data);
}
}
My SubmissionsView table submissions_views has the following fields:
id
submission_id
user_ip
created
I'm trying to set up counterCache to keep track of additions to that table, but not sure how to set it up. I'm currently adding the counterCache in my $belongsTo within my Submission model:
class Submission extends AppModel {
var $belongsTo = array(
'User' => array(
'className' => 'User'
),
'SubmissionsViews' => array(
'counterCache' => true
)
);
But it's telling me it can't find Submission.submissions_views_id. In the documentation, all it said was that I needed to add a field to my submissions table called submissions_views_count, so I'm confused how to get this working?
Model names should be singlular, so SubmissionViews should be SubmissionView.
Your association is incorrect. Submission hasMany SubmissionView, not belongsTo.
counterCache needs to be specified in the SubmissionView model inside the belongsTo config for Submission, not in the Submission model file. Please read the manual more carefully.
I have two models: Event and Match. An event can have many matches. My Match model has $order = array('match_order' => 'asc'). The match_order column in my matches database table is just a simple INT column to order matches.
I have a controller method that updates the order of matches. However, because I fetch matches through the event, they don't seem to come in ascending by match_order, but by the id column. Here is an example call:
<?php
class EventsController extends AppController {
public function view($id) {
$event = $this->Event->find('first', array(
'conditions' => array(
'Event.id' => $id
),
'contain' => array('Match')
));
}
}
All the related matches come back as expected, but just not in the right order. I've cleared my model caches, is there anything else I need to do to get the matches order by the value in the Match model?
Solved it. Forgot I could specify order in my model associations, so I've added the following to my Event model:
<?php
class Event extends AppModel {
public $hasMany = array(
'Match' => array(
'order' => array(
'match_order' => 'asc'
)
)
);
}
I'm trying to use the FileUpload plugin (https://github.com/webtechnick/CakePHP-FileUpload-Plugin) in my CakePHP (1.3) app.
I have two models: PendingContract and PendingContractFile. A PendingContract can have many PendingContractFile records. When saving a new PendingContract, I'd also like to save the uploaded PendingContractFile; however, my save method fails because PendingContract does not yet have an ID, and that is used as the foreign key in my PendingContractFile.
For clarity, here are my models:
<?php
class PendingContract extends AppModel {
var $name = 'PendingContract';
var $belongsTo = array(
'Supplier'
);
var $hasMany = array(
'PendingContractFile'
);
}
class PendingContractFile extends AppModel {
var $name = 'PendingContractFile';
var $belongsTo = array(
'PendingContract' => array(
'className' => 'PendingContract',
'foreignKey' => 'pending_contract_id'
),
'Author' => array(
'className' => 'User',
'foreignKey' => 'author_id'
)
);
}
And here is my controller method where I'm saving my PendingContract:
<?php
class PendingContractsController extends AppController {
function add() {
if (!empty($this->data)) {
if ($this->FileUpload->success) {
$this->Session->setFlash('Pending contract successfully created.');
$this->redirect(array('action' => 'index'));
}
else {
$this->Session->setFlash($this->FileUpload->showErrors());
}
}
}
}
Currently the error I'm getting is:
1452: Cannot add or update a child row: a foreign key constraint fails (pending_contract_files, CONSTRAINT pending_contract_files_ibfk_1 FOREIGN KEY (pending_contract_id) REFERENCES pending_contracts (id) ON DELETE CASCADE ON UPDATE CASCADE)
How can I use the FileUpload plugin so that it attaches the uploaded file with my new PendingContract record?
I took a look at the plugin, at it doesn't appear that it will save posted data along with uploaded files. It purposefully separates upload file data from any other input in the form and performs a save for each file.
Personally I would try other plugins such as https://github.com/josegonzalez/upload which do not rely in any controller-level code.
public function beforeSave($options = array()) {
if (!isset($this->data[$this->alias][$this->primaryKey])) {
$this->data[$this->alias][$this->primaryKey] = String::uuid();
}
return parent::beforeSave($options);
}
This will generate a new UUID for the record before save. You should probably only do this if the key is not already set.
i had a similar problem, what i did is unset the validation when adding a new PendingContractFile in your case. So before the saveAll method try adding:
unset($this->PendingContract->PendingContractFile->validate['pending_contract_id']);
so it wont check for the foreign_key.
hope it helps.
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.