what I am trying to do is like this:
$this->render('//article/overview'); ?>
The contents of overview is like this:
<?php
use yii\helpers\Html;
use yii\widgets\DetailView;
/* #var $this yii\web\View */
/* #var $model app\models\Article */
$this->title = $model->subject;
$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Articles'), 'url' => ['index']];
$this->params['breadcrumbs'][] = $this->title;
\yii\web\YiiAsset::register($this);
?>
<div class="article-view">
<h1><?= Html::encode($this->title) ?></h1>
<div style="margin-top:20px;"><?php echo $model->contents; ?></div>
and the related controller code is like:
public function actionOverview($id=1)
{
return $this->render('overview', [
'model' => $this->findModel($id),
]);
}
Solved in a different way.
I will welcome, if it can be done in a simpler way.
I want to display a view from a controller on home-page of my yii2-application.
so calling view
Then I removed the above code from the site/index.php and updated the related contrllers i.e controllers/SiteController.php
public function actionIndex()
{
$model = Article::find()->where(['id' => 1])->one();
return $this->render('//article/overview', [
'model' => $this->findModel(1),
]);
}
also added the line on top like:
use app\models\Article;
use app\models\ArticleSearch;
and added another action at the bottom of the controller:
protected function findModel($id)
{
if (($model = Article::findOne($id)) !== null) {
return $model;
}
throw new NotFoundHttpException(Yii::t('app', 'The requested page does not exist.'));
}
But I think it should be done in a simpler way, looks like a overkill to call just a view, which is already there.
Related
I have created a widget to render the form in layouts/main.php footer section.
This is a widget file named common\widget\SubscriberFormWidget.php
<?php
namespace common\widgets;
use Yii;
use yii\base\Widget;
use common\models\Subscriber;
class SubscriberFormWidget extends Widget
{
/**
*#return string
*/
public function run()
{
$model = new Subscriber();
return $this->render('_form', [
'model' => $model
]);
}
}
This is _form file used in above widget located in common\widget\views\_form.php
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
/* #var $this yii\web\View */
/* #var $model common\models\Subscriber */
/* #var $form yii\widgets\ActiveForm */
?>
<?php $form = ActiveForm::begin(['action' => ['subscriber/subscribe']]); ?>
<div class="row">
<div class="col-md-6">
<?= $form->field($model, 'email')->textInput(['maxlength' => true, 'placeholder' => 'Enter # Email to subscribe!'])->label(false) ?>
</div>
<div class="col-md-6">
<?= Html::submitButton('Subscribe', ['class' => 'btn btn-success']) ?>
</div>
</div>
<?php ActiveForm::end(); ?>
This is controller file frontend\controllers\SubscriberController.php
<?php
namespace frontend\controllers;
use Yii;
use common\models\Subscriber;
use common\models\SubscriberSearch;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;
/**
* SubscriberController implements the CRUD actions for Subscriber model.
*/
class SubscriberController extends Controller
{
/**
* Creates a new Subscriber model.
* If creation is successful, the browser will be redirected to the 'view' page.
* #return mixed
*/
public function actionSubscribe()
{
$model = new Subscriber();
if ($model->load(Yii::$app->request->post()) && $model->validate()) {
if ($model->sendEmail()) {
Yii::$app->session->setFlash('success', 'You have successfully subscribed My-Blog. You will get notification whenever New post is published');
return $this->redirect(Yii::$app->request->referrer);
} else {
Yii::$app->session->setFlash('error', 'Sorry, we are unable to subscribe for the provided email address.');
}
}
return $this->redirect([Yii::$app->request->referrer, ['model' => $model]]);
}
/**
* Creates a new Subscriber model.
* If unsubscribe is successful, the browser will be redirected to the 'view' page.
* #return mixed
*/
}
I have used widget in layouts\main.php in footer section
<footer class="footer">
<div class="container">
<div class="row">
<div class="col-md-3">
<p class="pull-left">© <?= Html::encode(Yii::$app->name) ?> <?= date('Y') ?></p>
</div>
<div class="col-md-6 text-justify" style="border-left : 2px solid black; border-right: 2px solid black">
<?= SubscriberFormWidget::widget(); ?>
<p>
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into elec
</p>
</div>
<div class="col-md-3">
<p class="pull-right"><?= Yii::powered() ?></p>
</div>
</div>
</div>
</footer>
This is model used for controller common\models\Subscriber.php
<?php
namespace common\models;
use Yii;
use yii\behaviors\TimestampBehavior;
use yii\db\ActiveRecord;
use yii\db\Expression;
/**
* This is the model class for table "subscriber".
*
* #property int $id
* #property string $email
* #property string $token
* #property int $status
* #property int $created_at
* #property int $updated_at
*/
class Subscriber extends \yii\db\ActiveRecord
{
const STATUS_DEACTIVE = 0;
const STATUS_ACTIVE = 1;
/**
* #inheritdoc
*/
public static function tableName()
{
return 'subscriber';
}
public function behaviors()
{
return [
'timestamp' => [
'class' => TimestampBehavior::className(),
'attributes' => [
ActiveRecord::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'],
ActiveRecord::EVENT_BEFORE_UPDATE => ['updated_at'],
],
'value' => new Expression('NOW()'),
],
];
}
/**
* #inheritdoc
*/
public function rules()
{
return [
[['email'], 'required'],
[['status', 'created_at', 'updated_at'], 'safe'],
[['email'], 'string', 'max' => 60],
[['token'], 'string', 'max' => 255],
[['token'], 'unique'],
[['email'], 'unique', 'targetClass' => '\common\models\Subscriber', 'message' => 'This email has already subscribed our blog.','filter' => ['!=','status' ,0]],
];
}
/**
* #inheritdoc
*/
public function attributeLabels()
{
return [
'id' => 'ID',
'email' => 'Email',
'token' => 'Token',
'status' => 'Status',
'created_at' => 'Created At',
'updated_at' => 'Updated At',
];
}
/**
* Generates subscriber token
*/
public function generateSubscriberToken()
{
return $this->token = Yii::$app->security->generateRandomString() . '_' . time();
}
/**
* Send Email when successfully subscribe
*/
public function sendEmail()
{
$subscribers = self::find()->where(['email' => $this->email])->one();
//set flag for sending email
$sendMail = false;
//email subject
$subject = '';
//generate token
$token = $this->generateSubscriberToken();
//if email found in subscribers
if ($subscribers !== null) {
//check if inactive
if ($subscribers->status !== self::STATUS_ACTIVE) {
//assign token
$subscribers->token = $token;
//set status to active
$subscribers->status = self::STATUS_ACTIVE;
print_r($subscribers->errors);
//update the recrod
if (!$subscribers->save()) {
return false;
}
//set subject
$subject = 'Welcome back ' . $this->email . 'Thank you for subscribing ' . Yii::$app->name . '<br /><br /> You will receive notification whenever new trick or post is published to website';
$sendMail = true;
}
} else { //if email does not exist only then insert a new record
$this->status = 1;
if (!$this->save()) {
return false;
}
$subject = 'Thank you ' . $this->email . ' for subscribing ' . Yii::$app->name . '<br /><br /> You will receive notification whenever new trick or post is published to website';
$sendMail = true;
}
//check if send mail flag set
if ($sendMail) {
return Yii::$app->mailer
->compose()
->setFrom(['noreply#my-blog.com' => Yii::$app->name . ' robot'])
->setTo('piyush#localhost')
->setSubject('Subscription : ' . Yii::$app->name)
->setHtmlBody($subject)
->send();
}
}
}
Now I want form to be worked with validation. If user enter any wrong input like already registered then, message for this should return to view file from which submitted form data.
Controlelr::redirect() accepts a url and an optional status code parameter.
You're not calling it correctly in the snippet you posed.
I believe you're trying to only use the url parameter (a single array argument) and skip the status code.
You also cannot rely on referrer to point you back to previous page. You need to save the route to return to in the form page
Url::remember([Yii::$app->requestedRoute]);
and later use that to return back to the form
return $this->redirect([Url::previous(), ['model' => $model]]);
You can set the Yii::$app->user->returnUrl and then use the $this->goBack() to navigate back to the previous page which ever it was.
A good approach is to add the beforeAction function inside your controller SubscribeController like below.
public function beforeAction($action)
{
if (parent::beforeAction($action)) {
if($action->id=='subscribe'){
Yii::$app->user->returnUrl = Yii::$app->request->referrer;
}
}
return true;
}
and replace the line
return $this->redirect([Yii::$app->request->referrer, ['model' => $model]]);
with the following
return $this->goBack();
Now from whichever page you submit the footer form it will navigate back to that page.
Another thing that i have noticed that you are not checking the sessionFlash error variable in your form, you need to get the error message like
if(Yii::$app->session->hasFlash('error')){
echo '<div class="alert alert-danger">
<strong>Danger!</strong>'. Yii::$app->session->getFlash('error').'
</div>';
}
A better way to display sessionFlash messages is provided in my previous answer you can follow that to avoid adding the code manually everywhere it will automatically show you the session mssages once set.
Hope it helps you out
I have a controller with a working action:
class ConfigurationController extends Controller {
public function actions() {
return [
'error' => [
'class' => 'yii\web\ErrorAction',
],
];
}
public function actionView() {
$myModel = ...
$this->render('view', ['model' => $myModel]);
}
}
All seems to be fine, however the layout file which is app/views/layout/main.php does not get shown. There is no special configuration about the layout. What could be wrong?
The main reason: I did not use the return statement. So the correct action is:
public function actionView() {
$myModel = ...
return $this->render('view', ['model' => $myModel]);
// ^^^^^^
}
More info can be found in the guide.
Note: Usually an empty page would be shown. But I also had a <?php $form = ActiveForm::begin(); ?> without an <?php ActiveForm::end(); ?> in the view file. This caused a partial rendering somehow (caused no exception). So I needed to correct this as well.
I'm just sharing my problem and what I've found out so if anyone else has a similar effect may be reminded that the return statement must not be forgotten.
I have a model with an attribute that holds a CSV string.
(The model is actually an ActiveRecord object but I guess this is not important. Correct me if I'm wrong.)
/**
* #property string $colors Can be something like "red" or "red,green,blue" or ""
*/
class Product extends Model {
}
And I have a form in which I'd like to display this attribute as a checkboxList so that the user can select the possible values with simple clicks instead of typing into a textInput.
Theoretically, it should look similar to this:
<?php $availableColors = ['red' => 'Red', 'green' => 'Green', 'blue' => 'Blue']; ?>
<?php $form = ActiveForm::begin([]); ?>
<?= $form->field($model, 'colors')->checkboxList($availableColors) ?>
<?php ActiveForm::end(); ?>
This does obviously not work since the field colors would need to be an array. But in my model it is a string.
What would be a good way to achieve that? With JS or pseudo attributes? The colors attribute must not be changed since it is already used in other contexts that shouldn't be modified.
You can override beforeValidate method in your model, to implode your colors array into string. In your view you can use following:
<?= $form->field($model, 'colors')->checkboxList($availableColors,
[
'item'=> function ($index, $label, $name, $checked, $value) use ($model) {
$colors = explode(';', $model->colors);
$checked = in_array($value, $colors);
return Html::checkbox($name, $checked, [
'value' => $value,
'label' => $label,
]);
}
]) ?>
CSV is a file format used for moving tabular data between programs that natively operate on incompatible formats. Using it as a model attribute is not very elegant (to say it nicely). In my opinion you should have started out storing your colors in an array.
That being said you can achieve converting the array data from the dropdown list to CSV using the beforeValidate() function in your model:
public function beforeValidate() {
$this->colors = explode(';', $this->colors);
return parent::beforeValidate();
}
I think this is a PHP question, but anyway you can use PHP explode for build the array you need. See here for more details and then user the array inside the checkboxList
Now I solved it with an extra model for the form. This seems to me a proper solution.
/**
* #property string $colors Can be something like "red" or "red,green,blue" or ""
*/
class Product extends Model {
}
/**
* #property string[] $colorsAsArray
*/
class ProductForm extends Product {
public function rules() {
return array_merge(parent::rules(), [
['colorsAsArray', 'safe'] // just to make it possible to use load()
]);
}
public function getColorsAsArray() {
return explode(',', $this->colors);
}
public function setColorsAsArray($value) {
$this->colors = self::implode($value);
}
protected static function implode($value) {
if ($value == 'none-value') return '';
return implode(',', $value);
}
/* - - - - - - - - - - optional - - - - - - - - - - */
public function attributeLabels() {
$attributeLabels = parent::attributeLabels();
return array_merge($attributeLabels, [
'colorsAsArray' => $attributeLabels['colors'],
]);
}
}
With this I can use the form that way:
<?php $availableColors = ['red' => 'Red', 'green' => 'Green', 'blue' => 'Blue']; ?>
<?php $form = ActiveForm::begin([]); ?>
<?= $form->field($model, 'colorsAsArray')
->checkboxList($availableColors, ['unselect' => 'none-value']) ?>
<?php ActiveForm::end(); ?>
Of course, now the controller has to use the inherited model class.
The solution deals also with the issue if no checkbox is selected. That is why 'none-value' is introduced.
I am trying to create an expandable grid view in Yii2 but I have some problems.
I get this warning:
PHP Warning – yii\base\ErrorException
reset() expects parameter 1 to be array, null given
in C:\xampp\htdocs\advanced\vendor\yiisoft\yii2\grid\DataColumn.php at line 129
$provider = $this->grid->dataProvider;
if ($this->label === null) {
if ($provider instanceof ActiveDataProvider && $provider->query instanceof ActiveQueryInterface) {
/* #var $model Model */
$model = new $provider->query->modelClass;
$label = $model->getAttributeLabel($this->attribute);
} else {
$models = $provider->getModels();
129 if (($model = reset($models)) instanceof Model) {
/* #var $model Model */
$label = $model->getAttributeLabel($this->attribute);
} else {
$label = Inflector::camel2words($this->attribute);
}
}
} else {
$label = $this->label;
}
This is my search model code:
<?php
namespace app\models;
use Yii;
use yii\data\ActiveDataProvider;
use yii\base\Model;
use app\models\Articles;
/**
*
*/
class ArticlesSearch extends Model
{
/* your calculated attribute */
public $article_num;
public $title;
public $jour_id;
/* setup rules */
public function rules() {
return [
/* your other rules */
[['title'], 'safe']
];
}
public function search($params) {
$query = Articles::find()->select('*')
->where(['`journal_id`'=>$this->jour_id]);
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
if (!($this->load($params) && $this->validate())) {
return $dataProvider;
}
$query->orderBy('`articles`.`publication_date` ASC ');
$query->andWhere(['LIKE','title',$this->title]);
// $query->orFilterWhere(['like', '`publishers`.`name`', $this->name]);
return $dataProvider;
}
public function getCount()
{
}
public function getModels()
{
}
}
I added two last methods to my class to bypass the following error:
Calling unknown method: app\models\ArticlesSearch::getCount()
Calling unknown method: app\models\ArticlesSearch::getModels()
I don't know why I need this two methods in my class. I have written two other search models and I didn't put these methods there and they work fine!!!
Controller code:
$dataProvider=new ArticlesSearch();
$dataProvider->jour_id=$param['journalID'];
$searchModel= $dataProvider->search(Yii::$app->request->queryParams);
return $this->render('index', [ 'searchModel' => $searchModel,
'dataProvider' => $dataProvider]);
View code:
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' =>$searchModel,
'columns' => [
[
'class'=>'kartik\grid\SerialColumn',
'contentOptions'=>['class'=>'kartik-sheet-style'],
//'width'=>'36px',
'header'=>'',
'headerOptions'=>['class'=>'kartik-sheet-style']
],
[
'class'=>'kartik\grid\CheckboxColumn',
'headerOptions'=>['class'=>'kartik-sheet-style'],
],
[
'class'=>'kartik\grid\ExpandRowColumn',
//'width'=>'50px',
'value'=>function ($model, $key, $index, $column) {
return GridView::ROW_COLLAPSED;
},
'detail'=>function ($model, $key, $index, $column) {
// return Yii::$app->controller->renderPartial('_expand-row-details', ['model'=>$articles]);
},
'headerOptions'=>['class'=>'kartik-sheet-style']
//'disabled'=>true,
//'detailUrl'=>Url::to(['/site/test-expand'])
],
[
'attribute'=>'Title',
//'value' => $model->title,
// 'width'=>'410px',
],
],
]);
?>
Any help would be much appreciated.
Try this amended code and see if it works. I've added some comments to explain what is going on.
<?php
namespace app\models;
use Yii;
use yii\data\ActiveDataProvider;
use app\models\Articles;
/**
** firstly, your search model needs to extend your original class, that was you have access to all the original attributes of your model, without having to declare them again.
**/
class ArticlesSearch extends Articles
{
//If your original model already has these properties, you don't need to declare them again.
public $article_num;
public $title;
public $jour_id;
/* Here you should declare rules for ALL the attributes that you want to use in search */
public function rules() {
return [
/* your other rules */
[['title', 'jour_id', 'article_num'], 'safe']
];
}
public function search($params) {
//Start by defining your basic search function
$query = Articles::find();
//Add in the dataProvider
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
//Start building the query that will be used to retrieve results
$query->where(['jour_id' => $this->jour_id]);
//Try to load the $params and validate them. If this fails, just return the dataProvider and do nothing else
if (!($this->load($params) && $this->validate())) {
return $dataProvider;
}
//If we're still in the method, carry on building the query
$query->orderBy('`articles`.`publication_date` ASC ');
//andFilterWhere() is better because it ignores empty values
$query->andFilterWhere(['LIKE','title',$this->title]);
return $dataProvider;
}
//Finally, remove the two extras functions you put in. If everything is working, you shouldn't need them.
}
Next, in your controller, use this code;
//Here you are telling Yii what model you want to use for searching, and for generating the form for the grid search.
$searchModel = new ArticlesSearch();
//I'm not sure where this $param is coming from.
$dataProvider->jour_id=$param['journalID'];
//Now you are actually setting up the dataProvider for the grid view. Notice that your $searchModel->search() method always returns a dataProvider, so this is the correct way to do it. Yii::$app->request->queryParams is loading the parameters that the search method will use for it's parameters.
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('index', ['searchModel' => $searchModel, 'dataProvider' => $dataProvider]);
Now you should be able to use the grid widget as normal, use the dataProvider and searchModel for your dataProvider and filterModel respectively, but don't mix them up!
Ok, i'm trying to make a survey tool.
So, i have a Model of the survey which reference to N questions. This is part of this model:
<?php
namespace app\models;
use Yii;
/**
* #return \yii\db\ActiveQuery
*/
public function getIdmateria0()
{
return $this->hasOne(Monmateria::className(), ['id' => 'idmateria']);
}
/**
* #return \yii\db\ActiveQuery
*/
public function getMonpreguntas()
{
return $this->hasMany(Monpregunta::className(), ['idencuesta' => 'id']);
}
}
Of course, the questions's model Monpregunta references the survey's model. Then i have a 1 to N relations between the survey and his questions.
In my ActionView i have a survey with N questions on it. Then i call a view of survey, which have a Create Form View rendered on it, in order to create the N answers of the survey. So, once question is responded, I need to know which number of question was. I need to keep the track of which number of questions i am responding, but for it i need to send a parameter from the form to my controller. But i do not know how to do it.
The Controller (here i need to keep the track of which questions is responded)
public function actionView($id)
{
//this is my id's survey
$model = $this->findModel($id);
//here i pre create my answer
$modelResultado = $this->crearResultado($id);
//here i call the view with a Create Form embedded. When the form action it's triggered
return $this->render('view', [
'model' => $model,
'modelResultado'=>$modelResultado,
'orden'=>$model->orden,
]);
}
//create the answer and setting the reference
protected function crearResultado($id) {
$modelResul = new \app\models\Monresultado;
$modelResul->idmonresultadocab = $id;
if ($modelResul->load(Yii::$app->request->post()) && $modelResul->save()) {
//flash
}
return $modelResul;
}
The view
<?php
use yii\helpers\Html;
use yii\widgets\DetailView;
/**
* #var yii\web\View $this
* #var app\models\Monresultadocab $model
*/
?>
<div class="monresultadocab-view">
<?= DetailView::widget([
'model' => $model,
'attributes' => [
'id',
'idencuesta',
],
]) ?>
<?php
echo $this->render('_formResultado', [
'model' => $modelResultado
]); ?>
</div>
[b]This is the _formResultado view, and here i wish to tell to the action which id's questions has been responded but i don't know how to send the parameter to the action!![/b]
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
use yii\helpers\ArrayHelper;
/**
* #var yii\web\View $this
* #var app\models\Monresultado $model
* #var yii\widgets\ActiveForm $form
*/
?>
<div class="monresultado-form">
<?php $form = ActiveForm::begin(); ?>
<?= $form->field($model, 'idrespuesta')->dropDownList(ArrayHelper::map(\app\models\Monrespuesta::find()->all(),'id','nombre'))?>
<?= $form->field($model, 'libre')->textInput(['maxlength' => 2000]) ?>
<div class="form-group">
[b]<?= Html::submitButton($model->isNewRecord ? 'Siguiente' : 'Siguiente', ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>[/b]
</div>
<?php ActiveForm::end(); ?>
</div>
If i send a parameter in the Html::submitButton tag, it doesn't work.
Thanks
i think u should pass the id of corresponding question in actionView of controller
like:
return $this->render('view', [
'model' => $this->findModel($id),
......
......
]);
hope it works!