Drupal 7 webform submission to remote mysql database - database

When a user fills out a webform on my Drupal 7 site, on submitting, I need the submission data to be sent over to another database. I'm using hook_webform_submission_insert in a custom module, but I can only get the sid and nid to be inserted into the table. I need my webform fields to be sent also; like first_name, last_name, email, etc. But I get errors upon submitting.
<?php
function hook_webform_submission_insert($node, $submission) {
// Insert a record into a 3rd-party module table when a submission is added.
db_insert('mymodule_table')
->fields(array(
'nid' => $node->nid,
'sid' => $submission->sid,
'foo' => 'foo_data',
))
->execute();
}
?>
I've tried 'first_name' => 'first_name', but it doesn't work. What am I doing wrong?

To get all the submitted data you'll have to go about this a slightly different way. First add an additional submit handler to call a custom function in your module using form alter:
function MYMODULE_form_alter(&$form, &$form_state, $form_id) {
if($form_id == "YOUR_WEBFORM_ID") {
$form['#submit'][] = 'MYMODULE_additional_insert';
}
}
Then access the form data and process accordingly from your custom function.
function MYMODULE_additional_insert($form, &$form_state) {
$data = $form['submitted'];
// Insert a record into a 3rd-party module table when a submission is added.
db_insert('mymodule_table')
->fields(array(
'foo' => $data['FOO_FIELD']['#value'],
))
->execute();
}

Related

How can I create a dynamic function that pulls data from mysql database and builds a form in Symfony 4?

I want to build a Controller that will load data from the database and built a form from it, but depending on the slug.
public function form($slug, Request $request){
$EntityName = 'App\\Entity\\' . ucwords($slug);
$item= $this->getDoctrine()->getRepository($EntityName)->find($id);
$form = $this->createFormBuilder($item)
So this means, if the slug is for example products then I want to build a form from my database table `products with all the fields that are inside this table:
So in products I have for example the fields id, name, color. So the output I would need is
->add('id', TextType::class, array('attr' => array('class' => 'form-control')))
->add('name', TextType::class, array('attr' => array('class' => 'form-control')))
->add('color', TextType::class, array('attr' => array('class' => 'form-control')))
But if my slug is for example members and in my members table the fields are id, username, email then a form with this fields should be builded:
->add('id', TextType::class, array('attr' => array('class' => 'form-control')))
->add('username', TextType::class, array('attr' => array('class' => 'form-control')))
->add('email', TextType::class, array('attr' => array('class' => 'form-control')))
The problem for me is now how to create this fields that are depending on the slug. I was thinking to somehow get the fields from the object $item. This is the object $item:
object(App\Entity\Members)#4788 (6) {
["id":"App\Entity\Members":private]=>
int(9)
["username":"App\Entity\Members":private]=>
string(13) "123"
["plainPassword":"App\Entity\Members":private]=>
NULL
["password":"App\Entity\Members":private]=>
string(0) ""
["email":"App\Entity\Members":private]=>
string(7) "1#12.sw"
["isActive":"App\Entity\Members":private]=>
bool(true)
}
I was trying to work with get_object_vars but this is not working with private objects, so I guess this is not a good solution. I also tried to create an array ($array = (array) $item;) from the object to build the form stucture with foreach. But it does not seem to be the right way neither.
Do you have any experience with functions that are loading data dynamically from database tables and create a form? I am happy for every idea or advice.
You could try something like this
$cmf = $em->getMetadataFactory();
$class = $cmf->getMetadataFor($entityName);
foreach ($class->fieldMappings as $fieldMapping) {
echo $fieldMapping['fieldName'] . "\n";
}
This will give you all fields for your Entity based on Doctrine's Metadata model.
Check out ClassMetadata API documentation for more information and especially which information you'll get there.
E.g. you could use something like
isNullable(string $fieldName) to ensure some 'mandatory validation'
getIdentifierColumnNames() to exclude all identifier columns (note: this will return column names not entity fields, but you can easily identify these
getTypeOfField to get some hint whether a field is a string or a number (again to allow more specific form attributes)
etc... just check the documentation and try it
BUT generally be careful using the ClassMetadata as your use case is not really what it's meant for - but we use it for a similar case as well.

Zend Framework 2 InputFilter and form fields

I have a 'users' table with many fields like username, name, email, password, sex, birthday, avatar, etc. I have described all of them in the Users model of my ZF2 application using the InputFilter.
The problem is the following:
- I have some forms like main settings (with only the name, sex and birthday fields in) and I would like to edit only these three fields in the database via this form (and respectively to edit other fields like password and email in other forms in other pages of the site). However, it doesn't seem to work:
$request = $this->getRequest();
if ($request->isPost()) {
$users = new Users();
$form->setInputFilter($users->getInputFilter());
$form->setData($request->getPost());
if ($form->isValid()) {
$users->exchangeArray($form->getData());
$this->getUsersTable()->editUser($users, $userInfo->user_id);
$message = 'Профилът ви беше успешно променен!';
}
}
Here is the form that I want to update only the fields name, birthday and sex.
And here is the model:
public function editUser(Users $user, $userID) {
$data = array(
'name' => $user->name,
'birthday' => $user->birthday,
'sex' => $user->sex,
);
$this->tableGateway->update($data, array('user_id' => $userID));
}
$form->getMessages() returns an empty array and var_dump($form->isValid) returns boolean false. If I delete all other MySQL fields and filters in the InputFilter and I keep only the three fields (name, birthday and sex) in the model, it's all ok and the form validation works. But when I describe all other fields of the 'users' table in the model, it doesn't. Hope I explained it well. Any ideas?
You can use Validation Groups to only validate certain fields when you only want to save partial data:
http://framework.zend.com/manual/2.0/en/modules/zend.form.quick-start.html#validation-groups
for example:
$form->setValidationGroup('name', 'sex', 'birthday');
$form->setData($data);
if ($form->isValid()) {
// Contains only the "name", "sex", "birthday"
$data = $form->getData();
}

Preventing password override on user admin page

I'm working on some admin functions to my site where a user with admin privileges is able to open a form for a given user ID. From this form they can change user permissions, alter the user password or other means.
However, when the form is constructed from the model, the password field pulls the hashed password from the database and populates the password field. As a result, when the admin goes to save the form the hashed password is treated as a plaintext and therefore hashed again, overwriting the original password.
What I need is some way to allow admin users to change the form but only have passwords hashed and updated on the database in the event that it is changed.
My thoughts are to construct the form setting password to blank:
view/User/edit.ctp:
echo $this->Form->input('User.password',array(
'value' => '',
'type' => 'password',
'autocomplete' => 'off'
)
);
And have some sort of check on the save to skip the password; but this is where I'm stuck.
controller/userscontroller.php
public function edit($id = null)
{
$this->User->id = $id;
if ($this->request->is('get'))
{
$this->request->data = $this->User->read();
} else {
//Something here????
if ($this->User->save($this->data))
{
$this->Session->setFlash('Your user has been updated.');
$this->redirect(array('action' => 'index'));
} else
{
$this->Session->setFlash('Unable to update your user.');
}
}
$this->set('groups',$this->User->Group->find('list'));//, array( 'fields' => array('id', 'name'))));
$this->set('sites',$this->User->Site->find('list'));//, array( 'fields' => array('id', 'name'))));
}
How do I check this and prevent the password from updating when there is no change?
Decided Solution:
As per the answers provided I used a second form on the same page that re-uses the signup validation that users go through. When updating site/group privileges the users are sent through one form while passwords through another.
I would build two admin forms, one for changing permissions and the other for updating the password only. While you are at it, the change password form should have a second field for validating the change.
There are some CakePHP plugins to help with managing users and specifically passwords.
I always create a new form specially for changing passwords. You should replace the password field with a link to change the password.
Alternatively, you could disable the input field and require a button to click and use javascript to remove the disabled attribute on the input element
echo $this->Form->input('User.password',array(
'value' => '',
'type' => 'password',
'autocomplete' => 'off',
'disabled' => true
)
);
Jquery because it's easy
$(function(){
$('UsersPassword').click(function(){
$(this).attr('disabled', '0');
});
});

How to create a ground of checkbox in Cakephp? and how to store it?

I have a field called Hobbies, I wish to store all the hobbies selected by the user to be stored in the database as CSV. How can I do this in Cakephp?
Paste into view (ie, views/users/add.ctp)
<?php echo $form->create('User', array('action' => 'add')) ?>
<?php echo $form->input('User.hobbies', array('type' => 'select',
'multiple' => 'checkbox',
'options' => array('sports' => 'sports',
'movies' => 'movies',
'games' => 'games'))) ?>
<?php echo $form->end('Save') ?>
Paste into Users controller (just a standard save method, nothing special here)
function add() {
if(!empty($this->data)) {
if($this->User->saveAll($this->data, array('validate' => 'first'))) {
$this->Session->setFlash('User saved successfully');
} else {
$this->Session->setFlash('User failed to save');
}
}
}
Paste into User model
function beforeValidate() {
// join hobbies into csv
if(!empty($this->data['User']['hobbies'])) {
$this->data['User']['hobbies'] = join(',', $this->data['User']['hobbies']);
}
return true;
}
Notes:
If you need to separate the hobbies back out when reading the User model, you could use the "afterFind" callback or check out the Serializable Behaviour http://blog.matsimitsu.nl/code/206/serializeable-behavior-for-cakephp that automatically serializes and deserializes whenever you try to add or pull out an array to/from the db.
You could add the beforeValidate code to the beforeSave callback instead, just depends what kind of validation you want to perform. having the code in beforeValidate will let you do a basic notEmpty check, however in beforeSave will mean you can check individual items are present in the array.
References:
http://book.cakephp.org/view/76/Callback-Methods
http://book.cakephp.org/view/189/Automagic-Form-Elements

Display custom validation messages using CakePHP $validate array

I'm trying to display custom messages like, 'this field should not be empty' or 'name not null' using the $validate array in the model. I have two controllers, main and users.
The index file of the main controller has the login and registration views. The action part of the login and register functions are in the user_controller. If the login and register function validate, they are redirected to the home page of the main controller,else they remain in the index page itself.
I want the validation messages to be displayed in the index page itself. But those messages appear only if there is a separate view file for login and register,i.e, /views/forms/register.ctp and /views/forms/login.ctp exist.
Is there a way to display those validation messages without having a separate view file for those functions? I have given my code below.Someone guide me please.
Model Class:
<?php
class User extends AppModel {
var $name = 'User';
var $components=array('Auth');
var $validate = array(
'name' => array(
'rule' => 'notEmpty',
'message' =>'Name cannot be null.'
),
'password' => array(
'rule' => 'notEmpty'
),
'email_id' => array(
'rule' => 'notEmpty'
)
);
function registerUser($data)
{
if (!empty($data))
{
$this->data['User']['name']=$data['User']['name'];
$this->data['User']['email_id']=$data['User']['email_id'];
$this->data['User']['password']=$data['User']['password'];
$existingUsers= $this->find('all');
foreach($existingUsers as $existingUser):
if($this->data['User']['email_id']==$existingUser['User']['email_id']){
return 0;
}
else{
$this->save($this->data);
$this->data['User']['id']= $this->find('all',array('fields' => array('User.id'),
'order' => 'User.id DESC'
));
$userId=$this->data['User']['id'][0]['User']['id'];
return $userId;
}
endforeach;
}
}
function loginUser($data)
{
$this->data['User']['email_id']=$data['User']['email_id'];
$this->data['User']['password']=$data['User']['password'];
$login=$this->find('all');
foreach($login as $form):
if($this->data['User']['email_id']==$form['User']['email_id'] && $this->data['User']['password']==$form['User']['password'])
{
$this->data['User']['id']= $this->find('all',array('fields' => array('User.id'),
'conditions'=>array('User.email_id'=> $this->data['User']['email_id'],'User.password'=>$this->data['User']['password'])
));
$userId=$this->data['User']['id'][0]['User']['id'];
return $userId;
}
endforeach;
}
}
?>
Controller Class:
<?php
class UsersController extends AppController
{
var $name = 'Users';
var $uses=array('Form','User','Attribute','Result');
var $helpers=array('Html','Ajax','Javascript','Form');
function register()
{
$this->Session->write('userId',$this->User->registerUser($this->data));
$this->User->data=$this->data;
if (!$this->User->validates())
{
$this->Session->setFlash('Please enter valid inputs');
$this->redirect('/main' );
return;
}
if($this->Session->read('userId')==0){
$this->Session->setFlash('You are already a registerd member.Log in your account');
$this->redirect('/main');
}
else{
$this->Session->setFlash('User account created');
$this->redirect('/main/home');
}
}
function login()
{
//$userId=$this->User->loginUser($this->data);
$this->Session->write('userId',$this->User->loginUser($this->data));
$this->User->data=$this->data;
if (!$this->User->validates())
{
$this->Session->setFlash('Please enter valid inputs');
$this->redirect('/main' );
return;
}
if($this->Session->read('userId')>0){
$this->Session->setFlash('Login Successful');
$this->redirect('/main/home');
break;
}
else{
$this->Session->setFlash('Username and password do not match.');
$this->redirect('/main');
}
}
}
?>
View Template:
<!-- File: /views/main/index.ctp-->
<div id="register">
<h3>Register</h3>
<?php
echo $form->create('User',array('action'=>'register'));
echo $form->input('name');
echo $form->input('email_id');
echo $form->input('password');
echo $form->end('Register');
?>
</div>
<div id="login">
<h3>Login</h3>
<?php
echo $form->create('User',array('action'=>'login'));
echo $form->input('email_id');
echo $form->input('password');
echo $form->end('Login');
?>
</div>
I think you're going about it the wrong way. You're doing way too much in the model, and you're also doing almost the same thing in the controller again after the fact. That's not good. Overall, honestly, the code is quite a mess for something so simple.
A huge WTF flag pops up here:
$existingUsers= $this->find('all');
foreach($existingUsers as $existingUser):
if($this->data['User']['email_id']==$existingUser['User']['email_id']){
You're seriously retrieving all users from the database (potentially a hugely expensive task) and then go through them one by one to compare a single field?!
You can simply define a validation rule that says 'email_id' should be unique, and Cake will automatically ask the database if the 'email_id' already exists. http://book.cakephp.org/view/472/isUnique
About your specific problem: You have the same form field twice on the same page, password and email_id fields for the same User model. There's no way for Cake to know which instance of the two fields is supposed to get the error message, they both have the same name. Also, I don't think you want to use validation error messages for the login form, you just want to see if the login was successful or not. Use Session::flash() instead to display an error message for a failed login, it's not field specific.
Take the login and register methods out of your model, they don't belong there. Only specify proper validation rules for the email, name and password fields in the model, they will automatically be checked upon calling $this->User->save() in the controller.
Don't hand-validate anything, unless there's really no way to do it with Cake validation rules (not the case here). If the built-in validation rules don't satisfy what you need to do, you can even make custom rules. http://book.cakephp.org/view/150/Custom-Validation-Rules
PS: Components are not for models. I think you need to learn more about the basics of Cake before continuing: http://book.cakephp.org/view/218/Tutorials-Examples
But, if you want to see your error messages that comes from the validate array you should access the $this->modelName->invalidFields() which will return you the fields that didn't pass the validation and the message that you have setted for them...

Resources