How can I valid random names fields in Yii? - yii2

I have a lot of fields generated from loops. I would like to validate them through validation rules (integer). I don't know how to throw so many fields with random names into the model to the rules () function. How can I validate fields without a model?
View:
<?= Html::input('number', 'file[' . $indexRow . ']' . '[' . $indexCell . ']', $cell, $options = ['class' => 'form-control', 'filter' => 'intval', 'integer']) ?>
Controller:
` public function actionEdit($fileName)
{
$siteHelper = new SiteHelper();
$editForm = new EditForm();
$preparedRows = $siteHelper->prepareRows($fileName);
$preparedHTML = '';
if (Yii::$app->request->isPost) {
$post = Yii::$app->request->post();
if (isset($post['file'])) {
$dataFile = $post['file'];
$preparedRows = $siteHelper->updateExcelFile($fileName, $dataFile);
Yii::$app->session->setFlash('success', 'Plik zostaƂ zaktualizowany!');
} else if (isset($post['EditForm'])) {
$events = $post['EditForm']['events'];
$preparedHTML = $siteHelper->prepareHTML($events, $preparedRows, $fileName);
Yii::$app->session->setFlash('success', 'Wygenerowano plik PDF!');
}
}
$viewParameters = [
'rows' => $preparedRows,
'editForm' => $editForm,
'scoreHTML' => $preparedHTML,
'downloadLink' => Url::toRoute(['site/download', 'fileName' => $fileName])
];
return $this->render('edit', $viewParameters);
}`
Model:
`
class EditForm extends Model
{
public $events;
public function rules()
{
return [
[['events'], 'required'],
['events', 'integer'],
];
}
}`

When you have an array you can use each validator:
https://www.yiiframework.com/doc/api/2.0/yii-validators-eachvalidator
The validation function should be:
public function rules()
{
return [
[['events'], 'each', 'rule' => ['required']],
[['events'], 'each', 'rule' => ['integer']],
];
}
You may need to avoid multidemnsional array in html and render the field like this:
<?= Html::input('number', 'file[' . $indexRow . '-' . $indexCell . ']', $cell, $options = ['class' => 'form-control', 'filter' => 'intval', 'integer']) ?>
later you can "explode" the row cell index (isn't it supposed to be col?) to identify the row and the column.
$rowCellIndecies = explode('-', $rowCellIndex);
explode function: https://www.php.net/manual/en/function.explode.php

Related

captcha not working using scenarios in yii2

I am trying to add captcha validation based on scenario, for which I am first retrieving number of failed attempts from database. For which I am using checkattempts() function. Based on the result I am displaying captcha in view and adding scenario condition in controller as below.
In LoginForm model:
public function rules()
{
return [
[['username', 'password'], 'required', 'on'=>'loginpage'],
[['username', 'password'], 'required', 'on'=>'withCaptcha'],
[['reference_url'], 'safe'],
[['verifyCode'], 'captcha', 'skipOnEmpty' => true,'on'=>'withCaptcha'],
['username','email', 'on'=>'loginpage', 'message'=> 'Please enter a valid email address'],
['password', 'validatePassword', 'on'=>'loginpage'],
['password', 'validatePassword', 'on'=>'withCaptcha'],
];
}
public function checkattempts($uname)
{
$user = \frontend\models\User::findByEmail($uname);
$ip = $this->get_client_ip();
if($user){
$data = (new Query())->select('*')
->from('login_attempts')
->where(['ip' => $ip])->andWhere(['user_ref_id' => $user->id])
->one();
if($data["attempts"] >=3){
return true;
}else{
return false;
}
}
return false;
}
in SiteController.php controller
public function actionLogin() {
if (!\Yii::$app->user->isGuest) {
return $this->redirect(Yii::$app->getUrlManager()->getBaseUrl() . '/../../');
}
$model = new \common\models\LoginForm();
$model->scenario = 'loginpage';
$captcha = false;
if(Yii::$app->request->post()){
$post_variables =Yii::$app->request->post('LoginForm');
if ($model->checkattempts($post_variables['username'])) {
$model->scenario = 'withCaptcha';
$captcha = true;
}
}
if ($model->load(Yii::$app->request->post()) && $model->validate()) {
$model->login(); print_r($model->getErrors()); exit;
} else {
return $this->render('login', [
'model' => $model, 'captcha' => $captcha,
]);
}
In my login.php view:
<?php if($captcha) { ?>
<?= $form->field($model, 'verifyCode')->widget(Captcha::className(),
['template' => '<div class="captcha_img">{image}</div>'
. '<a class="refreshcaptcha" href="#">'
. Html::img('/images/imageName.png',[]).'</a>'
. 'Verification Code{input}',
])->label(FALSE); ?>
<?php } ?>
In my controller when I am tring to print model errors at $model->login() function it is giving below error everytime even though the verification code is correct.
Array ( [verifycode] => Array ( [0] => The verification code is incorrect. ) )
Why is it failing every time. Is there any mistake in the code written?
Thanks in advance

Use closures in Yii2 ArrayDataProvider

In an ActiveDataProvider you can use closures as values, like:
$dataprovider = new ArrayDataProvider([
'allModels' => $array
]);
$gridColumns = [
'attrib_1',
[
'attribute' => 'attrib_2',
'label' => 'Label_2',
'value' => function($model) {
return Html::encode($model->value_2);
}
],
'attrib_3'
];
echo GridView::widget([
'dataProvider'=> $dataprovider,
'columns' => $gridColumns
]);
Is it possible to do the same or something like this, in an ArrayDataProvider?
Yes. Only difference is that $model is not an object but array so:
'value' => function($model) {
return Html::encode($model['value_2']);
}
For this purpose, I have created an extended version of ActiveDataProvider, that for each model got from provider I call a callback.
This is the custom ActiveDataProvider, put in common\components namespace in this case.
<?php
namespace common\components;
class CustomActiveDataProvider extends \yii\data\ActiveDataProvider
{
public $formatModelOutput = null;
public function getModels()
{
$inputModels = parent::getModels();
$outputModels = [];
if($this->formatModelOutput != null)
{
for($k=0;$k<count($inputModels);$k++)
{
$outputModels[] = call_user_func( $this->formatModelOutput, $k , $inputModels[$k]);
}
}
else
{
$outputModels = $inputModels;
}
return $outputModels;
}
}
This is the action in controller that uses it. For reusability, I call a model method instead calling a clousure, but you can call also a clousure.
public function actionIndex()
{
$query = Model::find();
$dataProvider = new \common\components\CustomActiveDataProvider([
'query' => $query,
'pagination' => ['pageSize' => null],
'formatModelOutput' => function($id, $model) {
return $model->dataModelPerActiveProvider;
}
]);
return $dataProvider;
}
At last, this is the method getDataModelPerActiveProvider in model:
public function getDataModelPerActiveProvider()
{
$this->id = 1;
// here you can customize other fields
// OR you can also return a custom array, for example:
// return ['field1' => 'test', 'field2' => 'foo', 'field3' => $this->id];
return $this;
}

Yii2: What is the correct way to define relationships among multiple tables?

In a controller I have the following code:
public function actionView($id)
{
$query = new Query;
$query->select('*')
->from('table_1 t1')
->innerJoin('table_2 t2', 't2.t1_id = t1.id')
->innerJoin('table_3 t3', 't2.t3_id = t3.id')
->innerJoin('table_4 t4', 't3.t4_id = t4.id')
->andWhere('t1.id = ' . $id);
$rows = $query->all();
return $this->render('view', [
'model' => $this->findModel($id),
'rows' => $rows,
]);
}
See the db schema: https://github.com/AntoninSlejska/yii-test/blob/master/example/sql/example-schema.png
In the view view.php are displayed data from tables_2-4, which are related to table_1:
foreach($rows as $row) {
echo $row['t2_field_1'];
echo $row['t2_field_2'];
...
}
See: Yii2 innerJoin()
and: http://www.yiiframework.com/doc-2.0/yii-db-query.html
It works, but I'm not sure, if it is the most correct Yii2's way.
I tried to define the relations in the model TableOne:
public function getTableTwoRecords()
{
return $this->hasMany(TableTwo::className(), ['t1_id' => 'id']);
}
public function getTableThreeRecords()
{
return $this->hasMany(TableThree::className(), ['id' => 't3_id'])
->via('tableTwoRecords');
}
public function getTableFourRecords()
{
return $this->hasMany(TableFour::className(), ['id' => 't4_id'])
->via('tableThreeRecords');
}
and then to join the records in the controller TableOneController:
$records = TableOne::find()
->innerJoinWith(['tableTwoRecords'])
->innerJoinWith(['tableThreeRecords'])
->innerJoinWith(['tableFourRecords'])
->all();
but it doesn't work. If I join only the first three tables, then it works. If I add the fourth table, then I receive the following error message: "Getting unknown property: frontend\models\TableOne::t3_id"
If I change the function getTableFourRecords() in this way:
public function getTableFourRecords()
{
return $this->hasOne(TableThree::className(), ['t4_id' => 'id']);
}
then I receive this error message: "SQLSTATE[42S22]: Column not found: 1054 Unknown column 'table_4.t4_id' in 'on clause'
The SQL being executed was: SELECT table_1.* FROM table_1 INNER JOIN table_2 ON table_1.id = table_2.t1_id INNER JOIN table_3 ON table_2.t3_id = table_3.id INNER JOIN table_4 ON table_1.id = table_4.t4_id"
You should have to define key value pair in the relation eg:
class Customer extends ActiveRecord
{
public function getOrders()
{
return $this->hasMany(Order::className(), ['customer_id' => 'id']); // Always KEY => VALUE pair this relation relate to hasMany relation
}
}
class Order extends ActiveRecord
{
public function getCustomer()
{
return $this->hasOne(Customer::className(), ['id' => 'customer_id']);
// Always KEY => VALUE pair this relation relate to hasOne relation
}
}
Now in your forth relation use:
public function getTableFourRecords()
{
return $this->hasOne(TableThree::className(), ['id' => 't4_id']);
}
You can read more on ActiveRecord here
Based on the answer of softark the simplest solution can look like this:
Model TableOne:
public function getTableTwoRecords()
{
return $this->hasMany(TableTwo::className(), ['t1_id' => 'id']);
}
Model TableTwo:
public function getTableThreeRecord()
{
return $this->hasOne(TableThree::className(), ['id' => 't3_id']);
}
Model TableThree:
public function getTableFourRecord()
{
return $this->hasOne(TableFour::className(), ['id' => 't4_id']);
}
Controller TableOneController:
public function actionView($id)
{
return $this->render('view', [
'model' => $this->findModel($id),
]);
}
The view table-one/view.php:
foreach ($model->tableTwoRecords as $record) {
echo ' Table 2 >> ';
echo ' ID: ' . $record->id;
echo ' T1 ID: ' . $record->t1_id;
echo ' T3 ID: ' . $record->t3_id;
echo ' Table 3 >> ';
echo ' ID: ' . $record->tableThreeRecord->id;
echo ' T4 ID: ' . $record->tableThreeRecord->t4_id;
echo ' Table 4 >> ';
echo ' ID: ' . $record->tableThreeRecord->tableFourRecord->id;
echo ' <br>';
}
A solution based on the GridView is also possible.
Model TableTwo:
public function getTableOneRecord()
{
return $this->hasOne(TableOne::className(), ['id' => 't1_id']);
}
public function getTableThreeRecord()
{
return $this->hasOne(TableThree::className(), ['id' => 't3_id']);
}
public function getTableFourRecord()
{
return $this->hasOne(TableFour::className(), ['id' => 't4_id'])
->via('tableThreeRecord');
}
The function actionView in TableOneController, which was generated with Gii for the model TableTwo was edited:
use app\models\TableTwo;
use app\models\TableTwoSearch;
...
public function actionView($id)
{
$searchModel = new TableTwoSearch([
't1_id' => $id, // the data have to be filtered by the id of the displayed record
]);
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('view', [
'model' => $this->findModel($id),
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
and also the views/table-one/view.php:
echo GridView::widget([
'dataProvider' => $dataProvider,
'columns' => [
'id',
't1_id',
'tableOneRecord.id',
't3_id',
'tableThreeRecord.id',
'tableThreeRecord.t4_id',
'tableFourRecord.id',
],
]);
See the code on Github.

cant import data csv to database in yii2

I am very new to web development.
I'm newbie in here, my first question in stackoverflow..
i am confused what error on code, Code will be store data array csv to a database,
sorry my bad english.
Controller
public function actionUpload()
{
$model = new Skt();
//error_reporting(E_ALL);
//ini_set('display_error', 1);
if ($model->load(Yii::$app->request->post())) {
$file = UploadedFile::getInstance($model, 'file');
$filename = 'Data.' . $file->extension;
$upload = $file->saveAs('uploads/' . $filename);
if ($upload) {
define('CSV_PATH', 'uploads/');
$csv_file = CSV_PATH . $filename;
$filecsv = file($csv_file);
foreach ($filecsv as $data) {
$modelnew = new Skt();
$hasil = explode(",", $data);
$no_surat= $hasil[0];
$posisi= $hasil[1];
$nama= $hasil[2];
$tgl_permanen= $hasil[3];
$grade= $hasil[4];
$tgl_surat= $hasil[5];
$from_date = $hasil[6];
$to_date = $hasil[7];
$modelnew->no_surat = $no_surat;
$modelnew->posisi = $posisi;
$modelnew->nama = $nama;
$modelnew->tgl_permanen = $tgl_permanen;
$modelnew->grade = $grade;
$modelnew->tgl_surat = $tgl_surat;
$modelnew->from_date = $from_date;
$modelnew->to_date = $to_date;
$modelnew->save();
//print_r($modelnew->validate());exit;
}
unlink('uploads/'.$filename);
return $this->redirect(['site/index']);
}
}else{
return $this->render('upload',['model'=>$model]);
}
return $this->redirect(['upload']);
}
Model
class Skt extends \yii\db\ActiveRecord
{
public static function tableName()
{
return 'skt';
}
public $file;
public function rules()
{
return [
[['file'], 'required'],
[['file'], 'file', 'extensions' => 'csv', 'maxSize' => 1024*1024*5],
[['no_surat'], 'required'],
[['tgl_surat', 'from_date', 'to_date'], 'string'],
[['no_surat', 'posisi', 'nama', 'tgl_permanen', 'grade'], 'string', 'max' => 255],
];
}
public function attributeLabels()
{
return [
'no_surat' => 'No Surat',
'posisi' => 'Posisi',
'nama' => 'Nama',
'tgl_permanen' => 'Tgl Permanen',
'grade' => 'Grade',
'tgl_surat' => 'Tgl Surat',
'from_date' => 'From Date',
'to_date' => 'To Date',
'file' => 'Select File'
];
}
}
thanks for helping..
change your code to the following to output the errors which could happen when you try to save. Errors could occur depending on your model rules.
if (!$modelnew->save()) {
var_dump($modelnew->getErrors());
}
getErrors() from Api
A better approach is to use exceptions to throw and catch errors on your import. Depends if you want to skip csv lines on errors or not.
finally it working with change this $hasil = explode(";", $data);

how to define id_pst in my action comment of yii2?

I have create a action comment like this:
public function actionComment($id)
{
$text = CommentModel::find($id)->where(true)->all();
if (isset($_POST['name'])) {
$text = new CommentModel();
$text->name=$_POST['name'];
$text->comment=$_POST['comment'];
$text->i_post=$_POST['id_post'];
$text->save();
$this->redirect('comment',array('id'=>$text->id));
}
return $this->render('comment',array("text"=>$text));
}
and comment view is:
<h2>List of all comments:</h2>
<?php
foreach($text as $text[0]){
echo "name"." ".":"." ".$text[0]->name."<br>" ;
echo "comment"." ".":"." ".$text[0]->comment."<br>";
?>
my comment model is:
public function attributeLabels()
{
return [
'id' => 'ID',
'name' => 'Name',
'comment' => 'Comment',
'id-post' => 'Id Post',
];
}
how should i define id_pst in my project to each post has sepecific comment??