How to use validationDefault method in cakephp 3 - php

I am trying to use validationDefault method in my model UsersTable :
<?php
namespace App\Model\Table;
use Cake\ORM\Table;
use Cake\Validation\Validator;
class UsersTable extends Table
{
public function initialize(array $config)
{
$this->addBehavior('Timestamp');
$this->setTable('users');
$this->setPrimaryKey('id');
}
public function validationDefault(Validator $validator)
{
$validator->add('login', [
'length' => [
'rule' => ['minLength',3],
'message' => __('Login need to be at least 3 characters long')
]
]);
return $validator;
}
}
In my Users controller i have the following code :
<?php
namespace App\Controller;
use Cake\Validation\Validator;
class UsersController extends AppController {
public function initialize() {
parent::initialize();
}
public function add() {
if ($this->request->is('post')) {
$user = $this->Users->newEntity($this->request->getData());
if ($this->Users->save($user)) {
$this->Flash->success(__('Your user has been created.'));
return $this->redirect(['action' => 'index']);
}
$this->Flash->error(__('Unable to create your user.'));
}
My form looks like :
<?= $this->Form->create('Users', ['url' => ['controller' => 'Users', 'action' => 'add']]); ?>
<fieldset>
<legend><?= __('Ajouter un utilisateur') ?></legend>
<?= $this->Form->control('login') ?>
<?= $this->Form->control('password') ?>
<?= $this->Form->control('role', [
'options' => ['admin' => 'Admin', 'author' => 'Author']
]) ?>
</fieldset>
<?= $this->Form->button(__('Ajouter')); ?>
<?= $this->Form->end() ?>
If I use form with "aa" in login form, data are insert in database. But they should not be insert because validator define minLength to 3.
It's seem like validationDefault is not call when I use save method.
Here a debug of var $user :
object(Cake\ORM\Entity) {
'login' => 'g',
'password' => '',
'role' => 'admin',
'[new]' => true,
'[accessible]' => [
'*' => true
],
'[dirty]' => [
'login' => true,
'password' => true,
'role' => true
],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'Users'
}
Note: if I use validator directly in controller, an error is print : Login need to be at least 3 characters long.But i don't want use validator directly in controller...
$validator = new Validator();
$validator->add('login', [
'length' => ['rule' => ['minLength', 3],
'message' => __('Login need to be at least 3 characters long')
]
]);
if($validator->errors($this->request->getData())) {
debug($validator->errors($this->request->getData()));
}

I ask this problem to channel #cakephp on IRC and they say to me to reinstall cakephp but using bake to generate model classes.
After that, all is working fine.

Related

yii2 session is not saved login from postman / app

I am using frontend module in yii2 framework for my website, and so far the website is working good for all things especially the session of login from web interface.
The model handling the login is the LoginForm model
namespace app\models;
use Yii;
use yii\base\Model;
/**
* Login form
*/
class LoginForm extends Model {
public function login()
{
if ($this->validate()) {
return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 7200 * 24 * 30 : 7200 * 24 * 30);
}
return false;
}
For website the default file for login is in SiteCotroller.php
use yii\web\Controller;
use frontend\models\ContactForm;
/**
* Site controller
*/
class SiteController extends Controller {
/**
* #inheritdoc
*/
public function behaviors() {
return [
'access' => [
'class' => AccessControl::className(),
'only' => ['logout', 'signup', 'index'],
'rules' => [
[
'actions' => ['signup'],
'allow' => true,
'roles' => ['?'],
],
[
'actions' => ['logout', 'index'],
'allow' => true,
'roles' => ['#'],
],
],
],
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'logout' => ['post'],
],
],
];
}
public function actionLogin() {
$this->layout = 'main';
if (!Yii::$app->user->isGuest) {
return $this->goHome();
}
$model = new LoginForm();
if ($model->load(Yii::$app->request->post()) && $model->login()) {
return $this->goBack();
} else {
return $this->render('login', [
'model' => $model,
]);
}
}
}
The login controller is working great for its own interface in the view file.
Now that I want to access login from app (ionic/angular), I copied this controller for login to AppController.php in the similar frontend folder:
class AppController extends \yii\rest\Controller {
public function actions()
{
$actions = parent::actions();
$actions['options'] = [
'class' => 'yii\rest\OptionsAction',
// optional:
'collectionOptions' => ['GET', 'POST', 'HEAD', 'OPTIONS'],
'resourceOptions' => ['GET', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'],
];
return $actions;
}
public function actionIndex() {
echo json_encode("hi");
}
public static function allowedDomains() {
return [
'*', // star allows all domains
'http://localhost:8100',
//'http://test2.example.com',
];
}
public function init()
{
parent::init();
\Yii::$app->user->enableSession = false;
}
public function beforeAction($action)
{
$this->enableCsrfValidation = false;
return parent::beforeAction($action);
}
public function behaviors()
{
$behaviors = parent::behaviors();
unset($behaviors['authenticator']);
// add CORS filter
$behaviors['corsFilter'] = [
'class' => \yii\filters\Cors::className(),
'cors' => [
'Origin' => ['*'],
'Access-Control-Request-Method' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'],
'Access-Control-Request-Headers' => ['*'],
'Access-Control-Allow-Credentials' => true,
],
];
$behaviors['contentNegotiator'] = [
'class' => \yii\filters\ContentNegotiator::className(),
'formats' => [
'application/json' => \yii\web\Response::FORMAT_JSON,
],
];
$behaviors['authenticator'] = [
'class' => HttpBearerAuth::className(),
'except' => ['login', 'checkuser'],
/*
'class' => CompositeAuth::className(),
'authMethods' => [
HttpBasicAuth::className(),
],
'except' => ['login', 'checkuser'],
*/
];
return $behaviors;
}
public function actionLogin() {
$model = new LoginForm();
$params = Yii::$app->request->post();
$model->username = $params['username'];
$model->password = $params['password'];
if ($model->login()) {
$user = User::findByUsername($model->username);
$response['success'] = Yii::$app->user->login($user);
$response['SessionID'] = Yii::$app->session->getId();
return $response;
} else {
$response['errors'] = $model->getErrors();
return $response;
}
}
The strange is, the action login in this AppController.php is working if I tried to login from app/postman, it returns true. However, the session is not saved, and SessionID is empty.
I have been exploring this for three days but still cant figure it out what is the problem.
This is my frontend/config/main.php file
$params = array_merge(
require __DIR__ . '/../../common/config/params.php',
require __DIR__ . '/../../common/config/params-local.php',
require __DIR__ . '/params.php',
require __DIR__ . '/params-local.php'
);
return [
'id' => 'app-frontend',
'defaultRoute' => 'site/index',
'basePath' => dirname(__DIR__),
'bootstrap' => ['log', 'gii'],
'controllerNamespace' => 'frontend\controllers',
'layout' => 'admin',
'components' => [
'request' => [
'parsers' => [
'application/json' => 'yii\web\JsonParser',
],
'csrfParam' => '_csrf-frontend',
],
'user' => [
'identityClass' => 'app\models\User',
'enableAutoLogin' => false,
'enableSession' => true,
'identityCookie' => ['name' => '_identity-frontend', 'httpOnly' => true, 'lifetime' => 3600 * 4],
'loginUrl' => ['site/login'],
],
'session' => [
'name' => 'advanced-frontend',
],
When I access the url directly into the yii2 web domain, I got this _csrf-frontend and advanced-frontend value in browser Cookies. But from app I dont get any.
Please I really appreciate the help. Many thanks in advance.
You cannot retrieve session from another app it is bound to domain. So what should you probably do is to handle that logic separate in your Ionic application which I think is preferred way. Or you can do it by storing sessions in DB (there is Yii class for that) and than handle it through extra requests (which is bad approach).

Yii2 Attach behavior to Application::EVENT_BEFORE_REQUEST

I am trying to check for a cookie on Application::EVENT_BEFORE_REQUEST. What I did is overriding the events function from Behavior model and return a custom function checkLanguage on the event that I mentioned above. I am triggering as beforeRequest in my controller ( in first I tried in the backend/config/main.php but it seems that the CheckIfLoggedIn class can't be reached from there ) and the request goes e to the public function events() in the CheckIfLoggedIn class but doesn't go on to the checkLanguage function.
This is my SiteController behaviors:
public function behaviors()
{
return [
'access' => [
'class' => AccessControl::className(),
'rules' => [
[
'actions' => ['login', 'error'],
'allow' => true,
],
[
'actions' => ['logout', 'index', 'language'],
'allow' => true,
'roles' => ['#'],
],
],
],
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'logout' => ['post'],
],
],
'as beforeRequest' => [
'class' => 'backend\components\CheckIfLoggedIn'
]
];
}
and CheckIfLoggedIn.php class:
<?php
namespace backend\components;
use yii\base\Behavior;
use yii\web\Application;
class CheckIfLoggedIn extends Behavior
{
public function events()
{
return [
Application::EVENT_BEFORE_REQUEST => "changeLanguage"
];
}
public function changeLanguage()
{
if(\Yii::$app->getRequest()->getCookies()->has('lang')){
\Yii::$app->language = \Yii::$app->getRequest()->getCookies()->getValue('lang');
}
}
}
The thing is you are trying to attach an Application event at controller level inside the behavior whereas the documentation says you should use the Application config .
Make the following updates, remove the events() function from your class.
backend/components/CheckIfLoggedIn.php
namespace backend\components;
use yii\base\Behavior;
class CheckIfLoggedIn extends Behavior
{
public function changeLanguage()
{
if(\Yii::$app->getRequest()->getCookies()->has('lang')){
\Yii::$app->language = \Yii::$app->getRequest()->getCookies()->getValue('lang');
}
}
}
and add the following to the common/config or backend/config if you want it for backend only
'on '.yii\web\Application::EVENT_BEFORE_REQUEST => [
'backend\components\CheckIfLoggedIn','changeLanguage'
] ,
remember to add it at the same level where id or components index is defined like this
return [
'id' => 'app-backend' ,
'on '.yii\web\Application::EVENT_BEFORE_REQUEST => [
'backend\components\CheckIfLoggedIn','changeLanguage'
] ,
For the first try add a die("hello world"); in the start of changeLanguage so you can confirm it is entering the function changeLanguage.
Hope it helps

Yii2 Weird Class not found with Empty name

May perfectly understand that a Class 'xxx' is not found.
But there i got an empty className as pasted below, happening in the context of a simple REST auth
PHP Fatal Error
Class '' not found
yii\base\ErrorException
/home/genie/yii-app/vendor/yiisoft/yii2/rest/IndexAction.php
Some code below, but why that guy throws an empty Classname like this ????
Controller that handles the login (User model implements the identityInterface :
<?php
namespace api\controllers;
use yii;
use yii\rest\ActiveController;
use common\models\User;
class RestController extends ActiveController
{
public $modelClass = '';
// public $modelClass = 'common\models\User';
public function behaviors()
{
$behaviors = parent::behaviors();
$behaviors['verbs'] = [
'class' => \yii\filters\VerbFilter::className(),
'actions' => [
'index' => ['get', 'head'],
],
];
$behaviors['access'] = [
'class' => \yii\filters\AccessControl::className(),
'only' => ['index'],
'rules' => [
[
'actions' => ['index'],
'allow' => true,
'roles' => ['#'],
],
],
];
$behaviors['authenticator'] = [
'class' => \yii\filters\auth\HttpBasicAuth::className(),
'auth' => function ($username, $password) {
$user = \common\models\User::findByUsername($username);
if ($user ) {
$pass = \common\models\User::validatePassword($password);
if($pass)
return $user;
}
}
];
return $behaviors;
}
}

cakephp 3 authentication with email claims that username column doesn't exist

I've setup Digest Authentication with email instead of username and tried to configure it properly according to the cookbook. Unfortunately some things are not totally clear. So when trying to edit a User on my cakephp3 application it returns the following error:
Error: SQLSTATE[42703]: Undefined column: 7 ERROR: Column users.username doesn't exist LINE 1: SELECT Users.id AS "Users__id", Users.username AS "Users__us... ^
The Other Crud Actions work though: Create Users, List Users and View a User doesn't return an error. But Login and Delete doesn't work either and in my pgAdmin there seems to be no data row inserted though data was shown (maybe a problem with my elasticsearch plugin?)
I've configured it like this:
CreateUsers Migration:
public function change()
{
$table = $this->table('users', ['id' => false, 'primary_key' => ['id']]);
$table->addColumn('id', 'uuid');
$table->addColumn('email', 'string', [
'default' => null,
'limit' => 254,
'null' => false,
]);
$table->addColumn('password', 'binary', [
'default' => null,
'null' => false,
]);
$table->addColumn('created', 'datetime', [
'default' => null,
'null' => false,
]);
$table->addColumn('modified', 'datetime', [
'default' => null,
'null' => false,
]);
$table->create();
}
AppController:
public function beforeFilter(Event $event)
{
parent::beforeFilter($event);
$this->loadComponent('Auth', [
'authenticate' => [
'Digest' => [
'fields' => ['username' => 'email'],
'userModel' => 'Users',
'finder' => 'auth'
],
],
'loginAction' => [
'controller' => 'Users',
'action' => 'login'
],
'authError' => 'Dazu hast du keine Rechte...',
'storage' => 'Memory',
'unauthorizedRedirect' => false
]);
}
UsersController:
public function beforeFilter(Event $event)
{
parent::beforeFilter($event);
// Load the Type using the 'Elastic' provider.
$this->loadModel('Users', 'Elastic');
$this->Auth->allow(['index', 'view']);
if (!$this->Auth->user()){
$this->Auth->allow('add');
}
}
public function edit($id = null)
{
$user = $this->Users->get($id, [
'contain' => []
]);
if ($this->request->is(['patch', 'post', 'put'])) {
$user = $this->Users->patchEntity($user, $this->request->data);
if ($this->Users->save($user)) {
$this->Flash->success(__('The user has been saved.'));
return $this->redirect(['action' => 'index']);
} else {
$this->Flash->error(__('The user could not be saved. Please, try again.'));
}
}
$this->set(compact('user'));
$this->set('_serialize', ['user']);
}
User Entity:
class User extends Entity
{
protected function _setPassword($password)
{
if (strlen($password) > 0) {
return (new DefaultPasswordHasher)->hash($password);
}
}
}
UsersTable:
public function initialize(array $config)
{
$this->addBehavior('Timestamp', [
'events' => [
'Model.beforeSave' => [
'created_at' => 'new',
'modified_at' => 'always'
]
]
]);
}
public function beforeSave(Event $event)
{
$entity = $event->data['entity'];
// Make a password for digest auth.
$entity->password = DigestAuthenticate::password(
$entity->email, //maybe change to username
$entity->plain_password,
env('SERVER_NAME')
);
$entity->created = Time::now();
return true;
}
public function findAuth(\Cake\ORM\Query $query, array $options)
{
$query
->select(['id', 'username', 'password'])
->where(['Users.active' => 1]);
return $query;
}
Template "Users Edit" View:
<nav class="large-3 medium-4 columns" id="actions-sidebar">
<ul class="side-nav">
<li class="heading"><?= __('Actions') ?></li>
<li><?= $this->Form->postLink(
__('Delete'),
['action' => 'delete', $user->id],
['confirm' => __('Are you sure you want to delete # {0}?', $user->id)]
)
?></li>
<li><?= $this->Html->link(__('List Users'), ['action' => 'index']) ?> </li>
</ul>
</nav>
<div class="users form large-9 medium-8 columns content">
<?= $this->Form->create($user) ?>
<fieldset>
<legend><?= __('Edit User') ?></legend>
<?php
echo $this->Form->input('email');
?>
</fieldset>
<?= $this->Form->button(__('Submit')) ?>
<?= $this->Form->end() ?>
</div>
The only thing I can see from the error message is that it might be a problem from the core in cakephp, at which I wouldn't hardcode any changes into, and the documentation doesn't clearly mention a step when using email instead of username column.
In UsersTable->findAuth() you try to select the field 'username'. the field does not exist, so you get a mysql-error.
public function findAuth(\Cake\ORM\Query $query, array $options)
{
$query
->select(['id', 'username', 'password'])
->where(['Users.active' => 1]);
return $query;
}
public function initialize() {
$this->loadComponent('Auth', [
'authenticate' => [
'Form' => [
'fields' => ['username' => 'email', 'password' => 'password'],
'scope' => ['status' => 1]
]
],
'loginRedirect' => '/',
'logoutRedirect' => '/',
'loginAction' => ['prefix' => false, 'controller' => 'Users', 'action' => 'login']
]);
}
in the given example username is cake auth format.In this email is table field.username is not table field. so no need username field in users table.
'fields' => ['username' => 'email', 'password' => 'password'],

in yii2 select2 how to add value in database if that option does not exists

How to add category in database if that does not exists in yii2 select2.
<?=
$form->field($model, 'question_category')->widget(Select2::classname(), [
'data' => ArrayHelper::map(Category::find()->all(),'category_name','category_name'),
'maintainOrder' => true,
'toggleAllSettings' => [
'selectLabel' => '<i class="glyphicon glyphicon-ok-circle"></i> Tag All',
'unselectLabel' => '<i class="glyphicon glyphicon-remove-circle"></i> Untag All',
'selectOptions' => ['class' => 'text-success'],
'unselectOptions' => ['class' => 'text-danger'],
],
'options' => ['multiple' => true, 'placeholder' => 'Select a Category ...'],
'pluginOptions' => [
'tags' => true,
'maximumInputLength' => 10
],
]);
?>
Create a model class for Category. Following code will help you to create model.
<?php
namespace app\models;
use Yii;
class Category extends \yii\db\ActiveRecord
{
public static function tableName()
{
return 'category';
}
public function rules()
{
return [
[['category_code', 'category_name'], 'required'],
];
}
public static function find()
{
return new CategoryQuery(get_called_class());
}
}

Categories