cakephp HABTM database upload cant find issue - cakephp

I am new to cakePHP and fairly new to PHP as well, I have gone through some Youtube videos to help me make a basic site set up with CRUD pages for everything. I am now trying to set up the user signup page to add a bunch of stuff to a HABTM table but cant figure out what is going wrong.
$this->User->create();
if ($this->User->save($this->data)) {
$lvl = $this->data['User']['level'];
$charids = $this->Kanji->find('list',array('conditions'=>array('grade' <= $lvl,'grade' >= 0)));
foreach ($charids as $charid){
$characterList = array('kanji_id'=>$charid,'user_id'=>$this->User->id, 'level'=>2);
$this->kanjisUsers->save($characterList);
A bit of clarification, The site is for a school project, I want it to help with learning Japanese and the idea is that you can put a string of Japanese text and it will simplify it to your level so when you sign up you tell it what your level of Japanese is (an int between 1 and 9) then it will go through the character list (kanjis table) and find all with a number equal to or less than your level (called 'grade' in the kanjis table) then I want it to add all these to the kanjis_users table with the int 2 to indicate it is known (then I will do it again with one level up characters and save them with int 1 for 'learning')
I had help with the code and am not sure how it all works, I have been changing lots of things and cant figure out what is wrong, any suggestions?
note: I also saw that the model was called kanjis_user.php and KanjisUser, I read the model should not be plural but when I tried to change it everything crashed, could this be a part of the problem?
I have also tried changing => with -> and vice versa and also created $characterList to remove it from the save function, don't know if these affected anything as it never worked...
Edit in response to comment by api55:
Here is the model relation For kanjis_users:
class KanjisUser extends AppModel {
var $name = 'KanjisUser';
//Validation stuff here
var $belongsTo = array(
'Kanji' => array(
'className' => 'Kanji',
'foreignKey' => 'kanji_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
Here is the kanji mode:
var $hasAndBelongsToMany = array(
'User' => array(
'className' => 'User',
'joinTable' => 'kanjis_users',
'foreignKey' => 'kanji_id',
'associationForeignKey' => 'user_id',
'unique' => true,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
)
);
Here is the user model:
var $hasAndBelongsToMany = array(
'Kanji' => array(
'className' => 'Kanji',
'joinTable' => 'kanjis_users',
'foreignKey' => 'user_id',
'associationForeignKey' => 'kanji_id',
'unique' => true,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
),
This is just the standard generated model (minus validation).
For clarification on the error, there is no error, it just doesn't work, It saves the user, but dose not add anything to the kanjis_users table. Here is the whole register controller as it is now:
function register() {
if (!empty($this->data)) {
$this->User->create();
if ($this->User->save($this->data)) {
$lvl = $this->data['User']['level'];
//$test = $this->Kanji->find ('list',array('Kanji.grade <='<=$lvl, 'AND'=>array('Kanji.grade >=' >= 1)));
$charids = $this->Kanji->find('list',array('conditions'=>array('grade <=' => $lvl,'grade >=' >= 1)));
//print_r($charids);
//exit();
//$this->kanjisUsers->save(array('kanji_id'=>$charids,'user_id'=>$this->User->id));
//$this->kanjisUsers->saveALL(array('kanji_id'=>$charids,'user_id'=>$this->User->id));
foreach ($charids as $charid){
//echo("<p>Charid: ".$charid." is: </p>");
//var_dump($charid);
$this->kanjisUsers->save(array('kanji_id'=>$charid,'user_id'=>$this->User->id),'level'=> 2);
}
//exit();
$this->Session->setFlash(__('The user has been saved', true));
$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('The user could not be saved. Please, try again.', true));
}
}
}
Both seem to generate a similar array, but however I change the code sometimes it will crash, sometimes it will save the user, but it will never do anything to the kanjis_users table.
What I want:(simplified for clarity:
User table has: name, username, pw, userID etc...
kanjis table has: a Chinese character per row with ID, English, grade(int from 0 to 9) etc...
kanjis_users has: ID, user_id, kanji_id, created(date), modified(date), level(int).
I want a user to put a level when they sign up and then when the user gets created it will populate the kanjis_users table with all the rows in the Kanji table that have a 'grade' between 1 and the level the user put in the sign up form (called 'level').
So what I am trying to do above is after the user is saved (this user create() at the top), I then test if the save was a success and then get the level the user put in the form, and try to get all the characters from the rows from the kanjis table where the level is equal to or less than that (note I don't want to get ones with 0 as they are the hardest ones...) and add them all to the kanjis_users table. (this is only for sign up, when using the system the user can add and remove characters as they wish)
I noticed with the print_r that it was getting all 12000~ rows from the kanjis table, so I think the filter was not working...
I hope this makes sense, please let me know if I need to put any further info.

Create an input box in the register view:
$this->Form->input('Kanji', array('type' => 'hidden'));
Then try this:
function register() {
if (!empty($this->data)) {
$this->User->create();
$lvl = $this->data['User']['level'];
$charids = $this->Kanji->find('list',array('conditions'=>array('grade <=' => $lvl,'grade >=' => 1)));
$i = 0;
foreach ($charids as $charid){
$this->data['Kanji']['Kanji'][$i] = $charid;
$i++;
}
if ($this->User->save($this->data)) {
$this->Session->setFlash(__('The user has been saved', true));
$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('The user could not be saved. Please, try again.', true));
}
}
}
How it works
You save current level to $lvl ($this->data['User']['level'];)
You find all Kanjis maching your criteria ($charids = $this->Kanji->find('list',array('conditions'=>array('grade <=' => $lvl,'grade >=' => 1)));)
You define a new variable equal 0 ($i = 0;)
You make a loop going through your data array, storing every Kanjis you found and assigning it to $this->data['Kanji']['Kanji'] as it would be default data if you have selected it in a form.
You save the data as it has been filled by a form.

Ok, I see wrong 2 things :D
1) You get 12000+ rows from the find beacause your find condition is wrong :S and the one commented that i gave you has typos
the find MUST BE like this:
$test = $this->Kanji->find ('list',array(
'conditions'=> array(
'Kanji.grade <=' => $lvl,
'AND' => array(
'Kanji.grade >=' => 1)
)
)
);
2) the data in your save is wrong:
The cookbook explains that the data passed needs to be like this :
Array
(
[ModelName] => Array
(
[fieldname1] => 'value'
[fieldname2] => 'value'
)
)
So your save part should look something like this:
$this->KanjiUser->create();
$data = array(
'KanjiUser' => array(
'KanjiUser.kanji_id'=>$charid,
'KanjiUser.user_id'=>$this->User->id,
'KanjiUser.level'=>2
)
);
if ($this->KanjiUser->save($data))
echo 'Done ;)';
else
echo 'error';
all this inside the foreach or you can do the saveAll approach
$data = array('KanjiUsers'=> array());
foreach ($charids as $charid){
$data ['KanjiUsers'][] = array(
'KanjiUser' => array(
'KanjiUser.kanji_id'=>$charid,
'KanjiUser.user_id'=>$this->User->id,
'KanjiUser.level'=>2
)
);
}
$this->KanjiUser->create();
if ($this->KanjiUser->saveAll($data['KanjiUser'))
echo 'Done ;)';
else
echo 'error';

Related

Duplicate model along with it's associations

I came across this post that gave an answer on how to do this but it's not quite working for me.
I have a model called SitePage which has many SitePageGroup which in turn has many SitePageContent
// SitePage Model
public $hasMany = array(
'SitePageGroup' => array(
'className' => 'FoCMS.SitePageGroup',
'foreignKey' => 'site_page_id',
'dependent' => FALSE,
),
);
// SitePageGroup Model
public $belongsTo = array(
'SitePage' => array(
'className' => 'FoCMS.SitePage',
'foreignKey' => 'site_page_id',
),
);
public $hasMany = array(
'SitePageContent' => array(
'className' => 'FoCMS.SitePageContent',
'foreignKey' => 'site_page_group_id',
'dependent' => FALSE,
),
);
// SitePageContent Model
public $belongsTo = array(
'SitePageGroup' => array(
'className' => 'FoCMS.SitePageGroup',
'foreignKey' => 'site_page_group_id',
),
);
Using the answer in that linked question I am seeing the parent model, SitePage being duplicated, but the associated models are being removed from the original and associated with the new one.
$record = $this->SitePage->find('first', array('condition' => array('SitePage.id' => $id)));
unset($record['SitePage']['id'], $record['SitePageGroup']['id'], $record['SitePageGroup']['SitePageContent']['id'] /* further ids */);
$this->SitePage->create();
$record['SitePage']['name'] = $record['SitePage']['name'].'-copy';
$record['SitePage']['friendly_name'] = $record['SitePage']['friendly_name'].' Copy';
if($this->SitePage->saveAll($record)){
$this->Session->setFlash('The site page has been saved', 'fo_message');
$this->redirect(array('action' => 'index'));
}else{
$this->Session->setFlash('The site page could not be saved. Please, try again.', 'fo_message');
}
Update
Debugging the record that I'm trying to reset I see the following
array(
'SitePage' => array(
'name' => 'test',
'friendly_name' => 'Test',
'order' => '82',
'created' => '2015-09-03 19:16:40',
'modified' => '2015-09-03 19:20:27'
),
'SitePageGroup' => array(
(int) 0 => array(
'id' => '55e88087-a4dc-4c37-89dc-f9c172b40463',
'site_page_id' => '55e88078-16c8-46ce-bf02-fa5372b40463',
'name' => 'group-1',
'friendly_name' => 'Group 1',
'order' => '1',
'created' => '2015-09-03 19:16:55',
'modified' => '2015-09-03 19:16:55'
),
(int) 1 => array(
'id' => '55e8809e-d018-4ebe-a4cf-fbef72b40463',
'site_page_id' => '55e88078-16c8-46ce-bf02-fa5372b40463',
'name' => 'group-2',
'friendly_name' => 'Group 2',
'order' => '2',
'created' => '2015-09-03 19:17:18',
'modified' => '2015-09-03 19:17:18'
)
)
)
The way I am getting this result is by doing this
$sitePage = $this->SitePage->find('first', array(
'conditions' => array(
'SitePage.id' => $id,
),
));
unset($sitePage['SitePage']['id'], $sitePage['SitePageGroup']['id'], $sitePage['SitePageGroup']['SitePageContent']['id'], $sitePage['SitePageGroup']['site_page_id'], $sitePage['SitePageGroup']['SitePageContent']['site_page_group_id'] /* further ids */);
debug($sitePage);
die();
But also also as you can see in the debug output the 3rd level of associated models are not being included, each of the SitePageGroup should also have a SitePageContent
I think a simple loop over the array of SitePageGroup should reset the id's and set the foreign keys to null, but I guess I also need to somehow include the SitePageContent that belongs to the SitePageGroup so I can reset those as well.
You need to ensure that all primary and foreign keys are set to null before saving. You only appear to be resetting the primary keys of your models but Cake needs to know that the foreign keys need generating so that they reference the new records.
Before calling $record it might be worth using debug($record); to check that everything in that array has been set/reset appropriately to ensure the copy will work as expected.
Update
Based on the array contents you've posted in your updated question it appears that you are not removing all the primary and foreign keys from your save data. You need to make sure that these are removed from everything you are about to save including the has many associations.
If you look at your array you should be able to see that unset($sitePage['SitePageGroup']['id']) will not remove the primary IDs of your SitePageGroup data as what you are unsetting doesn't correspond to array paths in your $sitePage array.
You can use CakePHP's Hash utility to remove the primary keys from the array like this:-
$sitePage = Hash::remove($sitePage, 'SitePageGroup.{n}.id');
And similarly for the foreign keys:-
$sitePage = Hash::remove($sitePage, 'SitePageGroup.{n}.site_page_id');

An instance of a model cannot be created once relation has been added CakePHP

I have three models in question: Customer, Company and User. Customer and User both belong to Company and Company has many Customers as following:
Customer:
var $belongsTo = array(
'Company' => array(
'className' => 'Company',
'foreignKey' => 'company_id',
'dependent' => false,
),
);
Company:
var $hasMany = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'company_id',
'dependent' => false
),
'Customer'=>array(
'className' => 'Customer',
'foreignKey' => 'company_id',
'dependent' => false
)
);
User:
var $belongsTo = array(
'Company' => array(
'className' => 'Company',
'foreignKey' => 'company_id',
'dependent' => false,
),
);
I have a problem when creating/editing Customer objects. Here is how to create form looks like:
echo $this->Form->input('Customer.customer_nr');
echo $this->Form->input('Customer.name');
echo $this->Form->input('Customer.phone');
echo $this->Form->input('Customer.email');
echo $this->Form->input('Customer.address');
echo $this->Form->input('Customer.post_nr');
echo $this->Form->input('Customer.city');
echo $this->Form->input('Customer.company_id', array('value' => $current_user['company_id'], 'type'=>'hidden'));
What I do in the end of the form is I take company_id from a currently logged in user and insert it as a Customer.company_id. It used to work without any problems before the new relations have been introduced. But now as I try to create/edit Customer, I receive the following SQL error:
Error: SQLSTATE[23000]: Integrity constraint violation: 1052 Column 'name' in where clause is ambiguous
Any help is much much appreciated.
Here is the controller add function:
function add() {
if (!empty($this->data) ) {
$this->Customer->create();
if ($this->Customer->save($this->data)) {
$this->Session->setFlash(__('Customer was saved'), 'positive_notification');
$this->redirect(array('controller'=>'events', 'action' => 'dashboard'));
} else {
$this->Session->setFlash(__('Customer has been saved. Please, try again'), 'negative_notification');
}
}
}
The error is definately not being cause by redirect as it was fully tested.
the problem is somewhere else.
It's in fact related to a find() call.
Try to locate the exact code that trigger the error and post it in your question.
Probably you set some conditions like
'conditions' => array(
'name' => 'john'
)
but you better do something like
'conditions' => array(
'User.name' => 'john'
)
after you created the relationship between User and Company (it's just an example, maybe the two tabler involved are others) Cake started to join the two tables. So when you search for a particular name mysql doesn't know if you want user name or company name because you have name column in both tables.
If you look at the generated query (the one that gives you that error) you'll see the two tables joined. If you don't want that join you have to specify recursive => -1
'conditions' => array(
'name' => 'john'
),
'recursive' => -1

Joins tables in Cakephp

i have a two tables namely; histories and users. i need to display data like:
id | Username | Lastest created Post | First created Post
the data of id and username is from users table and the last created and first created post data is from histories. i need to view all the users, their lastest created post and their first created post. please help me to make controller and view thanks
Try below.
<?php
$users = $this->User->find('all',array
(
'conditions' => array
(
//conditions goes here
),
'fields' => array
(
'User.id',
'User.username',
'History.Lastest created Post',
'History.First created Post'
)
));
?>
Assume that relation between 'User' and 'History' table is One-to-One and there's a 'user_id' column in History table, you may need to specify relation between them in History model, for example:
var $hasOne = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
Then, you need to perform joins to do this. For example, somewhere in your User model, try something like this:
class User extends AppModel {
....
function getAllUsersHistory{
$allHistories = $this->find('all', array(
'joins' => array(
'table' => 'history',
'alias' => 'HistoryJoin'
'type' => 'INNER',
'conditions' => array(
// your conditions, for example: 'History.user_id' => 'User.id'
)
),
'fields' => array(
'User.id',
'User.username',
'History.lastest_created_post',
'History.first_created_post'
)
));
return $allHistories;
}
.....
}

cakePHP saving entries to join table of existing records

first a short description. I have to models: accounts and users and a join table of it accounts_users. the models have a habtm associasions on each model:
User Model:
'Account' => array(
'className' => 'Account',
'joinTable' => 'accounts_users',
'foreignKey' => 'user_id',
'associationForeignKey' => 'account_id',
'unique' => true,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
)
Account Model:
'User' => array(
'className' => 'User',
'joinTable' => 'accounts_users',
'foreignKey' => 'account_id',
'associationForeignKey' => 'user_id',
'unique' => true,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
)
Now im trying to save manually relations between this two just to the join table acccounts_users from allready existing entries, here my code
$account = base64_decode($this->params['pass']['0']);
$token = $this->params['pass']['1'];
if($user = $this->User->findByToken($token))
{
// ZUr test zwecken
# $this->User->query(" INSERT INTO accounts_users (account_id ,user_id) VALUES (222, 223); ");
$aId = $this->Account->findById($account);
$this->data['User'][0]['user_id'] = $user['User']['id'];
$this->data['Account'][0]['account_id'] = $aId['Account']['id'];
$this->User->bindModel(array(
'hasMany' => array('AccountsUser')
));
$this->User->saveAll($this->data, array('validate' => false));
print_r('gefunden'); die;
$this->Redirect->flashSuccess('Account Invitation successfull. Log In!', array('controller' => 'users', 'action' => 'login'));
}
else
{
print_r('nicht gefunden'); die;
// user nicht gefunden zum login umleiten
$this->Redirect->flashWarning('Account Invitation error. Please try again!', array('controller' => 'users', 'action' => 'login'));
}
the results are a new entry on the accounts_users table but the user_id is 0. I don't understand why is missing the user id because its passed corectly. even if i pass in the data array some ids manually its writting just the account_id without the user id.
UPDATE
I played a little bit with the models and saved the data to the accounts_users thru the account model see the updated code:
$this->data['User']['id'] = $user['User']['id'];
$this->data['Account']['id'] = 33; #$aId['Account']['id'];
$this->Account->AccountsUser->create();
$this->Account->saveAll($this->data, array('validate' => false));
so now the script inserts both ids, BUT, if there is an entry form another user with the same account id the the user gets overwritten. anything else works. Any idears of how to create a new entry with an axisting account id for new user?
This are the mysql queries i get:
UPDATE accounts SET id = 33 WHERE accounts.id = 33
SELECT AccountsUser.user_id FROM accounts_users AS AccountsUser WHERE AccountsUser.account_id = 33
DELETE AccountsUser FROM accounts_users AS AccountsUser WHERE AccountsUser.account_id = 33 AND
INSERT INTO accounts_users (account_id,user_id) VALUES (33,'32')
Any idears why? Thanks in advance
CakePHP 1.x treats HABTM join tables quite 'dumb'; it will remove all existing records and insert new records to replace them. This is a major PITA if your join-table also contains additional data. (It's possible to prevent this from happening by adding some code in your beforeSave() callbacks)
CakePHP 2.1 has an option keepExisting for HABTM relations. This option prevents CakePHP from deleting the records in the JOIN table. If this is a new project I would really advise to use CakePHP 2.x as a lot has improved since CakePHP 1.x.
http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#hasmany-through-the-join-model
Some hints on saving data in the join table can be found here;
http://book.cakephp.org/2.0/en/models/saving-your-data.html#saving-hasmany-through-data
For CakePHP 1.3 (look below 'when HABTM becomes complicated)
http://book.cakephp.org/1.3/en/The-Manual/Developing-with-CakePHP/Models.html#saving-related-model-data-habtm

CakePHP 1.3 "Missing Controller" error - Controller Exists

I am having a problem with my CakePHP 1.3 app, and I'm not sure if it's a code issue or a DB issue.
I have a pretty simple function in one of my controllers, and whenever I add a query portion to that controller, I get the following (infuriating and completely unhelpful) error message:
Missing Controller
Error: InternalError.htmlController could not be found.
Error: Create the class InternalError.htmlController below in file:
app/controllers/internal_error.html_controller.php
Here is the Model ForecastZones
class ForecastZone extends AppModel {
var $name = 'ForecastZone';
var $displayField = 'name';
//The Associations below have been created with all possible keys, those that are not needed can be removed
var $belongsTo = array(
'State' => array(
'className' => 'State',
'foreignKey' => 'state_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
var $hasMany = array(
'ForecastZonePoly' => array(
'className' => 'ForecastZonePoly',
'foreignKey' => 'forecast_zone_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
)
);
}
Here is the controller function that inexplicably fails:
function poly($id = null) {
if (!$id) {
$this->Session->setFlash(__('Invalid forecast zone', true));
$this->redirect(array('action' => 'index'));
}
$this->layout = false;
$result = $this->ForecastZone->query("SELECT coords FROM forecast_zone_polies WHERE forecast_zone_id = $id;");
$this->set('forecastZone', $result);
}
CakePHP epic fails whenever I call this controller action. It hangs for a LONG time... and then instead of telling me something useful like "database query took too long" or "model association broken" or something like that... it just gives up and gives me this complete BS error message.
This is not a path issue, the paths are correct. If I remove the $result variable, everything works fine and I get the appropriate "forecastZone is not set" error message. The crux of this issue seems to be a query that takes forever, and then Cake not properly reporting the error message.
Please help clear this up for me. Very frustrating... not "Cake" at all by any stretch of the word.
EDIT: I wanted to add that I originally had been using
$this->ForecastZone->read(null,$id);
To grab the data, but the hanging & failing of the query kept happening so I switched to the raw query in hopes that might change something.
EDIT 2:
More things I tried:
Added this line to the top of my controller:
var $uses = array('ForecastZone','ForecastZonePolies');
And then tried to do things "the right way" that still fail. UGH!
$result = $this->ForecastZonePolies->find('all',array('conditions' => array('ForecastZonePolies.forecast_zone_id' => $id)));
$result = $this->ForecastZone->ForecastZonePolies->find('all',array('conditions' => array('ForecastZonePolies.forecast_zone_id' => $id)));
None of these work.
First I'd check app/tmp/logs/error.log and app/tmp/logs/debug.log. If this really is an internal error, you should get details there.
From the weird inflecting, I'd suggest you to put Router::parseExtensions('html') in your app/config/routes.php to make sure this isn't a redirect issue.

Resources