laravel custom validator messages translation from another location - laravel

How can I load translation from other location than resources/lang/*/validation.php to vendor/package/src/translation/*/validation.php?
I have created translation file on path vendor/package/src/translation/*/validation.php:
<?php
return [
'custom' => [
'search_text' => [
'string' => 'A nice message.',
'not_regex' => 'Regex failed.',
],
],
'attributes' => [
'search_text' => 'Search text',
],
];
I have booted my own Validator in service provider:
$this->app->validator->resolver( function( $translator, $data, $rules, $messages = array(), $customAttributes = array() ) {
return new MyValidator( $translator, $data, $rules, $messages, $customAttributes );
} );
and I have created ofc the validator class. But I have no idea how i can concate validator and translation from custom location to work. The output should be overloaded by the custom file if any intersection will appear in both files.
Thanks for help. :)

If your package contains translation files, you may use the loadTranslationsFrom method to inform Laravel how to load them, and should add the following to your service provider's boot method:
public function boot()
{
$this->loadTranslationsFrom(__DIR__.'/path/to/translations', 'name');
}
Package translations are referenced using the package::file.line syntax convention.
echo trans('name::file.line');

Related

Laravel FormRequest is modifying the input

I am having trouble using the Laravel Validator to validate some data. The validator is modifying properties of the input to null.
The data passed to be validated is a mix of array and objects (in this case, a model instance).
Just for clarification: I know how to use FormRequest in controllers, I am full aware that Laravel would inject the FormRequest in the methods, and FormRequest is primaly to be used to validate user data, etc, etc. The point is why the validator need to modify the data I sent to validation?
Here's an example that you can directly paste in a php artisan tinker session:
$rules = [
'users' => [
'required',
'array',
'min:1',
],
'users.*' => [
'required',
],
'users.*.name' => [
'required',
'string',
'max:255',
],
'users.*.age' => [
'required',
'integer',
],
'users.*.best_friend' => [
'required',
],
];
$data = [
'users' => [
(new \App\Models\User)->forceFill([
'name' => 'USER #1',
'age' => 30,
'best_friend' => (new \App\Models\User)->forceFill(['name' => 'User X'])
]),
],
];
echo 'BEFORE: ' . data_get($data, 'users.0.name'); // USER #1
$validator = Validator::make($data, $rules);
echo 'AFTER: ' . data_get($data, 'users.0.name'); // NULL
dd($data);
OK, the data PASSES. But the problem is that the validation modified the variable $data, setting null to the fields with these patterns: users.*.name, users.*.age and users.*.best_friend.
If I dare to validate any model attribute, it sets to null.
I debugged and I reached the source of the modification:
/vendor/laravel/framework/src/Illuminate/Validation/ValidationData.php:42:
/**
* Gather a copy of the attribute data filled with any missing attributes.
*
* #param string $attribute
* #param array $masterData
* #return array
*/
protected static function initializeAttributeOnData($attribute, $masterData)
{
$explicitPath = static::getLeadingExplicitAttributePath($attribute);
$data = static::extractDataFromPath($explicitPath, $masterData);
if (! str_contains($attribute, '*') || str_ends_with($attribute, '*')) {
return $data;
}
// here some debug info:
// $explicitPath="users"
// $attribute="users.*.name"
// $data=User
return data_set($data, $attribute, null, true);
}
I know data_set modifies by reference.
But I could not understand why the code modifies the data if there is data already there. Should not it check for data before setting to null?
The validator is making the validated properties of my model to be null. Why and how to fix?
Maybe a different approach? Maybe this could be considered an bug/improvement for the Illuminate lib?
Any help would be apreciated.
VERSIONS:
Laravel Framework 9.33.0
PHP 8.1.2
Laravel transforms the keys from your validation rules: name and age. But that didn't work as expected because users are objects. To solve that you need to call toArray() after forceFill
$data = [
'users' => [
(new \App\Models\User) -> forceFill([
'name' => 'USER #1',
'age' => 30,
'best_friend' => (new \App\Models\User) -> forceFill(['name' => 'User X'])
])->toArray(),
]
];
If you need validation for best_friend.name you need to call toArray() on that too. But without validation you will get the object as it is.

Understand a concept, is Service Provider the correct use case?

I have created a service provider that takes data from GITHUB API and stores it in a table in a database. Quite a simple thing but I'm wondering if is this how I'm should be using Service Providers?
The second question is about extending this, in reality I want to add more platforms and API's that do this (I currently have one other platform working) but I currently have it set up as a separate service provider. There's a lot of similarities, only some differences in what the API data returns - I feel like I should be abiding by DRY but think this could get complicated when more and more API's get added (def not KISS).
I really want to confirm this before I spend more time extending this platform so appreciate any advice!
<?php
namespace App\Providers;
use GuzzleHttp\Client;
use App\Projects;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Auth;
class GithubServiceProvider extends ServiceProvider
{
/**
* Register services.
*
* #return void
*/
public function register()
{
//
$this->app->singleton('github_client', function ($app) {
try {
$client = new Client();
$code = $_GET['code'] ?? false;
$res = $client->post('https://github.com/login/oauth/access_token', [
'headers' => [
'Accept' => 'application/json',
],
'query' => [
'client_id' => env('GITHUB_ID'),
'client_secret' => env('GITHUB_SECRET'),
'code'=>$code
]
]
);
} catch(\GuzzleHttp\Exception\RequestException $e) {
$response = $e->getResponse();
throw new \App\Exceptions\CustomException($response->getStatusCode());
}
$username = Auth::user()->username;
$user_id = Auth::user()->id;
if($res->getBody() && !isset(json_decode($res->getBody())->error)):
$access_token = json_decode($res->getBody())->access_token;
$projects = json_decode($client->get('https://api.github.com/user/repos', [
'headers' => [
'Authorization'=> 'token ' . $access_token
]
])->getBody());
$i = 0;
foreach($projects as $project):
$i++;
// dd($project->images);
Projects::updateOrCreate(
[
'platform' => 'github',
'user_id' => $user_id,
'project_id' => $project->id
],
[
'platform' => 'github',
'sorting' => $i,
'user_id' => $user_id,
'title' => $project->name,
'description' => strip_tags($project->description),
'images' => '',
'url' => $project->html_url,
'project_id' => $project->id
]
);
endforeach;
endif;
});
}
/**
* Bootstrap services.
*
* #return void
*/
public function boot()
{
//
}
}
A service provider adds services to the service (IoC) container.
In your case, it seems you want code that gets data from third-party providers in a consistent manner. So you probably want to define an interface for this, and then have individual classes that implement that interface (i.e. GithubProjectRepository, BitbucketProjectRepository, etc).
You’d probably use a service provider to register configured instances of GitHub, Bitbucket’s, etc SDKs. This would be instantiated and configured from your config files. You can then type-hint these SDKs in your class’s constructors and the container will give you configured instances instead of having to new-up all over your application.

How to fetch Rapidapi in laravel Controller?

I am trying to fetch API using unirest in my laravel application. I followed all the steps given in the docs but it's showing me an error. If there is a better alternative for unirest please let me know. Thanks in advance!
Here is my controller,
public function store(Request $request)
{
Love::create(
request()->validate([
'name_1' => ['required','max:255'],
'name_2' => ['required','max:255'],
],
[
'name_1.required' => 'You have to Enter Your name',
'name_2.required' => 'You have to Enter Your Crush name'
]));
$headers = array('Accept' => 'application/json');
$response = Unirest\Request::post("API_URL",
array(
"X-RapidAPI-Key" => "API_KEY"
)
);
dd($response);
return view("result");
}
Error
Class 'App\Http\Controllers\Unirest\Request' not found
You need to import the Unirest\Request class.
<?php
namespace Your\Namespace;
use Unirest\Request;
class YourClass{
...
If you don't import it, it will by default look for the class in the current namespace (in your case, App\Http\Controllers).

Using a php variable for validation in Laravel 4.2

I have this code that gets an object in a session and i put it in a certain variable. Then i wanted to use it in the laravel 4.2 'same' validation. So far here is the code i have
session_start();
$getsCapt = $_SESSION["captcha"];
$rules = array(
'st' => 'required',
'capt' => 'required|numeric|same:$getsCapt'
);
It is not doing anything. What i wanted to have is i'll compare the value i get from the session with the value i get from the textbox in my view named 'capt' but so far its not doing anything. any ideas to do this properly?
First of all, You are using same validator incorrectly.
same expects a form field name
Example:
same:field_name
Where, the given field must match the field under validation.
You could register and use a Custom Validation Rule
Validator::extend('captcha', function($attribute, $value, $parameters)
{
$captcha = \Session::get('captcha');
return $value == $captcha;
});
So later you can do:
//session_start(); - Please no need for this in Laravel
//$getsCapt = $_SESSION["captcha"]; - Also remove this not necessary
$rules = array(
'st' => 'required',
'capt' => 'required|numeric|captcha'
);
NB:
Use Session::put to save something to session e.g \Session::put('something');
There is also Session::get for retrieving value from session e.g \Session::get('something');
Please avoid using $_SESSION not Laravel way of doing things
[Edited] Where to Register Custom Validation Rule?
There are basically two ways you can register a custom validation rule in Laravel.
1. Resolving from a closure:
If you are resolving through closure you can add it to : app/start/global.php
Validator::extend('captcha', function($attribute, $value, $parameters)
{
$captcha = \Session::get('captcha');
return $value == $captcha;
});
2. Resolving from a class
This is the best and preferred way of extending custom validation rule as its more organised and easier to maintain.
i. Create your own validation class, CustomValidator.php, maybe in app/validation folder
<?php namespace App\Validation;
use Illuminate\Validation\Validator;
use Session;
class CustomValidator extends Validator{
public function validateCaptcha($attribute, $value, $parameters)
{
$captcha = Session::get('captcha');
return $value == $captcha;
}
}
NB: Notice the prefix validate used in the method name, validateCaptcha
ii. Create a Service Provider that will resolve custom validator extension in app/validation folder
<?php namespace App\Validation;
use Illuminate\Support\ServiceProvider;
class CustomValidationServiceProvider extends ServiceProvider {
public function register(){}
public function boot()
{
$this->app->validator->resolver(function($translator, $data, $rules, $messages){
return new CustomValidator($translator, $data, $rules, $messages);
});
}
}
iii. Then add CustomValidationServiceProvider under app/config/app.php providers array:
'providers' => array(
<!-- ... -->
'App\Validation\CustomValidationServiceProvider'
),
iv. And add the custom error message in app/lang/en/validation.php
return array(
...
"captcha" => "Invalid :attribute entered.",
...
)
Change single quotes to double quotes
$rules = array(
'st' => 'required',
'capt' => "required|numeric|same:$getsCapt"
);
Or simply concatenate the value
$rules = array(
'st' => 'required',
'capt' => 'required|numeric|same:' . $getsCapt
);

How to specify the default error message when extending the Validation class in Laravel 4

I use made use the extend function to extend and adding custom rules on the Validation Class of Laravel 4.
Validator::extend('foo', function($attribute, $value, $parameters)
{
return $value == 'foo';
});
When I validate the rule using the newly created custom extension, it returns validation.foo if the rule fails. Is there a way to define a generic/ default message when extending the validation class in Laravel 4?
The Laravel 4 docs specifically state you need to define an error message for your custom rules.
You have two options;
Option 1:
$messages = array(
'foo' => 'The :attribute field is foo.',
);
$validator = Validator::make($input, $rules, $messages);
Option 2:
Specify your custom messages in a language file instead of passing them directly to the Validator. To do so, add your messages to custom array in the app/lang/xx/validation.php language file:
'custom' => array(
'foo' => array(
'required' => 'We need to know your foo!',
),
),
In case someone is wondering about Laravel 5: just add your message to validation.php right under all the default messages. For example:
<?php
return [
// .. lots of Laravel code omitted for brevity ...
"timezone" => "The :attribute must be a valid zone.",
/* your custom global validation messages for your custom validator follow below */
"date_not_in_future" => "Date :attribute may not be in future.",
where date_not_in_future is your custom function validateDateNotInFuture.
Laravel will pick the message each time you use your rule for any field and you won't have to use custom array unless you want to override your global message for specific fields.
Full code to implement the validator follows.
Custom Validator (with a bonus gotcha comments for date_format and date_before localization):
<?php namespace App\Services\Validation;
use Illuminate\Validation\Validator as BaseValidator;
/**
* Class for your custom validation functions
*/
class Validator extends BaseValidator {
public function validateDateNotInFuture($attribute, $value, $parameters)
{
// you could also test if the string is a date at all
// and if it matches your app specific format
// calling $this->validateDateFormat validator with your app's format
// loaded from \Config::get, but be careful -
// Laravel has hard-coded checks for DateFormat rule
// to extract correct format from it if it exists,
// and then use for validateBefore. If you have some unusual format
// and date_format has not been applied to the field,
// then validateBefore will give unpredictable results.
// Your best bet then is to override protected function
// getDateFormat($attribute) to return your app specific format
$tomorrow = date('your app date format here', strtotime("tomorrow"));
$parameters[0] = $tomorrow;
return $this->validateBefore($attribute, $value, $parameters);
}
}
ValidatorServiceProvider file:
<?php namespace App\Providers;
namespace App\Providers;
use App\Services\Validation\Validator;
use Illuminate\Support\ServiceProvider;
class ValidatorServiceProvider extends ServiceProvider{
public function boot()
{
\Validator::resolver(function($translator, $data, $rules, $messages)
{
return new Validator($translator, $data, $rules, $messages);
});
}
public function register()
{
}
}
And then just add a line to config/app.php:
'App\Providers\RouteServiceProvider',
'App\Providers\ValidatorServiceProvider', // your custom validation
In addition to what TheShiftExchange has said, if you look in that validation.php language file you'll see all of the different rules that you can specify. So for instance, if your validator has entries like this:
class ArticleValidator extends Validator
{
public static $rules = [
'create' => [
'title' => ['required'],
'slug' => ['required', 'regex:([a-z\0-9\-]*)']
]
];
}
Then your custom validation rules may look like this:
'custom' => array(
'company_article_type_id' => array(
'required' => 'The slug field is really important',
'exists' => 'The slug already exists',
),
),
Notice how the 'required' and 'exists' keys in the custom validation rules match those in the validator above.

Resources