0

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">&copy; <?= 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.

Muhammad Omer Aslam
  • 22,976
  • 9
  • 42
  • 68
  • @muhammad This is not working.... please check – Piyush Gupta Feb 12 '18 at 17:35
  • the problem is that you are validating with `$model->validate()` but inside the rules, you are not using any validation like if the email already exists instead you are using the `sendMail` function to verify that if the user already exists and is `active` or `inactive` and then adding to the session `flash` message, it will display the error inside the flash messages only when the page is refreshed after adding the message or redirecting are you submitting the form with page reload ? – Muhammad Omer Aslam Feb 12 '18 at 17:37
  • I have used rule in model 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]], ]; } – Piyush Gupta Feb 12 '18 at 17:41
  • ok wait a minute let me check – Muhammad Omer Aslam Feb 12 '18 at 17:42
  • added an answer for you see if it helps you out – Muhammad Omer Aslam Feb 12 '18 at 18:06

2 Answers2

2

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]]);
Sarvar Nishonboyev
  • 12,262
  • 10
  • 69
  • 70
csminb
  • 2,382
  • 1
  • 16
  • 27
  • Yes, that's due to the fact the referrer contains the full url and not the route, perhaps its safer to use `\yii\helpers\Url::remember()` and `\yii\helpers\Url::previous()` – csminb Feb 12 '18 at 16:31
  • @PiyushGupta i've updated the answer.i think this aproach is somewhat better since `Yii::$app->request->referrer` can be almost anything (null or external domain or any user submitted data) – csminb Feb 12 '18 at 16:39
  • I also updated question in detail with code. hope you will understand issue....@csminb – Piyush Gupta Feb 12 '18 at 16:56
  • it should still work. add `Url::remember` in `SubscriberFormWidget.php` just before rendering and use `$this->redirect([Url::previous(), ['model' => $model]]);` in actionSubscribe() – csminb Feb 12 '18 at 17:01
  • It is redirecting me to "http://localhost/my-blog/frontend/web/my-blog/frontend/web/site/about" instead of "http://localhost/my-blog/frontend/web/site/about"................. @csminb – Piyush Gupta Feb 12 '18 at 17:08
1

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

Muhammad Omer Aslam
  • 22,976
  • 9
  • 42
  • 68
  • Thank you, your code is working fine for redirection previous address. but not showing error message. suppose if email already subscribed with active status, then It should display error message that I have use in rule() with filter propery – Piyush Gupta Feb 12 '18 at 18:16
  • ok it looked like from the title that the problem was redirection, are you using client validation on the frontend for the form doe it shows the error on runtime of no email provided? – Muhammad Omer Aslam Feb 12 '18 at 18:20
  • yes my problem has solved 90% but redirection with model error message is still pending.. No Iam not using other validation except of rule() function used in model... – Piyush Gupta Feb 12 '18 at 18:24
  • Why not validate your form via ajax before submitting. https://yii2-cookbook.readthedocs.io/forms-activeform-js/ – Kalu Feb 12 '18 at 18:41
  • ok give me some time let me reach home an i will sort it out @PiyushGupta – Muhammad Omer Aslam Feb 12 '18 at 20:20
  • ok @Muhammad, did you find any solution to solve this without using ajax – Piyush Gupta Feb 14 '18 at 07:42