Yii2 database session - store additional attributes and user information - php

I'm using Yii2's DBSession class to store web application sessions into a database table, called session.
This table by default only has 3 columns - id, expire and data.
I'd like to store additional information into this table, like the user_id of a logged in user.
Edit: So there's a parent class called yii\web\MultiFieldSession but no examples about how it's used. I'll see what I can discover...

create migration:
$this->createTable('session', [
'id' => $this->char(40)->notNull(),
'expire' => $this->integer(),
'data' => $this->binary(),
'user_id' => $this->integer()
]);
$this->addPrimaryKey('session_pk', 'session', 'id');
add this to config:
'components' => [
'session' => [
'class' => 'yii\web\DbSession',
'writeCallback' => function($session){
return [
'user_id' => Yii::$app->user->id
];
}
// 'db' => 'mydb', // the application component ID of the DB connection. Defaults to 'db'.
// 'sessionTable' => 'my_session', // session table name. Defaults to 'session'.
],

Check writeCallBack (a callback that will be called during session data writing)
By Using callback you can set your own database fields.. Used by composeField()

I didn't want a function in a config file, so I ended up with this:
config/web.php
'components' =>
[
'session' =>
[
'class' => 'app\models\Session',
],
],
models/Session.php
class Session extends \yii\web\DbSession
{
protected function composeFields($id = null, $data = null)
{
$fields = parent::composeFields($id, $data);
$fields['user_id'] = \Yii::$app->user->id;
return $fields;
}
}

Related

Where is session files stored in Yii2?

Where is session files stored in Yii2? I need to know the the exact location. There is options to create a session database.
The default session save path is '/tmp'. link
This path is accessible via the getSavePath() method in the session class file(yii2)
#property string $savePath The current session save path, defaults to '/tmp'.
For example, in xampp software (localhost) go to the following folder(default)
myDrive:\xampp\tmp // The drive where the software is installed
It is taken by default through the session_save_path method. Which depends on the settings of the php.ini file. In session.save_path="...\tmp"
But you can also configure it through the .htaccess file
To adjust Yii2, you can do the following. In the config web file
'components' => [
'session' => [
'name' => '_Session',
'savePath' => dirname(__DIR__) .'/sessions'
],
To save in the database(yii\web\DbSession) refer to this link.
Example:
'session' => [
'class' => 'yii\web\DbSession',
'name' => 'mySession',
// 'db' => 'mydb', // the application component ID of the DB connection. Defaults to 'db'.
// 'sessionTable' => 'my_session', // session table name. Defaults to 'session'.
'timeout' => 30 * 24 * 3600,
'cookieParams' => ['httponly' => true, 'lifetime' => 3600 * 24],
'writeCallback' => function ($session) {
return [
// 'user_id' => Yii::$app->user->id,
// 'last_write' => time(),
];
},
],
writeCallback: To create more data and columns in the database table
Good luck
Yii2 by default stores session files in #app/runtime/data folder.
And if you want to use database instead then yii2 guide is great resource. check this link: https://www.yiiframework.com/doc/guide/2.0/en/runtime-sessions-cookies#custom-session-storage.

CakePHP DC Users 8.5.1 customising to use email

I have a CakePHP application running on Cake PHP 3.8.13 and CakeDC Users 8.5.1.
I am currently able to log on using the username field, but I would like to use the email field for authentication. I have followed the instructions on https://github.com/CakeDC/users/blob/master/Docs/Documentation/Configuration.md#using-the-users-email-to-login but the system is still trying to use the username field. If I change email to username in the src/Template/Plugin/CakeDC/Users/Users/login.ctp I can log in using the username.
How can I get it to use the email field instead?
src/Application.php
<?php
namespace App;
use Cake\Core\Configure;
use Cake\Core\Exception\MissingPluginException;
use Cake\Error\Middleware\ErrorHandlerMiddleware;
use Cake\Http\BaseApplication;
use Cake\Http\Middleware\SecurityHeadersMiddleware;
use Cake\Routing\Middleware\AssetMiddleware;
use Cake\Routing\Middleware\RoutingMiddleware;
use Cake\Http\Middleware\EncryptedCookieMiddleware;
class Application extends BaseApplication
{
/**
* {#inheritDoc}
*/
public function bootstrap()
{
// Call parent to load bootstrap from files.
parent::bootstrap();
$this->addPlugin('AuditStash');
if (PHP_SAPI === 'cli') {
try {
$this->addPlugin('Bake');
} catch (MissingPluginException $e) {
// Do not halt if the plugin is missing
}
$this->addPlugin('Migrations');
}
/*
* Only try to load DebugKit in development mode
* Debug Kit should not be installed on a production system
*/
if (Configure::read('debug')) {
$this->addPlugin(\DebugKit\Plugin::class);
}
$this->addPlugin(\CakeDC\Users\Plugin::class);
Configure::write('Users.config', ['users']);
$identifiers = Configure::read('Auth.Identifiers');
$identifiers['Authentication.Password']['fields']['username'] = 'email';
Configure::write('Auth.Identifiers', $identifiers);
}
/**
* Setup the middleware queue your application will use.
*
* #param \Cake\Http\MiddlewareQueue $middlewareQueue The middleware queue to setup.
* #return \Cake\Http\MiddlewareQueue The updated middleware queue.
*/
public function middleware($middlewareQueue)
{
$securityHeaders = new SecurityHeadersMiddleware();
$securityHeaders
->setCrossDomainPolicy()
->setReferrerPolicy()
->setXFrameOptions()
->setXssProtection()
->noOpen()
->noSniff();
$middlewareQueue
// Add security headers
->add($securityHeaders)
// Catch any exceptions in the lower layers,
// and make an error page/response
->add(ErrorHandlerMiddleware::class)
// Handle plugin/theme assets like CakePHP normally does.
->add(new AssetMiddleware([
'cacheTime' => Configure::read('Asset.cacheTime')
]))
// Add routing middleware.
// Routes collection cache enabled by default, to disable route caching
// pass null as cacheConfig, example: `new RoutingMiddleware($this)`
// you might want to disable this cache in case your routing is extremely simple
->add(new RoutingMiddleware($this, '_cake_routes_'));
$cookies = new EncryptedCookieMiddleware(
// Names of cookies to protect
['remember_me', 'csrfToken'],
Configure::read('Security.cookieKey')
);
$middlewareQueue->add($cookies);
return $middlewareQueue;
}
}
src/config/users.php
<?php
return [
// Table used to manage users
'table' => 'Users',
// Controller used to manage users plugin features & actions
'controller' => 'Users',
// configure Auth component
'auth' => true,
// Password Hasher
'passwordHasher' => '\Cake\Auth\DefaultPasswordHasher',
// token expiration, 1 hour
'Token' => ['expiration' => 3600],
'Email' => [
// determines if the user should include email
'required' => true,
// determines if registration workflow includes email validation
'validate' => true,
],
'Registration' => [
// determines if the register is enabled
'active' => false,
// determines if the reCaptcha is enabled for registration
'reCaptcha' => true,
// allow a logged in user to access the registration form
'allowLoggedIn' => false,
//ensure user is active (confirmed email) to reset his password
'ensureActive' => false,
// default role name used in registration
'defaultRole' => 'user',
],
'reCaptcha' => [
// reCaptcha key goes here
'key' => null,
// reCaptcha secret
'secret' => null,
// use reCaptcha in registration
'registration' => false,
// use reCaptcha in login, valid values are false, true
'login' => false,
],
'Tos' => [
// determines if the user should include tos accepted
'required' => true,
],
'Social' => [
// enable social login
'login' => false,
// enable social login
'authenticator' => 'CakeDC/Users.Social',
],
'GoogleAuthenticator' => [
// enable Google Authenticator
'login' => false,
'issuer' => null,
// The number of digits the resulting codes will be
'digits' => 6,
// The number of seconds a code will be valid
'period' => 30,
// The algorithm used
'algorithm' => 'sha1',
// QR-code provider (more on this later)
'qrcodeprovider' => null,
// Random Number Generator provider (more on this later)
'rngprovider' => null
],
'Profile' => [
// Allow view other users profiles
'viewOthers' => false,
'route' => ['plugin' => 'CakeDC/Users', 'controller' => '\Users', 'action' => 'profile'],
],
'Key' => [
'Session' => [
// session key to store the social auth data
'social' => 'Users.social',
// userId key used in reset password workflow
'resetPasswordUserId' => 'Users.resetPasswordUserId',
],
// form key to store the social auth data
'Form' => [
'social' => 'social'
],
'Data' => [
// data key to store the users email
'email' => 'email',
// data key to store email coming from social networks
'socialEmail' => 'info.email',
// data key to check if the remember me option is enabled
'rememberMe' => 'remember_me',
],
],
// Avatar placeholder
'Avatar' => ['placeholder' => 'CakeDC/Users.avatar_placeholder.png'],
'RememberMe' => [
// configure Remember Me component
'active' => false,
'checked' => true,
'Cookie' => [
'name' => 'remember_me',
'Config' => [
'expires' => '1 month',
'httpOnly' => true,
]
]
],
];
src/Template/Plugin/CakeDC/Users/Users/login.ctp
<?php
use Cake\Core\Configure;
?>
<div class="users form">
<?= $this->Flash->render('auth') ?>
<?= $this->Form->create() ?>
<fieldset>
<legend><?= __d('CakeDC/Users', 'Please enter your username and password') ?></legend>
<?= $this->Form->control('email', ['required' => true]) ?>
<?= $this->Form->control('password', ['required' => true]) ?>
</fieldset>
<?= $this->Form->button(__d('CakeDC/Users', 'Login')); ?>
<?= $this->Form->end() ?>
</div>
Add this code
$identifiers = Configure::read('Auth.Identifiers');
$identifiers['Authentication.Password']['fields']['username'] = 'email';
Configure::write('Auth.Identifiers', $identifiers);
to your pluginBootstrap() function in Application, or ensure the identifier configuration is correctly updated in the config/users.php file, so the plugin will be able to read it and configure Authentication internally.

Yii2 Load schedule using unclead/yii2-multiple-input

I am using unclead / yii2-multiple-input widget.
I want to generate different number of rows with values from my database.
How can i do this?
I can design my columns in view and edit data manualy after page generated. But miss how to program the number of rows and its values in the view.
My code in view:
<?= $form->field($User, 'User')->widget(MultipleInput::className(), [
'min' => 0,
'max' => 4,
'columns' => [
[
'name' => 'name',
'title' => 'Name',
'type' => 'textInput',
'options' => [
'onchange' => $onchange,
],
],
[
'name' => 'birth',
'type' => \kartik\date\DatePicker::className(),
'title' => 'Birth',
'value' => function($data) {
return $data['day'];
},
'options' => [
'pluginOptions' => [
'format' => 'dd.mm.yyyy',
'todayHighlight' => true
]
]
],
]
])->label(false);
How can I make (for example) 8 rows with different values, and also have the ability to edit/remove/update some of them?
You need to look into the documentation as it says that you need to assign a separate field into the model which will store all the schedule in form of JSON and then provide it back to the field when editing/updating the model.
You have not added the appropriate model to verify how are you creating the field User in your given case above. so, i will try to create a simple example which will help you implement it in your scenario.
For Example.
You have to store a user in the database along with his favorite books.
User
id, name, email
Books
id, name
Create a field/column in you User table with the name schedule of type text, you can write a migration or add manually.
Add it to the rules in the User model as safe.
like below
public function rules() {
return [
....//other rules
[ [ 'schedule'] , 'safe' ]
];
}
Add the widget to the newly created column in ActiveForm
see below code
echo $form->field($model,'schedule')->widget(MultipleInput::class,[
'max' => 4,
'columns' => [
[
'name' => 'book_id',
'type' => 'dropDownList',
'title' => 'Book',
'items' => ArrayHelper::map( Books::find()->asArray()->all (),'id','name'),
],
]
]);
When saving the User model convert the array to JSON string.
like below
if( Yii::$app->request->isPost && $model->load(Yii::$app->request->post()) ){
$model->schedule = \yii\helpers\Json::encode($model->schedule);
$model->save();
}
Override the afterFind() of the User model to covert the json back to the array before loading the form.
like below
public function afterFind() {
parent::afterFind();
$this->schedule = \yii\helpers\Json::decode($this->schedule);
}
Now when saved the schedule field against the current user will have the JSON for the selected rows for the books, as many selected, for example, if I saved three books having ids(1,2,3) then it will have json like below
{
"0": {
"book_id": "1"
},
"2": {
"book_id": "2"
},
"3": {
"book_id": "3"
}
}
The above JSON will be converted to an array in the afterFind() so that the widget loads the saved schedule when you EDIT the record.
Now go to your update page or edit the newly saved model you will see the books loaded automatically.

Yii2 Kartik EditableColumn Dropdown Relation Returns wrong Value

I have an issue with a Gridview using kartik\grid\EditableColumn, after changing the value I am returned the wrong value for the column when it updates. I am returned the dropdown key/main table integer rather than the string contained in a linked table.
I have two tables
Leads - columns id and status_id
Related fields - model, field, related_value, related_value
The relation is based on in this case
model:"Leads",
field:"status_id",
related_id:status_id
I have the following relation in my model
public function getStatus()
{
return $this->hasOne(RelatedFields::className(), ["related_id" => "status_id"])->andOnCondition(["status.field" => "status_id", "status.model"=>"Leads"])->from(["status" => RelatedFields::tableName()]);
}
I also created the following as a test based on this link
public function getStatusValue()
{
return $this->status->related_value;
}
Here is the column code
[
'class' => 'kartik\grid\EditableColumn',
'attribute' => 'status_id',
'value'=>'status.related_value',
//'value' => function($model){ return $model->status->related_value; },
//'value' => function($model){ return $model->StatusValue; },
//'refreshGrid' => true,//Works but not nice
'vAlign'=>'middle',
'hAlign'=>'center',
'pageSummary' => true,
'readonly' => false,
'width'=>'10%',
'filter'=>Html::activeDropDownList($searchModel, 'status', ArrayHelper::map(RelatedFields::Find()->where(['model' =>"Leads","field"=>"status_id"])->all(), 'related_id', 'related_value'),['class' => 'form-control','prompt' => Yii::t('app', '')]),
'editableOptions'=> [
//'attribute'=>'status_id',
//'value'=>'status.related_value',
//'header' => 'profile',
//'format' => Editable::FORMAT_BUTTON,
'inputType' => Editable::INPUT_DROPDOWN_LIST,
'data'=> ArrayHelper::map(RelatedFields::Find()->where(['model' =>"Leads","field"=>"status_id"])->all(), 'related_id', 'related_value'),
]
],
Commented out are a number of lines in my attempts to fix the issue as well as combinations of them, however all result in the wrong value.
If for example I select the related value "New" which has a related_id 1, after the column has been updated I get the value 1 instead of "New".
When the table is first loaded/reloaded the value does show correctly.
I could reload the grid, but this seems wrong just to fix 1% of the data shown on the page.
I your model take a public variable $status_value
create an assigning value method
public function getStatusValue()(){
return $this->status_value= $this->status->related_value;
}
Now in Gridview use getStatusValueenter code heremethod with assigning value as below
use yii\helpers\Url;
$gridColumns = [
[
'class' => 'kartik\grid\EditableColumn',
'attribute' => 'status_value',
'pageSummary' => true,
'readonly' => false,
'value' => function($model){ return $model->statusValue; }, // assign value from getStatusValue method
'editableOptions' => [
'header' => 'status_value',
'inputType' => kartik\editable\Editable::INPUT_TEXT,
'options' => [
'pluginOptions' => [
]
]
],
],
];
If you follow Kartik guide, he suggest to add EditableColumnAction to better handle the editable column:
The EditableColumnAction offers a quick easy way to setup your
controller action for updating, saving and managing the EditableColumn
output from GridView. This action class extends from yii\rest\Action
and hence all properties available with yii\rest\Action are applicable
here. The basic setup of the column involves setting up the controller
action and the EditableColumn.
So you need to add an EditableColumnAction in your controller to handle the update of the model:
public function actions()
{
return ArrayHelper::merge(parent::actions(), [
'edit-lead' => [
'class' => EditableColumnAction::class,
'modelClass' => Leads::class
]
]);
}
In your GridView editable column configuration, include the above
controller action for processing the Editable within editableOptions.
For example
And in your column code you need to add the action to editableOptions property:
'editableOptions' => [
...
'formOptions' => ['action' => ['/leads/edit-lead']]
]
Now, according to the guide, you can add to your action the outputValue property:
'outputValue' => function (Leads $model) {
return $model->status->related_value;
}

Multiple Login table in yii2 IdentityInterface error while login

I have 2 different tables user and organiser and i am trying to create 2 different login for both users.
I am able to sign them up easily and get the record in database but after saving the record i get the error on following code line
if ($user = $model->signup()) {
if (Yii::$app->getUser()->login($user)) { //yii\web\IdentityInterface error
return $this->goHome();
}
}
Following is my configuration module
'components' => [
'user' => [
'identityClass' => 'common\models\User',
'enableAutoLogin' => true,
'identityCookie' => [
'name' => '_frontendOrganiser', // unique for frontend
],
],
'users' => [
'class' => 'yii\web\User',
'identityClass' => 'common\models\Users',
'enableAutoLogin' => false,
'enableSession' => true,
'identityCookie' => [
'name' => '_frontendUser', // unique for frontend
],
],
'session' => [
'name' => 'PHPFRONTSESSID',
'savePath' => sys_get_temp_dir(),
],
]
So what is wrong am i doing here? Do i need to create any other Interface or something or provide different interface for different module?
And i had to do this because organiser table uses password_hash technique to log in the users where my user table is from another project and they uses md5 technique, so i had to create separate module for both users.
Argument 1 passed to yii\web\User::login() must be an instance of yii\web\IdentityInterface, instance of common\models\Users given, called in C:\xampp\htdocs\project\frontend\controllers\SiteController.php on line 202 and defined
The exact error statement is as above.
I think your user model don't implement the Identity interface correctly.
Try check you data model also (in your DB) this must contain all the field managed bay the interface.
And be sure you User implement the Identity Interface correctly.
and mapping the interface method with your model correctly..
See the interface doc for this http://www.yiiframework.com/doc-2.0/yii-web-identityinterface.html

Categories