How to save embedded forms in symfony 1? - php

I have a form which represnts single answer object (this is standard propel generated form I have not changed much there only some validation rules) and another form that represents a collection of answers code as below:
class BbQuestionAnswersForm extends sfForm {
public function __construct($defaults = array(), $options = array(), $CSRFSecret = null) {
parent::__construct($defaults, $options, $CSRFSecret);
}
public function configure() {
if (!$questions = $this->getOption('questions')) {
throw new InvalidArgumentException('The form need array of BbExamQuestion objects.');
}
if (!$taker = $this->getOption('taker')) {
throw new InvalidArgumentException('The form need BbExamtaker object.');
}
if (!$user = $this->getOption('questions')) {
throw new InvalidArgumentException('The form need sfGuardUser object.');
}
foreach($questions as $question) {
$answer = new BbExamAnswer();
$answer->setBbExamQuestion($question);
$answer->setBbExamTaker($taker);
$answer->setCreatedBy($user);
$answer->setUpdatedBy($user);
$form = new BbExamAnswerForm($answer, array('question' => $question));
$this->embedForm($question->getId(), $form);
}
$this->widgetSchema->setNameFormat('solve[%s]');
}
}
Everything(validation, display) goes fine with this form until I try to save it. Part of action which trying to save the form:
...
$this->form = new BbQuestionAnswersForm(null, array('questions' => $this->questions, 'taker' => $this->taker, 'user' => $this->getUser()->getGuardUser()));
if($request->isMethod('post')) {
$this->form->bind($request->getParameter($this->form->getName()));
if($this->form->isValid()) {
if($this->form->save()) {
$this->getUser()->setFlash('success', 'Save goes fine.');
$this->redirect($this->generateUrl('#bb'));
} else {
$this->getUser()->setFlash('error', 'Upps an error occurred.');
}
}
}
When I send valid form I receive "Call to undefined method BbQuestionAnswersForm::save()" error.
I tried to write this method like this:
public function save() {
$conn = Propel::getConnection(ZlecPeer::DATABASE_NAME);
$conn->beginTransaction();
try{
foreach($this->getEmbeddedForms() as $form) {
$form->save();
}
$conn->commit();
} catch(Exception $e) {
$conn->rollback();
echo 'upps something goes wrong';
die($e->getMessage());
return false;
}
return true;
}
but it doesnt work, I receive exception without any message.
What am I doing wrong, how to make save method work?

I believe your BbQuestionAnswersForm is extending the wrong object. It should extend BaseFormDoctrine or possibly BaseBbQuestionAnswersForm if you are using the framework correctly.
Edit: Just noticed you are using propel but it should be the same thing. Try:
class BbQuestionAnswersForm extends BaseBbQuestionAnswersForm
and less likely:
class BbQuestionAnswersForm extends BaseFormPropel

Save method should looks like this:
public function save($con = null) {
if (null === $con) {
$con = Propel::getConnection(BbExamAnswerPeer::DATABASE_NAME);
}
$con->beginTransaction();
try{
foreach($this->embeddedForms as $name => $form) {
if(!isset($this->values[$name]) || !is_array($this->values[$name])) {
continue;
}
if($form instanceof sfFormObject) {
$form->updateObject($this->values[$name]);
$form->getObject()->save($con);
$form->saveEmbeddedForms($con);
} else {
throw new Exception('Embedded form should be an instance of sfFormObject');
}
}
$con->commit();
} catch(Exception $e) {
$con->rollBack();
throw $e;
return false;
}
return true;
}

Related

How to "catch" a handled exception in a class from the script using the the class?

I have a script that looks like this:
require ("class-pdowrapper.php");
$db = new DB();
$baddata = 'abc'; //should be an integer!!
$db->bind("myinteger", $baddata);
$sql = "INSERT INTO t_numbers (theinteger) VALUES (:myinteger)";
$result = $db->query($sql);
if ($result == 1) {
echo "success.";
} else {
echo "didn't work..."; //question relates to this line
}
If $baddata is "abc" i.e. a string, the class will handle the exception and output something like this:
Unhandled Exception.
SQLSTATE[HY000]: General error: 1366 Incorrect integer value: 'abc' for column 'theinteger' at row 1
You can find the error back in the log
The class does this by something like:
private function query($statement) {
try {
// stuff
}
catch (PDOException $e) {
echo $this->ExceptionLog($e->getMessage(), $query);
die();
}
}
private function ExceptionLog($message, $sql = "") {
$exception = 'Unhandled Exception. <br />';
$exception .= $message;
$exception .= "<br /> You can find the error back in the log.";
if (!empty($sql)) {
$message .= "\r\nRaw SQL : " . $sql;
}
return $exception;
}
So the question is, how can a script acknowledge the exception thrown and handled in the class and use the knowledge to do something. In my case, I could show the user an error or email the administrator to show that something isn't working with the database-related code.
If you catch the exception, then it is stopped from travelling up the stack, so you do not have the opportunity to »re-catch« it.
Basically you can either »re-throw« an Exception of a custom type, by creating a custom Error type like so:
class DataInsertException extends Exception {}
and than later:
try {
// stuff
}
catch (PDOException $e) {
echo $this->ExceptionLog($e->getMessage(), $query);
throw new DataInsertException();
}
That way you can »filter« exceptions within the try catch statement:
try {} catch (DataInsertException $error) {}
whenever you deal with the database. All other Errors wont be caught by that block.
Or you can also add a $errors property to your DB class.
class DB {
private $errors;
public function __construct(){
$this->errors = [];
}
public function insert(){
try {}
catch($error) {
array_push($this->errors, $error);
}
}
public function getErrors() {
return $this->errors;
}
}
That way, you can check the $db Object after each Transaction if it has Errors and react on that.
I would prefer a custom Error
You need validate all your variables before you send it to server
it is right way because you don't send incorrect request to server side.
like this (it is only sample)
<?php
class sampleClass
{
public function __construct($data) {
if ($this->validateData($data)) {
//do something
$this->runRequest($data);
} else {
//parse errors
//var_dump($data);
}
}
private function validateData($data) {
foreach($data as $currentData) {
if ($currentData['type'] == 'integer' && is_int($currentData['value'])) {
return false;
}
if ($currentData['type'] == 'string' && is_string($currentData['value'])) {
return false;
}
}
return true;
}
private function runRequest($data) {
}
}
$data = [
'correct_age' => [
'type' => 'integer',
'value' => 88
],
'incorrect_age' => [
'type' => 'integer',
'value' => 'abc'
],
'incorrect_name' => [
'type' => 'string',
'value' => 111
]
];
$test = new sampleClass($data);
There are many options here. Here're some of them:
You can rethrow an Exception in you catch (PDOException $e) { block and make another try ... catch ... in your script. So you would handle logging in your query method and then handle application-related logic in the script file itself:
try {
$result = $db->query($sql);
} catch (AnotherException $e) {
...
}
You can return complex result from query(), like this (it's only a draft):
$result = array(
'status' => true|false (or 'error' => true|false),
'data' => ... (valid data on success, error details data on error)
)
And then you can handle this result with no problems.

$this->addError is not working in yii model class

I am trying to do the custom validation.All is working fine except $this->addError .
It is not adding the error as it should. this is my controller action
public function actionVerifyAnswer()
{
if(isset(Yii::app()->session['_emailId']))
{
$model=new LoginForm;
$model->scenario='verifyAns';
if(isset($_POST['LoginForm']))
{
$model->attributes=$_POST['LoginForm'];
if($model->validate())
{
$theQuestion=$model->secretQuestion;
$theAnswer= strtolower($model->verifyAnswer);
$usersModel=new Users;
$emailUser= Yii::app()->session['_emailId'];
$userRecord=$usersModel->find('loginEmail=:email AND secretQuestion=:que',array(':email'=>$emailUser,':que'=>$theQuestion));
if(empty($userRecord))
{
throw new CHttpException('You have selected the wrong question');
}
else
{
$usersAnswer= strtolower($userRecord->secretAnswer);
if($theAnswer === $usersAnswer)
{
$this->redirect('changePassword');
}
else
{
throw new CHttpException('you have given the wrong answer');
}
}
}
else
{
throw new CHttpException('model->validate()');
}
}
else
{
$secretQuestion= Yii::app()->params['secretQuestion'];
$this->render('verifyAnswer', array('model'=>$model,
'secretQuestion'=>$secretQuestion,
));
}
}
else
{
Yii::app()->user->loginRequired();
}
}
and this is my custom validation in the model
public function checkQuestion()
{
if(!$this->hasErrors())
{
$model=new LoginForm;
$theQuestion=$this->secretQuestion;
$usersModel=new Users;
$emailUser= Yii::app()->session['_emailId'];
$userRecord=$usersModel->find('loginEmail=:email AND secretQuestion=:que',array(':email'=>$emailUser,':que'=>$theQuestion));
if(empty($userRecord))
{
//this line is not working
$this->addError('secretQuestion', 'Incorrect Combination');
//if i throw exception here. Then exception is working
}
}
}
How can i make $this->addError working?
Custom validation should be triggered from within the model validate ;
public function rules()
{
// NOTE: you should only define rules for those attributes that
// will receive user inputs.
return array(
array('the_answer', 'checkQuestion'),
....
);
}
public function checkQuestion($attribute,$params)
{
....
if(empty($userRecord))
{
//this line is not working
$this->addError('secretQuestion', 'Incorrect Combination');
//if i throw exception here. Then exception is working
}
}
I have solved the issue and i am posting the answer because it might help somebody else also.
I needed to remove the
else{
//not the code inside
}
of this condition
if(isset($_POST['LoginForm']))
{
Note:- I just removed the else and its braces {} and left the code inside the else
as it was i.e :-
$secretQuestion= Yii::app()->params['secretQuestion'];
$this->render('verifyAnswer', array('model'=>$model,
'secretQuestion'=>$secretQuestion,
));

jaxl return to function that it's called from

I have library for xmpp transactions used jaxl libraries:
class xmpp{
public function register_user($username, $password){
require_once 'JAXL/jaxl.php';
$this->client = new JAXL(array(
'jid' => 'localhost',
'log_level' => JAXL_ERROR
));
$this->username = $username;
$this->password = $password;
$this->client->require_xep(array(
'0077' // InBand Registration
));
$thisClassObject =& $this;
$this->client->add_cb('on_stream_features', function($stanza) use(&$thisClassObject) {
$thisClassObject->client->xeps['0077']->get_form('localhost');
return array($thisClassObject, 'wait_for_register_form');
});
$this->client->start();
return;
}
public function wait_for_register_response($event, $args) {
if($event == 'end_stream') {
return;
}
else if($event == 'stanza_cb') {
$stanza = $args[0];
if($stanza->name == 'iq') {
if($stanza->attrs['type'] == 'result') {
echo "registration successful".PHP_EOL."shutting down...".PHP_EOL;
$this->client->end_stream();
return 'logged_out';
}
else if($stanza->attrs['type'] == 'error') {
$error = $stanza->exists('error');
echo "registration failed with error code: ".$error->attrs['code']." and type: ".$error->attrs['type'].PHP_EOL;
echo "error text: ".$error->exists('text')->text.PHP_EOL;
echo "shutting down...".PHP_EOL;
$this->client->end_stream();
return "logged_out";
}
}
}
}
public function wait_for_register_form($event, $args) {
$stanza = $args[0];
$query = $stanza->exists('query', NS_INBAND_REGISTER);
if($query) {
$form = array();
$instructions = $query->exists('instructions');
if($instructions) {
echo $instructions->text.PHP_EOL;
}
$this->client->xeps['0077']->set_form($stanza->attrs['from'], array('username' => $this->username, 'password' => $this->password));
return array($this, "wait_for_register_response");
}
else {
$this->client->end_stream();
return "logged_out";
}
}
}
these code are same as register_user.php, but implemented in a class;
i use this class in my code in this way:
$xmppObj = new xmpp();
$xmppObj('user','password');
/*
some more code after this
/*
when it execute , create user successfully but it's print a message ('registration successful ...') and application exited and it doesn't execute "some code after this" after the class function, in the other word it doesn't follow the code...
What can I do for solve this problem, a person can help me that familiar with JAXL library.
Looks like you are pretty much using the same code as found inside examples/register_user.php. Once user registration is successful, script closes XMPPStream as evident from this section of the code:
if($stanza->attrs['type'] == 'result') {
echo "registration successful".PHP_EOL."shutting down...".PHP_EOL;
$this->client->end_stream();
return 'logged_out';
}
You MUST instead call $client->send_end_stream(); and not $client->end_stream();. This will make sure underlying XMPPStream makes proper FSM state transition. Also add a callback for on_disconnect event, inside this callback you can again try to connect back with newly registered XMPP account and it should just work fine.
Note: Kindly checkout latest code from the repository. I made some updates which will allow core JAXLLoop to be re-initialized. If you are interested in details, here is the commit log.

How can I return a data object when throwing a RestException?

function put($id, $name)
{
try
{
product::put($id, $name);
}
catch(\util\BadNameException $e)
{
throw new RestException(400, "Please supply a better name.");
}
}
When returning the error message I also want to include the result of (array)product::getNamingConvention() in the error. How can I do that?
I could just return a custom array with the error message and data, but I don't know how to set the status code to 400 in that case?
I'm using Restler 3.
Responder class is responsible for giving a structure to error response and success response
You can extend the Responder class as shown below to add data property
use \Luracast\Restler\Responder;
use \Luracast\Restler\Defaults;
class MyResponder extends Responder
{
public static $data = null;
public function formatError($statusCode, $message)
{
$r = array(
'error' => array(
'code' => $statusCode,
'message' => $message
)
);
if (isset(self::$data)) {
$r['data'] = self::$data;
}
return $r;
}
}
Defaults::$responderClass = 'MyResponder';
And then set the data from your class as shown below
function put($id, $name)
{
try
{
product::put($id, $name);
}
catch(\util\BadNameException $e)
{
MyResponder::$data = product::getNamingConvention();
throw new RestException(400, "Please supply a better name.");
}
}

PHP exception and user message

I have a project object.
A user can assign a worker object for it.
99% of the case the project object has all the proper fields set.
In some cases, the project is missing a field.
In order for a worker to be assign to a project the project must have all the required fields setup.
To solve this, I throw exceptions like this: ( don't try to find a pattern here, the real example is more complex )
if ($project->startDate == false ) {
throw new Exception("Missing startDate attribute for project id: $id");
}
if ($project->finishDate == false ) {
throw new Exception("Missing finishDate attribute for project id: $id");
}
if ($project->startDate > $project->finishDate ) {
throw new Exception("Invalid start date for project id: $id");
}
The problem with this is that I need to display a custom message to the user each time. For example if the first error is thrown the user should see: 'Please setup the project start date' and so on.
How can I do this ?
Just define your own exception class and your whole code in a try .. catch:
class FormException extends Exception {
private var $userMessage;
public function __construct($message, $userMessage) {
parent::__construct($message);
$this->userMessage = $userMessage;
}
public function getUserMessage() {return $this->userMessage;}
}
try {
// Whole code goes here, probably a function call
throw new FormException("Missing startDate attribute for project id: $id",
'Please setup the project start date');
} catch (FormException $e) {
echo $e->getUserMessage();
error_log($e->getMessage());
}
By the way, if you want to include variable contents in a string, either use double quotes ("id: $id") or concatenate ('id: ' . $id).
Try this:
try
{
$Message = '';
if ($project->startDate == false ) {
$Message = "Please setup the project start date\n";
throw new Exception("Missing startDate attribute for project id: $id");
}
if ($project->finishDate == false ) {
$Message = "Please setup the project finish date\n";
throw new Exception("Missing finishDate attribute for project id: $id");
}
if ($project->approved == false ) {
$Message = "Please setup the project approved field\n";
throw new Exception("Missing approved attribute for project id: $id");
}
}
catch(Exception $Ex)
{
// Log in some way you like the $Ex-getMessage();
echo nl2br($Message);
}
Inside your project class, create a new method, probably called getErrorMessage.
That function should do the checks (no need to have the concrete way of validation outside of project or create a validation object for project). Then:
if ($message = $project->getErrorMessage())
{
throw new Exception(sprintf('%s (project id: %d)', $message, $id));
}
If you decide to not throw exceptions for design reasons, this still is of use.
You gain more flexibility with a validator object that can naturally provide more detailed information than a single method. So it might be the better thing to do:
class ProjectValidator
{
private $project;
private $errors;
public function __construct($project)
{
$this->project = $project;
}
public function isValid()
{
// run your checks, add errors to the array as appropriate.
$errors = array();
if (!$this->project->startDate)
{
$errors[] = 'Missing startDate attribute';
}
if (!$this->project->finishDate)
{
$errors[] = 'Missing finishDate attribute';
}
if (!$this->project->approved)
{
$errors[] = 'Missing approved attribute';
}
$this->errors = $errors;
return (bool) count($this->errors);
}
public function getErrors()
{
return $this->errors;
}
}
$validator = new ProjectValidator($project);
if (!$validator->isValid())
{
// throw an exception or do whatever you want in case it's not valid
$errors = $validator->getMessages();
$message = sprintf("Project id: %d has the following %d error(s):\n", $id, count($errors));
foreach($errors as $index => $error)
{
$message .= sprintf("%d. %s\n", $index+1, $error);
}
throw new Exception($message);
}

Categories