0

I've a subscribe newsletter form and I'm trying to show an error message if email is already exists in database. Here's the code from Subscriber model Subscriber.php

<?php

namespace common\models;

use yii\db\ActiveRecord;

class Subscriber extends ActiveRecord
{
    public static function tableName()
    {
        return 'subscriber';
    }

    public function rules()
    {
        return [
            ['email', 'filter', 'filter' => 'trim'],
            ['email', 'required'],
            ['email', 'email'],
            [
                'email', 'unique',        
                'message' => 'This email address has already been taken.'
            ]
        ];
    }
}
?>

Controller action:

 public function actionSubscriber()
    {
        $subscriber_model  = new Subscriber();

        $request = Yii::$app->request;
        if($request->isAjax && $subscriber_model->load($request->post())){
            $subscriber_model->save();
        }
    }

View Code:

<?php
use yii\helpers\Html;
use yii\bootstrap\ActiveForm;
use yii\helpers\Url;
?>

<h3>Subscribe to Newsletter</h3>
<?php $form = ActiveForm::begin(['id' => $subscriber_model->formName(), 'action' => ['project/subscriber'], 'enableAjaxValidation' => true]); ?>
    <div class="input-group">
        <?= $form->field($subscriber_model, 'email')->textInput()->label(false); ?>                             
        <span class="input-group-btn">
            <?php echo Html::submitButton('Sign Up', ['class' => 'btn btn-primary subscribe-btn']); ?>
        </span>                             
    </div>
<?php ActiveForm::end(); ?>

<?php
$script = <<< JS
    $('#{$subscriber_model->formName()}').on('beforeSubmit', function(e){
        var form = $(this);
        $.post(
            form.attr("action"),
            form.serialize()
        ).done(function(data){      
            form.trigger("reset");
        });
        return false;
    });
JS;
$this->registerJs($script);
?>

The current code show "empty email" and "invalid email" message but validation fails for unique email. Please tell me what is the problem in my code.

Alex
  • 21
  • 1
  • 5
  • I think [this](http://stackoverflow.com/questions/41205936/yii2-unique-validator-ignored) should help you – gmc Apr 16 '17 at 10:52
  • It would return the `This email address has already been taken.` message after submitting the form. Yes, you need to implement an AJAX request to check during form validation. However, it would still fail when you click submit. – Wade Apr 17 '17 at 00:52
  • Please show us your action function in your controller. – Wade Apr 17 '17 at 01:13
  • I've tried but its not working for me. If possible can u please update my model code which enable error message if unique validation fails. – Alex Apr 17 '17 at 02:31
  • Using $form->formName worst solution ever. Use $form->id. And use pjax for such cases... =\ – user1954544 Apr 17 '17 at 11:34

2 Answers2

0

You need to also define rules on your AR Model. Your Form model has it's own built in validation, yes.. but, the email rule tells it to look at your AR model, which doesn't have any rules.

Aside from anything else, if you break it down, your Subscriber AR model should be able to function independent of the Form model. What about during CRUD for updating the Subscriber, outside of the Form. It needs to know the rules too :)

<?php

namespace common\models;

use yii\db\ActiveRecord;

class Subscriber extends ActiveRecord
{
    public static function tableName()
    {
        return '{{%subscriber}}';
    }

    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            ['email', 'filter', 'filter' => 'trim'],
            ['email', 'required'],
            ['email', 'email'],
            [
                'email', 'unique',
                'targetClass' => '\common\models\Subscriber',         
                'message' => 'This email address has already been taken.'
            ]
        ];
    }
}
?>

The {{%subscriber}} is for supported table prefixes. I always replace them like that, just in case in the future I (or someone) adds table prefixes.

targetAttribute isn't needed if the column name is the exact same.


Edit

Rewrite your action like this:

public function actionSubscriber()
{
    $subscriber_model  = new Subscriber();

    $request = Yii::$app->request;
    if($request->isAjax && $subscriber_model->load($request->post()) && $subscriber_model->validate() && $subscriber_model->save())
    {
        // return something here
    }
}

Are you sure your creating an AJAX request from your view? It won't work if your not, and you didn't show your view. If you are, then return an appropriate successful AJAX response. If your not using AJAX, then you would want to set a success flash message.

Wade
  • 3,757
  • 2
  • 32
  • 51
  • As you've said i've removed the SubscribeNewsletterForm model now i'm using only one model for collecting and saving user input (Subscriber). I've also added the action code. Saving and other validation is working but message 'This email address has already been taken.' is still not showing. Can u please update my code? – Alex Apr 17 '17 at 02:49
  • Try adding back `'targetClass' => '\common\models\Subscriber',` to the AR model. I don't know why you removed it. Im not positive, but I think it's necessary to declare the AR your comparing too. I always include it as I think it creates a new instance of the class. --- Also, are you creating an AJAX request from your view? It won't run if your not. -- I updated my code to show the action better. – Wade Apr 17 '17 at 06:56
  • Hi wade thanks for your response. I've added the view code. As I said I'm able to store the email in database and my code display error if an incorrect email or nothing entered in input box. Only nothing happen when I entered the same email ID that already exists in table. I just want to display an error in that case on client side "This email has already been taken". Thanks. – Alex Apr 17 '17 at 11:31
0

try this instead

[['email'], 'unique',
'targetClass' => Suscriber::className(),
'message' => 'This email is already taken']