Related
I'm trying to test a method which is using a service, and apparently it's not possible to test it like a normal method.
Does someone know what to do ?
I have this code for the moment :
namespace PlatformBundle\Tests;
use PlatformBundle\Controller\PaymentController;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class PaymentControllerTest extends WebTestCase
{
private $payment;
public function __construct() { parent::__construct(); $this->payment = new PaymentController(); }
public function testSendEmail()
{
$param = array(
'info' => array(
'email' => 'test#test.com', 'name' => 'test', 'fare' => 'test', 'id' => 'test'
)
);
$this->assertEquals(true, $this->invokeMethod($this->payment, 'sendEmail', $param));
}
/**
* Call protected/private method of a class.
*
* #param object &$object Instantiated object that we will run method on.
* #param string $methodName Method name to call
* #param array $parameters Array of parameters to pass into method.
*
* #return mixed Method return.
*/
public function invokeMethod(&$object, $methodName, array $parameters = array())
{
$reflection = new \ReflectionClass(get_class($object));
$method = $reflection->getMethod($methodName);
$method->setAccessible(true);
return $method->invokeArgs($object, $parameters);
}
}
The controller where the method sendEmail is :
<?php
namespace PlatformBundle\Controller;
use PlatformBundle\Entity\Customer;
use PlatformBundle\Entity\Promocode;
use PlatformBundle\Entity\Transfer;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Config\Definition\Exception\Exception;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
class PaymentController extends Controller
{
public function checkoutAction(Request $req)
{
if (! $req->isMethod('POST')) throw new AccessDeniedHttpException();
$info = $req->request->all();
$this->container->get('platform.formSecurity')->testAllInformation($info);
$this->saveCustomerIntoDb($info);
$info['payed'] = false;
$session = $req->getSession();
$session->set('info', $info);
$info['date'] = $this->container->get('platform.useful')->reverseDateFormat($info['date']);
return $this->render('PlatformBundle:Payment:checkout.html.twig', array(
'isIndex' => false,
'info' => $info,
'stripe' => $this->stripeConfig()
));
}
public function cancelAction(Request $req)
{
$req->getSession()->invalidate();
return $this->render('PlatformBundle:Payment:cancel.html.twig', array('isIndex' => false));
}
public function successAction(Request $req)
{
$session = $req->getSession();
$info = $session->get('info');
if ($info['payed']) {
$req->getSession()->invalidate();
if ($info === null) throw new Exception('Please contact us to make sure that the payment has been done and that your order has been taken into account.');
$this->saveTransferIntoDb($info);
$customer = $this->getDoctrine()->getManager()->getRepository('PlatformBundle:Customer')->findOneBy(array(
'email' => $info['email']
));
$transfer = $this->getDoctrine()->getManager()->getRepository('PlatformBundle:Transfer')->findOneBy(
array('customer' => $customer->getId()),
array('id' => 'desc'),
1
);
$info['id'] = $transfer->getId();
$info['date'] = $this->container->get('platform.useful')->reverseDateFormat($info['date']);
$this->sendEmail($info);
// if 5 payments done, send a promocode
if (is_int($customer->getPayments() / 5)) {
$this->createAndSendNewPromocode($customer);
}
return $this->render('PlatformBundle:Payment:success.html.twig', array(
'isIndex' => false,
'info' => $info
));
} else return new RedirectResponse('cancel');
}
private function sendEmail($info)
{
$mail = $this->container->get('platform.mail');
$mail->send(
$info['email'],
'You have ordered a transfer for Dublin',
$this->renderView('PlatformBundle:Mail:orderSucceed.html.twig', array('info' => $info)),
'info#dubair.ie'
);
$mail->send(
'info#airportcollections.net, info#dubair.ie, info#365onlineholidays.com',
'A customer ordered a transfer for Dublin',
$this->renderView('PlatformBundle:Mail:report.html.twig', array('info' => $info)),
'info#dubair.ie'
);
}
private function saveCustomerIntoDb($info)
{
// test if the customer already exist
$customersList = $this->getDoctrine()->getManager()->getRepository('PlatformBundle:Customer')
->findByEmail($info['email']);
$customerExists = (sizeof($customersList) == 1 ? true : false);
if ($customerExists) {
$customer = $customersList[0];
} else {
// Create the entity
$customer = new Customer();
// dateRegistration, country and ip are automatically created in the constructor
$customer->setEmail($info['email']);
$customer->setPayments(0);
}
$customer->setName($info['name']);
$customer->setPhone($info['phone']);
$em = $this->getDoctrine()->getManager();
$em->persist($customer);
$em->flush();
}
private function saveTransferIntoDb($info)
{
$customers = $this->getDoctrine()->getManager()->getRepository('PlatformBundle:Customer')
->findByEmail($info['email']);
$customer = $customers[0];
$customer->setPayments($customer->getPayments() + 1);
// make promocode outdated
if ($info['promocode'] != '') {
$promocode = $this->getDoctrine()->getManager()->getRepository('PlatformBundle:Promocode')
->findOneBy(array(
'value' => $info['promocode'],
'outdated' => 0,
'type' => 'short'
));
$promocode->setOutdated(1);
}
// test if transfer already exist
$transferList = $this->getDoctrine()->getManager()->getRepository('PlatformBundle:Transfer')->findBy(
array(
'customer' => $customer,
'pickup' => $info['pickup'],
'destination' => $info['destination'],
'pickupTime' => $info['pickupTime'],
'address' => $info['address']
), // criteria
array('pickup' => 'desc'), // sorting
5, // Limit
0 // Offset
);
// if transfer doesn't already exist, create it
if (sizeof($transferList) == 0) {
$transfer = new Transfer();
$transfer->setPickup($info['pickup']);
$transfer->setDestination($info['destination']);
$dateArray = explode('-', $info['date']);
$transfer->setDate(new \DateTime($dateArray[2].'-'.$dateArray[1].'-'.$dateArray[0]));
$transfer->setAddress($info['address']);
$transfer->setFlightTime($info['flightTime']);
$transfer->setPickupTime($info['pickupTime']);
$transfer->setSeats($info['seats']);
$transfer->setAirline($info['airline']);
$transfer->setFlight($info['flight']);
$transfer->setType($info['type']);
$transfer->setBags($info['bags']);
$transfer->setFare($info['fare']);
// join
$transfer->setCustomer($customer);
$em = $this->getDoctrine()->getManager();
$em->persist($transfer);
$em->flush();
}
}
private function createAndSendNewPromocode($customer)
{
$newPromocode = $this->container->get('platform.useful')->createRandomPassword();
$promocode = new Promocode();
$promocode->setValue($newPromocode);
$promocode->setType('short');
$promocode->setDiscount(10);
$em = $this->getDoctrine()->getManager();
$em->persist($promocode);
$em->flush();
$mail = $this->container->get('platform.mail');
$mail->send(
$customer->getEmail(),
'A promotional code for your next transfer on dubair.ie !',
$this->renderView('PlatformBundle:Mail:promocode.html.twig', array(
'customer' => $customer,
'promocode' => $newPromocode
)),
'info#dubair.ie'
);
}
private function stripeConfig()
{
$stripe = array(
"secret_key" => "xx",
"publishable_key" => "xx"
);
\Stripe\Stripe::setApiKey($stripe['secret_key']);
return $stripe;
}
public function stripeChargeAction(Request $req)
{
$this->stripeConfig();
$info = $req->getSession()->get('info');
$amount = ($info['fare'] * 100);
$info['payed'] = true;
$req->getSession()->set('info', $info);
$token = $req->request->get('stripeToken');
$customer = \Stripe\Customer::create(array(
'email' => $req->request->get('email'),
'card' => $token
));
$charge = \Stripe\Charge::create(array(
'customer' => $customer->id,
'amount' => $amount,
'currency' => 'eur'
));
return new RedirectResponse('success');
}
}
thanks
I'm using ZF2 TableGateway to update some data:
$this->update($data, array('id' => $id)));
I want to add a limit to this query, such as:
$this->update($data, array('id' => $id)))->limit(1);
This doesn't work however. Any ideas how to achieve this?
Thank you!
In You Controller use this:
import:
use Zend\Db\TableGateway\TableGateway
in main controller
public function updateuserAction() {
$form = new UserRegistration();
$request = $this->getRequest();
$id = $this->params()->fromRoute('id');
if(!$id) {
$this->redirect()->toUrl('/REDIRECT_URL');
}
if ($request->isPost()) {
$registeruser = new UserRegistrationModel();
$formValidator = new UserRegistrationValidator(); {
$form->setInputFilter($formValidator->getInputFilter());
$form->setData($request->getPost());
}
if ($form->isValid()) {
$data = $form->getData();
unset($data['submit']);
$this->getUsersTable()->update($data, array('uid' => $id));
return $this->redirect()->toUrl('/REDIRECT_URL');
}
$view = new ViewModel(array('form' => $form, 'action' => $this->params()->fromRoute('action')));
return $view;
}
else {
$form->setData($this->getUsersTable()->select(array('uid' => $id))->current());
}
$view = new ViewModel(array('form' => $form, 'id' => $id , 'rowset' => $this->getUsersTable()->select(array('uid' => $id))->current()));
return $view;
}
public function getUsersTable() {
if (!$this->RegisterUser) {
$article = $this->RegisterUser = new TableGateway(
'users', $this->getServiceLocator()->get('Zend\Db\Adapter\Adapter')
);
}
return $this->RegisterUser;
}
//// Controller ends
and in View :
// to get id ( IN FORM )
$form->setAttribute('action', $this->url('user',
array('action' => 'updateuser', 'id' => $id)));
/REDIRECT_URL is your url to redirect user,
CHANGE UID to id from database table,
getUsersTable() is table gateway
I'm trying to capture the value selected from a dropDownList from a CActiveForm and then do some logic on it after submit... to then run a query. Based on this query i would then update the gridview. This form doesn't have any attributes in a table that relate to it but its more so I can create a filter for my GridView after some logic.
Where would i perform this logic? I'm new to php, yii, and javascript so I'd appreciate at least pointing me to the right direction. I've tried to look at yii documentation and books but haven't found what i need. i think I'm missing something critical from php and web dev to be stuck on something so trivial as a drop down.
This is my view for admin.php which then calls the advanced_search.php form by renderPartial
<?php
/* #var $this UserController */
/* #var $model User */
$this->breadcrumbs=array(
'Manage Users',
);
/*$this->menu=array(
array('label'=>'List User', 'url'=>array('index')),
array('label'=>'Create User', 'url'=>array('create')),
);
*/
Yii::app()->clientScript->registerScript('search', "
$('.asearch-button').click(function(){
$('.asearch-form').toggle();
$('.bsearch-form').toggle();
return false;
});
$('.bsearch-button').click(function(){
$('.bsearch-form').toggle();
$('.asearch-form').toggle();
return false;
});
$('.bsearch-form form').submit(function(){
$('#user-grid').yiiGridView('update', {
data: $(this).serialize()
});
return false;
});
$('.asearch-form form').submit(function(){
$('#user-grid').yiiGridView('update', {
data: $(this).serialize()
});
return false;
});
");
?>
<h2>Manage Users</h2>
<?php echo CHtml::link('Basic Search','#',array('class'=>'bsearch-button')); ?>
<br/>
<!-- basic search-form -->
<div class="bsearch-form" style="display:">
<?php $this->renderPartial('search',array('model'=>$model)); ?>
</div>
<?php echo CHtml::link('Advanced Search','#',array('class'=>'asearch-button')); ?>
<!-- advanced search-form -->
<div class="asearch-form" style="display:none">
<?php $this->renderPartial('advanced_search',array('model'=>$model)); ?>
</div>
<?php $this->widget('bootstrap.widgets.TbGridView', array(
'type'=>'striped condensed hover',
'id'=>'user-grid',
'selectableRows'=>1,
'selectionChanged'=>
'function(id){ location.href = $.fn.yiiGridView.getSelection(id);}',
'dataProvider'=>$model->search(),
'filter'=>$model,
'columns'=>array(
'username',
'email',
'fname',
'lname',
array(
'name' => 'combineRoles',
'value' => '($data->getCombineRoles())',
'header'=> CHtml::encode($model->getAttributeLabel('combineRoles')),
//'filter'=> CHtml::activeTextField($model, 'combineRoles'),
),
)));
?>
my advanced_search.php
<?php
/* #var $this UserController */
/* #var $model User */
/* #var $form CActiveForm */
?>
<div class="wide form">
<?php $form = $this->beginWidget('CActiveForm', array(
'action' => Yii::app()->createUrl($this->route),
'method' => 'get',
)); ?>
<?php
$dataRole = array('Project Mentor', 'Personal Mentor', 'Domain Mentor', 'Mentee');
$dataRoleVal = array(0,1,2,3);
echo $form->dropDownList($model, 'firstField', array_combine($dataRoleVal, $dataRole),
array('style' => ''));
echo $form->dropDownList($model, 'criteria', array('Exactly', 'Greater Than',
'Less Than'), array('style' => ''));
echo $form->textField($model, 'quantity', array('hint'=>'', 'style' => ''));
$data = array('Enabled', 'Disabled');
$data1 = array(0,1);
echo $form->dropDownList($model, 'disable', array_combine($data1, $data), array('style' => ''));
?>
<?php echo CHtml::submitButton('Search', array("class" => "btn btn-primary")); ?>
<?php $this->endWidget(); ?>
<!-- search-form -->
</div>
my UserController.php
public function actionAdmin()
{
echo("<script>console.log('actionAdmin');</script>");
$model=new User('search');
$model->unsetAttributes(); // clear any default values
if(isset($_GET['User'])){
$model->attributes=$_GET['User'];
}
$this->render('admin',array(
'model'=>$model,
));
}
Update: as requested here is my model User.php
<?php
/**
* This is the model class for table "user".
*
* The followings are the available columns in table 'user':
* #property string $id
* #property string $username
* #property string $password
* #property string $email
* #property string $fname
* #property string $mname
* #property string $lname
* #property string $pic_url
* #property integer $activated
* #property string $activation_chain
* #property integer $disable
* #property string $biography
* #property string $linkedin_id
* #property string $fiucs_id
* #property string $google_id
* #property integer $isAdmin
* #property integer $isProMentor
* #property integer $isPerMentor
* #property integer $isDomMentor
* #property integer $isStudent
* #property integer $isMentee
* #property integer $isJudge
* #property integer $isEmployer
*
* The followings are the available model relations:
* #property Administrator $administrator
* #property DomainMentor $domainMentor
* #property Mentee $mentee
* #property Message[] $messages
* #property Message[] $messages1
* #property PersonalMentor $personalMentor
* #property ProjectMentor $projectMentor
* #property Ticket[] $tickets
* #property Ticket[] $tickets1
* #property Domain[] $domains
*/
class User extends CActiveRecord
{
public $password2;
public $vjf_role;
public $men_role;
public $rmj_role;
/* advanced search variables */
public $firstField;
public $quantity;
public $criteria;
/*assign variables */
public $userDomain;
public $userId;
/*temporary variables currently not stored in db*/
public $employer;
public $position;
public $start_year;
public $degree;
public $field_of_study;
public $school;
public $graduation_year;
public $combineRoles;
/*Change the value when the system is deploy */
public static $admin = 5;
/* The most expert in the Domain */
public static $condition = 8;
/**
* Returns the static model of the specified AR class.
* #param string $className active record class name.
* #return User the static model class
*/
public static function model($className = __CLASS__)
{
return parent::model($className);
}
/**
* #return string the associated database table name
*/
public function tableName()
{
return 'user';
}
/**
* #return array validation rules for model attributes.
*/
public function rules()
{
// NOTE: you should only define rules for those attributes that
// will receive user inputs.
return array(
array('username, password, password2, email, fname, lname', 'required'),
array('activated, disable, isAdmin, isProMentor, isPerMentor, isDomMentor, isStudent, isMentee, isJudge, isEmployer', 'numerical', 'integerOnly' => true),
array('username, fname, mname, activation_chain, linkedin_id, fiucs_id, google_id', 'length', 'max' => 45),
array('password, email, pic_url', 'length', 'max' => 255),
array('lname', 'length', 'max' => 100),
array('biography', 'length', 'max' => 500),
// The following rule is used by search().
// Please remove those attributes that should not be searched.
array('id, username, password, email, fname, mname, lname, pic_url, activated, activation_chain, disable, biography, linkedin_id, fiucs_id, google_id, isAdmin, isProMentor, isPerMentor, isDomMentor, isStudent, isMentee, isJudge, isEmployer', 'safe', 'on' => 'search'),
);
}
public function validatePassword($password)
{
$hasher = new PasswordHash(8, false);
return $hasher->CheckPassword($password, $this->password);
}
/**
* #return array relational rules.
*/
public function relations()
{
// NOTE: you may need to adjust the relation name and the related
// class name for the relations automatically generated below.
return array(
'administrator' => array(self::HAS_ONE, 'Administrator', 'user_id'),
'domainMentor' => array(self::HAS_ONE, 'DomainMentor', 'user_id'),
'mentee' => array(self::HAS_ONE, 'Mentee', 'user_id'),
'messages' => array(self::HAS_MANY, 'Message', 'receiver'),
'messages1' => array(self::HAS_MANY, 'Message', 'sender'),
'personalMentor' => array(self::HAS_ONE, 'PersonalMentor', 'user_id'),
'projectMentor' => array(self::HAS_ONE, 'ProjectMentor', 'user_id'),
'tickets' => array(self::HAS_MANY, 'Ticket', 'assign_user_id'),
'tickets1' => array(self::HAS_MANY, 'Ticket', 'creator_user_id'),
'domains' => array(self::MANY_MANY, 'Domain', 'user_domain(user_id, domain_id)'),
);
}
/**
* #return array customized attribute labels (name=>label)
*/
public function attributeLabels()
{
return array(
'id' => 'User ID',
'username' => 'User Name',
'password' => 'Password',
'password2' => 'Re-type Password',
'email' => 'e-mail',
'fname' => 'First Name',
'mname' => 'Middle Name',
'lname' => 'Last Name',
'pic_url' => 'Pic Url',
'activated' => 'Activated',
'activation_chain' => 'Activation Chain',
'disable' => 'Disabled',
'biography' => 'Biography',
'linkedin_id' => 'Linkedin',
'fiucs_id' => 'Fiucs',
'google_id' => 'Google',
'isAdmin' => 'Administrator',
'isProMentor' => 'Project Mentor',
'isPerMentor' => 'Personal Mentor',
'isDomMentor' => 'Domain Mentor',
'isStudent' => 'Student',
'isMentee' => 'Mentee',
'isJudge' => 'Judge',
'isEmployer' => 'Employer',
'vjf_role' => 'Virtual Job Fair Roles:',
'men_role' => 'Mentoring Platform Roles:',
'rmj_role' => 'Remote Mobil Judge Roles:',
'employer' => 'Current Employer',
'position' => 'Position',
'start_year' => 'Start Year',
'degree' => 'Highest Degree',
'field_of_study' => 'Field of Study',
'school' => 'University',
'graduation_year' => 'Graduation Year',
'rmj_role' => 'Remote Mobil Judge Roles:',
'firstField' => 'Type: ',
'criteria' => 'Assigned to: ',
'quantity' => 'projects, mentors, or mentees',
'combineRoles' => 'Roles',
);
}
/**
* Retrieves a list of models based on the current search/filter conditions.
* #return CActiveDataProvider the data provider that can return the models based on the search/filter conditions.
*/
public function search()
{
// Warning: Please modify the following code to remove attributes that
// should not be searched.
if ($this->firstField === 0) {
$this->isProMentor = 1;
} else if ($this->firstField === 1) {
$this->isPerMentor = 1;
} else if ($this->firstField === 2) {
$this->isDomMentor = 1;
} else if ($this->firstField === 3) {
$this->isMentee = 1;
}
$criteria = new CDbCriteria;
//$criteria->compare('id', $this->id, true);
$criteria->compare('username', $this->username, true);
//$criteria->compare('password',$this->password,true);
$criteria->compare('email', $this->email, true);
$criteria->compare('fname', $this->fname, true);
//$criteria->compare('mname', $this->mname, true);
$criteria->compare('lname', $this->lname, true);
//$criteria->compare('pic_url',$this->pic_url,true);
$criteria->compare('activated', $this->activated);
//$criteria->compare('activation_chain',$this->activation_chain,true);
$criteria->compare('disable', $this->disable);
//$criteria->compare('biography',$this->biography,true);
//$criteria->compare('linkedin_id',$this->linkedin_id,true);
//$criteria->compare('fiucs_id',$this->fiucs_id,true);
//$criteria->compare('google_id',$this->google_id,true);
//$criteria->compare('isAdmin', $this->isAdmin);
$criteria->compare('isProMentor', $this->isProMentor);
$criteria->compare('isPerMentor', $this->isPerMentor);
$criteria->compare('isDomMentor', $this->isDomMentor);
$criteria->compare('isStudent', $this->isStudent);
$criteria->compare('isMentee', $this->isMentee);
//$criteria->compare('isJudge', $this->isJudge);
//$criteria->compare('isEmployer', $this->isEmployer);
return new CActiveDataProvider($this, array(
'criteria' => $criteria,
));
}
public function getCombineRoles(){
$st = '';
if ($this->isProMentor)
$st .= 'Project ';
if ($this->isPerMentor)
$st .= 'Personal ';
if ($this->isDomMentor)
$st .= 'Domain ';
if ($this->isMentee)
$st .= 'Mentee';
return $st;
}
/* retrieve all user ids in the system */
public static function getAllUserId()
{
$userid = User::model()->findBySql("SELECT id from user, user_domain WHERE ");
return $userid;
}
public static function getCurrentUser()
{
$username = Yii::app()->user->name;
$user = User::model()->find("username=:username", array(':username' => $username));
return $user;
}
public static function getCurrentUserId()
{
$username = Yii::app()->user->name;
$user = User::model()->find("username=:username", array(':username' => $username));
if ($user == null) { Yii::app()->getController()->redirect('/coplat/index.php/site/login'); }
return $user->id;
}
public static function getUser($userid)
{
$user = User::model()->findByPk($userid);
return $user;
}
public static function getUserName($userid)
{
$user = User::model()->findByPk($userid);
return $user->username;
}
public function isAdmin()
{
return $this->isAdmin;
}
public function isProMentor()
{
return $this->isProMentor;
}
public function isPerMentor()
{
return $this->isPerMentor;
}
public function isDomMentor()
{
return $this->isDomMentor;
}
public function isMentee()
{
return $this->isMentee;
}
public function isJudge()
{
return $this->isJudge;
}
public function isEmployer()
{
return $this->isEmployer;
}
public function isStudent()
{
return $this->isStudent;
}
public static function isCurrentUserAdmin()
{
$username = Yii::app()->user->name;
$user = User::model()->find("username=:username", array(':username' => $username));
if ($user == null)
return false;
return $user->isAdmin;
}
public static function isCurrentUserMentee()
{
$username = Yii::app()->user->name;
$user = User::model()->find("username=:username", array(':username' => $username));
if ($user == null)
return false;
return $user->isMentee;
}
public static function isCurrentUserProMentor()
{
$username = Yii::app()->user->name;
$user = User::model()->find("username=:username", array(':username' => $username));
if ($user == null)
return false;
return $user->isProMentor;
}
public static function isCurrentUserDomMentor()
{
$username = Yii::app()->user->name;
$user = User::model()->find("username=:username", array(':username' => $username));
if ($user == null)
return false;
return $user->isDomMentor;
}
public static function isCurrentUserPerMentor()
{
$username = Yii::app()->user->name;
$user = User::model()->find("username=:username", array(':username' => $username));
if ($user == null)
return false;
return $user->isPerMentor;
}
public static function isCurrentUserJudge()
{
$username = Yii::app()->user->name;
$user = User::model()->find("username=:username", array(':username' => $username));
if ($user == null)
return false;
return $user->isJudge;
}
public static function isCurrentUserEmployer()
{
$username = Yii::app()->user->name;
$user = User::model()->find("username=:username", array(':username' => $username));
if ($user == null)
return false;
return $user->isEmployer;
}
public static function isCurrentUserStudent()
{
$username = Yii::app()->user->name;
$user = User::model()->find("username=:username", array(':username' => $username));
if ($user == null)
return false;
return $user->isStudent;
}
OK I do not know what kind logic you were trying. I guess you want to list the data based on search criteria given in advanced_search.php. You need to write the logic for results in Search function.
If you are unable to understand let me know. Also activated, disable, isProMentor etc are individual fields? Or paste your entire model User.php here
$criteria->compare('activated', $this->activated);
$criteria->compare('disable', $this->disable);
$criteria->compare('isProMentor', $this->isProMentor);
$criteria->compare('isPerMentor', $this->isPerMentor);
$criteria->compare('isDomMentor', $this->isDomMentor);
$criteria->compare('isStudent', $this->isStudent);
$criteria->compare('isMentee', $this->isMentee);
In my application, only the admin user can create and, theoretically, edit users. So far, using only the Symfony security system (no FOSUserBundle management - its complexity is not required), creating users with varying roles is just fine. The challenge that totally escapes me is how to edit a user without knowing the user's password. I keep running into the expected validation error
Password cannot be empty
. How can editing be accomplished? I'm surely missing something very fundamental here.
Edit action:
public function editAction($id) {
$em = $this->getDoctrine()->getManager();
$user = $em->getRepository('ManaClientBundle:User')->find($id);
$form = $this->createForm(new UserType(), $user);
return array(
'form' => $form->createView(),
'user' => $user,
'title' => 'Edit user',
);
}
Update action:
public function updateAction(Request $request, $id) {
$em = $this->getDoctrine()->getManager();
$user = $em->getRepository('ManaClientBundle:User')->find($id);
$originalPassword = $user->getPassword();
$form = $this->createForm(new UserType(), $user);
$form->bind($request);
if ($form->isValid()) {
$plainPassword = $form->get('password')->getData();
if (!empty($plainPassword)) {
//encode the password
$encoder = $this->container->get('security.encoder_factory')->getEncoder($entity); //get encoder for hashing pwd later
$tempPassword = $encoder->encodePassword($entity->getPassword(), $entity->getSalt());
$user->setPassword($tempPassword);
}
else {
$user->setPassword($originalPassword);
}
$em->persist($user);
$em->flush();
return $this->redirect($this->generateUrl('user_main', array()));
}
User form:
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('enabled', 'choice', array(
'choices' => array('Yes' => 'Yes', 'No' => 'No'),
'expanded' => true,
'multiple' => false,
'label' => 'Enabled: ',
))
->add('fname')
->add('sname')
->add('email')
->add('username')
->add('password', 'repeated', array(
'type' => 'password',
'invalid_message' => 'Password fields do not match',
'first_options' => array('label' => 'Password'),
'second_options' => array('label' => 'Repeat Password'),
))
->add('role', 'choice', array(
'choices' => array('ROLE_USER' => 'User', 'ROLE_ADMIN' => 'Admin'),
'expanded' => true,
'multiple' => false,
'label' => 'Group: ',
))
;
}
Until I see a more elegant solution, here's what I came up with:
Create a UserEditType form class with all fields but the password field(s)
Assign UserEditType to a validation group other than Default
Configure the password length constraint to the validation group in 2.
Modify the edit and update actions to use UserEditType
And now users can be edited without having the password!
UserEditType:
class UserEditType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('enabled', 'choice', array(
'choices' => array('Yes' => 'Yes', 'No' => 'No'),
'expanded' => true,
'multiple' => false,
'label' => 'Enabled: ',
))
->add('fname')
->add('sname')
->add('email')
->add('username')
->add('role', 'choice', array(
'choices' => array('ROLE_USER' => 'User', 'ROLE_ADMIN' => 'Admin'),
'expanded' => true,
'multiple' => false,
'label' => 'Group: ',
))
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver->setDefaults(array(
'data_class' => 'Mana\ClientBundle\Entity\User',
'validation_groups' => array('edit'),
));
}
Password in User entity:
* #ORM\Column(name="userpass", type="string", length=100, nullable=false)
* #Assert\NotBlank(message="Password may not be empty")
* #Assert\Length(
* min = "5",
* max = "12",
* minMessage = "Password must be at least 5 characters long",
* maxMessage = "Password cannot be longer than than 12 characters",
* groups = {"Default"}
* )
Update action:
public function updateAction(Request $request, $id) {
$em = $this->getDoctrine()->getManager();
$user = $em->getRepository('ManaClientBundle:User')->find($id);
$form = $this->createForm(new UserEditType(), $user);
$form->bind($request);
if ($form->isValid()) {
$em->persist($user);
$em->flush();
return $this->redirect($this->generateUrl('user_main', array()));
}
return array(
'form' => $form->createView(),
'user' => $user,
'title' => 'Edit user',
);
}
I've had the same problem here in my project.
I solved it by removing the password field from the form just for my edit action.
So, in my UserController, I changed the editAction:
//find the line where the form is created
$editForm = $this->createForm(new UserType($this->container), $entity)
->remove('password'); //add this to remove the password field
I do something like this (untested code)
My User entity has a password property mapped to DB
It also has a 'plainPassword' property, that is not mapped
class User {
// mapped
private string $username;
// mapped
private string $password;
// not mapped - simple php property
private string $plainPassword;
// getters/setters
...
}
The form, uses the plainPassword property, not the mapped password.
class UserType extends AbstractType {
...
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('username', TextType::class)
->add('plainPassword', PasswordType::class, ['required' => false])
}
...
}
And then somewhere, controller in this example, we check if the plainPassword is not empty - thus the password is trying to be changed.
public function updateUserAction(User $user, Request $request)
{
$form = $this->formFactory->createForm(UserType::class, $user);
if($request->getMethod() === 'POST') {
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
if(0 !== strlen($user->getPlainPassword()) {
$encoder = $this->encoderFactory->getPasswordHasher($user);
$salt = rtrim(str_replace('+', '.', base64_encode(random_bytes(32))), '=');
$user->setSalt($salt);
$hashedPassword = $encoder->hash($user->getPlainPassword(), $user->getSalt());
$user->setPassword($hashedPassword);
$user->setPlainPassword(null);
}
$this->em->persist($user);
$this->em->flush();
return $this->redirectToRoute('something');
}
}
}
If you want to use the remove() function then apply also at the form setting. At least in Symfony 3.3. In this way you will avoid the password confirmation stated by #pusle above:
$form = $this->formFactory->createForm()->remove("current_password");
$form->setData($user)->remove("current_password");
Here the whole method in the ProfileController of the FOSUserBundle. It works for me:
public function editDiffAction($id, Request $request)
{
$userManager = $this->get('fos_user.user_manager');
$user = $userManager->findUserBy(['id' => $id]);
$event = new GetResponseUserEvent($user, $request);
if (null !== $event->getResponse()) {
return $event->getResponse();
}
$form = $this->formFactory->createForm()->remove("current_password");
$form->setData($user)->remove("current_password");
$form->handleRequest($request);
if ($form->isValid()) {
$event = new FormEvent($form, $request);
$userManager = $this->get('fos_user.user_manager');
$userManager->updateUser($user);
$url = $this->generateUrl('fos_user_profile_show_diff', array('id' => $user->getId() ));
$response = new RedirectResponse($url);
return $response;
}
return $this->render('#FOSUser/Profile/edit_diff.html.twig', array(
'form' => $form->createView(),
'user_id' => $user->getId(),
));
}
Just add 'disabled' => 'disabled' and this field won't be taken into account.
I have a simple user manager in my backend, and I want to be able to edit the user without setting a new password/repeating the old password every time.
Right now if I leave the password fields blank when editing a user, symfony2 complains that a password must be entered, and of course I want this functionality when I register new users, but when I edit them, I'd like for the form to just ignore the password boxes if they aren't filled out.
How is this accomplished?
For someone else's reference, I worked this one out this way.
My formType:
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('username', 'text', array('label' => 'Servernamn '))
->add('plainPassword', 'repeated', array('type' => 'password', 'first_name' => 'Lösenord för server ', 'second_name' => 'Upprepa lösenord för server',));
$builder-> addValidator(new CallbackValidator(function(FormInterface $form){
$username = $form->get('username')->getData();
if (empty($username)) {
$form['username']->addError(new FormError("Du måste ange ett namn för servern"));
}
}));
}
My updateAction:
public function updateServerAction($id)
{
$em = $this->getDoctrine()->getEntityManager();
$entity = $em->getRepository('BizTVUserBundle:User')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find Container entity.');
}
$originalPassword = $entity->getPassword();
$editForm = $this->createForm(new editServerType(), $entity);
$request = $this->getRequest();
$editForm->bindRequest($request);
if ($editForm->isValid()) {
$plainPassword = $editForm->get('plainPassword')->getData();
if (!empty($plainPassword)) {
//encode the password
$encoder = $this->container->get('security.encoder_factory')->getEncoder($entity); //get encoder for hashing pwd later
$tempPassword = $encoder->encodePassword($entity->getPassword(), $entity->getSalt());
$entity->setPassword($tempPassword);
}
else {
$entity->setPassword($originalPassword);
}
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('Server'));
}
Thus updating my users password should it be set, otherwise keep the original password.
On your entity setter for the password property make it like this:
/**
* Set password
*
* #param string $password
* #return User
*/
public function setPassword($password)
{
if (!is_null($password)) {
$this->password = $password;
}
return $this;
}
The trick is to check if the parameter being passed is empty and only setting if it's not.
If you need add required option to $options array. Example:
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('username', 'text', array('label' => 'Servernamn '))
->add('plainPassword', 'repeated', array(
'type' => 'password',
'first_name' => 'password',
'second_name' => 'repeat_password',
'required' => false,
));
}
What I did was to leave the UserType as is and only remove the password field in the controller:
/**
* #Route("/users/edit/{username}", name="user_edit")
*/
public function editAction(Request $request, User $user)
{
$form = $this->createForm(UserType::class, $user);
// Remove the password field when editing the user
// Otherwise it must be entered each time the user is edited
// We can change the password via a special edit-user-password page
$form->remove('password');
$form->handleRequest($request);
if ($form->isValid()) {
// ...
}
}
This way you can have a single UserType, reusable in the editAction and the newAction.
Be sure to add the line $form->remove('password'); before $form->handleRequest($request);.
There is an article gives many options to achieve your request, from the Symfony2 cookbook, in particular, the section below worked for me:
Customizing your Form based on the underlying Data
Below is the implementation:
public function buildForm(FormBuilderInterface $builder, array $options)
{
// ...
$builder->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event) {
$product = $event->getData();
$form = $event->getForm();
// check if the Product object is "new"
// If no data is passed to the form, the data is "null".
// This should be considered a new "Product"
if (!$product || null === $product->getId()) {
$form->add('name', 'text');
}
});
}
For the validation use 2 different validation groups and set the correct one by the callable function.
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add(
'oldPassword',
PasswordType::class,
[
'constraints' => new UserPassword([
'groups' => 'profile_password',
]),
'mapped' => false,
'required' => false,
]
);
$builder->add(
'plainPassword',
PasswordType::class,
[
'constraints' => new NotBlank([
'groups' => 'profile_password',
]),
'mapped' => false,
'required' => false,
]
);
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(
[
'validation_groups' => function (FormInterface $form) {
$newPassword = $form->get('plainPassword')->getData();
$oldPassword = $form->get('oldPassword')->getData();
if ($oldPassword || $newPassword) {
return ['profile', 'profile_password'];
} else {
return ['profile'];
}
},
]
);
}
Symfony 5 method:
Inside a controller class:
/**
* #Route("/user/{id}/edit")
*/
public function updateUser(
$id, // See 'Route' annot., needed to `find()` User, adjust to your needs
User $user,
Request $request,
UserPasswordEncoderInterface $passwordEncoder,
UserRepository $userRepository
): Response {
$form = $this->createForm(UserFormType::class, $user);
// if password field is required by default, we have to `add()` it again
// with `required` set to `false`
$form->add(
'plainPassword',
PasswordType::class,
[
// do not use Constraint NotBlank()
'required' => false,
'mapped' => false
]
);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$entity = $userRepository->find($id); // as above, adjust to your needs
if (!$entity) {
throw $this->createNotFoundException('User does not exist!');
}
$originalPassword = $entity->getPassword();
/** #var User $user */
$user = $form->getData();
// keep original password if new one isn't provided
if (!empty($form['plainPassword']->getData())) {
$user->setPassword(
$passwordEncoder->encodePassword(
$user,
$form['plainPassword']->getData()
)
);
} else {
// set old password
$user->setPassword($originalPassword);
}
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
return new Response('User updated!');
}
return $this->render(
'admin/user_edit.html.twig',
[
'requestForm' => $form->createView(),
]
);
}
You can do something with
if (empty($password)) {
//edit the password
}
Try fixing that into your form's PHP.