I'm developing in a system using Laravel 5.3 and I"m trying to update custom validation rules. The architecture is currently as follows:
ProfileStoreRequest --> ProfileValidator
ClientStoreRequest --> ClientValidator
...
What I'm basically trying to do here is to have only one object named "StoreRequest" which will call the correct validator depending on the route which is being called. Here's what I have so far:
In my routes:
Route::group([
'prefix' => 'manage',
'namespace' => 'Manage',
'validators' => [
'manage.profile.storeAjax' => [
'name' => "required|max:40",
'color' => "integer|digits_between:0,7",
'service' => "integer", //digits_between:3,10
'company_id' => "required|integer|exists:companies,id,deleted_at,NULL",
'site_id' => "integer|siteIdExists"
]
]], function () {
Route::post('/site/storeAjax', 'SiteController#storeAjax')->name('manage.site.storeAjax');
Route::post('/company/storeAjax', 'CompanyController#storeAjax')->name('manage.company.storeAjax');
Route::post('/employee/store', 'EmployeeController#store')->name('manage.employee.store');
Route::post('/employee/addProfile', 'EmployeeController#addProfile')->name('manage.employee.addProfile');
Route::post('/employee/removeProfile', 'EmployeeController#removeProfile')->name('manage.employee.removeProfile');
Route::post('/employee/addSite', 'EmployeeController#addSite')->name('manage.employee.addSite');
Route::post('/employee/removeSite', 'EmployeeController#removeSite')->name('manage.employee.removeSite');
Route::post('/message/storeAjax', 'MessageController#storeAjax')->name('manage.message.storeAjax');
Route::post('/profile/storeAjax', 'ProfileController#storeAjax')->name('manage.profile.storeAjax');
Route::post('/timeEntry/storeAjax', 'TimeEntryController#storeAjax')->name('manage.timeEntry.storeAjax');
});
Next is my StoreRequest:
namespace App\Http\Requests;
use App\Http\Validators\ProfileValidator;
use Auth;
//use App\Model\TimeEntry;
use DateUtil;
use Illuminate\Contracts\Validation\Factory as ValidationFactory;
class StoreRequest extends AbstractRequest {
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize() {
// prj(__METHOD__);
$authorized = parent::authorize();
if ($authorized) {
$user = Auth::user();
if ($user && $user->can('write')) {
return true;
} else {
return false;
}
}
return false;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules() {
parent::rules();
return $this->route()->getAction()['validators'][$this->route()->getName()];
}
/**
* User messages
*
* #return array
*/
public function messages() {
$messages = array_merge(parent::messages(), [
'exists' => 'The selected :attribute is invalid for this time entry id.'
]);
return $messages;
}
public function validate()
{
parent::validate();
}
}
And of course, all of my custom validators are registered using service providers (here's an example with a profileValidator):
profileServiceProvider.php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Validator;
class ProfileServiceProvider extends ServiceProvider {
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot() {
Validator::extend('siteIdExists', 'App\Http\Validators\ProfileValidator#validateSiteIdExists');
}
/**
* Register any application services.
*
* #return void
*/
public function register() {
//
}
}
ProfileValidator.php
namespace App\Http\Validators;
use App\Model\Site;
use Mockery\Exception;
class ProfileValidator
{
public function validateSiteIdExists($attribute, $value, $parameters, $validator)
{
if ($value == -1)
return true;
else
{
return Site::where(
[
['id', '=', $value],
['company_id', '=', $validator->getData()['company_id']]
]
)->whereNull('deleted_at')->exists();
}
}
}
So basically, my StoreRequest is capable of loading its validation rules from the route... however, no matter how hard I try, I can't figure how to bind the validator I want. Can anyone help me?
Related
I'm currently using Laravel with Infyom to build a backend, one thing that I'm not understanding is how to have multiple rules for validation depending on the API route, for instance, when I'm creating a user, both fields email and role should be provided to the endpoint, but when updating the user (due to login), only the email and password is required to be present. What I want is to have a different set o rules depending on the endpoint being used, is that possible and how? Currently, the endpoint always returns
{
"message": "The given data was invalid.",
"errors": {
"role": [
"The role field is required."
]
}
}
my routes/api.php looks like this
<?php
use Illuminate\Http\Request;
Route::middleware('auth:api')->get('/user', function (Request $request) {
return $request->user();
});
Route::post('login', 'TokenAPIController#loginUser');
Route::put('login/updateToken/{id}', 'TokenAPIController#updateToken');
Route::resource('users', 'UserAPIController');
Route::resource('roles', 'RoleAPIController');
Route::resource('roles', 'roleAPIController');
Route::resource('product_variants', 'Product_variantAPIController');
Route::resource('product_images', 'Product_imageAPIController');
Route::resource('product_categories', 'Product_categoryAPIController');
Route::resource('products', 'ProductAPIController');
Route::resource('orders', 'OrderAPIController');
Route::resource('order_products', 'Order_productAPIController');
Route::resource('notifications', 'NotificationAPIController');
Route::resource('factories', 'FactoryAPIController');
Route::resource('statuses', 'StatusAPIController');
My user app/Models/User.php
<?php
namespace App\Models;
use Eloquent as Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class User extends Model
{
use SoftDeletes;
public $table = 'users';
const CREATED_AT = 'created_at';
const UPDATED_AT = 'updated_at';
protected $dates = ['deleted_at'];
public $fillable = [
'name',
'role',
'email',
'password',
'remember_token',
'notification_token',
'factory',
'api_token'
];
/**
* The attributes that should be casted to native types.
*
* #var array
*/
protected $casts = [
'id' => 'integer',
'name' => 'string',
'role' => 'integer',
'email' => 'string',
'password' => 'string',
'remember_token' => 'string',
'notification_token' => 'string',
'factory' => 'integer',
'api_token' => 'string'
];
/**
* Validation rules
*
* #var array
*/
public static $rules = [
'role' => 'required',
'email' => 'required'
];
/**
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
**/
public function role()
{
return $this->belongsTo(\App\Models\Role::class, 'role');
}
/**
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
**/
public function factory()
{
return $this->belongsTo(\App\Models\Factory::class, 'factory');
}
/**
* #return \Illuminate\Database\Eloquent\Relations\HasMany
**/
public function orders()
{
return $this->hasMany(\App\Models\Order::class, 'customer');
}
/**
* #return \Illuminate\Database\Eloquent\Relations\HasMany
**/
public function order1s()
{
return $this->hasMany(\App\Models\Order::class, 'responsible');
}
/**
* #return \Illuminate\Database\Eloquent\Relations\HasMany
**/
public function notifications()
{
return $this->hasMany(\App\Models\Notification::class, '"from"');
}
/**
* #return \Illuminate\Database\Eloquent\Relations\HasMany
**/
public function notification2s()
{
return $this->hasMany(\App\Models\Notification::class, '"to"');
}
}
And finally, my TokenAPIController.php
<?php
namespace App\Http\Controllers\API;
use App\Http\Requests\API\CreateUserAPIRequest;
use App\Http\Requests\API\UpdateUserAPIRequest;
use App\Models\User;
use App\Repositories\UserRepository;
use Illuminate\Http\Request;
use App\Http\Controllers\AppBaseController;
use Response;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Hash;
class TokenAPIController extends AppBaseController
{
/** #var UserRepository */
private $userRepository;
public function __construct(UserRepository $userRepo)
{
$this->userRepository = $userRepo;
}
public function loginUser(UpdateUserAPIRequest $request)
{ /** #var User $user */
$input = $request->all();
if (!isset($input['email'])) {
return $this->sendError('Email is required');
}
if (!isset($input['password'])) {
return $this->sendError('Password is required');
}
$user = User::where('email', $input['email'])
->first();
if (empty($user)) {
return $this->sendError('User not found');
}
$validCredentials = Hash::check($input['password'], $user["password"]);
if ($validCredentials) {
return $this->updateToken($user["id"]);
}
return $this->sendError('No match');
}
public function updateToken($id)
{
// $input = $request->all();
/** #var User $user */
$user = $this->userRepository->find($id);
if (empty($user)) {
return $this->sendError('User not found');
}
$token = Str::random(60);
$user->forceFill([
'api_token' => hash('sha256', $token),
])->save();
return $this->sendResponse($user->toArray(), 'User updated successfully');
}
}
Edit your App\Http\Requests\API\UpdateUserAPIRequest file and adjust the rules it returns from rules.
this CreatePost.php
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class CreatePost 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 [
'title' => 'required|string',
'content' => 'required',
];
}
}
anyone please help me ?? Class App\Http\Request\CreatePost does not exist
thank
you need to import the model on top
for example
use App\Supplier;
I have generated new form Request for the controller, but I do not know how to filter data before there will handle in the validator and so on.
Are there some native solutions in Laravel for this case?
class TestRequest 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 [
"title" => "required|string",
"order" => "required|integer"
];
}
}
class TestController extends Controller
{
public function store(TestRequest $request, $chapterId)
{
// some business logic
}
}
There is some solution in Laravel 5.5 but in this example author uses
validate for filtering data from request, but I need to use filter inside TestRequest
$data = $this->validate(request(), [
//...
]); // I can't use this inside TestRequest
You can use my package: https://github.com/mikicaivosevic/laravel-filters
It's allows you to filter request values before validation...
<?php
class LoginRequest extends FormRequest {
//Filter
public function filters()
{
return [
'name' => 'lower',
'id' => 'int',
];
}
//...
}
Convert $request->name value into lowercase.
Conert $request->id value into integer.
I have model like this
class test extends Model
{
public $rules = [
'title' => 'required',
'name' => 'required',
];
protected $fillable = ['title','name'];
}
And controller like this
public function store(Request $request)
{
$test=new test; /// create model object
$validator = Validator::make($request->all(), [
$test->rules
]);
if ($validator->fails()) {
return view('test')->withErrors($validator)
}
test::create($request->all());
}
Validation show error like this
The 0 field is required.
I want show this
The name field is required.
The title field is required.
I solve it
public function store(Request $request)
{
$test=new test; /// create model object
$validator = Validator::make($request->all(),$test->rules);
if ($validator->fails()) {
return view('test')->withErrors($validator)
}
test::create($request->all());
}
You are doing it the wrong way. The rules array should either be in your controller or better in a Form Request.
Let me show you a better approach:
Create a new Form Request file with php artisan make:request TestRequest.
Example TestRequest class:
namespace App\Http\Requests;
use App\Http\Requests\Request;
class TestRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation messages.
*
* #return array
*/
public function messages()
{
return [
'title.required' => 'A title is required.',
'name.required' => 'The name field is required'
];
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'title' => 'required',
'name' => 'required',
];
}
}
Inject the request object into your controller method.
public function store(TestRequest $request)
{
// You don't need any validation, this is already done
test::create($request->all());
}
You could also look at validating in your model and throwing a ValidationException which will be handled as usual in your controller (with the error bag etc). E.g:
abstract class BaseModel extends Model implements ModelInterface {
protected $validationRules = [];
/**
* Validate model against rules
* #param array $rules optional array of validation rules. If not passed will validate against object's current values
* #throws ValidationException if validation fails. Used for displaying errors in view
*/
public function validate($rules=[]) {
if (empty($rules))
$rules = $this->toArray();
$validator = \Validator::make($rules,$this->validationRules);
if ($validator->fails())
throw new ValidationException($validator);
}
/**
* Attempt to validate input, if successful fill this object
* #param array $inputArray associative array of values for this object to validate against and fill this object
* #throws ValidationException if validation fails. Used for displaying errors in view
*/
public function validateAndFill($inputArray) {
// must validate input before injecting into model
$this->validate($inputArray);
$this->fill($inputArray);
}
}
Then in my Controller:
public function store(Request $request) {
$person = $this->personService->create($request->input());
return redirect()->route('people.index', $person)->with('status', $person->first_name.' has been saved');
}
Finally in my base service class
abstract class BaseResourceService {
protected $dataService;
protected $modelClassName;
/**
* Create a resource
* #param array $inputArray of key value pairs of this object to create
* #returns $object
*/
public function create($inputArray) {
try {
$arr = $inputArray;
$object = new $this->modelClassName();
$object->validateAndFill($arr);
$this->dataService->create($object);
return $object;
}
catch (Exception $exception) {
$this->handleError($exception);
}
}
If the model validates it continues as usual. If there's a validation error it goes back to the previous page with the validation errors in the flash data/error bag.
I will most probably move the $person->validate() method to my service class, however it will still work as outlined above.
You can simply make your validation by writing in Model.
In your Model File
i.e. Models\Test.php
public static $createRules = [
'name'=>'required|max:111',
'email'=>'required|email|unique:users',
];
In Controller
public function store(Request $request)
{
$request->validate(ModalName::$createRules);
$data = new ModelName();
}
Just do this. Everything will be fine.
I am new in laravel 5. I have a dashboard page and a login page. whenever I go to localhost:8080/dashboard it always redirect me to localhost:8080/auth/login.
I wanted to show my dashboard localhost:8080/dashboard to be viewed without logging in first. Here is my code in VerifyCsfrToken
namespace App\Http\Middleware;
use Closure;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;
class VerifyCsrfToken extends BaseVerifier {
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
protected $except_urls = [
'dashboard/dashboard',
];
public function handle($request, Closure $next)
{
$regex = '#' . implode('|', $this->except_urls) . '#';
if ($this->isReading($request) || $this->tokensMatch($request) || preg_match($regex, $request->path()))
{
return $this->addCookieToResponse($request, $next($request));
}
throw new TokenMismatchException;
return parent::handle($request, $next);
}
}
routes.php
Route::get('dashboard', 'ReservationController#index');
Route::controllers([
'auth' => 'Auth\AuthController',
'password' => 'Auth\PasswordController',
]);
controller :
use App\reservations;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use Illuminate\Console\Scheduling\Schedule;
use Carbon\Carbon;
use Request;
class ReservationController extends Controller {
/*
|--------------------------------------------------------------------------
| Welcome Controller
|--------------------------------------------------------------------------
|
| This controller renders the "marketing page" for the application and
| is configured to only allow guests. Like most of the other sample
| controllers, you are free to modify or remove it as you desire.
|
*/
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('auth');
}
/**
* Show the application welcome screen to the user.
*
* #return Response
*/
public function schedule()
{
$schedules = schedules::all();
return view('calendar.schedule',compact('schedules'));
}
public function index()
{
return view('dashboard.dashboard');
}
public function create()
{
return view('reserve.reserve');
}
public function update()
{
return view('calendar.update');
}
public function login()
{
return view('login');
}
public function store(Requests\CreateReservationRequest $request)
{
$input = Request::all();
$reservation = new reservations(['user_id' => '13100024',
'status_id' => '1',
'room_id' => $input['room'],
'purpose' => $input['purpose'],
'start_time' => $input['date']." ".$input['hour1'].":".$input['minute1'].":00",
'end_time' => $input['date']." ".$input['hour2'].":".$input['minute2'].":00",
'add_date' => Carbon::now()
]);
$reservation->save();
return "success";
// return redirect('schedule');
}
This is what causes the issue:
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('auth');
}
It restricts access to the page to logged in users. Just remove it from your controller and users will be able to access the page whether they're logged in or not.
public function __construct()
{
$this->middleware('auth', ['except' => ['Whatever You want to Bypass']]);
}