Laravel 5.5 json validation rule fails - json

I have the following JSON posted to my laravel 5.5 application
{
"code": "asdfsadf",
"amount": "1000"
}
my validation rules look like this:
$validator = Validator::make($request->all(), [
'amount' => 'required|numeric|json',
'code' => 'required|alpha_num|json',
]);
and the amount fields always pass the JSON validation but the code fields always when passing anything but int value. I could not find any solution on the web.

There is no json validation rule that validates json on laravel. You have to build your own custom validator for that.
Something like below:
$data = json_decode($request->all(), true);
$rules = [
'amount' => 'required|numberic',
'code' => 'required|alpha_num'
];
$validator = Validator::make($data, $rules);
if ($validator->passes()) {
// ok validation
} else {
//handle errors
dd($validator->errors()->all());
}

Related

Cannot generate HalResource for object of type ArrayObject

I've some problems to return a paginator object as HAL json collection. I'm using the latest versions of zend-expressive and zend-expressive-hal.
This is the setting from my ConfigProvider:
public function __invoke() : array
{
return [
'dependencies' => $this->getDependencies(),
MetadataMap::class => $this->getHalConfig(),
];
}
public function getHalConfig() : array
{
return [
[
'__class__' => RouteBasedCollectionMetadata::class,
'collection_class' => RoleCollection::class,
'collection_relation' => 'user_roles',
'route' => 'api.user.roles',
],
];
}
And these are my handler methods:
public function get(ServerRequestInterface $request) : ResponseInterface
{
// read some records from the database
$select = new Select();
$select->from(['r' => 'user_roles']);
$select->columns(['id', 'name']);
$paginator = new RoleCollection(new DbSelect($select, $this->dbAdapter));
$paginator->setItemCountPerPage(25);
$paginator->setCurrentPageNumber(1);
return $this->createResponse($request, $paginator);
}
private function createResponse(ServerRequestInterface $request, $instance) : ResponseInterface
{
return $this->responseFactory->createResponse(
$request,
$this->resourceGenerator->fromObject($instance, $request)
);
}
The RoleCollection class is only an inheritance of the Paginator:
class RoleCollection extends Paginator
{
}
The error message which I get is:
Cannot generate Zend\Expressive\Hal\HalResource for object of type ArrayObject; not in metadata map
I think you are missing the metadata for the Role object itself.
For example this is something similar for my posts object:
MetadataMap::class => [
[
'__class__' => RouteBasedCollectionMetadata::class,
'collection_class' => Posts::class,
'collection_relation' => 'posts',
'route' => 'api.posts',
],
[
'__class__' => RouteBasedResourceMetadata::class,
'resource_class' => Post::class,
'route' => 'api.posts.view',
'extractor' => ArraySerializable::class,
],
],
You have only described the collection and the resource class is missing for a single role.
I also see the resource generator tries to parse an ArrayObject. This should be wrapped in a Role object, which you can add to the MetadataMap.
Where it goes wrong in your code is this line:
$paginator = new RoleCollection(new DbSelect($select, $this->dbAdapter));
This adds the result of a query into the paginator, but the paginator does not know how to handle it. If I remember correctly, the DbSelect return a ResultSet. I'm guessing this is where the ArrayObject is coming from. What you probably need is to override that ResultSet and make sure it returns an array of Role objects. You might want to look into the dbselect adapter and the hydrating resultset.
Once you have the Role object in the paginator, you can describe it in the metadata.
[
'__class__' => RouteBasedResourceMetadata::class,
'resource_class' => UserRole::class,
'route' => 'api.roles',
'extractor' => ...,
],
I use doctrine myself with hal so zend-db is out of my scope. If you need more help, I suggest the zf forums.

Yii2 REST do sorting when response JSON

When the JSON format is response, the original sorting of the data is lost. When response in XML, the sort is saved. How can I preserve the original sorting with JSON?
My controller:
use yii\rest\ActiveController;
class DomainController extends ActiveController
{
...
public function behaviors()
{
$behaviors = parent::behaviors();
$behaviors['corsFilter' ] = [
'class' => \yii\filters\Cors::className(),
];
$behaviors['contentNegotiator'] = [
'class' => \yii\filters\ContentNegotiator::className(),
'formats' => [
'application/json' => \yii\web\Response::FORMAT_JSON,
],
];
return $behaviors;
}
And action in the controller:
public function actionIndex()
{
$domains = Domain::find()
->leftJoin('WEB_DOMAIN_PRIORITY', 'WEB_DOMAIN_PRIORITY.id = WEB_DOMAIN.priority_id')
->orderBy(['priority' => SORT_DESC])->all();
$test = [];
foreach ($domains as $domain) {
$test[$domain->id] = $domain->title;
}
//echo "<pre>"; print_r($test);die; < -- its ok. right sort
//return $test; < -- its wrong. sort is changed
}
And if i change in behavior this:
'application/json' => \yii\web\Response::FORMAT_JSON,
To:
'application/json' => \yii\web\Response::FORMAT_XML,
I have xml response with right sort.
Only JSON response sorting my array by array keys(ASC).
Here
$test[$domain->id] = $domain->title;
you are adding new array keys. This could change order based on rest/Serializer.
You could apply preserveKeys to serializer as here http://www.yiiframework.com/doc-2.0/yii-rest-serializer.html#$preserveKeys-detail
or don't change keys order.
#Fabrizio Caldarelli not exactly. In this moment response preparing JsonResponseFormatter and it use yii\helpers\Json::encode for format data.

Laravel: validate json object

It's the first time i am using validation in laravel. I am trying to apply validation rule on below json object. The json object name is payload and example is given below.
payload = {
"name": "jason123",
"email": "email#xyz.com",
"password": "password",
"gender": "male",
"age": 21,
"mobile_number": "0322 8075833",
"company_name": "xyz",
"verification_status": 0,
"image_url": "image.png",
"address": "main address",
"lattitude": 0,
"longitude": 0,
"message": "my message",
"profession_id": 1,
"designation_id": 1,
"skills": [
{
"id": 1,
"custom" : "new custom1"
}
]
}
And the validation code is like below, for testing purpose i am validating name as a digits. When i executed the below code, the above json object is approved and inserted into my database. Instead, it should give me an exception because i am passing name with alpha numeric value, am i doing something wrong:
public function store(Request $request)
{
$this->validate($request, [
'name' => 'digits',
'age' => 'digits',
]);
}
Please try this way
use Validator;
public function store(Request $request)
{
//$data = $request->all();
$data = json_decode($request->payload, true);
$rules = [
'name' => 'digits:8', //Must be a number and length of value is 8
'age' => 'digits:8'
];
$validator = Validator::make($data, $rules);
if ($validator->passes()) {
//TODO Handle your data
} else {
//TODO Handle your error
dd($validator->errors()->all());
}
}
digits:value
The field under validation must be numeric and must have an exact length of value.
I see some helpful answers here, just want to add - my preference is that controller functions only deal with valid requests. So I keep all validation in the request. Laravel injects the request into the controller function after validating all the rules within the request. With one small tweak (or better yet a trait) the standard FormRequest works great for validating json posts.
Client example.js
var data = {first: "Joe", last: "Dohn"};
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("POST",'//laravel.test/api/endpoint');
xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xmlhttp.send(JSON.stringify(data));
project/routes/api.php
Route::any('endpoint', function (\App\Http\Requests\MyJsonRequest $request){
dd($request->all());
});
app/Http/Requests/MyJsonRequest.php (as generated by php artisan make:request MyJsonRequest)
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class MyJsonRequest extends FormRequest{
public function authorize(){
return true;//you'll want to secure this
}
public function rules(){
return [
'first' => 'required',
'last' => 'required|max:69',
];
}
//All normal laravel request/validation stuff until here
//We want the JSON...
//so we overload one critical function with SOMETHING LIKE this
public function all($keys = null){
if(empty($keys)){
return parent::json()->all();
}
return collect(parent::json()->all())->only($keys)->toArray();
}
}
Your payload should be payload: { then you can do
$this->validate($request->payload, [
'name' => 'required|digits:5',
'age' => 'required|digits:5',
]);
or if you are not sending the payload key you can just use $request->all()
$request->merge([
'meta_data' => !is_null($request->meta_data) ? json_encode($request->meta_data) : null
]);
validator = Validator::make($request->all(), [
'meta_data' => 'nullable|json'
]);
Use the Validator factory class instead using validate method derived from controller's trait. It accepts array for the payload, so you need to decode it first
\Validator::make(json_decode($request->payload, true), [
'name' => 'digits',
'age' => 'digits',
]);
Following the example of #tarek-adam, in Laravel 9 it would be:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class MyJsonRequest extends FormRequest{
public function authorize(){
return true;//you'll want to secure this
}
public function rules(){
return [
'first' => 'required',
'last' => 'required|max:69',
];
}
//All normal laravel request/validation stuff until here
//We want the JSON...
//so we overload one critical function with SOMETHING LIKE this
public function validationData()
{
if(empty($this->all())){
$res = [
'success' => false,
'message' => 'Check your request',
];
throw new HttpResponseException(
response()->json($res, 422)
);
}
return $this->all();
}
}

How to create associative array in Yii2 and convert to JSON?

I am using a calendar in my project and I want to pass data from my Event model to view file in JSON format. I tried following but it didn't work and am not able to display the data properly
$events = Event::find()->where(1)->all();
$data = [];
foreach ($events AS $model){
//Testing
$data['title'] = $time->title;
$data['date'] = $model->start_date;
$data['description'] = $time->description;
}
\Yii::$app->response->format = 'json';
echo \yii\helpers\Json::encode($data);
But it only returns one model in that $data array, the final data should be in following format:
[
{"date": "2013-03-19 17:30:00", "type": "meeting", "title": "Test Last Year" },
{ "date": "2013-03-23 17:30:00", "type": "meeting", "title": "Test Next Year" }
]
When you write this:
\Yii::$app->response->format = 'json';
before rendering data, there is no need to do any additional manipulations for converting array to JSON.
You just need to return (not echo) an array:
return $data;
An array will be automatically transformed to JSON.
Also it's better to use yii\web\Response::FORMAT_JSON constant instead of hardcoded string.
Another way of handling that will be using ContentNegotiator filter which has more options, allows setting of multiple actions, etc. Example for controller:
use yii\web\Response;
...
/**
* #inheritdoc
*/
public function behaviors()
{
return [
[
'class' => 'yii\filters\ContentNegotiator',
'only' => ['view', 'index'], // in a controller
// if in a module, use the following IDs for user actions
// 'only' => ['user/view', 'user/index']
'formats' => [
'application/json' => Response::FORMAT_JSON,
],
],
];
}
It can also be configured for whole application.
Update: If you are using it outside of controller, don't set response format. Using Json helper with encode() method should be enough. But there is also one error in your code, you should create new array element like this:
$data = [];
foreach ($events as $model) {
$data[] = [
'title' => $time->title,
'date' => $model->start_date,
'description' => $time->description,
];
}
You can try like this:
$events = Event::find()->select('title,date,description')->where(1)->all()
yii::$app->response->format = yii\web\Response::FORMAT_JSON; // Change response format on the fly
return $events; // return events it will automatically be converted in JSON because of the response format.
Btw you are overwriting $data variable in foreach loop you should do:
$data = [];
foreach ($events AS $model){
//Make a multidimensional array
$data[] = ['time' => $time->title,'date' => $model->start_date,'description' => $time->description];
}
echo \yii\helpers\Json::encode($data);

yii2 How to transfer post data from one view to two?

I am trying to create make a two-step form in yii2.
This is my SiteController.php
public function actionCreateCharacter()
{
$model = new Character();
var_dump(Yii::$app->request->post('Character'));
if ($model->load(Yii::$app->request->post())) {
$attributes=['imie','nazwisko','plec','wyznanie_id'];
if ($step1 = $model->validate($attributes)) {
//var_dump($step1);
// form inputs are valid, do something here
//var_dump(Yii::$app->request->post('Character');
return $this->render('createCharacterStep2', [
'model' => $model,
]);;
}
else {
// validation failed: $errors is an array containing error messages
$errors = $model->errors;
}
}
return $this->render('createCharacter', [
'model' => $model,
]);
}
public function actionCreateCharacterStep2()
{
$model2 = new Character();
var_dump($model);
if ($model2->load(Yii::$app->request->post())) {
var_dump(Yii::$app->request->post('Character'));
if ($model2->validate()) {
// form inputs are valid, do something here
return;
}
}
/*return $this->render('createCharacter2', [
'model' => $model,
]);*/
}
... and this is my Character.php (model + attributeLabels and tableName)
public function rules()
{
return [
[['user_id', 'imie', 'nazwisko', 'plec', 'wyznanie_id', 'avatar_src', 'avatar_svg'], 'required'],
[['user_id', 'wyznanie_id'], 'integer'],
[['avatar_svg'], 'string'],
[['imie'], 'string', 'max' => 15],
[['nazwisko'], 'string', 'max' => 20],
[['plec'], 'string', 'max' => 1],
[['avatar_src'], 'string', 'max' => 30]
];
}
I have access to $_POST by Yii::$app->request->post() in createCharacter - I get imie, nazwisko, plec and wyznanie_id.
But when I send the form in step 2 I have only post data from step 2.
How can I set the post data from step1+step2?
Sorry for my english and thanks in advance.
While rendering step2 from step1 action, you can always pass additional data to controller's action. So I added "STEPONEPOSTS" post variable which contains all posts of step 1. Check below.
public function actionCreateCharacter()
{
$model = new Character();
var_dump(Yii::$app->request->post('Character'));
if ($model->load(Yii::$app->request->post())) {
$attributes=['imie','nazwisko','plec','wyznanie_id'];
if ($step1 = $model->validate($attributes)) {
//var_dump($step1);
// form inputs are valid, do something here
//var_dump(Yii::$app->request->post('Character');
return $this->render('createCharacterStep2', [
'model' => $model,
'STEPONEPOSTS' => Yii::$app->request->post(),
]);;
}
else {
// validation failed: $errors is an array containing error messages
$errors = $model->errors;
}
}
return $this->render('createCharacter', [
'model' => $model,
]);
}
And now in step 2 view, you can get step 1 posts variable as
$STEPONEPOSTS
There is another way , if you have to table for step 1 and step 2. then save the data of step 1 first then step2 data. if you are not using two tables then you can create two form each form for each step and also create scenarios for each step according to the fields.I think this may help . You can use session also as per discussion in comments or you can use the extension array wizard but array wizard extension is not well documented , so i suggest you try my way i will help you.