CakePhp save changes only in middle table - cakephp

Here is the problem what I have
I have 3 tables m:n
Users -> addresses_users -> Addresses
During adding new user I am selecting from dropdown X Addresses and after I click save I have to save new user data (new user in Users table), make mapping in addresses_user table and Addresses table shouldnt be changed, cos I have already those addresses in DB.
So, if i use code like this
input in ctp with
name="addresses[]"
In controller
if ($this->request->is('post')) {
$user = $this->Users->patchEntity($user, $this->request->
if ($this->Users->save($user)) {
$this->Flash->success(__('The user has been saved.'));
return $this->redirect(['controller' => 'users', 'action' => 'view/' . $user->id]);
} else {
$this->Flash->error(__('The user could not be saved. Please, try again.'));
}
}
Cakephph makes mapping automatically and want to save new user, mapping and information about addresses.
Is there way to make this automatically with cakephp that he saves new user and make mapping or I should use another input name (that cakephp doesnt make patch with addresses) and after save make foreach with all addresses_id and save values direct to addresses_users table?
Or there is better ideas?

Related

Add a user in Cakephp

There is a problem when I write an add() function for UsersController.
public function add() {
if ($this->request->is('post')) {
$this->request->data['User']['book_id'] = $this->Book('id');
if ($this->User->saveAssociated($this->request->data)){
$this->request->data['User']['book_id'] = "ff";
$this->Session->setFlash(__('Save User'));
return $this->redirect(array('action' => 'index'));
}
$this->Session->setFlash(__('User Error.'));
}
}
If I add a User I want to save the book ID in my User book_id.
Anyone know how to deal with this problem? Thanks!
When you're adding a new user, does he get to select a book from a drop down(in the form)? If so, $this->request->data['User']['book_id'] will already contain the Book id.
There's no need to explicitly write $this->request->data['User']['book_id'] = $this->Book('id'); like you have done.
The way I see it, the add.ctp view file should contain a select box containing a list of all books which the user can select. On selecting one of those and submitting the form, $this->request->data['User']['book_id'] will automatically store the book_id, without having to write anything in the controller.
Please specify the add.ctp form fields so that I can assist you better.
Thanks!

How do I make it so when a user deletes something it still exists, but is just hidden from everyone?

Currently when a user deletes an item, it gets properly deleted, but due to the structure of my database and wanting to preserve data I would rather simply hide it from everyone except the admin but have it appear deleted to the user.
Here is an example of how I am currently deleting items from the View:
<?php echo $this->Form->postLink(__('Delete'), array('action' => 'delete', $serial['Serial']['id']), null, __('Are you sure you want to delete # %s?', $serial['Serial']['id'])); ?>
And here is the delete function that is called in the Controller:
public function delete($id = null) {
$this->Serial->id = $id;
if (!$this->Serial->exists()) {
throw new NotFoundException(__('Invalid serial'));
}
$this->request->onlyAllow('post', 'delete');
if ($this->Serial->delete()) {
$this->Session->setFlash(__('The serial has been deleted.'));
} else {
$this->Session->setFlash(__('The serial could not be deleted. Please, try again.'));
}
return $this->redirect(array('action' => 'index'));
}
}
How do I make it so when a user deletes something it still exists, but is just hidden from everyone?
By not deleting, but just flagging it as deleted (using an extra deleted column or similar) and excluding such flagged records from your find operations (using for example the Model::beforeFind() callback to inject a condition like deleted = 0).
Just search for "soft delete", and you'll find ready made behaviors such as
CakeDC Utils - SoftDelete
dereuromark CakePHP Tools - SoftDelete
Use database flag like deleted as enum which can be 1 Or 0 , So If you click on delete , it updates as 1 , Like soft delete.
So in model , use condition to fetch only recs with deleted status as 0 .
Or in controller use find query with condition to fetch only recs with deleted status as 0 .

Weird problems while getting ID just after saving data

I have different models which are an extension of another one.
classes (SubModel1, SubModel2, ..,6) extends -> HighModel extends -> Appmodel
Everytime I save data in each submodel I call a function in my HighModel to save the id of the just saved SubModel. It works perfectly with five SubModels, but in one the id is always: ($shouldBeThisId - 1)in both add and edit functions.... o_O
Here's my add function:
public function add() {
if ($this->request->is('post')) {
$this->M18Tab->create();
if ($this->M18Tab->save($this->request->data)) {
$data = $this->M18Tab->findById($this->M18Tab->id); //<---problem's here
if($this->M18Tab->M18Model->saveVersion($data)){
$this->Session->setFlash(__('The m18 tab has been saved'));
$this->redirect(array('action' => 'view', $this->M18Tab->id));
} else {
$this->Session->setFlash(__('Error with versioning system'));
}
} else {
$this->Session->setFlash(__('The m18 tab could not be saved. Please, try again.'));
}
}
//view variables
}
ok. so after saving I recall the "just saved data" and pass it to $this->SubModel->HighModel->saveVersion($data). and the weird thing is that if I try to force the id to find:
$data = $this->M18Tab->findById(33);
it'll still find the id preceding the just inserted one :0.
what could this be caused by? the code is the same for all 6 models and in only one I have this issue..
This is more of a comment/suggestion than an answer, but anyway...
If you're adding a new record, after calling save, you can get it's ID via:
$this->MyModel->getInsertId();
See that method in the cookbook. I know the id should also be available at $this->MyModel->id, but since it's not working in this case, you might want to try:
$data = $this->M18Tab->findById($this->M18Tab->getInsertId());

How to add index to database with belongsTo association

I'm looking for the best practice for adding an id to a database table. As an example I have a Member (model) that hasMany Recipe. The Recipe model states that it belongsTo Member:
public $belongsTo = array(
'Member' => array(
'className' => 'Member',
'foreignKey' => 'member_id',
)
);
I hoped that this would be sufficient when saving new recipes to the database, but I see that cake cannot figure out the relationship on its own because I am getting an error ("General error: 1364 Field 'member_id' doesn't have a default value") when using add:
public function add() {
if ($this->request->is('post')) {
$this->Recipe->create();
if ($this->Recipe->save($this->request->data)) {
$this->Session->setFlash(__('The recipe has been saved'));
$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('The recipe could not be saved. Please, try again.'));
}
}
}
Now, in this case I can easily get around the problem by adding
$this->request->data['Recipe']['member_id'] = $this->Auth->user('id');
but in general the ids that I will want to add will not be stored with the Auth data. So I would like to know how to access the id when saving something that belongsTo something else.
edit: I think my question was probably not worded well enough. I will restate what I'm looking for:
Say User hasMany A, which hasMany B, which hasMany C. C belongsTo B, A, and User (table for C contains user_id, a_id, and b_id). A user logs in, and picks a certain A, and then a certain B. The user now wants to add a new C. What is the best way to get all the id values for the new C entry? Do I just set a bunch of session variables? Should they all be passed in the URL? Is there some better way?
You need to provide any information that you want to save in the database. This means you either use the Auth information like you already stated or you get the information via a form where you can select from all the members available.
Either way the information has to come from somewhere. It can not create itself out of nothing.
If the user is selecting or picking A, B or C, them, somehow you can store their data and deal with them.
Usually, the common way is using forms. If you are using Javascript, you can set hidden inputs or if you are using AJAX, just using them inside it to call the final Cake functions.
You have to see how the user picks them, and then, store that ID in some variable to play with it. POST, GET, SESSIONS, COOKIES...

Creating muliple users in a loop from array

How does one go about creating multiple new users, say in a loop, from an array in a different controller?
I have an issue where attempting to create multiple users in a form submit fails, but creating a single new user works as designed. It appears the issue may be when saving the new user and then bringing the new user_id back in the return statement. Although the new id comes back, subsequent users (2nd, 3rd, etc) all get the same id value, and it appears that the subsequent $this->save calls modify the first created user rather than create add'l ones. Any none of the new users appear in the database. (again, the problem only happens when more than one new users will be created.)
My one small clue is that if I var_dump($user) in my importPublicLDAPUser() function (user.php) just after the $user = $this->save(array('User' => array( ... ))); then for the first element I see both 'modified' and 'created', whereas I see only 'modified' for the rest. This leads me to believe there's a step missing, like the user needs to be saved or commit (??) before the next user can be be created.
I tried changing this to $user = $this->User->save(array('group_id' => 3, ... and adding a 'create' before: $this->User->create(); but these produce errors 'Call to a member function save() on a non-object' and 'Call to a member function create() on a non-object'.
My application manages documents. Each document can have many authors, so it has controllers for: docs, doc-types, users, groups, and authors.
When a new document is entered, the form allows selection of multiple users to create 'Author' records. In addition to the local users table, it also searches our LDAP server (both via auto-sugggest) and also allows input into a text field. So, Authors are selected from
the existing table of users
via the LDAP helper
free text entry.
This result is two arrays: $Authors (from local users tables), and $badAuthors (LDAP and text-input) which the app then tries to add to the local users table when the form is submitted.
The form works just fine if:
one or more authors are added from local users table;
a single author is added from LDAP (succeeds in creating a new entry in users table), and zero or more local users
a single author is added from text input (also succeeds), and zero or more local users
However if two or more non-local users are added ($badAuthors has more than one element) then the form fails. "fails" means that either the Author or User save routine failed, and so it skips the Document commit $this->Docu->commit(); and I spit out an error via ajaxResponse. Thus, the app works as designed, but only with one new User entry at a time, even though the form is designed to allow Authors/badAuthors to be >1.
What I don't understand is why when I loop through bad authors why it doesn't correctly add the users if $badAuthors has more than one element.
As the user enters each name (which is checked against the users table and LDAP via ajax helpers, etc) and then selected, an author_name_list array is built. And then:
foreach($this->params['form']['author_name_list'] as $author_name){
$user_id = $this->Docu->User->field('id',array('User.name' => $author_name));
if($user_id){
$Authors['Author'][]=array(
'docu_id'=>$this->Docu->id
,'user_id'=>$user_id
);
}else{
$badAuthors[] = array('name'=>$author_name);
}
}
So $badAuthors is now those found in LDAP or entered manually.
So now I try to create/save all badAuthors...
docu controller (docu_controller.php):
if(count($badAuthors)){
foreach($badAuthors as $key => $author){
$this->Memo->User->create(); // ** <-- this was missing!! **
if($ldap_author = $this->Docu->User->importPublicLDAPUser($author['name'])){
unset($badAuthors[$key]);
$Authors['Author'] []= array(
'docu_id'=>$this->Docu->id
,'user_id'=>$ldap_author['User']['id']
,'precedence' => $author['precedence']
);
} elseif ($new_author = $this->Docu->User->newReadonlyUser($author['name'])) {
unset($badAuthors[$key]);
$Authors['Author'] []= array(
'docu_id'=>$this->Docu->id
,'user_id'=>$new_author['User']['id']
,'precedence' => $author['precedence']
);
}
}
}
if(!count($badAuthors)){
$authors_saved = true;
foreach($Authors['Author'] as $author_arr){
$this->Docu->Author->create();
if(!$this->Docu->Author->save(array('Author' => $author_arr))){
$authors_saved = false;
break;
}
}
}
user model (user.php)
function afterSave($created) {
if (!$created) {
$parent = $this->parentNode();
$parent = $this->node($parent);
$node = $this->node();
$aro = $node[0];
$aro['Aro']['parent_id'] = $parent[0]['Aro']['id'];
$this->Aro->save($aro);
}
}
function importPublicLDAPUser($cn){
App::import('Vendor','adLDAP',array('file'=>'adLDAP.php'));
$oLDAP = new adLDAP(Configure::read('LDAP_options.email'));
$oLDAP->authenticate(NULL, NULL);
$filter = '(&(cn='.$oLDAP->ldap_escape($cn).'))';
$ldap_res = #$oLDAP->search($filter, array('cn', 'uid','profitcenter'),1);
if(isset($ldap_res['count']) && ($ldap_res['count'] > 0)){//found it
$user = $this->save(array('User' => array(
'group_id' => 3,
'name' => $ldap_res[0]['cn'][0],
'username' => $ldap_res[0]['uid'][0],
'grpnum' => pc2grpnum($ldap_res[0]['profitcenter'][0])
)));
if($user){
$user['User']['id'] = $this->id;
}
return ($user ? $user : false);
}else{
return false;
}
}
Any suggestions? Thanks!!
It turns out that in my docu_controller.php I was missing a create() call. It seems that without a create, an object can still be saved/created when the other controller does a commit(). So before adding the create(), prior to the commit, in later loop iterations I was still modifying the original object, not any new ones. By adding a create in the controller, the save in the method function acts on the new user for each loop iteration.
in controller:
if(count($badAuthors)){
foreach($badAuthors as $key => $author){
$this->Memo->User->create();
if($ldap_author = $this->Memo->User->importPublicLDAPUser($author['name'])){
in method:
function importPublicLDAPUser($cn){
....
$user = $this->save(array('User' => array(...

Resources