Laravel 5 Custom Validator resolver not found - laravel

Hi Guys I would like to register my new CustomValidator class. This contains some validation for a mobile number I created this class
<?php
// This class is located in App\Validator
use Illuminate\Validation\Validator;
class CustomValidator extends Validator {
public function validateIsKyc($attribute, $value, $parameters)
{
return $value == 'is_kyc';
}
}
Now in my AppServiceProvider here's my code for loading that class
<?php
namespace App\Providers;
use Illuminate\Validation\Validator;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
Validator::resolver(function($translator, $data, $rules, $messages)
{
return new CustomValidator($translator, $data, $rules, $messages);
});
}
/**
* Register any application services.
*
* #return void
*/
public function register()
{
//
}
}
And on my request I tried using the custom validation like so
<?php
namespace App\Http\Requests;
use App\Http\Requests\Request;
class CreateRegistrationRequest extends Request
{
/**
* 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 [
'phone_no' => "required|isKyc",
];
}
}
But I get this error
FatalErrorException in AppServiceProvider.php line 23: Call to undefined method Illuminate\Validation\Validator::resolver()
Don't know what went wrong any idea please...

In the file App\Validator\CustomValidator.php you need to add at the beginning
<?php namespace App\Validator;
In the file AppServiceProvider.php you need to add at the beginning
use App\Validator\CustomValidator;
and also replace this
use Illuminate\Validation\Validator;
with this
use Validator;

My bad guys,
I already know the solution, i doing something wrong, i wasn't aware im putting my code into the register function instead to boot() function.
From
public function register()
{
Validator::resolver(function($translator, $data, $rules, $messages)
{
return new CustomValidator($translator, $data, $rules, $messages);
});
}
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
}
to this,
public function register()
{
}
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
Validator::resolver(function($translator, $data, $rules, $messages)
{
return new CustomValidator($translator, $data, $rules, $messages);
});
}
now i can manage my customvalidators to one class only. :)
GGWP Laravel!

Related

Laravel Http client before request send add headers

I'm using Http client for making outgoing HTTP requests. I've used it many places in the project.Now project has new requirement that I have to add a new header to every outgoing
requests. I can do this by adding it to every places. But I want to know , is there any kind of trigger or event which can give me ability to modify the headers just before the request send. There is an event Illuminate\Http\Client\Events\RequestSending which is only useful for inspecting the request.
This is possible to achieve without the need of a package. You can simple do something like this in a service provider:
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Http\Client\Factory as Http;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* #return void
*/
public function register()
{
$this->app->extend(Http::class, function ($service, $app) {
return $service->withOptions(['foo' => 'bar']);
});
}
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
//
}
}
Yes, this is possible with a fantastic package here. After installing the package you just set the default headers like,
Http::withDefaultOptions([
'headers' => [
'X-Bar-Header' => 'bar'
],
]);
But I was unfortunate, the package was not installed with my laravel 9-dev. So I had to extract the code for me. First, create a Factory class in your app\HttpClient directory,
<?php
namespace App\HttpClient;
use Illuminate\Http\Client\Factory as BaseFactory;
use Illuminate\Http\Client\PendingRequest;
use Illuminate\Support\Arr;
class Factory extends BaseFactory
{
protected $ignoreDefaultOptions = false;
protected $defaultOptions = [];
public function ignoreDefaultOptions()
{
$this->ignoreDefaultOptions = true;
return $this;
}
public function withoutDefaultOptions($keys = null)
{
if ($keys === null) {
return $this->ignoreDefaultOptions();
}
if (func_num_args() > 1) {
$keys = func_get_args();
}
$this->defaultOptions = with($this->defaultOptions, function ($options) use ($keys) {
foreach (Arr::wrap($keys) as $key) {
Arr::forget($options, $key);
}
return $options;
});
return $this;
}
public function withDefaultOptions(array $options)
{
$this->defaultOptions = array_merge_recursive($this->defaultOptions, $options);
return $this;
}
public function __call($method, $parameters)
{
if (static::hasMacro($method)) {
return $this->macroCall($method, $parameters);
}
if ($this->defaultOptions && ! $this->ignoreDefaultOptions) {
return tap(new PendingRequest($this), function ($request) {
$request->withOptions($this->defaultOptions)
->stub($this->stubCallbacks);
})->{$method}(...$parameters);
}
return parent::__call($method, $parameters);
}
}
Then, create a HttpServiceProver,
php artisan make:provider HttpServiceProvider
And put the following code there,
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Http\Client\Factory as BaseFactory;
use App\HttpClient\Factory;
class HttpServiceProvider extends ServiceProvider
{
/**
* Register services.
*
* #return void
*/
public function register()
{
$this->app->bind(
BaseFactory::class,
function ($app) {
return new Factory($app->make(Dispatcher::class));
}
);
}
/**
* Bootstrap services.
*
* #return void
*/
public function boot()
{
//
}
}
Now, register the newly created service provider in AppServiceProvider.php
public function register()
{
//...
app()->register(HttpServiceProvider::class);
}
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
//....
Http::withDefaultOptions([
'headers' => [
'X-Bar-Header' => 'bar'
],
]);
}
There are other options in this package. Please check it the package link for details.

How to fire an event after data inserted using boot? (laravel)

I am finding a way to somewhat fire an event after the Eloquent has finished creating.
Here's my code in Branch model:
class Branch extends Model
{
//some code here
public static function boot() {
parent::boot();
self::created(function (HistoryLog $model) {
$model->tag = 'Created';
$model->description = 'This branch was created by '. ucwords(auth()->user()->name());
$model->save();
});
}
}
What I'm trying to do is, I want to create a history_log after branch was created.
But this code returns an error:
Symfony\Component\Debug\Exception\FatalThrowableError : Argument 1 passed to
App\Vehicle::App{closure}() must be an instance of App\HistoryLog, instance of
App\Vehicle given, called in D:\document\My Documents\optodph\vendor\laravel\fr
amework\src\Illuminate\Events\Dispatcher.php on line 347
Can someone point out to me what's wrong with that code? And what's the right way to achieve this?
Laravel way to do this.
Create an Observer:
php artisan make:observer BranchObserver --model=Branch
Add your logic to the Observer:
<?php
namespace App\Observers;
use App\Branch;
class BranchObserver
{
/**
* Handle the Branch "created" event.
*
* #param \App\Branch $branch
* #return void
*/
public function created(Branch $branch)
{
// Add your logic here
}
}
Register it in AppServiceProvider:
<?php
namespace App\Providers;
use App\Branch;
use App\Observers\BranchObserver;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* #return void
*/
public function register()
{
//
}
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
Branch::observe(BranchObserver::class);
}
}
$model is a new record created. Is an instance of App\Vehicle not a App\HistoryLog.
Working code might look like this:
class Vehicle extends Model
{
//some code here
public static function boot() {
parent::boot();
self::created(function ($model) {
App\HistoryLog::create([...]);
});
}
}
You can also achieve this with Eloquent Observers https://laravel.com/docs/5.8/eloquent#observers

How to solve Class 'App\Http\Requests\Web\WebRequest' not found

I create a request in App\Http\Requests\Web in which it shows me the error.
Class 'App\Http\Requests\Web\WebRequest' not found
Here is the code of my Request CreateBucket.php:
<?php
namespace App\Http\Requests\Web;
class CreateBucket extends WebRequest
{
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'bucket_name' => 'required|string|string|max:30',
'bucket_type' => 'required|string|string|max:30',
'bucket_image' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg',
];
}
}
And Here is my code of Bucket Controller:
<?php
namespace App\Http\Controllers\Web;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Http\Requests\Web\CreateBucket;
use App\Bucket;
class BucketController extends Controller
{
public function index(Request $request)
{
$buckets = Bucket::orderBy('id','ASC')->paginate(10);
return view('buckets.index',compact('buckets',$buckets))
->with('i',($request->input('page',1) - 1) * 10);
}
public function create()
{
return view('buckets.create');
}
public function store(CreateBucket $request)
{
if($request->hasFile('bucket_image')) {
$bucket_image = $request->file('bucket_image');
$bucket_image_name = time().'.'.$bucket_image->getClientOriginalExtension();
$path = public_path('Storage/BucketImages');
$bucket_image->move($path, $bucket_image_name);
$bucket_image = 'Storage/BucketImages/'.$bucket_image_name;
} else {
$bucket_image = NULL;
}
$category = Category::create([
'bucket_name' => $request->input('bucket_name'),
'bucket_image'=> $bucket_image,
'bucket_type' => $request->input('bucket_type'),
]);
return redirect()->route('buckets.index')
->with('success','Bucket created successfully');
}
Please Help me to resolve this error. Thanks.
My WebRequest.php is missing in Requests folder that why he gave me this Error.
Here is the WebRequest.php file I created and my issue is resolve.
<?php
namespace App\Http\Requests\Web;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Foundation\Http\FormRequest;
class WebRequest 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 [
//
];
}
}

Call to a member function getEmailForPasswordReset() on null

I do not know why I get this error when I try to reset password.
I have another project with the same code and it works.
I noticed the error is in the "resources/views/auth/emails/password.php" file, that does have a null $user variable.
The strange thing is that in the function the view is called, the $user variable has value.
When the variable is passed to the view, it losts value.
public function emailResetLink(CanResetPasswordContract $user, $token, Closure $callback = null)
{
// We will use the reminder view that was given to the broker to display the
// password reminder e-mail. We'll pass a "token" variable into the views
// so that it may be displayed for an user to click for password reset.
$view = $this->emailView;
**// HERE THE $user VARIABLE HAS VALUE.**
**// IN THE $view VIEW THE $user VARIABLE IS NULL**
return $this->mailer->send($view, compact('token', 'user'), function ($m) use ($user, $token, $callback) {
$m->to($user->getEmailForPasswordReset());
if (! is_null($callback)) {
call_user_func($callback, $m, $user, $token);
}
});
}
UPDATED
This is the user model:
<?php
namespace App;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Zizaco\Entrust\Traits\EntrustUserTrait;
class User extends Authenticatable
{
use EntrustUserTrait; // add this trait to your user model
protected $fillable = [
'name', 'email'
];
protected $hidden = [
'password',
];
}
Got it,
the problem was a ViewComposer I had created and forgot.
This is the composer service provider:
class ComposerServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* #return void
*/
public function boot()
{
// Using class based composers...
View::composer(
'*', 'App\Http\ViewComposers\UserComposer'
);
}
/**
* Register the application services.
*
* #return void
*/
public function register()
{
//
}
}
And this is (edited to make it work) the UserComposer class:
class UserComposer
{
/**
* constructor.
*/
public function __construct()
{
}
/**
* Bind data to the view.
*
* #param View $view
* #return void
*/
public function compose(View $view)
{
$excludedViews = ['auth.emails.password'];
if (!in_array($view->getName() , $excludedViews))
{
$view->with('user', session('user'));
}
}
}
With the "excludedViews" variable I can inject "user" variable in all routes except the reset password.

Laravel authentication without global scope

In my Laravel app users can disable (not delete) their account to disappear from the website. However, if they try to login again their account should be activated automatically and they should log in successfully.
This is done with "active" column in the users table and a global scope in User model:
protected static function boot() {
parent::boot();
static::addGlobalScope('active', function(Builder $builder) {
$builder->where('active', 1);
});
}
The problem now is that those inactive accounts can't log in again, since AuthController does not find them (out of scope).
What I need to achieve:
Make AuthController ignore global scope "active".
If username and password are correct then change the "active" column value to "1".
The idea I have now is to locate the user using withoutGlobalScope, validate the password manually, change column "active" to 1, and then proceed the regular login.
In my AuthController in postLogin method:
$user = User::withoutGlobalScope('active')
->where('username', $request->username)
->first();
if($user != null) {
if (Hash::check($request->username, $user->password))
{
// Set active column to 1
}
}
return $this->login($request);
So the question is how to make AuthController ignore global scope without altering Laravel main code, so it will remain with update?
Thanks.
Create a class GlobalUserProvider that extends EloquentUserProvider like below
class GlobalUserProvider extends EloquentUserProvider {
public function createModel() {
$model = parent::createModel();
return $model->withoutGlobalScope('active');
}
}
Register your new user provider in AuthServiceProvider:
Auth::provider('globalUserProvider', function ($app, array $config) {
return new GlobalUserProvider($this->app->make('hash'), $config['model']);
});
Finally you should change your user provider driver to globalUserProvider in auth.php config file.
'providers' => [
'users' => [
'driver' => 'globalUserProvider',
'model' => App\Models\User::class
]
]
protected static function boot()
{
parent::boot();
if (\Auth::check()) {
static::addGlobalScope('active', function(Builder $builder) {
$builder->where('active', 1);
});
}
}
Please try this for login issue, You can activate after login using withoutGlobalScopes().
#Sasan's answer is working great in Laravel 5.3, but not working in 5.4 - createModel() is expecting a Model but gets a Builder object, so when EloquentUserProvider calls $model->getAuthIdentifierName() an exception is thrown:
BadMethodCallException: Call to undefined method Illuminate\Database\Query\Builder::getAuthIdentifierName() in /var/www/vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php:2445
Instead, follow the same approach but override more functions so that the right object is returned from createModel().
getQuery() returns the builder without the global scope, which is used by the other two functions.
class GlobalUserProvider extends EloquentUserProvider
{
/**
* Get query builder for the model
*
* #return \Illuminate\Database\Eloquent\Builder
*/
private function getQuery()
{
$model = $this->createModel();
return $model->withoutGlobalScope('active');
}
/**
* Retrieve a user by their unique identifier.
*
* #param mixed $identifier
* #return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveById($identifier)
{
$model = $this->createModel();
return $this->getQuery()
->where($model->getAuthIdentifierName(), $identifier)
->first();
}
/**
* Retrieve a user by their unique identifier and "remember me" token.
*
* #param mixed $identifier
* #param string $token
* #return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveByToken($identifier, $token)
{
$model = $this->createModel();
return $this->getQuery()
->where($model->getAuthIdentifierName(), $identifier)
->where($model->getRememberTokenName(), $token)
->first();
}
}
Sasan Farrokh has a right answer. The only thing not to rewrite createModel but newModelQuery and this will work
protected function newModelQuery($model = null)
{
$modelQuery = parent::newModelQuery();
return $modelQuery->withoutGlobalScope('active');
}
Extend the AuthController with the code you used in your OP. That should work.
public function postLogin(Request $request)
{
$user = User::withoutGlobalScope('active')
->where('username', $request->username)
->first();
if($user != null){
if (Hash::check($request->password, $user->password)){
$user->active = 1;
$user->save();
}
}
return $this->login($request);
}
I resolved it by creating the new package.
mpyw/scoped-auth: Apply specific scope for user authentication.
Run composer require mpyw/scoped-auth and modify your User model like this:
<?php
namespace App;
use Illuminate\Auth\Authenticatable;
use Illuminate\Contracts\Auth\Authenticatable as UserContract;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Mpyw\ScopedAuth\AuthScopable;
class User extends Model implements UserContract, AuthScopable
{
use Authenticatable;
public function scopeForAuthentication(Builder $query): Builder
{
return $query->withoutGlobalScope('active');
}
}
You can also easily pick Illuminate\Auth\Events\Login to activate User on your Listener.
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Event;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
class EventServiceProvider extends ServiceProvider
{
/**
* The event listener mappings for the application.
*
* #var array
*/
protected $listen = [
\Illuminate\Auth\Events\Login::class => [
\App\Listeners\ActivateUser::class,
],
];
/**
* Register any events for your application.
*
* #return void
*/
public function boot()
{
parent::boot();
//
}
}
 
<?php
namespace App\Listeners;
use Illuminate\Auth\Events\Login;
class ActivateUser
{
/**
* Handle the event.
*
* #param Illuminate\Auth\Events\Login $event
* #return void
*/
public function handle(Login $event)
{
$event->user->fill('active', 1)->save();
}
}
 
I had to use
->withoutGlobalScopes() instead
in order for it to work

Resources