How to add Recaptcha v2 to Laravel and Vue js? - laravel

I'm following this article on how to add Recaptcha v2 to a Laravel and Vue js project.
https://www.itechempires.com/2018/05/how-to-implement-google-recapcha-with-vuejs-and-laravel-5-6/
I'm trying to implement it in my project but I'm getting this error on the page:
The recaptcha verification failed. Try again.
And in the network tab this error:
{recaptcha: ["The recaptcha verification failed. Try again."]}
And in the console:
POST http://highrjobsadminlte.test/submit 422 (Unprocessable Entity)
I'm trying to implement this on a Contact Form on my Contact.vue page.
Contact.vue:
<form #submit.prevent="submit">
<div class="form-group">
<label for="name">Name</label>
<input type="text" class="form-control" name="name" id="name" v-model="fields.name" />
<div v-if="errors && errors.name" class="text-danger" style="font-size: 13px;">{{ errors.name[0] }}</div>
</div>
<div class="form-group">
<label for="email">E-mail</label>
<input type="email" class="form-control" name="email" id="email" v-model="fields.email" />
<div v-if="errors && errors.email" class="text-danger" style="font-size: 13px;">{{ errors.email[0] }}</div>
</div>
<div class="form-group">
<label for="message">Message</label>
<textarea class="form-control" id="message" name="message" rows="5" v-model="fields.message"></textarea>
<div v-if="errors && errors.message" class="text-danger" style="font-size: 13px;">{{ errors.message[0] }}</div>
</div>
<div class="form-group">
<vue-recaptcha
v-model="fields.recaptcha"
ref="recaptcha"
#verify="onVerify"
sitekey="6LcAHcoZAAAAAFDOejn9e2LrogSpF41RMlXtrpDa">
</vue-recaptcha>
</div>
<div v-if="errors && errors.recaptcha" class="text-danger" style="font-size: 13px;">{{ errors.recaptcha[0] }}</div>
<button type="submit" class="btn btn-primary mt-3">Send message</button>
</form>
export default {
name: "Contact",
data() {
return {
fields: {},
errors: {},
}
},
methods: {
onVerify(response) {
this.fields.recaptcha = response;
},
submit() {
this.errors = {};
axios.post('/submit', this.fields, {
headers:{
'Content-Type':'application/json',
'Accept':'application/json'
}
}).then(({data: {fields}}) => {
this.$toast.success('Message sent successfully!');
this.$refs.recaptcha.reset();
}).catch(error => {
if (error) {
this.errors = error.response.data.errors || {};
}
});
},
},
}
Recaptcha.php (This is a rule):
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
use Zttp\Zttp;
class Recaptcha implements Rule
{
const URL = 'https://www.google.com/recaptcha/api/siteverify';
/**
* Determine if the validation rule passes.
*
* #param string $attribute
* #param mixed $value
* #return bool
*/
public function passes($attribute, $value)
{
return Zttp::asFormParams()->post(static::URL, [
'secret' => config('services.recaptcha.secret'),
'response' => $value,
'remoteip' => request()->ip()
])->json()['success'];
}
/**
* Get the validation error message.
*
* #return string
*/
public function message()
{
return 'The recaptcha verification failed. Try again.';
}
/**
* Determine if Recaptcha's keys are set to test mode.
*
* #return bool
*/
public static function isInTestMode()
{
return Zttp::asFormParams()->post(static::URL, [
'secret' => config('services.recaptcha.secret'),
'response' => 'test',
'remoteip' => request()->ip()
])->json()['success'];
}
}
ContactFormController.php:
<?php
namespace App\Http\Controllers;
use App\Mail\ContactEmail;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
use App\Rules\Recaptcha;
class ContactFormController extends Controller
{
public function submit(Request $request, Recaptcha $recaptcha) {
$this->validate($request, [
'name' => 'required|string',
'email' => 'required|email',
'message' => 'required',
'recaptcha' => ['required', $recaptcha],
]);
$contact = [];
$contact['name'] = $request->get('name');
$contact['email'] = $request->get('email');
$contact['subject'] = $request->get('subject');
$contact['message'] = $request->get('message');
// Mail Delivery logic goes here
Mail::to(config('mail.from.address'))->send(new ContactEmail($contact));
return response(['contact' => $contact], 200);
}
}
web.php:
Route::post('/submit', 'ContactFormController#submit');
And I have set recaptcha key and secret in config/services.php to point to my .env variables which are set in my .env file.

Related

SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'player_id' cannot be null

I have this error when I try to add data. I have 2 tables on my database, the (players) table and the (stats) table which are connected with the "player_id" being the foreign key to the players table.
players table:enter image description here
stats table:enter image description here
StatsController:
public function index(){
$stats = Stats::all();
return view('stats', ['stats' => $stats]);
}
public function addstats(){
if($this->request->method() == 'POST'){
$alert = request() -> validate([
'points' => 'required|numeric',
'average_points' => 'required|numeric',
'games' => 'required|numeric',
'duration' => 'required|numeric'
]);
$stats = new Stats;
$stats->player_id = $this->request->id;
$stats->points = $this->request->get('points');
$stats->average_points = $this->request->get('average_points');
$stats->games = $this->request->get('games');
$stats->duration = $this->request->get('duration');
if($stats -> save()){
echo 'Success';
}
}
return view('addstats', ['player_id' => $this->request->player_id]);
}
public function editstats(Stats $stats){
if($this->request->method() == 'POST'){
$alert = request() -> validate([
'points' => 'required|numeric',
'average_points' => 'required|numeric',
'games' => 'required|numeric',
'duration' => 'required|numeric'
]);
$stats->player_id = $this->request->id;
$stats->fullname = $this->request->get('points');
$stats->age = $this->request->get('average_points');
$stats->height = $this->request->get('games');
$stats->weight = $this->request->get('duration');
if($stats -> save()){
return redirect('stats/');
}
}
return view('editstats', ['stats' => $stats]);
}
public function destroy(Stats $stats){
$stats->delete();
return redirect('stats/');
}
addstats blade php:
#section('content')
<div class="container">
<form action="{{route('addstats')}}" method="POST">
#csrf
<input type="hidden" name="id" value="{{$player_id}}">
<p>Overall Points:</p>
<input type="number" name="points" class="form-control" value="{{Request::old('points')}}" required>
<p style="color: red">#error('points') {{$message}} #enderror</p>
<p>Average points per game:</p>
<input type="number" name="average_points" class="form-control" value="{{Request::old('average_points')}}" required>
<p style="color: red">#error('average_points') {{$message}} #enderror</p>
<p>Games:</p>
<input type="number" name="games" class="form-control" value="{{Request::old('games')}}" required>
<p style="color: red">#error('games') {{$message}} #enderror</p>
<p>Duration:</p>
<input type="number" name="duration" class="form-control" value="{{Request::old('duration')}}" required>
<p style="color: red">#error('duration') {{$message}} #enderror</p>
<button class="btn btn-primary">Submit</button>
</form>
</div>
#endsection
my Routes:
Route::middleware('auth')->group(function(){
Route::get('/', [PlayersController::class, 'index']);
Route::post('/addplayer', [PlayersController::class, 'addplayer'])->name('addplayer');
Route::get('/addplayer', [PlayersController::class, 'addplayer'])->name('addplayer');
Route::get('/editplayer/{players}', [PlayersController::class, 'editplayer'])->name('editplayer');
Route::post('/editplayer/{players}', [PlayersController::class, 'editplayer'])->name('editplayer');
Route::get('/destroy/{players}', [PlayersController::class, 'destroy'])->name('destroy');
Route::get('/stats/{player_id?}', [StatsController::class, 'index']);
Route::post('/addstats', [StatsController::class, 'addstats'])->name('addstats');
Route::get('/addstats', [StatsController::class, 'addstats'])->name('addstats');
Route::get('/editstats/{stats?}', [StatsController::class, 'editstats'])->name('editstats');
Route::post('/editstats/{stats?}', [StatsController::class, 'editstats'])->name('editstats');
Route::get('/destroy/{stats?}', [StatsController::class, 'destroy'])->name('destroy');
});
class Stats extends Model
{
use HasFactory;
protected $table = 'stats';
protected $fillable = [
'player_id','points','average_points','games','duration'
];
}

How to change the role in migration table either admin or user based on the selection in laravel-8?

I developed one users table without role column , now i want to add role column in my migration table which contains either admin or user , if the registered person selects user radio btn in frontend UI page the role should be updated as user or if the person selects admin the role should be changed as admin in my migration table [in postman also how to pass role based on role the value should change in my database table] please help me to fix this issue
UsersMigration table
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateUsersTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('fullName');
$table->string('email')->unique();
$table->string('mobile')->unique();
$table->string('email_verified_at')->nullable();
$table->string('password');
$table->enum('role', ['user', 'admin'])->default('user'); //here
$table->rememberToken();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('users');
}
}
UserRegisterController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\User;
use JWTAuth;
use Tymon\JWTAuth\Exceptions\JWTException;
class UserController extends Controller
{
// AWS_USE_PATH_STYLE_ENDPOINT=false
public function __construct() {
$this->middleware('auth:api', ['except' => ['login', 'register']]);
}
public function register(Request $request)
{
$this->validate($request, [
'fullName'=>'required|string|between:3,15',
'email'=>'required|email|unique:users',
'password'=>'required|regex:/^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!#$%^&*-]).{6,}$/',
'mobile'=>'required|digits:10'
]);
$user = new User([
'fullName'=> $request->input('fullName'),
'email'=> $request->input('email'),
'password'=> bcrypt($request->input('password')),
'mobile'=>$request->input('mobile')
]);
$user->save();
return response()->json(['message'=>'Successfully Created user'],201);
}
}
i am pasting my frontend UI page also which should be written in vue.js
Register.vue
<template>
<div class="main">
<div v-if="flag==true" class="container">
<img id="side-img" src="../assets/sideImg.png" alt="notFound" />
<p id="side-content">Online Book Shopping</p>
<div class="box">
<div class="headings">
<h5 class="signin" v-on:click="flip();" id="login" :class="{ active: isLogin }" #click="isLogin = true">Login</h5>
<h5 class="signup" id="signup" :class="{ active: !isLogin }" #click="isLogin = false">signup</h5>
</div>
<form ref="myForm" #submit.prevent="handlesubmit">
<div class="fullname">
<p>FullName</p>
<input type="name" id="name-input" class="namebox" required v-model="fullName" autocomplete="off" pattern="[A-Za-z]{3,12}">
</div>
<div class="username">
<p>EmailID</p>
<input type="email" id="Email-input" class="emailbox" autocomplete="off" required v-model="email" pattern="^[a-z0-9._%+-]+#[a-z0-9.-]+\.[a-z]{2,4}$">
</div>
<div class="password-section">
<p>Password</p>
<input :type="password_type" class="password" :class="{'password-visible': isPasswordVisible }" id="passField" v-model="password" pattern="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[#$!%*?&])[A-Za-z\d#$!%*?&]{6,}$" required>
<i class="bi bi-eye-slash" id="togglePassword" #click="togglePassword();"></i>
</div>
<div class="mobile">
<p>MobileNumber</p>
<input type="tel" class="telephone" autocomplete="off" v-model="mobile" id="tel" pattern="^\d{10}$" required>
</div>
<div class="role-btns">
<input type="radio" id="user" name="user" vlaue="user" >
<label for="user" class="radio-label">User</label>
<input type="radio" id="admin" name="user" value="admin">
<label for="admin">Admin</label>
</div>
<button class="btn-section" id="btn" type="submit">Signup</button>
</form>
</div>
</div>
<Login v-if="flag==false" />
</div>
</template>
<script>
import service from '../service/User'
export default {
name: 'Register',
components: {
Login: () => import('./Login.vue')
},
data() {
return {
fullName: '',
email: '',
password: '',
mobile: '',
password_type: "password",
isLogin: false,
isPasswordVisible: false,
flag: true,
title: 'Online Book Shopping'
}
},
methods: {
flip() {
this.flag = !this.flag;
},
togglePassword() {
this.password_type = this.password_type === 'password' ? 'text' : 'password'
this.isPasswordVisible = !this.isPasswordVisible
},
handlesubmit() {
let userData = {
fullName: this.fullName,
email: this.email,
password: this.password,
mobile: this.mobile
}
service.userRegister(userData).then(response => {
if (response.status == 201) {
alert("user registered successfully");
this.$refs.myForm.reset();
this.$router.push('/login');
}
return response;
}).catch(error => {
alert("invalid credentials");
return error;
})
}
}
}
</script>
<style lang="scss" scoped>
#import "#/styles/Register.scss";
</style>
Change radio name
<div class="role-btns">
<input type="radio" id="user" name="role" vlaue="user" >
<label for="user" class="radio-label">User</label>
<input type="radio" id="admin" name="role" value="admin">
<label for="admin">Admin</label>
</div>
and in controller
$this->validate($request, [
'fullName'=>'required|string|between:3,15',
'email'=>'required|email|unique:users',
'password'=>'required|regex:/^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!#$%^&*-]).{6,}$/',
'mobile'=>'required|digits:10',
'role'=>'required|in:user,admin'
]);
$user = new User([
'fullName'=> $request->input('fullName'),
'email'=> $request->input('email'),
'password'=> bcrypt($request->input('password')),
'mobile'=>$request->input('mobile') ,
'role'=>$request->role ,
]);
$user->save();

Laravel 5 not displaying validator errors message after redirection

Error messages are not showing.I added the redirection in
sendFailedLoginResponse it is redirecting to the login page without error messages
protected function sendFailedLoginResponse(Request $request)
{
return redirect()->route("login")->withErrors([
$this->username() => [trans('auth.failed')],
]);
}
Blade
<div class="form-group col-md-12">
<input id="email" name="email" class="" type="email" placeholder="Your Email">
#if ($errors->has('email'))
<span class="invalid-feedback" role="alert">
<strong>{{ $errors->first('email') }}</strong>
</span>
#endif
return redirect()->route("login")->withErrors(['email' => trans('auth.failed')]);
Instead of array pass a message bag object like this.
$errors = new Illuminate\Support\MessageBag;
$errors->add('email', trans('auth.failed'));
return redirect()->route("login")->withErrors($errors);
The name of the input field should be the second argument of the withErrors() function.
Laravel documentation - Manually Creating Validators
protected function sendFailedLoginResponse(Request $request)
{
return redirect()->route("login")->withErrors(trans('auth.failed'), 'login');
}
Blade file
<div class="form-group col-md-12">
<input id="email" name="email" class="" type="email" placeholder="Your Email">
#if ($errors->has('email'))
<span class="invalid-feedback" role="alert">
<strong>{{ $errors->login->first('email') }}</strong>
</span>
#endif
If Your application is too large folow these steps
While You are making the validation there are several ways
Method one
Using Validator Facade
public function store(Request $request)
{
$input = $request->all();
$validator = \Validator::make($input, [
'post_name' => 'required',
'post_type' => 'required',
]);
if ($validator->fails()) {
return redirect()->back()
->withErrors($validator)
->withInput($input);
}
Post::create($input);
return redirect('post.index');
}
Method Two
using $this->validate(); Method
public function store(Request $request)
{
$this->validate($request, [
'post_name' => 'required',
'post_type' => 'required',
]);
Post::create($request->all());
}
Method Three
Using the request method
php artisan make:request PostStoreRequest
anf the file will be creted in app\Http\Requests with name PostStoreRequest.php
open you controller and add
use App\Http\Requests\PostStoreRequest;
now the file contents
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class PostStoreRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'post_name' => 'required',
'post_type' => 'required',
];
}
/**
* Custom message for validation
*
* #return array
*/
public function messages()
{
return [
'post_name.required' =>'Enter Post Name',
'post_type.required' =>'Enter Post Type',
];
}
}
if You want to customize the error message use messages function
now the store function
public function store(PostStoreRequest $request)
{
Post::create($request->all() );
return redirect()->route('post.index')->with('success','CrmHfcImageGallery Created Successfully');
}
now Comming to the view
to view all the messages add this in top of the blade file
#if ($errors->any())
{{ implode('', $errors->all('<div>:message</div>')) }}
#endif
To view particular message
<div class="col-sm-4">
<div class="form-group #if ($errors->has('post_name')) has-error #endif">
{!! Form::label('post_name','Post Name') !!}
{!! Form::text('post_name',old('post_name'),['placeholder'=>'Enter Post Name ','class' =>'form-control rounded','id' =>'post_name']) !!}
#if ($errors->has('post_name'))
<p class="help-block">{{ $errors->first('post_name') }}</p>
#endif
</div>
</div>
Hope it helps

No error messages from 422 response on laravel form request from vue component

I'm trying to submit a form request using axios, have it validated, and return errors if the validation fails. The problem is, when I submit the form, no error messages are returned for me to show on the client side. Here's the HTTP request and vue component:
<div class="card">
<div class="card-header">
<h4>Information</h4>
</div>
<div class="card-body">
<p v-if='!isEditMode'><strong>Name:</strong> {{businessData.name}}</p>
<div class="form-group" v-if='isEditMode'>
<strong><label for="business-name">Name</label></strong>
<input class="form-control" name="business-name" v-model='businessData.name'>
</div>
<p v-if='!isEditMode'><strong>Description:</strong> {{businessData.description}}</p>
<div class="form-group" v-if='isEditMode'>
<strong><label for="business-description">Description</label></strong>
<textarea class="form-control normal" name="business-description"
placeholder="Enter your services, what you sell, and why your business is awesome"
v-model='businessData.description'></textarea>
</div>
</div>
</div>
<div class="card">
<h4 class="card-header">Address Information</h4>
<div class="card-body">
<p v-if="!isEditMode"><strong>Street Address: </strong> {{businessData.street}}</p>
<div class="form-group" v-if='isEditMode'>
<strong><label for="business-street">Street Address: </label></strong>
<input type="text" class="form-control" name="business-street" v-model='businessData.street' placeholder="1404 e. Local Food Ave">
</div>
<p v-if="!isEditMode"><strong>City: </strong> {{businessData.city}}</p>
<div class="form-group" v-if='isEditMode'>
<strong><label for="business-city">City: </label></strong>
<input class="form-control" type="text" name="business-city" v-model='businessData.city'>
</div>
<p v-if="!isEditMode"><strong>State: </strong> {{businessData.state}}</p>
<div class="form-group" v-if='isEditMode'>
<strong><label for="business-state">State: </label></strong>
<select class="form-control" name="business-state" id="state" v-model="businessData.state" >...</select>
</div>
<p v-if="!isEditMode"><strong>Zip: </strong> {{businessData.zip}}</p>
<div class="form-group" v-if='isEditMode'>
<strong><label for="business-zip">Zip: </label></strong>
<input class="form-control" type="text" maxlength="5" name="business-zip" v-model='businessData.zip'>
</div>
</div>
</div>
<div class="card">
<h4 class="card-header">Contact Information</h4>
<div class="card-body">
<p v-if="!isEditMode"><strong>Phone: </strong> {{businessData.phone}}</p>
<div class="form-group" v-if='isEditMode'>
<strong><label for="business-phone">Phone: </label></strong>
<input class="form-control" type="tel" name="business-phone" v-model='businessData.phone'>
</div>
<p v-if="!isEditMode"><strong>Email: </strong> {{businessData.email}}</p>
<div class="form-group" v-if='isEditMode'>
<strong><label for="business-Email">Email: </label></strong>
<input class="form-control" type="email" name="business-email" v-model='businessData.email'>
</div>
</div>
</div>
</div>
<script>
export default {
data () {
return {
isEditMode: false,
businessData: this.business,
userData: this.user,
errors: []
}
},
props: {
business: {},
user: {},
role: {}
},
//Todo - Institute client side validation that prevents submission of faulty data
methods: {
validateData(data) {
},
saveBusinessEdits () {
axios.put('/businesses/' + this.business.id , {updates: this.businessData})
.then(response => {
console.log(response.data)
// this.businessData = response.data;
this.isEditMode = false;
})
.catch (response => {
console.log(response.data)
this.isEditMode = false;
})
},
saveUserEdits () {
axios.put('/profile/' + this.user.id , {updates: this.userData})
.then(response => {
console.log(response.data)
this.userData = response.data;
this.isEditMode = false;
})
.catch (response => {
console.log(response)
this.isEditMode = false;
})
}
}
}
Route
Route::put('/businesses/{id}', 'BusinessesController#update');
BusinessController and update function
public function update(BusinessRequest $request, $id)
{
$business = Business::find($id)->update($request->updates);
$coordinates = GoogleMaps::geocodeAddress($business->street,$business->city,$business->state,$business->zip);
if ($coordinates['lat']) {
$business['latitude'] = $coordinates['lat'];
$business['longitude'] = $coordinates['lng'];
$business->save();
return response()->json($business,200);
} else {
return response()->json('invalid_address',406);
}
$business->save();
return response()->json($business,200);
}
and BusinessRequest class
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'business-name'=> 'required|string|max:255',
'business-description'=> 'required|string',
'business-phone' => 'nullable|phone|numeric',
'business-email' => 'nullable|email',
'business-street'=> 'required|string',
'business-city' => 'required|string',
'business-state' => 'required|string|max:2',
'business-zip' => 'required|min:5|max:5|numeric',
];
}
public function messages() {
return [
'business-zip.min:5' =>'your zip code must be a 5 characters long',
'business-email.email'=>'your email is invalid',
'business-phone.numeric'=>'your phone number is invalid',
];
}
}
I don't understand why, even if input valid data, it responds with a 422 response and absolutely no error messages. Since this is laravel 5.6, the 'web' middleware is automatic in all of the routes in the web.php file. So this isn't the problem. Could anyone help me normalize the validation behavior?
In Laravel a 422 status code means that the form validation has failed.
With axios, the objects that are passed to the then and catch methods are actually different. To see the response of the error you would actually need to have something like:
.catch (error => {
console.log(error.response)
this.isEditMode = false;
})
And then to get the errors (depending on your Laravel version) you would have something like:
console.log(error.response.data.errors)
Going forward it might be worth having a look at Spatie's form-backend-validation package
You can use Vue.js and axios to validate and display the errors. Have a route called /validate-data in a controller to validate the data.
app.js file:
import Vue from 'vue'
window.Vue = require('vue');
window.axios = require('axios');
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
let token = document.head.querySelector('meta[name="csrf-token"]');
if (token) {
window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
} else {
console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
}
class Errors {
constructor() {
this.errors = {};
}
get(field) {
if (this.errors[field]) {
return this.errors[field][0];
}
}
record(errors) {
this.errors = errors;
}
clear(field) {
delete this.errors[field];
}
has(field) {
return this.errors.hasOwnProperty(field);
}
any() {
return Object.keys(this.errors).length > 0;
}
}
new Vue({
el: '#app',
data:{
errors: new Errors(),
model: {
business-name: '',
business-description: '',
business-phone: ''
},
},
methods: {
onComplete: function(){
axios.post('/validate-data', this.$data.model)
// .then(this.onSuccess)
.catch(error => this.errors.record(error.response.data.errors));
},
}
});
Make a route called /validate-data with a method in the controller, do a standard validate
$this->validate(request(), [
'business-name'=> 'required|string|max:255',
'business-description'=> 'required|string',
'business-phone' => 'nullable|phone|numeric',
'business-email' => 'nullable|email',
'business-street'=> 'required|string',
'business-city' => 'required|string',
'business-state' => 'required|string|max:2',
'business-zip' => 'required|min:5|max:5|numeric'
]
);
Then create your inputs in your view file, using v-model that corresponds to the vue.js data model fields. Underneath it, add a span with an error class (basic red error styling, for example) that only shows up if the errors exist. For example:
<input type="text" name="business-name" v-model="model.business-name" class="input">
<span class="error-text" v-if="errors.has('business-name')" v-text="errors.get('business-name')"></span>
Don't forget to include the app.js file in footer of your view file. Remember to include the tag, and run npm run watch to compile the vue code. This will allow you to validate all errors underneath their input fields.
Forgot to add, have a buttton that has #onclick="onComplete" to run the validate method.

FOSUserBundle register in AngularJS

I have a SPA web application that uses AngularJS for the frontend and Symfony2 for the backend.
I used FOSUserBundle for handling the User.
What I want to do right now is to use the AngularJS method of registering my User which is via Ajax
My problem is that whenever I submit the form, it prints "invalid form" in the console log.
Here's my current progress:
new.html
<form class="form-group text-left" ng-submit="submit()" novalidate name="userFrm">
<div class="form-group">
<label for="user.email" class="required">Email</label>
<input id="user.email" name="user.email" class="form-control" type="text" ng-model="user.email" />
</div>
<div class="form-group">
<label for="user.username" class="required">Username</label>
<input id="user.username" name="user.username" class="form-control" type="text" ng-model="user.username" />
</div>
<div class="form-group">
<label for="user.plainPassword" class="required">Password</label>
<input id="user.plainPassword" name="user.plainPassword" class="form-control" type="password" ng-model="user.plainPassword" />
</div>
<div class="form-group">
<label for="confirmPassword" class="required">Confirm Password</label>
<input id="confirmPassword" name="confirmPassword" compare-to="user.plainPassword" class="form-control" type="password" ng-model="confirmPassword" />
</div>
<input type="submit" value="Register" ng-disabled="userFrm.$invalid" class="btn btn-primary center-block col-lg-2" />
</form>
new.js
'use strict';
(function () {
angular.module('myApp.user.new', [])
.config(['$stateProvider', function ($stateProvider) {
$stateProvider
.state('user.new', {
url: "/new",
controller: "NewUserCtrl",
templateUrl: PATH + 'user/new/new.html'
});
}])
.controller('NewUserCtrl', ["$scope", "$http", "$state", function ($scope, $http, $state) {
var success = function (response) {
var valid = response.data.valid;
if (valid) {
$state.go('home');
} else {
console.log("invalid form");
}
};
var error = function (reason) {
console.log("Submission failed");
};
$scope.submit = function () {
var formData = {
fos_user_registration: $scope.user,
confirmPass: $scope.confirmPassword
};
$http.post(Routing.generate('fos_user_registration_register'), $.param(formData), {
headers: {'Content-Type': 'application/x-www-form- urlencoded'}
})
.then(success, error);
};
}]);
}());
RegistrationController.php (overridden from FOSUserBundle)
public function registerAction(Request $request) {
/** #var $formFactory \FOS\UserBundle\Form\Factory\FactoryInterface */
$formFactory = $this->get('fos_user.registration.form.factory');
/** #var $userManager \FOS\UserBundle\Model\UserManagerInterface */
$userManager = $this->get('fos_user.user_manager');
$user = $userManager->createUser();
$user->setEnabled(true);
$form = $formFactory->createForm();
$form->setData($user);
$form->handleRequest($request);
if ($form->isValid()) {
$user->addRole('ROLE_ADMIN');
$userManager->updateUser($user);
$response = ['valid' => true];
return new JsonResponse($response);
}
$response = ['valid' => false];
return new JsonResponse($response);
}
I don't see a CSRF token in your form. Your form may not be validated without CSRF token. Check here first; http://symfony.com/doc/current/cookbook/security/csrf_in_login_form.html
Also it may be better to generate your forms with twig templating engine for complete compatibility. See here; http://symfony.com/doc/current/book/forms.html
For further investigation why your form is not being validated, you can write an else block for $form->isValid() check and use the method in the answer to see your form errors. You can examine why your form is not being validated. https://stackoverflow.com/a/17428869/3399234
UPDATE
I come up with a solution. I used my Vagrant configuration which includes symfony 2.6.10. I have overridden the RegistrationFormType, place it in my own bundle and injected it as a service, just like FOS does. I replaced the FOS registration form with my own service alias. So I managed to switch off csrf protection in my overriden RegistrationFormType.
Also added to set plainPassword to user model to fix persistence error in USerManager.
The controller, overrides FOS registration controller.
<?php
namespace Acme\WebBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use FOS\UserBundle\Controller\RegistrationController as BaseController;
use Symfony\Component\HttpFoundation\Request as Request;
use Symfony\Component\HttpFoundation\JsonResponse as JsonResponse;
class RegistrationController extends BaseController
{
public function registerAction()
{
$request = Request::createFromGlobals();
$form = $this->container->get('fos_user.registration.form');
/** #var $userManager \FOS\UserBundle\Model\UserManagerInterface */
$userManager = $this->container->get('fos_user.user_manager');
$user = $userManager->createUser();
$form->setData($user);
$form->handleRequest($request);
if ($form->isValid()) {
$user->setEnabled(true);
$user->addRole('ROLE_ADMIN');
$userManager->updateUser($user);
$response = ['valid' => true];
return new JsonResponse($response);
} else {
$string = (string) $form->getErrors(true, false);
//Show errors
$response = ['valid' => false];
return new JsonResponse($response);
}
return $this->container->get('templating')->renderResponse('AcmeWebBundle:Default:index.html.twig');
}
}
Overriden FOS Registration form,
<?php
//Acme\WebBundle\Form\Type\RegistrationFormType.php
namespace Acme\WebBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class RegistrationFormType extends AbstractType
{
private $class;
/**
* #param string $class The User class name
*/
public function __construct($class)
{
$this->class = $class;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => $this->class,
'intention' => 'registration',
'csrf_protection' => false, //this line does the trick ;)
));
}
public function getParent()
{
return 'fos_user_registration';
}
public function getName()
{
return 'acme_user_registration';
}
}
Services.yml
services:
acme.registration.form.type:
class: Acme\WebBundle\Form\Type\RegistrationFormType
arguments: ["%fos_user.model.user.class%"]
tags:
- { name: form.type, alias: acme_user_registration }
index.html.twig
<script type="text/javascript">
var app = angular.module('myApp', []);
app.controller('NewUserCtrl', ["$scope", "$http", function ($scope, $http) {
var success = function (response) {
var valid = response.data.valid;
if (valid) {
$state.go('home');
} else {
console.log("invalid form");
}
};
var error = function (reason) {
console.log("Submission failed");
};
$scope.submit = function () {
var formData = {
fos_user_registration_form: $scope.user
};
$http.post('<YOUR URL HERE>', $.param(formData), {
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
}).then(success, error);
};
}]);
</script>
<div id="content" ng-app="myApp" ng-controller="NewUserCtrl" >
<form class="form-group text-left" ng-submit="submit()" novalidate name="userFrm">
<div class="form-group">
<label for="user.email" class="required">Email</label>
<input id="user.email" name="user.email" class="form-control" type="text" ng-model="user.email" />
</div>
<div class="form-group">
<label for="user.username" class="required">Username</label>
<input id="user.username" name="user.username" class="form-control" type="text" ng-model="user.username" />
</div>
<div class="form-group">
<label for="user.plainPassword.first" class="required">Password</label>
<input id="user.plainPassword.first" name="user.plainPassword.first" class="form-control" type="password" ng-model="user.plainPassword.first" />
</div>
<div class="form-group">
<label for="user.plainPassword.second" class="required">Confirm Password</label>
<input id="user.plainPassword.second" name="user.plainPassword.second" compare-to="user.plainPassword.first" class="form-control" type="password" ng-model="user.plainPassword.second" />
</div>
<input type="submit" value="Register" ng-disabled="userFrm.$invalid" class="btn btn-primary center-block col-lg-2" />
</form>
</div>
This is the fos_user configuration in config.yml to change default form with your overridden form whenever FOS User bundle's registration form is summoned.
config.yml
fos_user:
registration:
form:
type: acme_user_registration
And that's it I can post with the form and persist the user to database then return the {"valid":true} response as expected. And finally i have chance to learn how to inject AngularJS to Symfony 2, cheers for that.

Resources